0x00 CheckYourKey

86cb77d31d9e8ce6550422925af91bc.png

一道安卓逆向

先扔jeb里面看看,找到MainActivity

d463bc0c4c99ccf155176a2303f9aea.png

简单分析一下上面的java代码

可以看到关键判断函数为ooxx

f58ae7d004f6e2be45fa50038571898.png

但ooxx函数为空

d78c0e2c5eb883b9c4041cdaee875ad.png

再分析一下,可以看到static代码块中多了一条语句:

ff945557f0982d3970d6879a9d605ad.png

1
System.loadLibrary("CheckYourKey");

在Android开发中,原生代码一般用C\C++编写,然后编译为一个动态链接库,即 .so文件

loadLibrary的作用就是加载这个动态链接库,使后面的代码能成功调用到对应的原生函数。

静态代码块的执行时机非常早、比构造函数、onCreate都要早,在类加载的时候就被调用。库加载并非一定要在当前类、static块中!!!

加载库还有其他方法,例如使用 System.load(String)方法,其传入链接库的具体路径;甚至有的是在Native层中使用dlopen、mmap等方式来进行加载,相当于自己实现一个loadlibrary。其最终目的都是为了将代码加载入内存中

Android编译后的Apk文件 其实质上是一个Zip压缩包 ,打开后在其lib 目录中可以看到被 loadLibrary 加载的库。

如本题,将附件的文件后缀改为zip并打开

d5b7a5b683c0bf2407820f5becfd94b.png

查看lib文件夹找到关键.so文件

d5b7a5b683c0bf2407820f5becfd94b.png

将.so文件拖入IDA中进行静态分析

  1. 在Android系统中,对链接库进行加载的程序叫做linker,文件路径为/system/bin/linker。linker加载so的时候会依次调用其init_array中的函数来执行开发者的初始化代码,可在IDA中按shift+f7打开Segmentation视图,若有.init_array项,那么其中的函数就会被依次执行,这些函数都没有参数。

682d763cd77dbfd99a24b8e3183932a.png

  1. linker中加载so的函数叫做dlopen,而loadLibrary跟load其实也是基于dlopen,但其添加了一个回调就是JNI_OnLoad,只要在代码中定义一个名为JNI_OnLoad的函数,dlopen完成之后就会将其调用。JNI_OnLoad的定义如下:

    1
    jint JNI_OnLoad(JavaVM* vm, void* reserved)

IDA查看默认JNI函数入口 Java_com_ctf_CheckYourKey_MainActivity_ooxx

c0f92cdb36d40b4b86eeb9f86b7cee7.png

反编译查看伪代码找到关键加密代码段

94a94a9a430e42630f7c23812e3f550.png

发现进行了两次加密 分别是sub_F7DC函数base58加密和sub_13788函数base64加密

但是解密出来不对

因此我们在IDA中查找JNI_OnLoad函数并进行分析

12a70e00528b52c356ea7665bc25174.png

1b708082f0d055f7c334ea754d53a9c.png

简单分析可知 JNI_onLoad函数将sub_8965注册为 ooxx

查看sub_8965定位到关键代码段

24495cb4ceb6e78355e76fccfbaf484.png

可以看出sub_8965函数将输入的字符串先后进行了三次加密

1.sub_FB40:

标准 AES-128-ECB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
__int64 __fastcall sub_FB40(__int64 a1, __int64 a2, __int64 a3)
{
__int64 v3; // x0

sub_FBA8(a3, a1);
qword_412C0 = a3;
qword_412C8 = a2;
v3 = sub_FC20();
return sub_FEC8(v3);
}


__int64 __fastcall sub_FBA8(__int64 result, __int64 a2)
{
unsigned __int8 i; // [xsp+Fh] [xbp-11h]

for ( i = 0; i < 0x10u; ++i )
*(_BYTE *)(result + i) = *(_BYTE *)(a2 + i);
return result;
}


