首页
社区
课程
招聘
KCTF2021[秋季赛][第八题][群狼环伺]wp
2021-12-4 20:22 17172

KCTF2021[秋季赛][第八题][群狼环伺]wp

ccfer 活跃值
16
2021-12-4 20:22
17172

这是一道android题目
dex里没什么看点,直接到libcrackme.so里找到Java_www_vprotect_cn_crackme_GoodLuck
传了3个参数:(name, sn, time)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.text:00001860                 EXPORT Java_www_vprotect_cn_crackme_GoodLuck
.text:00001860 Java_www_vprotect_cn_crackme_GoodLuck   ; DATA XREF: LOAD:00000188↑o
.text:00001860                 PUSH            {LR}
.text:00001862                 BL              sub_87BE
.text:00001866                 ASRS            R5, R1, #0x1C
.text:00001868                 LDR             R3, [R4,R4]
.text:0000186A                 STRB            R1, [R1,#0xC]
.text:0000186C                 ORNS.W          R8, R0, #0x8800
.text:00001870                 DCB 0x50 ; P
.text:00001871                 DCB 0xB7
.text:00001872                 DCB    6
.text:00001873                 DCB 0x9A
.text:00001874                 BCC             loc_17B0
.text:00001876                 STRB            R5, [R1,#0x14]
.text:00001878                 LDRH            R2, [R5,R1]

看起来不太正常,似乎是有混淆,静态不太好分析,想动态也没有手机
先静态看着玩玩,发现JNI_OnLoad前面有几个函数没有混淆
先看到这个:

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
int __fastcall sub_12A0()
{
  ...
.text:000012D0                 LDR.W           R5, =(unk_5004 - 0x12DE)
.text:000012D4                 ADD             R6, SP, #0x558+var_528
.text:000012D6                 MOVS            R1, #0  ; int
.text:000012D8                 MOVS            R2, #0x64 ; 'd' ; size_t
.text:000012DA                 ADD             R5, PC  ; unk_5004
.text:000012DC                 MOV             R0, R6  ; void *
.text:000012DE                 ADD.W           R8, R5, #0x12A0
.text:000012E2                 ADD.W           R5, R5, #0x1440
.text:000012E6                 ADD.W           R4, SP, #0x558+var_529
.text:000012EA                 BLX             memset
.text:000012EE                 ADD.W           R8, R8, #0x1C
.text:000012F2                 ADDS            R5, #0xC
.text:000012F4                 B               loc_1302
.text:000012F6                 BL              sub_E60
.text:000012FA                 CMP             R8, R5
.text:000012FC                 STRB.W          R0, [R4,#1]!
.text:00001300                 BEQ             loc_1310
.text:00001302                 LDR.W           R3, [R8,#4]!
.text:00001306                 AND.W           R1, R3, #0xF
.text:0000130A                 ASRS            R0, R3, #4
.text:0000130C                 CMP             R3, #0
.text:0000130E                 BNE             loc_12F6
  ...
}

sub_E60有多处调用,可以F5出来自己写程序跑,把unk_5004后面的数据都解密出来得到20个字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
java/lang/String
utf-8
getBytes
(Ljava/lang/String;)[B
cat /proc/%d/cmdline
www.vprotect.cn
cat /proc/%d/status
TracerPid:
cat /proc/%d/maps
/system/bin/linker
rtld_db_dlactivity
%04x
popen
pclose
fgets
sprintf
malloc
gettimeofday
getpid
free
%08X%08X%08X%08X%08X%08X%08X%08X

猜测是一些anti和时间检查,动态调试应该会用到
再继续看其它函数,又注意到:

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
int __fastcall sub_788(int *a1, unsigned int *a2, int *a3)
{
  ...
  v3 = a1 + 4;
  v4 = a1 + 0x24;
  v5 = _byteswap_ulong(*a2);
  v6 = _byteswap_ulong(a2[1]);
  v7 = (v6 ^ (v5 >> 4)) & 0xF0F0F0F;
  v8 = v7 ^ v6;
  v9 = v5 ^ (16 * v7);
  v10 = (unsigned __int16)(v8 ^ HIWORD(v9));
  v11 = v10 ^ v8;
  v12 = v9 ^ (v10 << 16);
  v13 = (v12 ^ (v11 >> 2)) & 0x33333333;
  v14 = v13 ^ v12;
  v15 = v11 ^ (4 * v13);
  v16 = (v14 ^ (v15 >> 8)) & 0xFF00FF;
  v17 = v14 ^ v16;
  v18 = __ROR4__(v15 ^ (v16 << 8), 31);
  v19 = (v18 ^ v17) & 0xAAAAAAAA;
  v20 = v17 ^ v19;
  v21 = v19 ^ v18;
  v22 = __ROR4__(v20, 31);
  do
  {
    v23 = *(v3 - 4);
    v3 += 4;
    v24 = *(v3 - 7) ^ __ROR4__(v21, 4);
    v22 ^= dword_31B0[(v24 & 0x3F) + 0x20] ^ dword_31B0[((v21 ^ v23) & 0x3F) + 0x60] ^ dword_31B0[(((v21 ^ v23) >> 8) & 0x3F) + 0xA0] ^ dword_31B0[(((v21 ^ v23) >> 16) & 0x3F) + 0xE0] ^
           dword_31B0[(((v21 ^ v23) >> 24) & 0x3F) + 0x120] ^ dword_31B0[((v24 >> 8) & 0x3F) + 0x160] ^ dword_31B0[(HIWORD(v24) & 0x3F) + 0x1A0] ^ dword_31B0[(HIBYTE(v24) & 0x3F) + 0x1E0];
    v25 = *(v3 - 6) ^ v22;
    v26 = __ROR4__(v22, 4);
    v27 = v26 ^ *(v3 - 5);
    v21 ^= dword_31B0[(v25 & 0x3F) + 0x60] ^ dword_31B0[((v25 >> 8) & 0x3F) + 0xA0] ^ dword_31B0[(HIWORD(v25) & 0x3F) + 0xE0] ^ dword_31B0[(HIBYTE(v25) & 0x3F) + 0x120] ^
           dword_31B0[(v27 & 0x3F) + 0x20] ^ dword_31B0[((v27 >> 8) & 0x3F) + 0x160] ^ dword_31B0[(HIWORD(v27) & 0x3F) + 0x1A0] ^ dword_31B0[(HIBYTE(v27) & 0x3F) + 0x1E0];                        
  }
  while ( v3 != v4 );
  v28 = a1 + 0x44;
  while ( 1 )
  {
    v29 = *(v4 - 4);
    v4 += 4;
    v30 = v26 ^ *(v4 - 7);
    v21 ^= dword_31B0[(v30 & 0x3F) + 32] ^ dword_31B0[((v22 ^ v29) & 0x3F) + 96] ^ dword_31B0[(((v22 ^ v29) >> 8) & 0x3F) + 160] ^ dword_31B0[(((v22 ^ v29) >> 16) & 0x3F) + 224] ^
           dword_31B0[(((v22 ^ v29) >> 24) & 0x3F) + 288] ^ dword_31B0[((v30 >> 8) & 0x3F) + 352] ^ dword_31B0[(HIWORD(v30) & 0x3F) + 416] ^ dword_31B0[(HIBYTE(v30) & 0x3F) + 480];             
    v31 = v21 ^ *(v4 - 6);
    v32 = __ROR4__(v21, 4);
    v33 = v32 ^ *(v4 - 5);
    v22 ^= dword_31B0[(HIWORD(v33) & 0x3F) + 416] ^ dword_31B0[(v31 & 0x3F) + 96] ^ dword_31B0[((v31 >> 8) & 0x3F) + 160] ^ dword_31B0[(HIWORD(v31) & 0x3F) + 224] ^
           dword_31B0[(HIBYTE(v31) & 0x3F) + 288] ^ dword_31B0[(v33 & 0x3F) + 32] ^ dword_31B0[((v33 >> 8) & 0x3F) + 352] ^ dword_31B0[(HIBYTE(v33) & 0x3F) + 480];               
    if ( v4 == v28 )
      break;
    v26 = __ROR4__(v22, 4);
  }
  v34 = a1 + 0x64;
  while ( 1 )
  {
    v35 = *(v28 - 4);
    v28 += 4;
    v36 = v35 ^ v21;
    v37 = *(v28 - 7) ^ v32;
    v22 ^= dword_31B0[(v37 & 0x3F) + 32] ^ dword_31B0[(v36 & 0x3F) + 96] ^ dword_31B0[((v36 >> 8) & 0x3F) + 160] ^ dword_31B0[(HIWORD(v36) & 0x3F) + 224] ^
           dword_31B0[(HIBYTE(v36) & 0x3F) + 288] ^ dword_31B0[((v37 >> 8) & 0x3F) + 352] ^ dword_31B0[(HIWORD(v37) & 0x3F) + 416] ^ dword_31B0[(HIBYTE(v37) & 0x3F) + 480];
    v38 = v22 ^ *(v28 - 6);
    v39 = *(v28 - 5) ^ __ROR4__(v22, 4);
    v21 ^= dword_31B0[(v38 & 0x3F) + 96] ^ dword_31B0[((v38 >> 8) & 0x3F) + 160] ^ dword_31B0[(HIWORD(v38) & 0x3F) + 224] ^ dword_31B0[(HIBYTE(v38) & 0x3F) + 288] ^
           dword_31B0[(v39 & 0x3F) + 32] ^ dword_31B0[((v39 >> 8) & 0x3F) + 352] ^ dword_31B0[(HIWORD(v39) & 0x3F) + 416] ^ dword_31B0[(HIBYTE(v39) & 0x3F) + 480];
    if ( v28 == v34 )
      break;
    v32 = __ROR4__(v21, 4);
  }
  ...
  return result;
}

看特征比较像des类的算法,最后确认应该是3des-ecb,我大概只能走这么远了,然后在大帅锅的支持和指导下学习一下unidbg的使用
unidbg里磕磕绊绊地勉强能动态调试,ida经常会卡死,几分钟就要重开一下,好在anti什么的我不需要关心,大帅锅已经hook过掉了
我只需直接研究算法,那么就直奔sub_788看看这个3des,断下来观察传入参数,果然是对输入的sn做加密

1
2
公开序列号:DA 21 5D 85 F8 2B 13 FB 41 65 CF 78 2F A6 E6 9A        (输入sn经两次hex编码)
加密后得到:BD 3A B0 69 39 40 F8 CD 42 0D E3 8A 79 DB 52 BD        输出到r2寄存器:0x40392030

后面就浪费很多时间,单步胡乱走,走走看看,一直找不到更多线索,3des还会多次调用,但也没发现对数据做什么进一步有效处理
就是花了好几个小时也没啥进展,偶然一次在单步中注意到一个次数据读取,从0x40392020处读取到一个字节BD,突然敏感起来,这个地址临近前面的3des加密结果返回地址,而且刚好等于那个字节BD

1
2
0x40392020 : BD ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??
0x40392030 : BD 3A B0 69 39 40 F8 CD 42 0D E3 8A 79 DB 52 BD

这就大胆猜想一下了,也许是比较结果?进一步证实确实16个字节都一致,这里全凭运气哈

 

那么解决方案就来了,这次输入名字KCTF,然后再去下个断点把0x40392020处的16字节抓出来
得到:45 68 97 A3 29 2A 7F D4 F5 90 73 57 46 02 AE D5
然后3des解密一下就应该可以得到答案了
扩展密钥直接抓出来用,大概spbox做了什么手脚,花了很多时间都没凑对结果,F5出来的代码也一直没调对,最后还是大帅锅动作敏捷先搞对了,得到flag:
3633386636373733353933626439316437383865383931653663663432656661
我无奈,只能做点力所能及的工作了:提交答案!


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

最后于 2021-12-8 10:11 被ccfer编辑 ,原因:
收藏
点赞3
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回