-
-
[原创]看雪CTF2017 第十二题分析
-
2017-6-25 12:06 4491
-
先说一下如何过反调试,调试环境为ida和android模拟器,ida卡到不行啊,羡慕有真机的。
模拟器启动程序后使用ida附加,注意先不要点check按钮,不然so后启动。
ida在exit处下断点,继续执行程序,点check按钮加载so,不出意外ida会断在exit函数处。
在ida的threads窗口找到当前线程,将其挂起即可,去除反调试功能。
在check函数中依然有反调试检查,由于程序有cache检查的bug,可继续调试,真机还不确定。
使用工具dex2jar和jd-gui反编译java代码,主要函数如下:
public void onClick(View paramAnonymousView) { paramAnonymousView = Main.this.editText.getText().toString().trim(); if (ua.check(ca.a(paramAnonymousView) + ba.a(paramAnonymousView) + ab.a(paramAnonymousView)) == 1) { bc.showresult(Main.this.tv, true); return; } bc.showresult(Main.this.tv, false); }
首先,对输入sn进行编码,分别为ca.a,ba.a,ab.a完成后组成字符串传给ua.check,并且返回1
ua.check是个so库,通过ida进行反编译,其check入口为:
v6 = &s; memset(&s, 0, 0x25u); v7 = v5; do { v8 = *(_DWORD *)v7; v7 += 8; v9 = *((_DWORD *)v7 - 1); *(_DWORD *)v6 = v8; *((_DWORD *)v6 + 1) = v9; v10 = v6 + 8; v6 += 8; } while ( v7 != v5 + 32 ); *v10 = *(_DWORD *)v7; ((void (__fastcall *)(_JNIEnv *, _JavaVM *, const char *))v3->functions->ReleaseStringUTFChars)(v3, v4, v5); v11 = strlen(&s); result = do_check((int *)&s, v11) != 0;
继续跟踪do_check函数
if ( len == 36 ) { sn_2 = j_j_malloc(0x28u); sn_3 = (int)sn_2; if ( sn_2 ) { j_j_memcpy(sn_2, sn_1, v4); *(_BYTE *)(sn_3 + 36) = 4; *(_BYTE *)(sn_3 + 37) = 4; *(_BYTE *)(sn_3 + 38) = 4; *(_BYTE *)(sn_3 + 39) = 4;
输入sn长度必须为36,且后面补4个4
do { v8 = byte_5D24[v2]; // des key v9 = 0; do { *(&key_bits[8 * v2] + v9) = (v8 >> (7 - v9)) & 1; ++v9; } while ( v9 != 8 ); ++v2; } while ( v2 != 8 ); sub_1650((int)key_bits);
上面是设置des密钥,这个过程被作者修改过,我们可以把扩展后key直接拿出来用。
do { v11 = (void *)(sn_3 + v10); j_j_memcpy(&dest, (const void *)(sn_3 + v10), 8u); v15 = 0; v16 = 0; des_encrypt((int)&dest, (int)&v15); v10 += 8; j_j_memcpy(v11, &v15, 8u); } while ( v10 != 40 );
对sn进行des加密
update_key2((int)&key2, (int)&v15); rc6_encrypt(sn_3, 0x20u, (char *)&key2, 16); v12 = 0; while ( *(_BYTE *)(sn_3 + v12) == byte_5D3D[v12] ) { if ( ++v12 == 32 ) { result = 1; goto LABEL_14; } }
上面是更新rc6的key(替换key后4字节为des加密结果+32处4字节),并且进行rc6加密。加密结果与必须为:
42 D3 C3 C2 F1 2A E9 2D 66 C9 28 22 2C EB 54 0E 94 07 E5 77 4A 92 B7 92 2E 5D FD F0 F3 54 9F C6
rc6扩展key也被修改过,如下:
*a1 = 0xB7E15163; v3 = a1; v4 = a1 + 43; v5 = a2; do { v3[1] = *v3 + 0x61C88647; ++v3; } while ( v3 != v4 ); // 下面是标准p,q // const unsigned int p = 0xb7e15163; // const unsigned int q = 0x9e3779b9 // <- 标准
逆向方法如下:
1. 作者给出了前8个字符为kxuectf{,猜测最后一个字符为}。前8个字符正好是des块长度,也就是说,我们知道了rc6的部分明文和密文,可以通过rc6解密穷举出rc6后4位key和全部rc6的明文。
unsigned char buf11[32] = { 0x42, 0xD3, 0xC3, 0xC2, 0xF1, 0x2A, 0xE9, 0x2D, 0x66, 0xC9, 0x28, 0x22, 0x2C, 0xEB, 0x54, 0x0E, 0x94, 0x07, 0xE5, 0x77, 0x4A, 0x92, 0xB7, 0x92, 0x2E, 0x5D, 0xFD, 0xF0, 0xF3, 0x54, 0x9F, 0xC6 }; unsigned char buf12[32] = { 0}; unsigned char buf99[32] = { 0x4C, 0xD9, 0xA3, 0xE6, 0xED, 0xFE, 0xD1, 0x05, 0xC8, 0x97, 0x65, 0xD4, 0x9A, 0x53, 0xF4, 0xD1 }; unsigned char key2[32] = { 0x65, 0x48, 0x32, 0xEF, 0xBA, 0xCD, 0x56, 0x4E, 0x0F, 0x9B, 0x1D, 0x27, 0xff, 0xff, 0xff, 0xff, 0x00 }; int *i = (int *)&key2[12]; while (*i) { if (((*i) & 0xFFFFFF) == 0) printf("%08X\n", (*i)); ss_decrypt(key2, buf11, buf12, 16); if (memcmp(buf99, buf12, 8) == 0) { printf("key:\n"); printhex((char *)key2, 16); ss_decrypt(key2, buf11, buf12, 32); for (int j = 0; j < 32; j+= 8) des.decrypt((char *)buf12+j, (char *)buf13+j); printhex((char *)buf13, 32); } (*i)--; } //key //65 48 32 EF BA CD 56 4E 0F 9B 1D 27 13 6A 7E 1F //78 6B 68 72 70 67 73 7D 51 34 70 65 6C 63 67 72 //71 36 66 49 34 65 6C 79 61 67 72 65 72 32 67 76
作者的rc6算法效率真的很低啊。穷举过程非常漫长。。。。
获取rc6 key之后,下面穷举sn中的后4字节:其中0x7B为已知 } 编码得来
unsigned char buf15[8] = { 0xFF, 0xFF, 0xFF, 0x7B, 4, 4, 4, 4 }; int *i = (int *)&buf15[0]; while (*i) { if (((*i) & 0xFFFF) == 0) printf("%08X\n", (*i)); des.encrypt((char *)buf15, (char *)buf13); if (*(int*)buf13 == 0x1F7E6A13) { printhex((char *)buf15, 8); } (*i)--; } return 0; // sn 后4字节为 61 3E 36 7B
将得到完全36字节进行反向编码,依然穷举:
unsigned char buf4[36] = { 0x78, 0x6B, 0x68, 0x72, 0x70, 0x67, 0x73, 0x7D, 0x51, 0x34, 0x70, 0x65, 0x6C, 0x63, 0x67, 0x72, 0x71, 0x36, 0x66, 0x49, 0x34, 0x65, 0x6C, 0x79, 0x61, 0x67, 0x72, 0x65, 0x72, 0x32, 0x67, 0x76, 0x61, 0x3E, 0x36, 0x7B }; for (int i = 0; i < 36; i++) { printf("%02X:", buf4[i]); for (int j = 0x00; j < 0xff; j++) { unsigned char ch = encrypt_abch(j); if (ch == buf4[i]) { printf("%c ", j); } } printf("\n"); }
注意要使用ab.a中的编码算法,得出:
sn=kxuectf{D3crypted1sV3rylntere5tin91}
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界