void sub_FC20()
{
unsigned __int8 v0; // [xsp+18h] [xbp-18h]
unsigned int j; // [xsp+1Ch] [xbp-14h]
unsigned int i; // [xsp+20h] [xbp-10h]
unsigned __int8 v3; // [xsp+24h] [xbp-Ch]
unsigned __int8 v4; // [xsp+25h] [xbp-Bh]
unsigned __int8 v5; // [xsp+26h] [xbp-Ah]
unsigned __int8 v6; // [xsp+27h] [xbp-9h]
__int64 v7; // [xsp+28h] [xbp-8h]

v7 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
for ( i = 0; i <= 3; ++i )
{
byte_412D0[4 * i] = *(_BYTE *)(qword_412C8 + 4 * i);
byte_412D0[(4 * i) | 1] = *(_BYTE *)(qword_412C8 + ((4 * i) | 1));
byte_412D0[(4 * i) | 2] = *(_BYTE *)(qword_412C8 + ((4 * i) | 2));
byte_412D0[(4 * i) | 3] = *(_BYTE *)(qword_412C8 + ((4 * i) | 3));
}
while ( i <= 0x2B )
{
for ( j = 0; j <= 3; ++j )
*(&v3 + j) = byte_412D0[4 * i - 4 + j];
if ( (i & 3) == 0 )
{
v0 = v3;
v3 = v4;
v4 = v5;
v5 = v6;
v6 = v0;
v3 = sub_1005C(v3);
v4 = sub_1005C(v4);
v5 = sub_1005C(v5);
v6 = sub_1005C(v6);
v3 ^= byte_33C10[i >> 2];
}
byte_412D0[4 * i] = byte_412D0[4 * i - 16] ^ v3;
byte_412D0[(4 * i) | 1] = byte_412D0[(4 * i - 16) | 1] ^ v4;
byte_412D0[(4 * i) | 2] = byte_412D0[(4 * i - 16) | 2] ^ v5;
byte_412D0[(4 * i) | 3] = byte_412D0[(4 * i - 16) | 3] ^ v6;
++i;
}
_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
}

2.sub_F7DC:

标准base58

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
unsigned __int8 *__fastcall sub_F7DC(__int64 a1, unsigned __int64 a2)
{
unsigned __int64 v2; // x11
__int64 v3; // x10
bool v5; // [xsp+18h] [xbp-78h]
unsigned __int64 v6; // [xsp+30h] [xbp-60h]
div_t v7; // [xsp+38h] [xbp-58h]
unsigned __int64 v8; // [xsp+40h] [xbp-50h]
unsigned __int64 v9; // [xsp+48h] [xbp-48h]
int v10; // [xsp+54h] [xbp-3Ch]
unsigned __int64 v11; // [xsp+58h] [xbp-38h]
unsigned __int64 j; // [xsp+60h] [xbp-30h]
unsigned __int64 v13; // [xsp+60h] [xbp-30h]
unsigned __int8 *v14; // [xsp+68h] [xbp-28h]
unsigned __int64 i; // [xsp+78h] [xbp-18h]

for ( i = 0LL; !*(_BYTE *)(a1 + i); ++i )
;
v14 = (unsigned __int8 *)malloc((unsigned int)(138 * a2 / 0x64) + 2);
memset(v14, 0, 138 * a2 / 0x64 + 2);
for ( j = 0LL; j < i; ++j )
{
v2 = j;
v14[v2] = *(_BYTE *)off_41008;
}
v11 = 0LL;
while ( j < a2 )
{
v3 = j++;
v10 = *(unsigned __int8 *)(a1 + v3);
v9 = 0LL;
v8 = 138 * a2 / 0x64 + 1;
while ( 1 )
{
if ( v10 || (v5 = 0, v9 < v11) )
v5 = v8 != 0;
if ( !v5 )
break;
v7 = div(v10 + (v14[--v8] << 8), 58);
v14[v8] = v7.rem;
v10 = LOBYTE(v7.quot);
++v9;
}
v11 = v9;
}
v13 = i;
v6 = 138 * a2 / 0x64 + 1 - v11 - i;
while ( v13 < i + v11 )
{
v14[v13] = *((_BYTE *)off_41008 + v14[v13 + v6]);
++v13;
}
if ( v6 )
v14[v13] = 0;
return v14;
}

