-
-
[原创]cybricsctf-paired
-
2021-7-30 21:09 9492
-
paired
解包,先得到4个文件,运行app1.exe
app1
这几个函数不用再logic.dll里面分析,动调就可以知道是设置一个有id的储存
WinMain
1 | wndclass.lpfnWndProc = (WNDPROC)WndProc; |
根据窗口类lpfnWndProc的成员根据这里找到窗口过程
STAGE 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | GetWindowTextA(edit_hWnd, String, 128 ); v15 = (unsigned __int8 * )&v19; do { v16 = v15[String - (CHAR * )&v19]; v17 = * v15 - v16; if ( v17 ) break ; + + v15; } while ( v16 ); if ( !v17 ) { SetWindowTextW(hWnd[ 0 ], L "STAGE 2" ); pass_to_stage2 = 1 ; } return 0i64 ; |
这里获取edit控件字符串并检测,进入第二关
STAGE 2
case WM_KEYDOWN(0x100):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | else if ( pass_to_stage2 = = 1 ) { j = i; if ( wParam ! = aEnable[i] ) { i = 0 ; return 0i64 ; } + + i; if ( !aEnable[j + 1 ] ) { pass_to_stage3 = 1 ; SetWindowTextW(_hwnd, L "STAGE 3..." ); add_to_storage( 1i64 , hWnd); return 0i64 ; } } |
按键要等于'ENABLE'
进入第三关
STAGE 3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | case 0x400u : / / WM_USER if ( !get_flag ) { GetWindowTextA(edit_hWnd, String, 128 ); add_to_storage( 2i64 , String); get_flag = 1 ; } v7 = VirtualAlloc( 0i64 , 0x200ui64 , 0x1000u , 0x40u ); get_data_from_storage((unsigned int )(_wParam + 32 ), v7, 128i64 ); v19 = 0i64 ; v20 = 0i64 ; v21 = 0i64 ; v22 = 0i64 ; v23 = 0i64 ; v24 = 0i64 ; v25 = 0i64 ; v26 = 0i64 ; get_data_from_storage( 2i64 , &v19, 128i64 ); success + = ((__int64 (__fastcall * )(_QWORD, __int128 * ))v7)((unsigned int )_wParam, &v19); if ( success = = 32 ) { MessageBoxA( 0i64 , "Well done!" , "WINNER!" , 0 ); return 0i64 ; } |
需要把消息WM_USER放入队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | if ( pass_to_stage3 ) { if ( wParam = = 'F' ) / / fake { v11 = 0i64 ; v12 = 32i64 ; while ( 1 ) { PostMessageA(_hwnd, 0x400u , v11 + + , 0i64 ); if ( ! - - v12 ) break ; _hwnd = hWnd[ 0 ]; } } } |
这里可以加入WM_USER消息,动调查看v7函数之后,得到输入为this_is_just_some_random_string!,是fake flag,很明显不可能只分析一个文件
app2
直接进入sub_7FF64E911210()函数分析
1 2 3 4 | hWnd = HWND_MESSAGE| 0x2 ; LODWORD(l) = get_data_from_storage( 1i64 , &hWnd); v1 = hWnd; if ( hWnd ! = HWND_MESSAGE| 0x2 ) |
这里获取id==7的储存赋值给hwnd,再看前面通过第二关时
1 | add_to_storage( 1i64 , hWnd); |
只有通过第二关,这里条件才为真, 接下来关注消息的发送, 这里所用的消息发送都将发送wparam==32的消息,执行id==64的代码,
1 2 | PostMessageA(v1, 0x400u , 0x20ui64 , 0i64 ); for ( i = - 1i64 ; i = = - 1 ; get_data_from_storage( 3i64 , &i) ); |
先动调app1,进入第二关后,把断点下在第三关,然后动调app2, 运行过发送消息
1 | ((void (__fastcall * )(__int64, void * * , __int64))logic_dll_add_to_storage)( 3i64 , &retaddr, 8i64 ); |
v7函数, 把基地址存为id==3的储存中, 储存完成后 app2的循环停止
1 2 3 4 5 6 | get_data_from_storage( 2i64 , v61); v3 = - 1i64 ; do + + v3; while ( v61[v3] ); if ( (_DWORD)v3 = = 32 ) |
这里是长度检验, id==2是app1的输入框内容, 先判断不成立的情况, 这里的代码由随机数组成, srand(time(0)), 明显有很大问题
判断成立的情况
1 2 3 | v10 = _mm_load_si128((const __m128i * )&byte_7FF64E926630); for ( j = 0i64 ; j < 32 ; j + = 16i64 ) * (__m128i * )&v61[j] = _mm_xor_si128(v10, _mm_loadu_si128((const __m128i * )&v61[j])); |
一段异或加密, 到这里
1 2 3 4 5 6 7 | get_data_from_storage( 2i64 , v62); do { * (__m128i * )&v62[ 4 * v16] = _mm_xor_si128(_mm_loadu_si128((const __m128i * )&v62[ 4 * v16]), v17); v16 + = 4i64 ; } while ( v16 < 8 ); |
又是一段异或加密
1 2 | get_data_from_storage( 5i64 , &i); while ( (_DWORD)i ! = 'cybr' ); |
这里有循环条件,这是其实是rbyc, id==5的由来(app1中):
1 2 3 4 5 6 | __int64 sub_1AD49780000() { GetWindowTextA((HWND) 0xBD04BA , byte_1AD49760000, 128 ); ((void (__fastcall * )(__int64, __int64, __int64))logic_dll_add_to_storage)( 5i64 , 0x1AD49760000i64 , 4i64 ); return 0i64 ; } |
输入放入id==5, 检查前面4个字节为'rbyc', 第二次异或加密
1 2 3 4 5 6 | do { * (__m128i * )&v62[ 4 * v16] = _mm_xor_si128(_mm_loadu_si128((const __m128i * )&v62[ 4 * v16]), v17); v16 + = 4i64 ; } while ( v16 < 8 ); |
然后来到swith结构, 像极了虚拟机
1 | switch ( * ((_BYTE * )&unk_7FF64E910000 + index + 100864 ) ) / / byte_7FF64E928A00 |
然后慢慢单步走, 慢慢会发现先一直都是1, 2, 然后走5, 6, 8, 3, 8, a, 接下来就又是1, 2......., 最后, 2, 3, 4
(地址只是一个临时的值,主要怎么计算得到)
case 1:
app1中的执行的代码
1 2 3 | unk_19BE3430010 = 0i64 ; / / unk_19BE3430010 = = * ( id = = 6 的数据 + byte_7FF64E928A00[index + 1 ]) / / 0 = = * (dword * )(byte_7FF64E928A00 + index + 2 ) ((void (__fastcall * )(__int64, __int64, __int64))logic_dll_add_to_storage)( 10i64 , 0x7FF786F70000i64 , 8i64 ); |
case 2:
app1中的执行的代码
1 2 | ((void (__fastcall * )(__int64, __int64, __int64))logic_dll_add_to_storage)( 7i64 , 0x19BE3430070i64 , 8i64 ); ((void (__fastcall * )(__int64, __int64, __int64))logic_dll_add_to_storage)( 10i64 , 0x7FF786F70000i64 , 8i64 ); |
id == 7的数据换为(id==6的数据 + byte_7FF64E928A00[index + 1])
case 3:
1 | v25 = * (unsigned int * )((char * )&unk_7FF64E910000 + index + 100865 ); / / * (dword * )(byte_7FF64E928A00 + index + 1 ) |
1 2 | v26 = &i; i = i = = v25; / / 出现了比较 |
1 | LODWORD(l) = add_to_storage( 7i64 , v26); / / 比较结果在 id = = 7 数据中 |
接下来会执行case 8, 将结果(1)加在 *(id==6的数据 + 8)中
这里就和app1中的加到32有点像了
case 4:
这里是最后的指令, 先前的两步2, 3, 分别是把数据给到id == 7(就是每次比较的结果相加的数据)和 将id == 7与32做比较(和app1重合了), 若为真, 则id == 7 变为1
1 | if ( l ! = 1 ) |
则app1中的执行的代码
1 | return 32i64 ; |
这样就正确了
case 5:
将id==7的数据换为(dword )(byte_7FF64E928A00 + index + 1)
case 6:
app1中的执行的代码
1 2 | unk_27DA7C30008 = 0x81818181i64 ; / / * ( id = = 6 的数据 + byte_7FF64E928A00[index + 1 ]) = ( id = = 2 的数据( input )) ((void (__fastcall * )(__int64, __int64, __int64))logic_dll_add_to_storage)( 10i64 , 0x7FF786F70000i64 , 8i64 ); |
case 8:
app1中的执行的代码:
1 2 | ((void (__fastcall * )(__int64, __int64, __int64))logic_dll_add_to_storage)( 7i64 , 0x27DA7C30008i64 , 8i64 ); ((void (__fastcall * )(__int64, __int64, __int64))logic_dll_add_to_storage)( 10i64 , 0x7FF786F70000i64 , 8i64 ); |
(id == 7的数据) = *(id==6的数据 + byte_7FF64E928A00[index + 1])
app2:
1 | get_data_from_storage( 7i64 , &i); |
1 2 3 | for ( ii = - 1i64 ; ii = = - 1 ; get_data_from_storage( 7i64 , &ii) ) ; i + = ii; |
1 2 | remove_data( 7i64 ); LODWORD(l) = add_to_storage( 7i64 , &i); |
case a:
1 2 | unk_27DA7C30010 = 0i64 ; ((void (__fastcall * )(__int64, __int64, __int64))logic_dll_add_to_storage)( 10i64 , 0x7FF786F70000i64 , 8i64 ); |
将 id==7 赋值给*(id == 6 + 10)
综上可以发现
这里的1, 2指令中的其他一堆赋值貌似没什么用,主要就是每次取字符串的4个字节来加法,加数为指令3的参数(后面一个值), 结果存放在偏移量为0x10的地址中, 然后比较一下是否为32
解密脚本
减法解密直接python敲出来就行了
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 | #include<iostream> using namespace std; int main() { unsigned char byte_7FF64E926630[ 16 ] = { 0x81 , 0x81 , 0x81 , 0x81 , 0x81 , 0x81 , 0x81 , 0x81 , 0x81 , 0x81 , 0x81 , 0x81 , 0x81 , 0x81 , 0x81 , 0x81 }; unsigned char a[] = { 0x90 , 0x9a , 0x9a , 0x90 , 0x9a , 0x80 , 0x8b , 0x99 , 0x80 , 0x8b , 0xb8 , 0x90 , 0x96 , 0x87 , 0xa7 , 0x91 , 0xc0 , 0x80 , 0x8c , 0xd3 , 0x9c , 0x8d , 0xa7 , 0x8f , 0xb3 , 0x84 , 0xc9 , 0x81 , 0xd2 , 0xd2 , 0xd9 , 0x9f }; char key[] = { 0x72 , 0x62 , 0x79 , 0x63 }; for ( int i = 0 ; i < 32 ; i + + ) { a[i] ^ = key[i % 4 ]; } for ( int i = 0 ; i < 32 ; i + + ) { a[i] ^ = 0x81 ; } for ( int i = 0 ; i < 32 ; i + + ) { printf( "%c" , a[i]); } system( "pause" ); return 0 ; } |