LuckyStar hctf2018
程序注册有TLS回调函数
char __stdcall TlsCallback_0(int a1, int a2, int a3) { char result; // al HMODULE v4; // eax HMODULE v5; // eax HMODULE v6; // eax int (__usercall *ptr_main)@<eax>(int@<ebp>); // esi signed int v8; // edi HANDLE v9; // eax result = a2; if ( a2 == 1 ) { v4 = GetModuleHandleA("ntdll.dll"); dword_407378 = sub_401360(v4, "a7d7bcc95a86a6df3b0a1ccb3c69d440");//此处应该是获取库中的某个函数,我们动态调试的时候看一下 v5 = GetModuleHandleA("ntdll.dll"); dword_407380 = (int (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD))sub_401360(v5, "8eb35a28209979fe6a9983cff0d23c5a"); v6 = GetModuleHandleA("kernel32.dll"); dword_407374 = sub_401360(v6, "05577e1568efa10b8166728bfb414c59"); srand(0x61616161u);//设置随机数的种子(影响后面main函数中的rand) ptr_main = main_401780;//main函数,IDA载入发现已经加密,无法正常解析。 v8 = 440; do { ptr_main = (int (__usercall *)@<eax>(int@<ebp>))((char *)ptr_main + 1); result = byte_417000[rand() % 8216]; *((_BYTE *)ptr_main - 1) ^= result;//对main进行异或解密 --v8; } while ( v8 ); } else if ( a2 == 3 ) { v9 = GetCurrentThread(); result = dword_407380(v9, 17, 0, 0); } return result; }
我们动态调试,查看前面要用到库中的哪些函数,并且在main函数解密完成后dump文件,以备后面IDA分析。
发现前面获取的库函数与反调试相关。
重命名后的TLS
char __stdcall TlsCallback_0(int a1, int a2, int a3) { char result; // al HMODULE v4; // eax HMODULE v5; // eax HMODULE v6; // eax int (__usercall *ptr_main)@<eax>(int@<ebp>); // esi signed int v8; // edi HANDLE v9; // eax result = a2; if ( a2 == 1 ) { v4 = GetModuleHandleA("ntdll.dll"); NtQuerySystemInformation = sub_401360((int)v4, "a7d7bcc95a86a6df3b0a1ccb3c69d440"); v5 = GetModuleHandleA("ntdll.dll"); NtSetInformationThread = (int (__stdcall *)(_DWORD, _DWORD, _DWORD, _DWORD))sub_401360( (int)v5, "8eb35a28209979fe6a9983cff0d23c5a"); v6 = GetModuleHandleA("kernel32.dll"); *(_DWORD *)CheckRemoteDebuggerPresent = sub_401360((int)v6, "05577e1568efa10b8166728bfb414c59"); srand(0x61616161u); ptr_main = main_401780; v8 = 440; do { ptr_main = (int (__usercall *)@<eax>(int@<ebp>))((char *)ptr_main + 1); result = byte_417000[rand() % 8216]; *((_BYTE *)ptr_main - 1) ^= result; --v8; } while ( v8 ); } else if ( a2 == 3 ) { v9 = GetCurrentThread(); result = NtSetInformationThread(v9, 17, 0, 0); } return result; }
IDA分析mian解密完成时dump的文件
int __usercall sub_401780@<eax>(int a1@<ebp>, int a2@<edi>, int a3@<esi>) { signed int v3; // esi int v4; // ST08_4 int v5; // ST0C_4 int v6; // ST10_4 int v7; // ST14_4 int v8; // ST18_4 int v9; // ST1C_4 int v10; // ST20_4 int v11; // ST24_4 int v12; // ST28_4 int v13; // ST2C_4 int v14; // ecx const char *v15; // eax int v17; // [esp-CCh] [ebp-D8h] int v18; // [esp-C8h] [ebp-D4h] int v19; // [esp-C4h] [ebp-D0h] int v20; // [esp-C0h] [ebp-CCh] int v21; // [esp-BCh] [ebp-C8h] int v22; // [esp-B8h] [ebp-C4h] int v23; // [esp-B4h] [ebp-C0h] int v24; // [esp-B0h] [ebp-BCh] int v25; // [esp-ACh] [ebp-B8h] int v26; // [esp-A8h] [ebp-B4h] int v27; // [esp-A4h] [ebp-B0h] int v28; // [esp-A0h] [ebp-ACh] int v29; // [esp-9Ch] [ebp-A8h] int v30; // [esp-98h] [ebp-A4h] int v31; // [esp-94h] [ebp-A0h] int v32; // [esp-90h] [ebp-9Ch] int v33; // [esp-8Ch] [ebp-98h] int v34; // [esp-88h] [ebp-94h] int v35; // [esp-84h] [ebp-90h] int v36; // [esp-80h] [ebp-8Ch] int v37; // [esp-7Ch] [ebp-88h] int v38; // [esp-78h] [ebp-84h] int v39; // [esp-74h] [ebp-80h] int v40; // [esp-70h] [ebp-7Ch] int v41; // [esp-6Ch] [ebp-78h] int v42; // [esp-68h] [ebp-74h] int v43; // [esp-64h] [ebp-70h] int v44; // [esp-60h] [ebp-6Ch] int v45; // [esp-5Ch] [ebp-68h] int v46; // [esp-58h] [ebp-64h] int v47; // [esp-54h] [ebp-60h] int v48; // [esp-50h] [ebp-5Ch] int v49; // [esp-4Ch] [ebp-58h] int v50; // [esp-48h] [ebp-54h] int v51; // [esp-44h] [ebp-50h] int v52; // [esp-40h] [ebp-4Ch] int v53; // [esp-3Ch] [ebp-48h] int v54; // [esp-38h] [ebp-44h] int v55; // [esp-34h] [ebp-40h] __int128 v56; // [esp-30h] [ebp-3Ch] __int64 v57; // [esp-20h] [ebp-2Ch] int v58; // [esp-18h] [ebp-24h] __int16 v59; // [esp-14h] [ebp-20h] unsigned int v60; // [esp-4h] [ebp-10h] int v61; // [esp+0h] [ebp-Ch] int v62; // [esp+4h] [ebp-8h] int retaddr; // [esp+Ch] [ebp+0h] v61 = a1; v62 = retaddr; v60 = (unsigned int)&v61 ^ __security_cookie; v17 = a3; CreateThread(0, 0, StartAddress, 0, 0, 0); // 播放歌曲, printf_401020("%s ", (unsigned int)aMmmmmmmmmmmmmm); while ( !song_final_sign_40737C ) { Sleep(0x7D0u); printf_401020(">"); } printf_401020(" "); v3 = 0; do *((_BYTE *)&loc_4015E0 + v3++) ^= byte_417000[rand() % 8216];// main中解密输入的加密处理函数 while ( v3 < 383 ); printf_401020("Shining! ", a2, v17); system("cls"); v58 = 0; v56 = 0i64; v57 = 0i64; v59 = 0; memset(&v38, 0, 0x46u); printf_401020("My Darling Darling Please! input your key! "); scanf_401050("%29s", &v56); ((void (__stdcall *)(__int128 *, int *, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, _DWORD))loc_4015E0)( &v56, &v38, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56); *(_OWORD *)&v20 = xmmword_403520; // 0DEF0A07232EEBC954C11473ABD57E649 *(_OWORD *)&v24 = xmmword_403530; // 5CCC8CA3C67C5A6A96E49835683F2AC v36 = 0; LOWORD(v37) = 0; *(_OWORD *)&v28 = 0i64; *(_OWORD *)&v32 = 0i64; v14 = strcmp((const char *)&v38, (const char *)&v20); if ( v14 ) v14 = -(v14 < 0) | 1; v15 = "Maybe next year"; if ( !v14 ) v15 = "Nice Job~"; printf_401020(v15); system("pause"); return 0; }
发现main中对loc_4015E0函数进行了解密,后面用他来加密输入。动态调试,解密后dump
loc_4015E0函数
char __cdecl encode_4015E0(const char *myinput, const char *en_temp) { int v2; // esi unsigned int size; // kr00_4 signed int v4; // edi int v5; // ecx const char *v6; // eax int v7; // eax char v8; // al int v9; // ecx int v10; // ecx unsigned int v11; // ecx int v12; // eax char v13; // al signed int v14; // edi char result; // al signed int v16; // esi char v17; // al char v18; // cl signed int v19; // [esp+Ch] [ebp-8h] signed int v20; // [esp+10h] [ebp-4h] v2 = 0; size = strlen(myinput); v4 = 0; v20 = 4 * size / 3; if ( v20 > 0 ) { do { v5 = v4 & 3; if ( v4 & 3 ) { v8 = myinput[v2 - 1]; if ( v5 == 1 ) { v9 = myinput[v2++]; v7 = (v9 >> 4) | 16 * (v8 & 3); } else if ( v5 == 2 ) { v10 = myinput[v2++]; v7 = (v10 >> 6) | 4 * (v8 & 0xF); } else { v7 = v8 & 0x3F; } } else { v6 = &myinput[v2++]; v7 = *v6 >> 2; } en_temp[v4++] = base64_table_4033C8[v7]; // abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/ } while ( v4 < v20 ); } if ( strlen(myinput) % 3 == 1 ) { v11 = 4 * size / 3; v12 = 16 * (myinput[v2 - 1] & 3); *(_WORD *)&en_temp[v20 + 1] = '=='; v13 = base64_table_4033C8[v12]; //魔改base64,编码表将大写字母与小写字母替换了位置。 } else { if ( strlen(myinput) % 3 != 2 ) goto LABEL_15; v11 = 4 * size / 3; v13 = base64_table_4033C8[4 * (myinput[v2 - 1] & 0xF)]; en_temp[v20 + 1] = 61; } en_temp[v11] = v13; LABEL_15: en_temp[strlen(en_temp)] = 0; v14 = 0; v19 = strlen(en_temp); if ( v19 > 0 ) { do { v16 = 6; do { v17 = rand() % 4; v18 = v16; v16 -= 2; result = v17 << v18; en_temp[v14] ^= result; // 获取伪随机序列,输入进行魔改base64编码后与伪随机序列进行了异或操作。 } while ( v16 > -2 ); ++v14; } while ( v14 < v19 ); } return result; }
最终main函数
int __usercall sub_401780@<eax>(int a1@<ebp>) { signed int v1; // esi int v2; // ecx const char *v3; // eax __int128 tg; // [esp-C0h] [ebp-CCh] __int128 v6; // [esp-B0h] [ebp-BCh] __int128 v7; // [esp-A0h] [ebp-ACh] __int128 v8; // [esp-90h] [ebp-9Ch] int v9; // [esp-80h] [ebp-8Ch] __int16 v10; // [esp-7Ch] [ebp-88h] int temp_str; // [esp-78h] [ebp-84h] __int128 myinput; // [esp-30h] [ebp-3Ch] __int64 v13; // [esp-20h] [ebp-2Ch] int v14; // [esp-18h] [ebp-24h] __int16 v15; // [esp-14h] [ebp-20h] unsigned int v16; // [esp-4h] [ebp-10h] int v17; // [esp+0h] [ebp-Ch] int v18; // [esp+4h] [ebp-8h] int retaddr; // [esp+Ch] [ebp+0h] v17 = a1; v18 = retaddr; v16 = (unsigned int)&v17 ^ __security_cookie; printf_401020("%s ", aMmmmmmmmmmmmmm); printf_401020(" "); v1 = 0; do *((_BYTE *)encode_4015E0 + v1++) ^= byte_417000[rand() % 8216]; while ( v1 < 383 ); printf_401020("Shining! "); system("cls"); v14 = 0; myinput = 0i64; v13 = 0i64; v15 = 0; memset(&temp_str, 0, 0x46u); printf_401020("My Darling Darling Please! input your key! "); scanf_401050("%29s", &myinput); encode_4015E0((const char *)&myinput, (const char *)&temp_str);// 魔改base64编码 +异或伪随机序列 tg = xmmword_403520; // 0DEF0A07232EEBC954C11473ABD57E649 v6 = xmmword_403530; // 5CCC8CA3C67C5A6A96E49835683F2AC v9 = 0; v10 = 0; v7 = 0i64; v8 = 0i64; v2 = strcmp((const char *)&temp_str, (const char *)&tg);// { // 0x49, 0xE6, 0x57, 0xBD, 0x3A, 0x47, 0x11, 0x4C, 0x95, 0xBC, 0xEE, 0x32, 0x72, 0xA0, 0xF0, 0xDE, // 0xAC, 0xF2, 0x83, 0x56, 0x83, 0x49, 0x6E, 0xA9, 0xA6, 0xC5, 0x67, 0x3C, 0xCA, 0xC8, 0xCC, 0x05 // }; if ( v2 ) v2 = -(v2 < 0) | 1; v3 = "Maybe next year"; if ( !v2 ) v3 = "Nice Job~"; printf_401020(v3); system("pause"); return nullsub_2((unsigned int)&v17 ^ v16); }
魔改base64与标准base64编码只存在字母大小写的不同,好解决,关键在于后面与伪随机序列的获取。
方案1、动态调试,在base64编码后,进行异或操作时,编写脚本,将每一位异或数据记录下来。
方案2、先获取魔改base64编码后的结果,然后在函数结束后获得异或加密结果,将二者进行异或,便可得到得到伪随机序列
程序最后将输入的加密结果进行比较,动态调试时dump比较数据。
wp:
import base64 tg=[0x49, 0xE6, 0x57, 0xBD, 0x3A, 0x47, 0x11, 0x4C, 0x95, 0xBC, 0xEE, 0x32, 0x72, 0xA0, 0xF0, 0xDE, 0xAC, 0xF2, 0x83, 0x56, 0x83, 0x49, 0x6E, 0xA9, 0xA6, 0xC5, 0x67, 0x3C, 0xCA, 0xC8, 0xCC, 0x05] order=[0x08,0x81,0x39,0x8d,0x40,0x09,0x42,0x14,0xd0,0xf2,0x98,0x66,0x33,0xd6,0xc9,0xb2,0xc1,0x95,0xb6,0x1e,0xc7,0x2d,0x1c,0xef,0xd2,0xb2,0x5f,0x66,0x8c,0xb9,0xf1,0x38,0x14,0x08,0x8f,0xce,0xe9,0x7f,0x0d,0x05,] b64='' for i in range(len(tg)): b64+=chr(order[i]^tg[i]) print(b64) t='' for c in b64: n=ord(c) if n>=ord('A') and n<=ord('Z'): t+=chr(n|0x20) elif n>=ord('a') and n<=ord('z'): t+=chr(n&0xdf) else: t+=c print(t) flag=base64.b64decode(t.encode()).decode() print(flag)
hctf{1zumi_K0nat4_Mo3}
攻防世界里提交失败 0.0 Orz