0x0 环境
——————–
环境:
系统:Windows 10
工具:IDA 7.X
——————–
0x1 跑起来
这个CM和前面几个不一样,打开UI还会放音乐,就像是玩小霸王(红白机)那种游戏带的背景音乐。听起来还是蛮带感的。
输入密码后弹出错误提示“Nope, thats not it!”

0x2 IDA分析
只有一处引用字符串可以很快定位到关键函数

函数从流程图来看挺大的F5以下看下函数大致流程。
INT_PTR __stdcall DialogFunc(HWND hWnd, UINT a2, WPARAM a3, LPARAM a4) { switch ( a2 ) { case 0x110u: ..... break; case 0x111u: ...... lstrcpyA(String1, String2); GetDlgItemTextA(hWnd, 100, byte_406949, 512); if ( lstrcmpA(byte_406949, String1) ) MessageBoxA(hWnd, aNopeThatsNotIt, Caption, 0x10u); else MessageBoxA(hWnd, aYepThatsTheRig, aGoodBoy, 0x40u); ...... break; case 0x10u: break; } return 0; }
能看出直接定位是在DLG的窗口消息回调当中,作者并没有做什么隐藏和加壳。
0x3 分析解码逻辑
.text:00401163 loc_401163: ; CODE XREF: DialogFunc+95↑j .text:00401163 lea edx, g_name .text:00401169 push edx ; lpString .text:0040116A call lstrlenA .text:0040116F mov ebp, eax ; name_length; .text:00401171 mov ecx, 5 .text:00401176 xor esi, esi ; index; .text:00401178 xor eax, eax ; j = 0; .text:0040117A do_while: ; CODE XREF: DialogFunc+E5↓j .text:0040117A mov cl, [esi+edx] ; cl = g_name_buf[i]; .text:0040117D mov bl, cl .text:0040117F xor bl, (g_key1+1)[eax] .text:00401185 inc eax .text:00401186 cmp eax, 5 .text:00401189 mov [edx+esi], bl ; g_name_buf[i] ^= g_key1[i]; .text:0040118C mov g_key1[eax], cl ; g_key1[j] = cl; .text:00401192 jnz short loc_401196 ; if (j == 5) .text:00401194 xor eax, eax ; j = 0; .text:00401196 loc_401196: ; CODE XREF: DialogFunc+DE↑j .text:00401196 inc esi .text:00401197 cmp esi, ebp ; index < name_length; .text:00401199 jb short do_while ; .text:00401199 ; ; .text:0040119B xor edi, edi ; edi = j; .text:0040119D xor ecx, ecx ; ecx = index; .text:0040119F test ebp, ebp ; ebp = name_length; .text:004011A1 jbe short do_while_2_end ; for () .text:004011A3 do_while_2: ; CODE XREF: DialogFunc+113↓j .text:004011A3 mov bl, (g_key1+6)[edi] .text:004011A9 mov esi, ebp ; ebp = name_length; .text:004011AB sub esi, ecx .text:004011AD dec esi ; t = name_length - index - 1; .text:004011AE mov al, [edx+esi] ; tt = g_name_buf[t]; .text:004011B1 xor bl, al .text:004011B3 inc edi ; j++; .text:004011B4 mov [edx+esi], bl ; g_name_buf[t] ^= g_key1[5 + j]; .text:004011B7 mov (g_key1+5)[edi], al ; g_key[4 + j] = tt; .text:004011BD cmp edi, 5 .text:004011C0 jnz short loc_4011C4 ; if (j == 5) .text:004011C2 xor edi, edi ; j = 0; .text:004011C4 loc_4011C4: ; CODE XREF: DialogFunc+10C↑j .text:004011C4 inc ecx ; index++; .text:004011C5 cmp ecx, ebp ; index < name_length; .text:004011C7 jb short do_while_2 .text:004011C9 do_while_2_end: ; CODE XREF: DialogFunc+ED↑j .text:004011C9 xor esi, esi ; esi = j; .text:004011CB xor edi, edi ; edi = index; .text:004011CD test ebp, ebp ; ebp = name_length; .text:004011CF jbe short do_while3_end ; edi = j; .text:004011D1 do_while3: ; CODE XREF: DialogFunc+13C↓j .text:004011D1 mov al, [edx+edi] ; t1 = g_name_buf[index]; .text:004011D4 mov cl, (g_key1+0Bh)[esi] .text:004011DA xor cl, al ; t = t1 ^ g_key2[1 + j]; .text:004011DC inc esi ; j++; .text:004011DD mov [edx+edi], cl ; g_name_buf[index] = t; .text:004011E0 mov (g_key1+0Ah)[esi], al ; g_key2[j] = t1; .text:004011E6 cmp esi, 5 .text:004011E9 jnz short loc_4011ED ; if (j == 5) .text:004011EB xor esi, esi ; j = 0; .text:004011ED loc_4011ED: ; CODE XREF: DialogFunc+135↑j .text:004011ED inc edi ; index++; .text:004011EE cmp edi, ebp ; index < name_length; .text:004011F0 jb short do_while3 ; t1 = g_name_buf[index]; .text:004011F2 do_while3_end: ; CODE XREF: DialogFunc+11B↑j .text:004011F2 xor edi, edi ; edi = j; .text:004011F4 xor ecx, ecx ; ecx = index; .text:004011F6 test ebp, ebp ; ebp = name_length; .text:004011F8 jbe short do_while4_end .text:004011FA do_while4: ; CODE XREF: DialogFunc+16A↓j .text:004011FA mov bl, (g_key1+10h)[edi] ; t = g_key2[6 + j]; .text:00401200 mov esi, ebp .text:00401202 sub esi, ecx .text:00401204 dec esi ; t1 = name_length - index - 1; .text:00401205 mov al, [edx+esi] ; t2 = g_name_buf[t1]; .text:00401208 xor bl, al .text:0040120A inc edi ; j++; .text:0040120B mov [edx+esi], bl ; g_name_buf[t1] = t ^ t2; .text:0040120E mov (g_key1+0Fh)[edi], al ; g_key2[5 + j ] = t2; .text:00401214 cmp edi, 5 .text:00401217 jnz short loc_40121B ; if (j == 5) .text:00401219 xor edi, edi ; j = 0; .text:0040121B loc_40121B: ; CODE XREF: DialogFunc+163↑j .text:0040121B inc ecx ; index++; .text:0040121C cmp ecx, ebp .text:0040121E jb short do_while4 ; t = g_key2[6 + j]; .text:00401220 do_while4_end: ; CODE XREF: DialogFunc+144↑j .text:00401220 lea edi, g_undef1 .text:00401226 xor eax, eax ; eax = index; .text:00401228 test ebp, ebp .text:0040122A mov dword ptr g_undef1, 0 .text:00401234 jbe short do_while5_end .text:00401236 .text:00401236 do_while5: ; CODE XREF: DialogFunc+197↓j .text:00401236 mov ecx, eax .text:00401238 and ecx, 3 .text:0040123B mov bl, [edi+ecx] ; t = g_undef1[index & 3]; .text:0040123E lea esi, [edi+ecx] .text:00401241 mov cl, [edx+eax] ; t1 = g_name_buf[index + index] .text:00401244 add bl, cl .text:00401246 inc eax ; index++; .text:00401247 cmp eax, ebp .text:00401249 mov [esi], bl ; g_undef1[index & 3] = t + t1; .text:0040124B jb short do_while5 .text:0040124D do_while5_end: ; CODE XREF: DialogFunc+180↑j .text:0040124D pop ebp .text:0040124E mov ecx, 0Ah .text:00401253 mov eax, dword ptr g_undef1 .text:00401258 xor ebx, ebx .text:0040125A do_while6: ; CODE XREF: DialogFunc+1B6↓j .text:0040125A xor edx, edx .text:0040125C div ecx .text:0040125E add dl, 30h ; '0' .text:00401261 mov (g_string+1)[ebx], dl ; String1[index] = (DWORD)g_undef1 / 10 + 0x30 .text:00401267 inc ebx ; index++; .text:00401268 test eax, eax .text:0040126A jnz short do_while6 ; ; ; .text:0040126C push (offset g_string+1) ; lpString .text:00401271 call lstrlenA .text:00401276 xor ebx, ebx .text:00401278 do_while7: ; CODE XREF: DialogFunc+1D2↓j .text:00401278 mov cl, g_string[eax] .text:0040127E mov String2[ebx], cl .text:00401284 inc ebx .text:00401285 dec eax .text:00401286 jnz short do_while7 ; ; ; .text:00401288 push offset String2 ; lpString2 .text:0040128D push (offset g_string+1) ; lpString1 .text:00401292 call lstrcpyA ; ; ; .text:00401297 push 200h ; cchMax .text:0040129C push offset password ; lpString .text:004012A1 push 64h ; 'd' ; nIDDlgItem .text:004012A3 push [ebp+hWnd] ; hDlg .text:004012A6 call GetDlgItemTextA ; ; ; .text:004012AB push (offset g_string+1) ; lpString2 .text:004012B0 push offset password ; lpString1 .text:004012B5 call lstrcmpA .text:004012BA or eax, eax .text:004012BC jnz short check_err ; ; ; .text:004012BE push 40h ; '@' ; uType .text:004012C0 push offset aGoodBoy ; "Good boy..." .text:004012C5 push offset aYepThatsTheRig ; "Yep, thats the right code!\n\rGo write "... .text:004012CA push [ebp+hWnd] ; hWnd .text:004012CD call MessageBoxA .text:004012D2 jmp short fun_end
算法有点长,但是还原后实际上并没有多少。KEY完全是由name计算出来的。里面多少还是有点坑。比如说在使用lstrlenA()计算name的长度时,在函数内部会将char*的buf指针+1。但是我在vs2019使用lstrlenA()字符串buf指针并不会+1。又再者里面有用到一个全局的g_key开始在IDA分析时我是当做2个keybuf来进行编写代码的,但是调试后发现就算定义两个相邻的数组在实际内存中中间或多或少会有多个’0x0’来间隔。也有可能是被IDA自动分析给误导了。
0x4 C语言源码
// ConsoleApplication1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include <iostream> #include <stdlib.h> #include <windows.h> BYTE g_key1[22] = { 0x00, 0xAA, 0x89, 0x0C4, 0x0FE, 0x46, 0x78, 0x0F0, 0x0D0, 0x3 ,0x0E7, 0x0F7, 0x0FD, 0x0F4, 0x0E7, 0x0B9, 0x0B5, 0x1B, 0x0C9, 0x50, 0x73, 0x0 }; bool check(char* user, char* password) { int user_length = lstrlenA(user); if (user_length < 3) { return false; } user = user + 1; char g_old_name[256] = { 0x0 }; for (int i = 0, j = 0; i < user_length; i++) { char t = user[i]; char tt = t ^ g_key1[j+1]; j++; user[i] = tt; g_key1[j] = t; if (j == 5) { j = 0; } } for (int i = 0, j = 0; i < user_length; i++) { char t = g_key1[6 + j]; char tt = user[user_length - i - 1]; char ttt = user[user_length - i - 1]; user[user_length - i - 1] = tt ^ t; j++; g_key1[5 + j] = ttt; if (j == 5) { j = 0; } } for (int i = 0, j = 0; i < user_length; i++) { char t = user[i]; char tt = g_key1[j + 0xB]; char ttt = tt ^ t; j++; user[i] = ttt; g_key1[0xA + j] = t; if (j == 5) { j = 0; } } for (int i = 0, j = 0; i < user_length; i++) { char t = g_key1[j + 0x10]; char tt = user[user_length - i - 1]; char ttt = t ^ tt; j++; user[user_length - i - 1] = ttt; g_key1[j + 0xF] = tt; if (j == 5) { j = 0; } } BYTE g_undef1[4] = { 0x0 }; for (int i = 0; i < user_length; i++) { int j = i & 3; char t = g_undef1[j]; char tt = user[i] + t; g_undef1[j] = tt; } char String1[512] = { 0x0 }; int i = 0; int s = 0; int y = 0; do { int p = *(int*)g_undef1; s = p / 10; y = p % 10; *(int*)g_undef1 = s; String1[i+1] = (y + 0x30); i++; } while (s != 0); int s1 = strlen(String1 + 1); char String2[512] = { 0x0 }; i = 0; for (s1; s1 != 0; s1--) { String2[i++] = String1[s1]; } strcpy(String1 + 1, String2); if (strcmp(String1 + 1, password) != 0) { return false; } return true; } int main() { char name[256] = "CTFHUB"; //char password[256] = { 0x0 }; char password[256] = "111111111111"; //reg(name, password); if (check(name, password)) { std::cout << "ok \n"; } return 0; }
发表评论