-
-
[原创] 看雪ctf 207 第三题-CrackMe
-
2017-10-30 11:06 2911
-
第三道题目出的有点问题,导致了问题多解的情况,有点忙,正规的思路还没有来的及分析,只能写一下非预期的做法了 = =
基本逻辑
运行效果如上,要求输入一个序列号即输入ok,错误则什么都不输出
主要函数如下
int __stdcall main_logic_434EF0(HWND hDlg, int a2, int a3, int a4) { int v4; // edx@1 int v5; // eax@14 int v6; // edx@14 int v7; // ecx@14 int v8; // edx@14 int v9; // edx@14 int v10; // ST0C_4@17 CHAR *v11; // esi@17 int v12; // eax@17 int v13; // eax@19 int v14; // edx@19 int v15; // ecx@19 signed int v16; // eax@20 int v17; // ST0C_4@22 int v18; // ST08_4@22 int v19; // eax@22 int v20; // edx@22 int v21; // ecx@22 int v23; // [esp+0h] [ebp-1A4Ch]@14 int v24; // [esp+4h] [ebp-1A48h]@22 int v25; // [esp+8h] [ebp-1A44h]@22 int v26; // [esp+Ch] [ebp-1A40h]@1 int i; // [esp+1C4h] [ebp-1888h]@14 char hex_string[1032]; // [esp+1D0h] [ebp-187Ch]@16 char v29[40]; // [esp+5D8h] [ebp-1474h]@14 int v30; // [esp+600h] [ebp-144Ch]@14 char v31; // [esp+60Ch] [ebp-1440h]@14 char v32; // [esp+60Dh] [ebp-143Fh]@14 char bdecode_2; // [esp+A14h] [ebp-1038h]@14 char v34; // [esp+A15h] [ebp-1037h]@14 char bdecode_1; // [esp+E1Ch] [ebp-C30h]@14 char v36; // [esp+E1Dh] [ebp-C2Fh]@14 CHAR String; // [esp+1224h] [ebp-828h]@14 char v38; // [esp+1225h] [ebp-827h]@14 int v39; // [esp+162Ch] [ebp-420h]@14 char v40; // [esp+1638h] [ebp-414h]@1 char v41; // [esp+1639h] [ebp-413h]@1 int v42; // [esp+1A40h] [ebp-Ch]@1 unsigned int v43; // [esp+1A48h] [ebp-4h]@1 int savedregs; // [esp+1A4Ch] [ebp+0h]@1 memset(&v26, 0xCCu, 0x1A40u); v43 = (unsigned int)&savedregs ^ dword_49B344; v42 = 0; v40 = 0; memset_42D5E6((int)&v41, 0, 0x3FF); v26 = a2; if ( a2 == 0x10 ) ExitProcess(0); //anti if ( v26 == 0x110 ) { v42 = sub_42D4F1(); if ( v42 == 1 ) ExitProcess(0);//anti v42 = 0; v42 = sub_42E428(); if ( v42 == 1 ) ExitProcess(0); v42 = 0; v42 = sub_42D825(); if ( v42 == 1 ) ExitProcess(0);//anti sub_42D14F(hDlg, 1); goto LABEL_21; } if ( v26 != 0x111 ) { LABEL_21: v16 = 0; goto LABEL_22; } v26 = (unsigned __int16)a3; if ( (unsigned __int16)a3 == 0x3EA ) { String = 0; memset_42D5E6((int)&v38, 0, 0x3FF); bdecode_1 = 0; memset_42D5E6((int)&v36, 0, 0x3FF); v5 = GetDlgItemTextA(hDlg, 0x3E9, &String, 0x401); //string you input v39 = sub_42DE51(v7, v6, &v23 == &v23, v5); bdecode_2 = 0; memset_42D5E6((int)&v34, 0, 0x3FF); base64_decode_42D267(v8, (int)&String, 0x400, (int)&bdecode_1);// base64decode v31 = 0; memset_42D5E6((int)&v32, 0, 0x3FF); base64_decode_42D267(v9, (int)&bdecode_1, 0x400, (int)&bdecode_2); sub_42D96A((int)&bdecode_2, (int)&v31, 0x400); v30 = 3; sub_42DA78((int)&bdecode_2, 3, (int)v29); for ( i = 0; i < 0x20; ++i ) sub_42DF05((int)&hex_string[2 * i], "%02x", v29[i]); v10 = strlen_42D794(hex_string); v11 = &String + strlen_42D794(&String); v12 = strlen_42D794(hex_string); if ( !check1_42DB27((int)hex_string, (int)&v11[-v12], v10) )// 6 == { sub_42D0B4(); if ( (unsigned __int8)check2_42D9AB((int)&unk_49B000, (int)&v31) == 1 ) { v13 = MessageBoxA(0, "ok", "CrackMe", 0); sub_42DE51(v15, v14, &v23 == &v23, v13); } } } v16 = 1; LABEL_22: v17 = v4; v18 = v16; sub_42D65E((int)&savedregs, (int)&dword_435250); v19 = sub_42D1E5(v18, v17, (unsigned int)&savedregs ^ v43, v25, v23, v24, v26); return sub_42DE51(v21, v20, 1, v19); }
程序加上了很多反调试,一言不合就 exitprocess
基本逻辑是
- 首先获取用户的输入 ==> GetDlgItemTextA
- 然后对用户的输入进行两次base64decode(这是后面调试的时候发现的)
- 程序会生成一个字符串
183920f00e15a0433ee3a8fc90dd9ac164c4142ccf63ca189a8f645ec96ff8de - 接下来就是两个 check ,这两个check过了就会 弹框 ok
两个check 函数
check1
for ( i = 0; i < 0x20; ++i ) sub_42DF05((int)&hex_string[2 * i], "%02x", v29[i]); v10 = strlen_42D794(hex_string); v11 = &String + strlen_42D794(&String); v12 = strlen_42D794(hex_string); if ( !check1_42DB27((int)hex_string, (int)&v11[-v12], v10) ) {
string 是程序生成的字符串,是固定的, v11 是我们输入的字符串,v12,v10 是两个串的长度,所以输入的参数是
( 程序串,输入串-len(程序串),len(程序串))
check1 的代码有点长, 不过调试的时候发现 a3 也就是上面 v10 的长度是固定的0x40 ,所以会直接跑到 default那里执行
int __cdecl sub_43AF50(int a1, int a2, int a3) { int result; // eax@11 int v4; // [esp+0h] [ebp-28h]@20 int v5; // [esp+4h] [ebp-24h]@15 int v6; // [esp+8h] [ebp-20h]@13 int v7; // [esp+Ch] [ebp-1Ch]@7 int v8; // [esp+10h] [ebp-18h]@5 int v9; // [esp+14h] [ebp-14h]@3 int v10; // [esp+1Ch] [ebp-Ch]@2 int v11; // [esp+1Ch] [ebp-Ch]@4 int v12; // [esp+1Ch] [ebp-Ch]@6 int v13; // [esp+1Ch] [ebp-Ch]@12 int v14; // [esp+1Ch] [ebp-Ch]@14 int v15; // [esp+1Ch] [ebp-Ch]@19 switch ( a3 ) { case 0: result = 0; break; case 1: result = equal_43B180((unsigned __int8 *)a1, (unsigned __int8 *)a2); break; case 2: v15 = equal_43B180((unsigned __int8 *)a1, (unsigned __int8 *)a2); if ( v15 ) v4 = v15; else v4 = equal_43B180((unsigned __int8 *)(a1 + 1), (unsigned __int8 *)(a2 + 1)); result = v4; break; case 3: v13 = equal_43B180((unsigned __int8 *)a1, (unsigned __int8 *)a2); if ( v13 ) { v6 = v13; } else { v14 = equal_43B180((unsigned __int8 *)(a1 + 1), (unsigned __int8 *)(a2 + 1)); if ( v14 ) v5 = v14; else v5 = equal_43B180((unsigned __int8 *)(a1 + 2), (unsigned __int8 *)(a2 + 2)); v6 = v5; } result = v6; break; case 4: v10 = equal_43B180((unsigned __int8 *)a1, (unsigned __int8 *)a2); if ( v10 ) { v9 = v10; } else { v11 = equal_43B180((unsigned __int8 *)(a1 + 1), (unsigned __int8 *)(a2 + 1)); if ( v11 ) { v8 = v11; } else { v12 = equal_43B180((unsigned __int8 *)(a1 + 2), (unsigned __int8 *)(a2 + 2)); if ( v12 ) v7 = v12; else v7 = equal_43B180((unsigned __int8 *)(a1 + 3), (unsigned __int8 *)(a2 + 3)); v8 = v7; } v9 = v8; } result = v9; break; default: result = sub_43B1F0(a1, a2, a3); break; } return result; }
sub_43B1f0 如下
也是一堆的 case什么的, 不过前面是进行字符串的比较,如果字符串一样就可以过,跳到check2,虽然不知道是怎么回事,但是总之是进入第二个check了,先看看第二个check吧
while ( a3 >= 0x20 ) { v6 = equal4_43BB60((unsigned __int8 *)a1, (unsigned __int8 *)a2); if ( v6 ) return v6; v7 = equal4_43BB60((unsigned __int8 *)(a1 + 4), (unsigned __int8 *)(a2 + 4)); if ( v7 ) return v7; v8 = equal4_43BB60((unsigned __int8 *)(a1 + 8), (unsigned __int8 *)(a2 + 8)); if ( v8 ) return v8; v9 = equal4_43BB60((unsigned __int8 *)(a1 + 0xC), (unsigned __int8 *)(a2 + 0xC)); if ( v9 ) return v9; v10 = equal4_43BB60((unsigned __int8 *)(a1 + 0x10), (unsigned __int8 *)(a2 + 0x10)); if ( v10 ) return v10; v11 = equal4_43BB60((unsigned __int8 *)(a1 + 0x14), (unsigned __int8 *)(a2 + 0x14)); if ( v11 ) return v11; v12 = equal4_43BB60((unsigned __int8 *)(a1 + 0x18), (unsigned __int8 *)(a2 + 0x18)); if ( v12 ) return v12; v13 = equal4_43BB60((unsigned __int8 *)(a1 + 0x1C), (unsigned __int8 *)(a2 + 0x1C)); if ( v13 ) return v13; a1 += 0x20; a2 += 0x20; a3 -= 0x20; } ......
check2
check2 的输入是 一堆 01, base64decode(base64decode(输入串))
进去看看,发现程序有点复杂呀
while ( 1 ) { v3 = string; input_chr = (char)*string; if ( input_chr == 0x20 ) break; if ( v13 != 8 || v12 != 3 ) { input_chr = (char)*string; if ( input_chr == 'z' ) { v3 = (_BYTE *)(v10 + v12); if ( v10 + v12 >= 0xA ) { LOBYTE(v3) = 0; return sub_42DE51(input_chr, v2, 1, (int)v3); } if ( !*(_DWORD *)(unknow + 0x28 * (v10 + v12) + 4 * v13) ) v12 += v10; } if ( *string == 'l' && v8 + v13 < 0xA ) { v3 = (_BYTE *)(unknow + 0x28 * v12); input_chr = v8 + v13; if ( *(_DWORD *)&v3[4 * (v8 + v13)] ) { LOBYTE(v3) = 0; return sub_42DE51(input_chr, v2, 1, (int)v3); } *(_DWORD *)(unknow + 0x28 * v12 + 4 * v13) = 4; v13 += v8; } if ( *string == 'q' && ((v11 + v12) & 0x80000000) == 0 ) { v3 = (_BYTE *)(unknow + 0x28 * (v11 + v12)); input_chr = v13; if ( *(_DWORD *)&v3[4 * v13] ) { LOBYTE(v3) = 0; return sub_42DE51(input_chr, v2, 1, (int)v3); } *(_DWORD *)(unknow + 0x28 * v12 + 4 * v13) = 4; v12 += v11; } if ( *string == 'p' && ((v9 + v13) & 0x80000000) == 0 ) { v3 = (_BYTE *)(unknow + 0x28 * v12); input_chr = v9 + v13; if ( *(_DWORD *)&v3[4 * (v9 + v13)] ) { LOBYTE(v3) = 0; return sub_42DE51(input_chr, v2, 1, (int)v3); } v13 += v9; } } ++string; } LOBYTE(v3) = 1; return sub_42DE51(input_chr, v2, 1, (int)v3); }
一个byte 一个byte的取 base64decode(base64decode(输入串))
然后后面进行一堆的操作和判断,return 0,不过最前面的地方有一个
input_chr==0x20 就break 了,直接就跳到了后面 return 1
所以这里只要可以构造出一个 0x20 出来就赢了
最后构造
II183920f00e15a0433ee3a8fc90dd9ac164c4142ccf63ca189a8f645ec96ff8de
可以直接过
呃,好吧,这怎么看都不是flag的样子
发现有一些类似莫斯点码的东西,不知道有什么用
嘛,暂时就先到这里吧,期待大佬 正规writeup = =
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。