吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5074|回复: 4
收起左侧

[Android CTF] RoarCTF Reverse time writeup

[复制链接]
SuperGate 发表于 2019-11-19 18:08

RoarCTF 2019 Reverse - time

题目下载链接:https://pan.baidu.com/s/1qmOWR08PclxSNXNwSwvxpA
提取码:mi03


文件是Android backup文件,用abe.jar进行解包,得到一个apk和一个db.db数据库文件。

jadx打开apk分析具体流程:

MainActivity.OnCreate

McAB1s.md.png
发现会初始化一个变量passwd

MainActivity.dbIsExists

McAatg.md.png
查询db.db文件是否存在

MainActivity类中的其他重要方法

McA0pj.md.png
发现加载了一个本地库文件,并且opendb函数中调用了DatabaseHelper类中的方法。

DatabaseHelper

McAdhQ.md.png


对上面的函数流程进行大概分析我们可以知道,这个apk负责加密一个数据库并且不断地写+1进去,并且在题目中的源文件中我们也刚好解压出了一个数据库文件,所以接下来我们要做的就是对加密算法进行逆向,然后反推出原本的数据库文件。

第一层加密

使用sqlcipher 加密数据库。上面的getWritableDatabase方法即是对数据库文件进行加密,并且将传入的参数passwd当作密钥。这个加密方式由于是官方加密,所以我们可以在网上找到相应的工具对其进行解密,所以只需要找到passwd的值即可。

第二层加密

so库里面自写的一个加密函数:

int sub_940()
{
  FILE *v0; // eax
  FILE *v1; // esi
  int v2; // edi
  unsigned int v3; // esi
  _BYTE *v4; // eax
  int v5; // edx
  int v6; // ecx
  int v7; // ST34_4
  unsigned int v8; // edi
  int v9; // ebx
  FILE *v10; // esi
  void *v12; // [esp+8h] [ebp-134h]
  size_t v13; // [esp+Ch] [ebp-130h]
  FILE *v14; // [esp+14h] [ebp-128h]
  char dest[275]; // [esp+29h] [ebp-113h]

  v0 = fopen("/data/data/ctf.wm.timedatabase/databases/db.db", "rb");
  if ( v0 )
  {
    v1 = v0;
    fseek(v0, 0, 2);
    v2 = ftell(v1);
    fseek(v1, 0, 0);
    v14 = v1;
    v12 = malloc(v2);
    fread(v12, 1u, v2, v1);
    memcpy(dest, &unk_3004, 0xFFu);
    v13 = v2;
    if ( v2 )
    {
      v3 = 0;
      v4 = v12;
      v5 = v2;
      v6 = 0;
      do
      {
        v7 = v5;
        v8 = v3
           + 1
           - 255
           * (((unsigned int)(((unsigned __int64)(-2139062143LL * (signed int)(v3 + 1)) >> 32) + v3 + 1) >> 31)
            + ((signed int)(((unsigned __int64)(-2139062143LL * (signed int)(v3 + 1)) >> 32) + v3 + 1) >> 7));
        v9 = (unsigned __int8)dest[v8];
        v6 = (v9 + v6) % 255;
        dest[v8] = dest[v6];
        dest[v6] = v9;
        *v4++ ^= dest[(v9 + (unsigned int)(unsigned __int8)dest[v8]) % 0xFF];
        --v5;
        v3 = v3
           + 1
           - 255
           * (((unsigned int)(((unsigned __int64)(-2139062143LL * (signed int)(v3 + 1)) >> 32) + v3 + 1) >> 31)
            + ((signed int)(((unsigned __int64)(-2139062143LL * (signed int)(v3 + 1)) >> 32) + v3 + 1) >> 7));
      }
      while ( v7 != 1 );
    }
    fclose(v14);
    v10 = fopen("/data/data/ctf.wm.timedatabase/databases/db.db", "wb");
    fwrite(v12, 1u, v13, v10);
    fclose(v10);
  }
  return _stack_chk_guard;
}

程序流程看起来虽然很复杂,但是容易发现加密方式实际上就是对明文的每一位进行异或操作,并且密钥的顺序虽然会改变,但是改变的顺序也是固定的,所以密文用相同的算法进行计算得到的结果就是明文。

因此,很快写出第二层的解密脚本:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<cmath>
#include<map>
#define LL long long
using namespace std;
const int inf=0x3f3f3f3f;
unsigned char key[]={
    0xAF,0x75,0x38,0xFA,0xEC,0xBB,0x1D,0x2F,0x7A,0x53,0x70,0x12,
0x9E,0x23,0x2E,0x6E,0x4D,0x46,0xBA,0x2B,0x15,0xD2,0xA3,0x35,0x32,0xFC,0x8D,0x8C,
0x30,0xAA,0xDB,0x47,0x4B,0xFD,0x11,0xA0,0xA1,0x6F,0x17,0x86,0x02,0xBD,0x7F,0x7B,
0x07,0xF6,0x0E,0xCF,0x26,0x88,0x27,0xC7,0xDD,0x7E,0x5F,0xD7,0x42,0xCE,0x49,0x43,
0x28,0x00,0x61,0xFE,0xE3,0xD9,0x94,0x1B,0x9B,0x21,0x2A,0x69,0x9A,0x0C,0xDE,0x31,
0x82,0x48,0xD0,0x77,0xC3,0x2C,0x3F,0x24,0x73,0xBC,0xF9,0x22,0x3E,0x51,0x09,0x96,
0xD6,0xE8,0x60,0x57,0x39,0x0B,0x04,0x95,0x14,0x84,0x5A,0xCC,0xEE,0xEA,0x1C,0x6D,
0x7D,0x40,0x1E,0x3A,0x2D,0xAD,0x55,0x01,0x63,0x91,0x05,0xA7,0xA4,0x29,0x92,0x8B,
0xC0,0xA8,0x58,0x8F,0x4A,0x6C,0x5D,0xB8,0xCB,0xB9,0x1A,0x98,0x81,0xE5,0xE4,0x0A,
0x18,0xAC,0x3C,0x08,0x20,0x45,0x67,0x89,0x72,0x4C,0x8A,0x64,0xFB,0x19,0xB3,0xD5,
0x4E,0xA6,0x13,0x9F,0x52,0x59,0xBF,0xA2,0xC1,0xC8,0x66,0x34,0xB5,0x06,0x71,0x62,
0x0D,0x9D,0xF5,0x99,0xE9,0xB2,0xBE,0x90,0x79,0x74,0x6A,0x9C,0xB1,0x36,0xF7,0x7C,
0xF4,0xE7,0xD8,0x44,0x78,0x41,0xDA,0xE1,0xEB,0xF8,0xD4,0x3B,0xC6,0xE2,0xAE,0xD1,
0x0F,0xA9,0xA5,0xE6,0xD3,0x50,0xF1,0xB6,0xDF,0xC5,0xB0,0x3D,0xC9,0x8E,0xF0,0x65,
0x54,0xCD,0xC4,0x97,0x1F,0xEF,0xF2,0xAB,0x25,0x10,0x93,0x80,0x6B,0x37,0xE0,0xB4,
0x5B,0x03,0x87,0x5C,0x76,0x83,0xCA,0x85,0xED,0x56,0xDC,0x4F,0x16,0x5E,0xB7,0xF3,
0xC2,0x68,0x33
};
int main(){
    FILE *v0; // eax
  FILE *v1; // esi
  int v2; // edi
  unsigned int v3; // esi
  char *v4; // eax
  int v5; // edx
  int v6; // ecx
  int v7; // ST34_4
  unsigned int v8; // edi
  int v9; // ebx
  FILE *v10; // esi
  void *v12; // [esp+8h] [ebp-134h]
  size_t v13; // [esp+Ch] [ebp-130h]
  FILE *v14; // [esp+14h] [ebp-128h]
  char dest[275]; // [esp+29h] [ebp-113h]

  v0 = fopen("C:\\Users\\Dell\\Desktop\\db.db", "rb");
  if ( v0 )
  {
    v1 = v0;
    fseek(v0, 0, 2);
    v2 = ftell(v1);
    fseek(v1, 0, 0);
    v14 = v1;
    v12 = malloc(v2);
    fread(v12, 1u, v2, v1);
    memcpy(dest, key, 0xFFu);
    v13 = v2;
    if ( v2 )
    {
      v3 = 0;
      v4 = (char *)v12;
      v5 = v2;
      v6 = 0;
      do
      {
        v7 = v5;
        v8 = v3
           + 1
           - 255
           * (((unsigned int)((0xFFFFFFFF80808081LL * (signed int)(v3 + 1) >> 32) + v3 + 1) >> 31)
            + ((signed int)((0xFFFFFFFF80808081LL * (signed int)(v3 + 1) >> 32) + v3 + 1) >> 7));
        v9 = (unsigned __int8)dest[v8];
        v6 = (v9 + v6) % 255;
        dest[v8] = dest[v6];
        dest[v6] = v9;
        *v4++ ^= dest[(v9 + (unsigned int)(unsigned __int8)dest[v8]) % 0xFF];
        --v5;
        v3 = v3
           + 1
           - 255
           * (((unsigned int)((0xFFFFFFFF80808081LL * (signed int)(v3 + 1) >> 32) + v3 + 1) >> 31)
            + ((signed int)((0xFFFFFFFF80808081LL * (signed int)(v3 + 1) >> 32) + v3 + 1) >> 7));
      }
      while ( v7 != 1 );
    }
    fclose(v14);
    v10 = fopen("C:\\Users\\Dell\\Desktop\\decode.txt","wb");
    fwrite(v12, 1u, v13, v10);
    fclose(v10);
  }
}