3.sun_13788:

变表base64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
__int64 __fastcall sub_13788(__int64 a1, unsigned int a2, __int64 a3)
{
__int64 v3; // x13
__int64 v4; // x14
__int64 v5; // x16
__int64 v6; // x16
__int64 v7; // x13
__int64 v8; // x13
__int64 v9; // x13
__int64 v10; // x13
__int64 v11; // x13
unsigned __int8 v13; // [xsp+2Ah] [xbp-26h]
unsigned __int8 v14; // [xsp+2Bh] [xbp-25h]
unsigned int v15; // [xsp+2Ch] [xbp-24h]
unsigned int v16; // [xsp+2Ch] [xbp-24h]
unsigned int v17; // [xsp+2Ch] [xbp-24h]
unsigned int v18; // [xsp+2Ch] [xbp-24h]
unsigned int i; // [xsp+30h] [xbp-20h]
int v20; // [xsp+34h] [xbp-1Ch]

v20 = 0;
v13 = 0;
v15 = 0;
for ( i = 0; i < a2; ++i )
{
v14 = *(_BYTE *)(a1 + i);
if ( v20 )
{
if ( v20 == 1 )
{
v20 = 2;
v4 = v15++;
*(_BYTE *)(a3 + v4) = byte_33F0F[(16 * (v13 & 3)) | ((int)v14 >> 4) & 0xF];
}
else
{
v20 = 0;
v5 = v15;
v16 = v15 + 1;
*(_BYTE *)(a3 + v5) = byte_33F0F[(4 * (v13 & 0xF)) | ((int)v14 >> 6) & 3];
v6 = v16;
v15 = v16 + 1;
*(_BYTE *)(a3 + v6) = byte_33F0F[v14 & 0x3F];
}
}
else
{
v20 = 1;
v3 = v15++;
*(_BYTE *)(a3 + v3) = byte_33F0F[((int)v14 >> 2) & 0x3F];
}
v13 = v14;
}
if ( v20 == 1 )
{
v7 = v15;
v17 = v15 + 1;
*(_BYTE *)(a3 + v7) = byte_33F0F[16 * (v13 & 3)];
v8 = v17++;
*(_BYTE *)(a3 + v8) = 61;
v9 = v17;
v15 = v17 + 1;
*(_BYTE *)(a3 + v9) = 61;
}
else if ( v20 == 2 )
{
v10 = v15;
v18 = v15 + 1;
*(_BYTE *)(a3 + v10) = byte_33F0F[4 * (v13 & 0xF)];
v11 = v18;
v15 = v18 + 1;
*(_BYTE *)(a3 + v11) = 61;
}
*(_BYTE *)(a3 + v15) = 0;
return v15;
}

0d299fca4a55a37c2313529532365bf.png

最后按顺序逆回去就可以得到flag

68747470733a2f2f626c75336d6f6f6e2e6f73732d636e2d68616e677a686f752e616c6979756e63732e636f6d2f696d672f3230323231323133303832303337312e706e67.png

0x01 huowang

57592db664e1a7f7f7825ce2393f9a1.png

迷宫题 要求最短路径

常规查壳,64位无壳elf文件

2c15da48d67af4a09df4f2594c662a9.png

参考资料

checkyourkey

Write-up/RCTF.md at main · b3f0re-team/Write-up · GitHub

[原创]Android逆向新手答疑解惑篇——JNI与动态注册-Android安全-看雪论坛-安全社区|安全招聘|bbs.pediy.com