-
-
[原创] KCTF2021秋季赛 声名远扬
-
2021-12-2 02:15 12494
-
考察C++逆向。
总体思路:动态调试,黑盒测试。
Windows 32位程序,无壳。
注:以下分析如未作特殊说明,默认基址为0x251000
题目存在一些花指令,不多做阐述,nop修复即可。
比较多的虚函数,同时能看到关键词DuiLib
有关DuiLib
能够从网上找到N篇文章介绍切入点,随便挂一个
Dump微信PC端的界面Duilib文件-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com
通过虚函数跳转最终定位到按钮回调函数sub_26D2D0
简单调试分析后,能够发现首先是进行了变表的base64编码,具体位置在0x26E530
,伪代码分析特征还是比较明显的
当然最大的特征当属编码表,其特征位于函数0x26E250
,伪代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | int __cdecl base64Maps( int a1) { int * v1; / / eax char v3[ 8 ]; / / [esp + 4h ] [ebp - 68h ] BYREF int v4; / / [esp + Ch] [ebp - 60h ] _BYTE base64[ 65 ]; / / [esp + 10h ] [ebp - 5Ch ] BYREF unsigned int v6[ 2 ]; / / [esp + 51h ] [ebp - 1Bh ] BYREF int v7; / / [esp + 68h ] [ebp - 4h ] v4 = 0 ; sub_26D5D0((char * )v6 + 3 , 8u ); qmemcpy(base64, "prvo9CHSJOcPIb6xRVUXQz0qBGDE72LNZduaefYT5K_8-4FAhlimjkngt1yMWs3w!" , sizeof(base64)); v1 = ( int * )__FrameHandler3::TryBlockMap::TryBlockMap( (__FrameHandler3::TryBlockMap * )v3, (const struct _s_FuncInfo * )base64, (unsigned int )v6); sub_26EA90((char * )v6 + 3 , * v1, v1[ 1 ]); v7 = 0 ; sub_26EAD0((void * )a1, ( int )v6 + 3 ); v4 | = 1u ; v7 = - 1 ; sub_26EA70(); return a1; } |
下面进行验证
动态调试得到编码串
尝试变表解密
1 2 3 4 5 6 7 8 | import base64 baseMaps = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" newMaps = "prvo9CHSJOcPIb6xRVUXQz0qBGDE72LNZduaefYT5K_8-4FAhlimjkngt1yMWs3w!" cipherText = "EHsnG0bjGT44BqIhETj!" cipherText = cipherText.translate(cipherText.maketrans(newMaps, baseMaps)) print (base64.b64decode(cipherText.encode( 'utf-8' )).decode()) |
解得lovectf{mas0n}
,验证成功
进入下一步
经过调试分析确定总体逻辑如下图
单步调试过程中会发现在verify
运行至一个段间跳转时出现异常。
反复调试后,最终发现突破点在于其进行原跳转前,32位至64位模式的切换。
只不过在这里的表现形式与往常有所不同
关于32位程序至64位程序的切换,可以参考文章
CTF中32位程序调用64位代码的逆向方法 - 安全客,安全资讯平台 (anquanke.com)
知晓其模式切换后,强制指定PE64标识
放入IDA,为方便定位函数,Rebase Segment,基址设为0
通过调试得到调用函数地址,减去基址后得到偏移量0x146f0
定位函数,得到伪代码后,看到了函数调用
汇编下形式为call rdi
翻找之后能够知道rdi
来自于指令mov rdi, [rsp+arg_0]
向上分析
确定函数偏移为0x145AC
简单分析伪代码,结合代码复用,能够确定其check逻辑如下
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 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | __int64 __fastcall sub_145AC(char * a1, __int64 a2) { unsigned int v4; / / edx char v5; / / al __int64 v6; / / rcx int v7; / / edx char v8; / / al char * v9; / / rcx __int64 v10; / / rax unsigned int v11; / / edx char v12; / / al __int64 v13; / / rcx char v14; / / cl __int64 v15; / / r8 int * v16; / / rax char v17; / / al __int64 v18; / / rcx char v19; / / cl __int64 v20; / / r8 int * v21; / / rax int v23; / / [rsp + 4h ] [rbp - 3Ch ] BYREF char v24; / / [rsp + 8h ] [rbp - 38h ] __int16 v25; / / [rsp + 9h ] [rbp - 37h ] char v26; / / [rsp + Bh] [rbp - 35h ] unsigned int v27; / / [rsp + Ch] [rbp - 34h ] char v28[ 48 ]; / / [rsp + 10h ] [rbp - 30h ] BYREF v27 = 44 ; v4 = 0 ; * (__m128i * )v28 = _mm_load_si128((const __m128i * )&xmmword_14408); * (__m128i * )&v28[ 32 ] = _mm_load_si128((const __m128i * )&xmmword_143F8); * (__m128i * )&v28[ 16 ] = _mm_load_si128(xmmword_14418); do { v5 = v4 - 52 ; v6 = v4 + + ; v28[v6] ^ = v5; } while ( v4 < v27 ); / / 还原base64编码串 v28[v27] = 0 ; v7 = 0 ; v8 = * a1; if ( * a1 ) { v9 = a1; do { if ( v8 ! = v9[v28 - a1] ) / / strcmp break ; + + v9; + + v7; v8 = * v9; } while ( * v9 ); } v10 = v7; v11 = 0 ; v24 = - 48 ; if ( a1[v10] = = v28[v10] ) / / bingo { v23 = 0x78063019 ; / / 正确 v25 = 0 ; v26 = 0 ; do { v12 = v11 - 52 ; v13 = v11 + + ; * ((_BYTE * )&v23 + v13) ^ = v12; } while ( v11 < 4 ); v24 = 0 ; v14 = v23; if ( (_BYTE)v23 ) { v15 = a2 - (_QWORD)&v23; v16 = &v23; do { * ((_BYTE * )v16 + v15) = v14; v16 = ( int * )((char * )v16 + 1 ); v14 = * (_BYTE * )v16; } while ( * (_BYTE * )v16 ); } } else { v23 = 0x3C002078 ; / / 错误 v25 = 0 ; v26 = 0 ; do { v17 = v11 - 52 ; v18 = v11 + + ; * ((_BYTE * )&v23 + v18) ^ = v17; } while ( v11 < 4 ); v24 = 0 ; v19 = v23; if ( (_BYTE)v23 ) { v20 = a2 - (_QWORD)&v23; v21 = &v23; do { * ((_BYTE * )v21 + v20) = v19; v21 = ( int * )((char * )v21 + 1 ); v19 = * (_BYTE * )v21; } while ( * (_BYTE * )v21 ); } } return 0i64 ; } |
简单异或还原明文验证猜想
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 | """ v23 = 0x78063019; v25 = 0; v26 = 0; do { v12 = v11 - 52; v13 = v11++; *((_BYTE *)&v23 + v13) ^= v12; } while ( v11 < 4 ); """ v11 = 0 v23 = bytearray( int .to_bytes( 0x78063019 , length = 4 , byteorder = "little" )) while 1 : v12 = v11 - 52 v13 = v11 v11 + = 1 v23[v13] ^ = v12 & 0xff if v11 > = 4 : break print (v23.decode( 'gbk' )) # 正确 |
最终解题脚本如下
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 | import base64 """ v27 = 44; v4 = 0; *(__m128i *)v28 = _mm_load_si128((const __m128i *)&xmmword_15408); *(__m128i *)&v28[32] = _mm_load_si128((const __m128i *)&xmmword_153F8); *(__m128i *)&v28[16] = _mm_load_si128(xmmword_15418); do { v5 = v4 - 52; v6 = v4++; v28[v6] ^= v5; } while ( v4 < v27 ); v28[v27] = 0; """ xmmArr = [ 0x0B3E38188BB9CBA9DBAFFB697ABA2948B , 0x0BFDBD9AAD6D4BCA1878490B0B5AE858C , 0x0F8D6D7A7BAB89480B78A94B9AE ] v27 = 44 v28 = b'' for xmm in xmmArr: v28 + = int .to_bytes(xmm, length = 16 , byteorder = "little" ) v28 = bytearray(v28) v4 = 0 while 1 : v5 = v4 - 52 v6 = v4 v4 + = 1 v28[v6] ^ = v5 & 0xff if v4 > = v27: break v28[v27] = 0 print (v28) cipherText = v28.decode() baseMaps = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" newMaps = "prvo9CHSJOcPIb6xRVUXQz0qBGDE72LNZduaefYT5K_8-4FAhlimjkngt1yMWs3w!" cipherText = cipherText.translate(cipherText.maketrans(newMaps, baseMaps)) print (base64.b64decode(cipherText.encode( 'utf-8' )).decode()) |