接下来要做的就是找到第一层加密的密钥passwd。
题目更新了提示,且标识重要,提示是长者的生日,原程序有个getcurrentTime的函数和getrandom的函数,怀疑getcurrenttime的值应该是电脑时间为长者生日时得到的。

passwd = String.valueOf(new Random(System.currentTimeMillis() / 86400000).nextInt())

currentTimeMillis方法计算电脑当前时间到1970.1.1 00:00的毫秒数,而除以86400000则刚好精确到日期。
所以时间精确到天数之后,计算长者出生日期和1970.1.1的天数差(有符号),最后当作随机数种子生成password。

出key脚本

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.TimeZone;

public class ReTime {
    public static void main(String args[]){
        String passwd = String.valueOf(new Random(-15842).nextInt());
        System.out.println(passwd);
    }
}

Key:

hex:0x84A5FF23
dec:-2069496029
unsigned dec :2225471267

第一层解密:
首先使用sqlcipher-shell64.exe打开上述经过第二层解密的文件decode.db,输入以下命令:

    PRAGMA key = '-2069496029';
    ATTACH DATABASE 'plaintext.db' AS plaintext KEY '';
    SELECT sqlcipher_export('plaintext');
    DETACH DATABASE plaintext;

即得到解密后的文件plaintext


拿到这个 plaintext 之后,用数据库软件打开发现并没有任何有价值的信息,然后用二进制方式打开,在下方发现了flag
MceB5T.png



免费评分

参与人数 1威望 +3 吾爱币 +12 热心值 +1 收起 理由
qtfreet00 + 3 + 12 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

chenlongjs 发表于 2019-11-20 14:28
牛逼啊!我的兄弟!
你上当了 发表于 2019-11-20 15:12
qq1534355592 发表于 2019-11-21 10:34
寻求大佬   破解一款APK  有偿   1214303874  @
hesqiang 发表于 2020-12-23 09:44
感谢 ,吾爱破解论坛因你更精彩!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-28 11:39

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表