-
-
[原创]看雪.纽盾 KCTF晋级赛2019 Q2 第四题 达芬奇密码
-
发表于: 2019-6-18 16:01 3381
-
运行程序,随便输入点什么,信息框提示“Wrong!”,在MessageBoxW下断点,回溯就能定位到点击事件了。
int __thiscall click(CWnd *this) { CWnd *v1; // esi int v2; // eax WCHAR String; // [esp+Ch] [ebp-310h] char v5; // [esp+Eh] [ebp-30Eh] char input; // [esp+20Ch] [ebp-110h] char v7; // [esp+20Dh] [ebp-10Fh] DWORD v8; // [esp+30Ch] [ebp-10h] CWnd *v9; // [esp+310h] [ebp-Ch] int v10; // [esp+314h] [ebp-8h] DWORD flOldProtect; // [esp+318h] [ebp-4h] v1 = this; v9 = this; String = 0; memset(&v5, 0, 0x1FEu); input = 0; memset(&v7, 0, 0xFFu); CWnd::GetDlgItemTextW(v1, 1000, &String, 20); if ( wcslen(&String) == 16 ) // key的长度为16 { v2 = 0; while ( !(*(&String + v2) & 0xFF00) ) // unicode转ascii { *(&input + v2) = *((_BYTE *)&String + 2 * v2); if ( ++v2 >= 16 ) { v8 = 64; flOldProtect = 0; VirtualProtect(check, 0xD17u, 0x40u, &flOldProtect); if ( GetLastError() ) return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); qmemcpy(check, byte_11C47B8, 0x330u); // 修改check函数 VirtualProtect(check, 0xD17u, flOldProtect, &v8); if ( !GetLastError() ) { v10 = 0; v10 = check(&input); // 调用 if ( v10 == 1 ) return CWnd::MessageBoxW(v9, L"Congratulations! You are right!", 0, 0); } v1 = v9; return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); } } } return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); }
代码调用的是修改后的check函数,我们可以动态调试,在执行过qmemcpy后,把修改后的check函数复制到文件中,方便IDA分析。真正的check函数如下
int __thiscall click(CWnd *this) { CWnd *v1; // esi int v2; // eax WCHAR String; // [esp+Ch] [ebp-310h] char v5; // [esp+Eh] [ebp-30Eh] char input; // [esp+20Ch] [ebp-110h] char v7; // [esp+20Dh] [ebp-10Fh] DWORD v8; // [esp+30Ch] [ebp-10h] CWnd *v9; // [esp+310h] [ebp-Ch] int v10; // [esp+314h] [ebp-8h] DWORD flOldProtect; // [esp+318h] [ebp-4h] v1 = this; v9 = this; String = 0; memset(&v5, 0, 0x1FEu); input = 0; memset(&v7, 0, 0xFFu); CWnd::GetDlgItemTextW(v1, 1000, &String, 20); if ( wcslen(&String) == 16 ) // key的长度为16 { v2 = 0; while ( !(*(&String + v2) & 0xFF00) ) // unicode转ascii { *(&input + v2) = *((_BYTE *)&String + 2 * v2); if ( ++v2 >= 16 ) { v8 = 64; flOldProtect = 0; VirtualProtect(check, 0xD17u, 0x40u, &flOldProtect); if ( GetLastError() ) return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); qmemcpy(check, byte_11C47B8, 0x330u); // 修改check函数 VirtualProtect(check, 0xD17u, flOldProtect, &v8); if ( !GetLastError() ) { v10 = 0; v10 = check(&input); // 调用 if ( v10 == 1 ) return CWnd::MessageBoxW(v9, L"Congratulations! You are right!", 0, 0); } v1 = v9; return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); } } } return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0); }
代码调用的是修改后的check函数,我们可以动态调试,在执行过qmemcpy后,把修改后的check函数复制到文件中,方便IDA分析。真正的check函数如下
signed int __cdecl check(char *input) { signed int v1; // eax char v2; // cl signed int v3; // ecx signed int v4; // eax signed int v5; // eax signed int v6; // esi signed int v7; // ecx __int16 v8; // dx char *mul; // edi __int16 v10; // ax signed int v11; // eax signed int v12; // ecx unsigned __int16 v13; // bx signed int v14; // esi signed int v15; // ecx __int16 v16; // dx char *v17; // edi __int16 v18; // ax signed int v19; // eax signed int v20; // ecx unsigned __int16 v21; // bx unsigned int v22; // eax signed int v23; // ecx unsigned __int16 v24; // dx char v25; // dl signed int v26; // eax __int16 v27; // si int v28; // eax int true; // [esp+8h] [ebp-90h] int v31; // [esp+Ch] [ebp-8Ch] int v32; // [esp+10h] [ebp-88h] int v33; // [esp+14h] [ebp-84h] int v34; // [esp+18h] [ebp-80h] int v35; // [esp+1Ch] [ebp-7Ch] int v36; // [esp+20h] [ebp-78h] int v37; // [esp+24h] [ebp-74h] int calc1; // [esp+28h] [ebp-70h] int calc2; // [esp+2Ch] [ebp-6Ch] int v40; // [esp+30h] [ebp-68h] int v41; // [esp+34h] [ebp-64h] int other; // [esp+38h] [ebp-60h] int v43; // [esp+3Ch] [ebp-5Ch] int re1; // [esp+40h] [ebp-58h] int re2; // [esp+44h] [ebp-54h] int broad2; // [esp+48h] [ebp-50h] int v47; // [esp+4Ch] [ebp-4Ch] int v48; // [esp+50h] [ebp-48h] int v49; // [esp+54h] [ebp-44h] char v50; // [esp+58h] [ebp-40h] int result; // [esp+5Ch] [ebp-3Ch] int v52; // [esp+60h] [ebp-38h] int v53; // [esp+64h] [ebp-34h] int v54; // [esp+68h] [ebp-30h] char v55; // [esp+6Ch] [ebp-2Ch] int broad1; // [esp+70h] [ebp-28h] int v57; // [esp+74h] [ebp-24h] int v58; // [esp+78h] [ebp-20h] int v59; // [esp+7Ch] [ebp-1Ch] char v60; // [esp+80h] [ebp-18h] int v61; // [esp+84h] [ebp-14h] int v62; // [esp+88h] [ebp-10h] int v63; // [esp+8Ch] [ebp-Ch] int v64; // [esp+90h] [ebp-8h] char v65; // [esp+94h] [ebp-4h] v31 = 0x646E9881; v1 = 0; true = 0xE38C9616; v32 = 0x81DC0884; v33 = 0x4F484DBE; v34 = 0; v35 = 0; v36 = 0; v37 = 0; calc1 = 0; calc2 = 0; re1 = 0; re2 = 0; do { v2 = *((_BYTE *)&v32 + v1) ^ input[v1 + 8]; *((_BYTE *)&calc1 + v1) = *((_BYTE *)&true + v1) ^ *((_BYTE *)&true + v1 + input - (char *)&true); *((_BYTE *)&re1 + v1++) = v2; } while ( v1 < 8 ); // 异或解密两个long long数字 true = 0; broad1 = 0; v57 = 0; v58 = 0; v59 = 0; v60 = 0; v61 = 0; v62 = 0; v63 = 0; v64 = 0; v65 = 0; broad2 = 0; v47 = 0; v48 = 0; v49 = 0; v50 = 0; result = 0; v52 = 0; v53 = 0; v54 = 0; v55 = 0; v31 = 0; v32 = 0; v33 = 0; LOBYTE(v34) = 0; v3 = 8; LOBYTE(true) = 8; v4 = 7; do { if ( *((_BYTE *)&calc1 + v4) ) // 最后一位char不为0,即第一个数字要>0x00FFFFFFFFFFFFFF break; --v3; --v4; } while ( v4 >= 0 ); if ( v3 == 8 ) { v5 = 7; do { if ( *((_BYTE *)&re1 + v5) ) // 同上 break; --v3; --v5; } while ( v5 >= 0 ); if ( v3 == 8 && !(calc2 & 0xF0000000) ) // calc2不得大于0x0fffffff,即第一个数字要<0x0FFFFFFFFFFFFFFF { v6 = 0; do { v40 = 0; v41 = 0; other = 0; v43 = 0; v7 = 0; v8 = *((unsigned __int8 *)&calc1 + v6); mul = (char *)&v40 + v6; do { // 每位key乘以key,等价于求平方 v10 = *((unsigned __int8 *)&other + v6) + v8 * *((unsigned __int8 *)&calc1 + v7); mul[v7] = *((_BYTE *)&other + v6) + v8 * *((_BYTE *)&calc1 + v7); ++v7; *((_BYTE *)&other + v6) = HIBYTE(v10); } while ( v7 < 8 ); LOBYTE(v11) = 0; v12 = 0; do { v13 = (char)v11 + *((unsigned __int8 *)&broad1 + v12 + v6) + (unsigned __int8)mul[v12]; *((_BYTE *)&broad1 + v12++ + v6) = v13; v11 = (signed int)v13 >> 8; } while ( v12 < 9 ); ++v6; } while ( v6 < 8 ); v14 = 0; do // 跟上面一模一样 { v40 = 0; v41 = 0; other = 0; v43 = 0; v15 = 0; v16 = *((unsigned __int8 *)&re1 + v14); v17 = (char *)&v40 + v14; do { v18 = *((unsigned __int8 *)&other + v14) + v16 * *((unsigned __int8 *)&re1 + v15); v17[v15] = *((_BYTE *)&other + v14) + v16 * *((_BYTE *)&re1 + v15); ++v15; *((_BYTE *)&other + v14) = HIBYTE(v18); } while ( v15 < 8 ); LOBYTE(v19) = 0; v20 = 0; do { v21 = (char)v19 + *((unsigned __int8 *)&v61 + v20 + v14) + (unsigned __int8)v17[v20]; *((_BYTE *)&v61 + v20++ + v14) = v21; v19 = (signed int)v21 >> 8; } while ( v20 < 9 ); ++v14; } while ( v14 < 8 ); LOBYTE(v22) = v50; v23 = 0; do { v24 = (unsigned __int8)v22 + 7 * *((unsigned __int8 *)&v61 + v23);// 第二个数字计算平方后乘以7 *((_BYTE *)&broad2 + v23++) = v24; v22 = (unsigned int)v24 >> 8; } while ( v23 < 17 ); v50 = HIBYTE(v24); v25 = 0; v26 = 0; do { v27 = *((unsigned __int8 *)&broad1 + v26) - *((unsigned __int8 *)&broad2 + v26) - v25;// 两个结果相减 *((_BYTE *)&result + v26) = v27; if ( v27 < 0 ) v25 = 1; ++v26; } while ( v26 < 17 ); if ( !v25 ) { v28 = 0; while ( *((_BYTE *)&result + v28) == *((_BYTE *)&true + v28) )// 对比结果是否等于8 { if ( ++v28 >= 17 ) return 1; } } } } return 0; }
我们设解密出来的第一个数字为x,第二个为y,结合代码可以得到一道方程
signed int __cdecl check(char *input) { signed int v1; // eax char v2; // cl signed int v3; // ecx signed int v4; // eax signed int v5; // eax signed int v6; // esi signed int v7; // ecx __int16 v8; // dx char *mul; // edi __int16 v10; // ax signed int v11; // eax signed int v12; // ecx unsigned __int16 v13; // bx signed int v14; // esi signed int v15; // ecx __int16 v16; // dx char *v17; // edi __int16 v18; // ax signed int v19; // eax signed int v20; // ecx unsigned __int16 v21; // bx unsigned int v22; // eax signed int v23; // ecx unsigned __int16 v24; // dx char v25; // dl signed int v26; // eax __int16 v27; // si int v28; // eax int true; // [esp+8h] [ebp-90h] int v31; // [esp+Ch] [ebp-8Ch] int v32; // [esp+10h] [ebp-88h] int v33; // [esp+14h] [ebp-84h] int v34; // [esp+18h] [ebp-80h] int v35; // [esp+1Ch] [ebp-7Ch] int v36; // [esp+20h] [ebp-78h] int v37; // [esp+24h] [ebp-74h] int calc1; // [esp+28h] [ebp-70h] int calc2; // [esp+2Ch] [ebp-6Ch] int v40; // [esp+30h] [ebp-68h] int v41; // [esp+34h] [ebp-64h] int other; // [esp+38h] [ebp-60h] int v43; // [esp+3Ch] [ebp-5Ch] int re1; // [esp+40h] [ebp-58h] int re2; // [esp+44h] [ebp-54h] int broad2; // [esp+48h] [ebp-50h] int v47; // [esp+4Ch] [ebp-4Ch] int v48; // [esp+50h] [ebp-48h] int v49; // [esp+54h] [ebp-44h] char v50; // [esp+58h] [ebp-40h] int result; // [esp+5Ch] [ebp-3Ch] int v52; // [esp+60h] [ebp-38h] int v53; // [esp+64h] [ebp-34h] int v54; // [esp+68h] [ebp-30h] char v55; // [esp+6Ch] [ebp-2Ch] int broad1; // [esp+70h] [ebp-28h] int v57; // [esp+74h] [ebp-24h] int v58; // [esp+78h] [ebp-20h] int v59; // [esp+7Ch] [ebp-1Ch] char v60; // [esp+80h] [ebp-18h] int v61; // [esp+84h] [ebp-14h] int v62; // [esp+88h] [ebp-10h] int v63; // [esp+8Ch] [ebp-Ch] int v64; // [esp+90h] [ebp-8h] char v65; // [esp+94h] [ebp-4h] v31 = 0x646E9881; v1 = 0; true = 0xE38C9616; v32 = 0x81DC0884; v33 = 0x4F484DBE; v34 = 0; v35 = 0; v36 = 0; v37 = 0; calc1 = 0; calc2 = 0; re1 = 0; re2 = 0; do { v2 = *((_BYTE *)&v32 + v1) ^ input[v1 + 8]; *((_BYTE *)&calc1 + v1) = *((_BYTE *)&true + v1) ^ *((_BYTE *)&true + v1 + input - (char *)&true); *((_BYTE *)&re1 + v1++) = v2; } while ( v1 < 8 ); // 异或解密两个long long数字 true = 0; broad1 = 0; v57 = 0; v58 = 0; v59 = 0; v60 = 0; v61 = 0; v62 = 0; v63 = 0; v64 = 0; v65 = 0; broad2 = 0; v47 = 0; v48 = 0; v49 = 0; v50 = 0; result = 0; v52 = 0; v53 = 0; v54 = 0; v55 = 0; v31 = 0; v32 = 0; v33 = 0; LOBYTE(v34) = 0; v3 = 8; LOBYTE(true) = 8; v4 = 7; do { if ( *((_BYTE *)&calc1 + v4) ) // 最后一位char不为0,即第一个数字要>0x00FFFFFFFFFFFFFF break; --v3; --v4; } while ( v4 >= 0 ); if ( v3 == 8 ) { v5 = 7; do { if ( *((_BYTE *)&re1 + v5) ) // 同上 break; --v3; --v5; } while ( v5 >= 0 ); if ( v3 == 8 && !(calc2 & 0xF0000000) ) // calc2不得大于0x0fffffff,即第一个数字要<0x0FFFFFFFFFFFFFFF { v6 = 0; do { v40 = 0; v41 = 0; other = 0; v43 = 0; v7 = 0; v8 = *((unsigned __int8 *)&calc1 + v6); mul = (char *)&v40 + v6; do { // 每位key乘以key,等价于求平方 v10 = *((unsigned __int8 *)&other + v6) + v8 * *((unsigned __int8 *)&calc1 + v7); mul[v7] = *((_BYTE *)&other + v6) + v8 * *((_BYTE *)&calc1 + v7); ++v7; *((_BYTE *)&other + v6) = HIBYTE(v10); } while ( v7 < 8 ); LOBYTE(v11) = 0; v12 = 0; do { v13 = (char)v11 + *((unsigned __int8 *)&broad1 + v12 + v6) + (unsigned __int8)mul[v12]; *((_BYTE *)&broad1 + v12++ + v6) = v13; v11 = (signed int)v13 >> 8; } while ( v12 < 9 ); ++v6; } while ( v6 < 8 ); v14 = 0; do // 跟上面一模一样 { v40 = 0; v41 = 0; other = 0; v43 = 0; v15 = 0; v16 = *((unsigned __int8 *)&re1 + v14); v17 = (char *)&v40 + v14; do { v18 = *((unsigned __int8 *)&other + v14) + v16 * *((unsigned __int8 *)&re1 + v15); v17[v15] = *((_BYTE *)&other + v14) + v16 * *((_BYTE *)&re1 + v15); ++v15; *((_BYTE *)&other + v14) = HIBYTE(v18); } while ( v15 < 8 ); LOBYTE(v19) = 0; v20 = 0; do { v21 = (char)v19 + *((unsigned __int8 *)&v61 + v20 + v14) + (unsigned __int8)v17[v20]; *((_BYTE *)&v61 + v20++ + v14) = v21; v19 = (signed int)v21 >> 8; } while ( v20 < 9 ); ++v14; } while ( v14 < 8 ); LOBYTE(v22) = v50; v23 = 0; do { v24 = (unsigned __int8)v22 + 7 * *((unsigned __int8 *)&v61 + v23);// 第二个数字计算平方后乘以7 *((_BYTE *)&broad2 + v23++) = v24; v22 = (unsigned int)v24 >> 8; } while ( v23 < 17 ); v50 = HIBYTE(v24); v25 = 0; v26 = 0; do { v27 = *((unsigned __int8 *)&broad1 + v26) - *((unsigned __int8 *)&broad2 + v26) - v25;// 两个结果相减 *((_BYTE *)&result + v26) = v27; if ( v27 < 0 ) v25 = 1; ++v26; } while ( v26 < 17 ); if ( !v25 ) { v28 = 0; while ( *((_BYTE *)&result + v28) == *((_BYTE *)&true + v28) )// 对比结果是否等于8 { if ( ++v28 >= 17 ) return 1; } } } } return 0; }
我们设解密出来的第一个数字为x,第二个为y,结合代码可以得到一道方程
x*x-7*y*y=8 (x>0x00FFFFFFFFFFFFFF)
这是非标准佩尔方程,解法参考维基百科
我们可以算出方程的初始解为(6,2),接下来的可以写代码解决,具体代码如下
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
他的文章
看原图
赞赏
雪币:
留言: