-
main函数:
-
int __cdecl __noreturn main_0(int argc, const char **argv, const char **envp) { _BYTE v3[6]; // [esp-4h] [ebp-D0h] printf("please Input the flag:\n"); scanf_s("%s", &input_string); MessageBoxW(0, L"Exception", L"Warning", 0); *(_DWORD *)v3 = sub_A3100F; MEMORY[0] = 1; sub_A31136(*(int *)&v3[2]); }
-
看到汇编中注册了SEH函数:
-
push offset sub_A3100F push large dword ptr fs:0 mov large fs:0, esp
-
跟进
-
char __cdecl sub_A319F0(int a1, unsigned int *a2, _BYTE *a3) { int v3; // eax char result; // al int v5[38]; // [esp+D0h] [ebp-A4h] BYREF unsigned int v6; // [esp+168h] [ebp-Ch] v6 = 0; j_memset(v5, 0, 0x90u); v5[0] = _byteswap_ulong(*a2); v5[1] = _byteswap_ulong(a2[1]); v5[2] = _byteswap_ulong(a2[2]); v5[3] = _byteswap_ulong(a2[3]); while ( v6 < 0x20 ) { v3 = sub_A31700(v5[v6], v5[v6 + 1], v5[v6 + 2], v5[v6 + 3], *(_DWORD *)(a1 + 4 * v6)); v5[v6++ + 4] = v3; } *a3 = HIBYTE(v5[35]); a3[1] = BYTE2(v5[35]); a3[2] = BYTE1(v5[35]); a3[3] = v5[35]; a3[4] = HIBYTE(v5[34]); a3[5] = BYTE2(v5[34]); a3[6] = BYTE1(v5[34]); a3[7] = v5[34]; a3[8] = HIBYTE(v5[33]); a3[9] = BYTE2(v5[33]); a3[10] = BYTE1(v5[33]); a3[11] = v5[33]; a3[12] = HIBYTE(v5[32]); a3[13] = BYTE2(v5[32]); a3[14] = BYTE1(v5[32]); result = v5[32]; a3[15] = v5[32]; return result; } unsigned int __cdecl sub_581700(int a1, int a2, int a3, int a4, int a5) { return a1 ^ sub_581760(a5 ^ a4 ^ a3 ^ a2); } int __cdecl sub_581760(int a1) { unsigned int v2; // [esp+D0h] [ebp-2Ch] unsigned int v3; // [esp+F4h] [ebp-8h] LOBYTE(v2) = sub_5819A0(HIBYTE(a1)); BYTE1(v2) = sub_5819A0(BYTE2(a1)); BYTE2(v2) = sub_5819A0(BYTE1(a1)); HIBYTE(v2) = sub_5819A0(a1); v3 = _byteswap_ulong(v2); return ((v3 >> 8) | (v3 << 24)) ^ ((v3 >> 14) | (v3 << 18)) ^ ((v3 >> 22) | (v3 << 10)) ^ v3 ^ ((v3 >> 30) | (4 * v3)); }
-
其实是个SM4加密
-
需要找到key的值
-
交叉引用到Handler函数
-
int __stdcall Handler_0(_DWORD **a1) { unsigned int v2[5]; // [esp+D0h] [ebp-18h] BYREF if ( **a1 == 0xC0000005 ) { qmemcpy(v2, "where_are_u_now?", 16); sub_581172(key, v2); SetUnhandledExceptionFilter(TopLevelExceptionFilter); } return 0; } unsigned int __cdecl sub_A31F50(int a1, unsigned int *a2) { unsigned int result; // eax unsigned int v3; // [esp+D0h] [ebp-B8h] unsigned int v4; // [esp+DCh] [ebp-ACh] unsigned int v5; // [esp+E0h] [ebp-A8h] unsigned int v6; // [esp+E4h] [ebp-A4h] int v7[35]; // [esp+E8h] [ebp-A0h] unsigned int v8; // [esp+174h] [ebp-14h] unsigned int v9; // [esp+178h] [ebp-10h] unsigned int v10; // [esp+17Ch] [ebp-Ch] unsigned int v11; // [esp+180h] [ebp-8h] v3 = 0; v8 = _byteswap_ulong(*a2); v9 = _byteswap_ulong(a2[1]); v10 = _byteswap_ulong(a2[2]); v11 = _byteswap_ulong(a2[3]); v4 = v8 ^ 0xA3B1BAC6; v5 = dword_A37A68[1] ^ v9; v6 = dword_A37A68[2] ^ v10; result = 12; v7[0] = dword_A37A68[3] ^ v11; while ( v3 < 0x20 ) { v7[v3 + 1] = *(&v4 + v3) ^ sub_A314E0(dword_A37A78[v3] ^ v7[v3] ^ v7[v3 - 1] ^ *(&v5 + v3)); *(_DWORD *)(a1 + 4 * v3) = v7[v3 + 1]; result = ++v3; } return result; }
-
可以直接动调出来key(0xA3B1BAC6 SM4一个特征值)
-
还有一个UEH
-
int __cdecl sub_582C30(_DWORD *a1) { int result; // eax char v2; // [esp+D3h] [ebp-11h] size_t i; // [esp+DCh] [ebp-8h] result = (int)a1; if ( *(_DWORD *)*a1 == 0xC0000005 ) { for ( i = 0; i < j_strlen(Str2); i += 2 ) { v2 = Str2[i]; Str2[i] = Str2[i + 1]; Str2[i + 1] = v2; } Str1 = sub_58126C(output_string); *(_DWORD *)(a1[1] + 176) = *(_DWORD *)(*a1 + 20);//eax *(_DWORD *)(a1[1] + 164) = *(_DWORD *)(*a1 + 24);//ebx *(_DWORD *)(a1[1] + 172) = *(_DWORD *)(*a1 + 28);//ecx *(_DWORD *)(a1[1] + 168) = *(_DWORD *)(*a1 + 32);//edx *(_DWORD *)(a1[1] + 156) = *(_DWORD *)(*a1 + 36);//edi *(_DWORD *)(a1[1] + 160) = *(_DWORD *)(*a1 + 40);//esi *(_DWORD *)(a1[1] + 184) = sub_581136;//eip return -1; } return result; } _BYTE *__cdecl sub_583090(char *Str) { int k; // [esp+E4h] [ebp-5Ch] int v3; // [esp+F0h] [ebp-50h] int j; // [esp+FCh] [ebp-44h] int v5; // [esp+108h] [ebp-38h] signed int i; // [esp+114h] [ebp-2Ch] _BYTE *v7; // [esp+120h] [ebp-20h] signed int v8; // [esp+12Ch] [ebp-14h] int v9; // [esp+138h] [ebp-8h] v5 = 0; v8 = j_strlen(Str); if ( v8 % 3 ) v9 = 4 * (v8 / 3) + 4; else v9 = 4 * (v8 / 3); v7 = malloc(__CFADD__(v9, 1) ? -1 : v9 + 1); v7[v9] = 0; for ( i = 0; i < v8; i += 3 ) { v3 = 0; for ( j = 0; j < 3; ++j ) v3 |= (unsigned __int8)Str[j + i] << (8 * (2 - j)); for ( k = 0; k < 4; ++k ) { if ( k >= 4 - (i + 3 - v8) && i + 3 > v8 ) v7[v5] = 33; else v7[v5] = ::Str[sub_5810FF((v3 >> (6 * (3 - k))) & 0x3F)]; ++v5; } } return v7; } int __cdecl sub_582760(int a1) { return (a1 + 24) % 64; }
-
对str2进行处理,将sm4产生的输出进行处理,发现是一个魔改的base64
-
(其中对表进行凯撒加密
-
UEH里面还修改了eip为sub_581136,进行最后的字符串比较
-
最后result=-1,异常返回继续执行
关于hadler0是怎么调用的
-
int __stdcall sub_582AB0(int a1, int a2, int a3, int a4) { size_t i; // [esp+D8h] [ebp-8h] for ( i = 0; i < j_strlen(Str); ++i ) { if ( Str[i] <= 122 && Str[i] >= 97 ) { Str[i] -= 32; } else if ( Str[i] <= 90 && Str[i] >= 65 ) { Str[i] += 32; } } MessageBoxA(0, "hooked", "successed", 0); AddVectoredExceptionHandler(0, Handler); return 0; }
-
交叉引用到上面这个函数
-
发现其对base64码表进行变换,并注册VEH,从而调用Handler
-
再交叉引用
-
int __cdecl sub_5827B0(int a1, char *String2, int a3) { DWORD LastError; // eax int flOldProtect[3]; // [esp+D0h] [ebp-90h] BYREF void *v6[3]; // [esp+DCh] [ebp-84h] BYREF LPCVOID lpAddress; // [esp+E8h] [ebp-78h] struct _MEMORY_BASIC_INFORMATION Buffer; // [esp+F4h] [ebp-6Ch] BYREF LPVOID lpBaseAddress; // [esp+118h] [ebp-48h] int v10; // [esp+124h] [ebp-3Ch] char *String1; // [esp+130h] [ebp-30h] int i; // [esp+13Ch] [ebp-24h] int v13; // [esp+148h] [ebp-18h] int v14; // [esp+154h] [ebp-Ch] v14 = a1; v13 = a1 + *(_DWORD *)(a1 + 60) + 24; for ( i = *(_DWORD *)(v13 + 104) + a1; i; i += 20 ) { String1 = (char *)GetModuleHandleW(0) + *(_DWORD *)(i + 12); if ( !stricmp(String1, String2) ) break; } if ( !i ) return 0; v10 = *(_DWORD *)(i + 16) + a1; if ( !v10 ) return 0; while ( 1 ) { if ( !*(_DWORD *)v10 ) return 0; lpBaseAddress = (LPVOID)v10; if ( *(_DWORD *)v10 == a3 ) break; v10 += 4; } lpAddress = (LPCVOID)(v10 >> 12 << 12); VirtualQuery(lpAddress, &Buffer, 0x3E8u); VirtualProtect((LPVOID)lpAddress, Buffer.RegionSize, 0x40u, &Buffer.Protect); v6[0] = sub_581023; if ( WriteProcessMemory((HANDLE)0xFFFFFFFF, lpBaseAddress, v6, 4u, 0) ) { VirtualProtect(Buffer.BaseAddress, Buffer.RegionSize, Buffer.Protect, (PDWORD)flOldProtect); return 1; } else { LastError = GetLastError(); printf("%d\n", LastError); return 0; } }//找到 user32.dll 对应的 IMAGE_IMPORT_DESCRIPTOR 结构体地址,然后找到 MessageBoxW 对应的 IMAGE_THUNK_DATA 结构体地址,用VirtualProtect修改页属性为可写,用WriteProcessMemory将IMAGE_THUNK_DATA字段覆写为sub_581023函数地址
-
int sub_581E40() { HMODULE ModuleHandleW; // eax ModuleHandleW = GetModuleHandleW(0); sub_58114A((int)ModuleHandleW, "User32.dll", "MessageBoxW"); return 0; }
-
其实就是IAT hook,将MessageBoxW的IAT地址替换为了sub_581023的函数地址,该函数完成了VEH的注册
-
再往上交叉,就找到一个rdata区的数据
-
.rdata:00587618 dd offset sub_581235
-
// write access to const memory has been detected, the output may be wrong! int __tmainCRTStartup() { int v1; // [esp+18h] [ebp-24h] signed __int32 v2; // [esp+1Ch] [ebp-20h] signed __int32 v3; // [esp+20h] [ebp-1Ch] v2 = *(_DWORD *)(j__NtCurrentTeb() + 4); v1 = 0; while ( 1 ) { v3 = _InterlockedCompareExchange(dword_58A6EC, v2, 0); if ( !v3 ) break; if ( v3 == v2 ) { v1 = 1; break; } } if ( dword_58A6FC == 1 ) { j__amsg_exit(31); goto LABEL_13; } if ( dword_58A6FC ) { dword_58A2DC = 1; goto LABEL_13; } dword_58A6FC = 1; if ( !j__initterm_e((_PIFV *)&First, (_PIFV *)&Last) ) { LABEL_13: if ( dword_58A6FC == 1 ) { j__initterm((_PVFV *)&dword_587000, (_PVFV *)&dword_587208); dword_58A6FC = 2; } if ( dword_58A6FC != 2 && CrtDbgReportW( 2, L"f:\\dd\\vctools\\crt\\crtw32\\dllstuff\\crtexe.c", 553, 0, L"%s", L"__native_startup_state == __initialized") == 1 ) { __debugbreak(); } if ( !v1 ) _InterlockedExchange(dword_58A6EC, 0); if ( dword_58A714 ) { if ( j___IsNonwritableInCurrentImage(&dword_58A714) ) dword_58A714(0, 2, 0); } CrtSetCheckCount(1); _initenv = envp; main(argc, (const char **)argv, (const char **)envp); } return 255; }
-
j__initterm_e函数调用了那个数据,且对全局/静态C++类的构造函数进行了初始化
-
再往上就到了start
异常处理的注册
-
有个调用链:start -> tmainCRTStartUp->initterm_e->IAThook ,修改了MessageBox中的IAT表
-
这时main函数里会调用Messageboxw,就调用了注册VEH的函数,并对码表进行变换
-
在main中注册了SEH
-
在VEH的handler中注册了UEH
异常处理的回调
- Windows异常处理:用户态发生异常->调试器-VEH--SEH--UEH
- 在main中内存写异常,各级异常处理的返回状态都未完成处理,那么会进行上述的完整回调
- VEH对sm4进行初始,得到key
- SEH对输入进行sm4加密,
- UEH进行base64魔改加密,最后比较
-
最后写下脚本
-
import sm4 key = sm4.SM4Key(b"where_are_u_now?") print key.decrypt(sm4_encoded)
-
flag{SM4foRExcepioN?!}
一些小tips
-
initterm:用于访问函数指针表并将其初始化的内部方法。
第一个指针位于表中的起始位置,第二个指针位于结束位置。
-
GetModuleHandleW:
HMODULE GetModuleHandleW( [in, optional] LPCWSTR lpModuleName//加载的模块的名称 );
-
HMODULE LoadLibraryA( [in] LPCSTR lpLibFileName );将指定的模块加载到调用进程的地址空间中
-
FARPROC GetProcAddress( [in] HMODULE hModule, [in] LPCSTR lpProcName );从指定的动态链接库 (DLL) 检索导出函数 (也称为过程) 或变量的地址。 BOOL WriteProcessMemory(将数据写入到指定进程中的内存区域。 [in] HANDLE hProcess, [in] LPVOID lpBaseAddress, [in] LPCVOID lpBuffer, [in] SIZE_T nSize, [out] SIZE_T *lpNumberOfBytesWritten ); PVOID AddVectoredExceptionHandler( ULONG First, PVECTORED_EXCEPTION_HANDLER Handler );注册向量化异常处理程序。 Firse:处理程序的调用顺序。 如果参数为非零值,则处理程序是要调用的第一个处理程序。 如果 参数为零,则处理程序是最后一个要调用的处理程序。 Handler:指向要调用的处理程序的指针。 LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter( [in] LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter//指向顶级异常筛选器函数的指针,每当 UnhandledExceptionFilter 函数获得控制权且进程未调试时,将调用该函数。 ); 使应用程序能够取代进程每个线程的顶级异常处理程序。 调用此函数后,如果在未调试的进程中发生异常,并且异常会将其引入未经处理的异常筛选器,该筛选器将调用 由 lpTopLevelExceptionFilter 参数指定的异常筛选器函数。 LONG UnhandledExceptionFilter( [in] _EXCEPTION_POINTERS *ExceptionInfo//指向 EXCEPTION_POINTERS 结构的指针,该结构指定异常和异常时处理器上下文的说明。 );应用程序定义的函数,它将未经处理的异常传递给调试器(如果正在调试进程)。 否则,它会选择显示 应用程序错误消息 框,并导致执行异常处理程序。 只能从异常处理程序的筛选器表达式内调用此函数。 typedef struct _EXCEPTION_POINTERS { PEXCEPTION_RECORD ExceptionRecord; PCONTEXT ContextRecord; } EXCEPTION_POINTERS, *PEXCEPTION_POINTERS; ExceptionRecord:指向 EXCEPTION_RECORD 结构的指针,该结构包含与计算机无关的异常说明。 ContextRecord:指向 CONTEXT 结构的指针,该结构包含异常时处理器状态的特定于处理器的说明。
总结:
很全面的一道题,知识点明确