-
-
[原创]看雪.纽盾 KCTF晋级赛2019 Q2 第四题 达芬奇密码
-
发表于: 2019-6-18 16:01 3546
-
运行程序,随便输入点什么,信息框提示“Wrong!”,在MessageBoxW下断点,回溯就能定位到点击事件了。
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 | 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函数如下
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 | 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函数如下
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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | 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,结合代码可以得到一道方程
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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | 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),接下来的可以写代码解决,具体代码如下
赞赏
他的文章
赞赏
雪币:
留言: