小Q是一个PC客户端安全爱好者。有一天他得到了一个未知工具,含有一个exe、一个sys和一份使用说明。说明写道:ACE的宝库钥匙由两串 token组成.运行这组程序.它们会将两串 token藏于内存中。宝库有两位守护者会制裁使用违禁工具的冒险家,在寻宝时务必谨慎小心。
题目
(一)解题过程 拿到hack.exe,浅分析一下发现加了VM,并且有检测黑客工具的行为,检测到了之后即使关闭黑客程序也会影响程序正常运行,但是xdbg稍微改一下还是可以动调的,在xdbg里下一些可能的函数断点,我这里在这些地方下了断点
运行发现程序会多次在WriteProcessMemory
下断下,hook一下观察传参
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 #include "pch.h" #include <windows.h> #include <shellapi.h> #include <detours.h> #include <tlhelp32.h> #include <stdlib.h> #include <stdio.h> #pragma comment(lib,"detours.lib" ) #define _KDEBUG #define DBGMGEBOX(fmt, ...) \ do { \ \ wsprintfA(out, fmt, __VA_ARGS__); \ MessageBoxA(NULL, out, "提示" , MB_OK); \ } while(0) char out[100 ];typedef BOOL (WINAPI* WriteProcessMemory_t) ( _In_ HANDLE hProcess, _In_ LPVOID lpBaseAddress, _In_reads_bytes_(nSize) LPCVOID lpBuffer, _In_ SIZE_T nSize, _Out_opt_ SIZE_T* lpNumberOfBytesWritten ) ;WriteProcessMemory_t TrueWriteProcessMemory = NULL ; BOOL WINAPI HookWriteProcessMemory ( _In_ HANDLE hProcess, _In_ LPVOID lpBaseAddress, _In_reads_bytes_(nSize) LPCVOID lpBuffer, _In_ SIZE_T nSize, _Out_opt_ SIZE_T * lpNumberOfBytesWritten ) { char fileName[12 ] = { 0 }; sprintf (fileName, "out%d.txt" , (int )hProcess % 1000 ); HANDLE hFile = CreateFile(fileName, FILE_APPEND_DATA, FILE_SHARE_READ, NULL , OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if (hFile == INVALID_HANDLE_VALUE) { DBGMGEBOX("CreateFile Fail" ); return TrueWriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten); } SetFilePointer(hFile, 0 , NULL , FILE_END); DWORD bytesWritten; BOOL result = WriteFile(hFile, lpBuffer, nSize, &bytesWritten, NULL ); CloseHandle(hFile); DBGMGEBOX("findProcess WriteProcessMemory:%p,size:%d\n" , hProcess,nSize); return TrueWriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten); } BOOL APIENTRY DllMain (HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); TrueWriteProcessMemory = (WriteProcessMemory_t)DetourFindFunction("kernel32.dll" , "WriteProcessMemory" ); DetourAttach(&(PVOID&)TrueWriteProcessMemory, HookWriteProcessMemory); DetourTransactionCommit(); break ; case DLL_PROCESS_DETACH: DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourDetach(&(PVOID&)TrueWriteProcessMemory, HookWriteProcessMemory); DetourTransactionCommit(); break ; } return TRUE; }
这里输出了三个txt文件
其中out200.txt文件有明显的PE头
去除前面的字节,把文件丢到DIE里分析一下发现是dll64文件并且貌似没加壳,所以hack.exe通过WriteProcessMemory
往某个进程写入了一个dll?怀疑是远程注入,至于做了什么,有可能跟token有关,继续分析。
ida64打开发现程序的dllMain入口还是被加密了
还是继续动调,随便找了个64位的可执行文件,拖到X64dbg里运行,直接用xdbg的注入方式将out200.dll注入进程,在
入口点下断点,并且对一些可疑的WINDOWS API下断观察进程行为
这里我对下面这几个API下了断点
运行,第一次成功在openProcess函数断下
观察传参窗口,找到了一个系统进程名称字符串winlogon.exe
,而这里调用的是openProcess,疑似是对系统进程winlogon.exe做了一些操作。继续分析,运行到返回,回溯一层函数,找到一段没有被加密的代码
汇编代码不是很好看,根据偏移在IDA里反汇编看看
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 __int64 __fastcall sub_1800063D0 (_DWORD *Dst, DWORD dwProcessId) { __m128i si128; __m128i v6; __int64 result; __m128i v10; size_t v14; __int64 v15; int v16; HANDLE Toolhelp32Snapshot; HANDLE v18; __int64 v21; CHAR Caption[16 ]; _RBP = (unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64; if ( !dwProcessId ) { *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x28 ) = 0xC65BF3E99CAA093C ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 8 ) = 0xC65BF3E99CAA093C ui64; *(_QWORD *)_RBP = 0xE795A71250E2465A ui64; si128 = _mm_load_si128((const __m128i *)_RBP); *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x20 ) = 0xE795A7603F90341F ui64; v6 = _mm_xor_si128(si128, *(__m128i *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x20 )); *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x20 ) = 0x84FAD5301FE45158 ui64; *(__m128i *)_RBP = v6; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x28 ) = 0xBF19D3ADD5D97A59 ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x1A0 ) = 0xE795A7603F90341F ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x30 ) = 0xBD1C9CA3F4C12EC9 ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x38 ) = 0xA727C05763438E84 ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x1A8 ) = 0xC65BF3E99CAA093C ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x1B0 ) = 0xCF59BCC699A060E9 ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x1B8 ) = 0xA727C0574231E1F6 ui64; __asm { vmovdqu ymm0, [rbp+210 h+var_70] vpxor ymm1, ymm0, ymmword ptr [rbp+210 h+Text] vmovdqa ymmword ptr [rbp+210 h+Text], ymm1 vzeroupper } MessageBoxA(0 i64, (LPCSTR)(_RBP + 32 ), (LPCSTR)((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64), 0 ); LABEL_3: GetLastError(); *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x20 ) = 0xE795A7603F90341F ui64; *(_QWORD *)_RBP = 0x88D6871250E2465A ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x28 ) = 0xC65BF3E99CAA093C ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 8 ) = 0xC65BF98DB9906C58 ui64; *(__m128i *)_RBP = _mm_xor_si128( _mm_load_si128((const __m128i *)_RBP), *(__m128i *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x20 )); return sub_180006A00((void *)((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64)); } if ( !(unsigned __int8)sub_180006FC0() ) { *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x1A0 ) = 0xE795A7603F90341F ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x48 ) = 0x44F651D568826090 i64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x1C0 ) = 0x52C9FCDC77FF5FC3 i64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x20 ) = 0xDDD2E92971C27548 ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x28 ) = 0xA43EB7C9E8CF4E1C ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x30 ) = 0xA62FD5B4C980079C ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x38 ) = 0xD55585772756849A ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x40 ) = 0x52C9FCDC7DDE2DAC i64; v10 = _mm_load_si128((const __m128i *)(_RBP + 64 )); *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x1C8 ) = 0x44F651D568826090 i64; _XMM2 = _mm_xor_si128(v10, *(__m128i *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x1C0 )); *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x1A8 ) = 0xC65BF3E99CAA093C ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x1B0 ) = 0xCF59BCC699A060E9 ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x1B8 ) = 0xA727C0574231E1F6 ui64; __asm { vmovdqu ymm0, [rbp+210 h+var_70] vpxor ymm1, ymm0, ymmword ptr [rbp+210 h+Text] vmovdqa [rbp+210 h+var_1D0], xmm2 vmovdqa ymmword ptr [rbp+210 h+Text], ymm1 vzeroupper } sub_180006A00((void *)(_RBP + 32 )); } v14 = -1 i64; if ( dwProcessId == -1 ) { Dst[34 ] = GetCurrentProcessId(); *((_QWORD *)Dst + 13 ) = -1 i64; } else { Dst[34 ] = dwProcessId; v18 = OpenProcess(0x1FFFFF u, 0 , dwProcessId); *((_QWORD *)Dst + 13 ) = v18; if ( !v18 ) { *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x38 ) = 0xA727C0574231E1F6 ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x20 ) = 0x84FAD53051F54450 ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x1A0 ) = 0xE795A7603F90341F ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x28 ) = 0xA92981ACBCD97A59 ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x30 ) = 0xCF59BCC699AA419B ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x1A8 ) = 0xC65BF3E99CAA093C ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x1B0 ) = 0xCF59BCC699A060E9 ui64; *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x1B8 ) = 0xA727C0574231E1F6 ui64; __asm { vmovdqu ymm0, [rbp+210 h+var_70] vpxor ymm1, ymm0, ymmword ptr [rbp+210 h+Text] vmovdqa ymmword ptr [rbp+210 h+Text], ymm1 vzeroupper } sub_180006A00((void *)(_RBP + 32 )); goto LABEL_3; } } Dst[30 ] = 0x1FFFFF ; v15 = -1 i64; do ++v15; while ( *((_BYTE *)Dst + v15) ); if ( !v15 ) { v16 = Dst[34 ]; Toolhelp32Snapshot = CreateToolhelp32Snapshot(2u , 0 ); if ( Toolhelp32Snapshot != (HANDLE)-1 i64 ) { *(_DWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x60 ) = 304 ; memset ((void *)(_RBP + 100 ), 0 , 0x12C ui64); if ( Process32First(Toolhelp32Snapshot, (LPPROCESSENTRY32)(_RBP + 96 )) ) { while ( *(_DWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 0x68 ) != v16 ) { if ( !Process32Next(Toolhelp32Snapshot, (LPPROCESSENTRY32)(_RBP + 96 )) ) goto LABEL_20; } do ++v14; while ( *(_BYTE *)(_RBP + 140 + v14) ); memmove(Dst, (const void *)(_RBP + 140 ), v14); } LABEL_20: CloseHandle(Toolhelp32Snapshot); } } *((_QWORD *)Dst + 18 ) = sub_1800068D0(Dst, Dst); v21 = 0 i64; *(_DWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64) + 8 ) = Dst[34 ]; *(_QWORD *)_RBP = 0 i64; EnumWindows(EnumFunc, (unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0 ui64); result = *(_QWORD *)_RBP; if ( *(_QWORD *)_RBP ) v21 = *(_QWORD *)_RBP; *((_QWORD *)Dst + 16 ) = v21; return result; }
这段代码一次进行了获取进程pid,打开进程,遍历模块等操作,并且在函数失败后做了一些奇奇怪怪的东西,对一些地址赋上了一些64位的值,猜测是隐藏字符串来打印调试信息用的,再通过messagebox和outputDebugString给出调试信息,显示打开进程失败,猜测是因为hack.exe启动是管理员启动,这里失去了管理员权限。
分析完这个函数,继续回溯一层,运行到返回,定位到这个地方
继续根据偏移转到IDA里看反汇编
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 int sub_180001990 () { size_t v0; DWORD v1; __m128i Dst; __int64 v4; __int64 v5; __m128i Src; Dst.m128i_i64[0 ] = 0xE795A7603F90341F ui64; Dst.m128i_i64[1 ] = 0xC65BF3E99CAA093C ui64; Src.m128i_i64[0 ] = 0x89FAC00F53FE5D68 ui64; Src.m128i_i64[1 ] = 0xC65BF3E9F9D26C12 ui64; Src = _mm_xor_si128(_mm_load_si128(&Src), Dst); v0 = -1 i64; do ++v0; while ( Src.m128i_i8[v0] ); memmove(dword_1800349A0, &Src, v0); Dst.m128i_i64[0 ] = 0 i64; v4 = 0 i64; v5 = 15 i64; sub_180004770(&Dst, &Src, v0); v1 = sub_1800070A0(&Dst); sub_1800063D0(dword_1800349A0, v1); return atexit(sub_180020C90); }
前面应该是一个加密的字符串操作,用python打印出字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def hex_xor_to_string (a, b ): result = a ^ b hex_str = hex (result)[2 :] if len (hex_str) % 2 != 0 : hex_str = '0' + hex_str result_str = '' .join(chr (int (hex_str[i:i+2 ], 16 )) for i in range (0 , len (hex_str), 2 )) return result_str x1 = 0xE795A7603F90341F x2 = 0xC65BF3E99CAA093C y1 = 0x89FAC00F53FE5D68 y2 = 0xC65BF3E9F9D26C12 result1 = hex_xor_to_string(x1, y1) result2 = hex_xor_to_string(x2, y2) print ("Result 1:" , result1)print ("Result 2:" , result2)
得到的刚好是winlogon.exe字符串,然后程序将这个字符串转移到了dword_1800349A0全局变量中,目的应该是隐藏字符串,接着sub_180004770函数也是一个类似memmove操作,把这个字符串传到了Dst局部变量中,接着在sub_1800070A0中传入这个字符串,貌似是在根据字符串获取进程PID,接着调用sub_1800063D0函数根据pid打开进程,并将进程句柄存储到了某个地方
1 2 v18 = OpenProcess(0x1FFFFF u, 0 , dwProcessId); *((_QWORD *)Dst + 13 ) = v18;
随后return exit退出。
随后我在退出函数传参的时候看到了一个hProcess
一个全局变量,很有可能在其他地方对句柄进行了读取,交叉引用一下定位到如下函数
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 void sub_180007C10 () { HANDLE v1; void *v2; __int128 v3; __int128 v4; HANDLE FileA; __int64 v8; _BYTE *v9; unsigned __int64 v10; _QWORD *v11; char Buffer; _RBP = (unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64; while ( byte_180032C00 ) { if ( !byte_180034961 && !byte_180034960 ) sub_1800041D0(); v1 = hProcess; v2 = (void *)(qword_180034968 + 2766 ); *(_BYTE *)_RBP = 15 ; WriteProcessMemory(v1, v2, (LPCVOID)((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64), 1u i64, 0 i64); byte_180032C00 = 0 ; *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64) + 0x20 ) = 0xAA32D3B2B7C50388 ui64; *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64) + 0x10 ) = 0xA0A195500DCC0E5C ui64; *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64) + 0x18 ) = 0x943E9588CFCF645D ui64; v3 = *(_OWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64) + 0x10 ); *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64) + 0x28 ) = 0xA727C0574231D098 ui64; v4 = *(_OWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64) + 0x20 ); *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64) + 0x80 ) = 0xE795A7603F90341F ui64; *(_OWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64) + 0x60 ) = v3; *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64) + 0x88 ) = 0xC65BF3E99CAA093C ui64; *(_OWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64) + 0x70 ) = v4; *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64) + 0x90 ) = 0xCF59BCC699A060E9 ui64; *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64) + 0x98 ) = 0xA727C0574231E1F6 ui64; __asm { vmovdqu ymm0, [rbp+0 D0h+var_50] vpxor ymm1, ymm0, ymmword ptr [rbp+0 D0h+FileName] vmovdqa ymmword ptr [rbp+0 D0h+FileName], ymm1 vzeroupper } FileA = CreateFileA((LPCSTR)(_RBP + 96 ), 0x40000000 u, 0 , 0 i64, 3u , 0x80 u, 0 i64); if ( FileA != (HANDLE)-1 i64 ) { ((void (__fastcall *)(unsigned __int64))loc_180007A20)(_RBP + 48 ); v8 = -1 i64; if ( *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64) + 0x48 ) < 0x10 ui64 ) { do ++v8; while ( *(_BYTE *)(_RBP + 48 + v8) ); v9 = (_BYTE *)(_RBP + 48 ); } else { v9 = *(_BYTE **)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64) + 0x30 ); do ++v8; while ( v9[v8] ); } WriteFile(FileA, v9, v8, (LPDWORD)(_RBP + 8 ), 0 i64); CloseHandle(FileA); v10 = *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64) + 0x48 ); if ( v10 >= 0x10 ) { v11 = *(_QWORD **)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64) + 0x30 ); if ( v10 + 1 >= 0x1000 ) { v11 = (_QWORD *)*(v11 - 1 ); if ( (unsigned __int64)(*(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0 ui64) + 0x30 ) - (_QWORD)v11 - 8 i64) > 0x1F ) invalid_parameter_noinfo_noreturn(); } j_j_free(v11); } } } }
看到了有WriteProcessMemory写入hProcess内存操作,CreateFileA,WriteFile,打开和写入文件操作,但是并没有找到hProcess的赋值语句,也就是说这个进程句柄还不知道是谁的,猜测赋值被隐藏了,但是可以猜测可能是winlogon.exe进程句柄。byte_180032C00是一个全局的标志变量,强制函数只能执行一次,对应的是运行程序时仅一次的初始化操作。接着看一下CreateFileA函数,同样的文件名被隐藏了,python解析一下
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 def hex_xor_to_string (a, b ): result = a ^ b hex_str = hex (result)[2 :] if len (hex_str) % 2 != 0 : hex_str = '0' + hex_str result_str = '' .join(chr (int (hex_str[i:i+2 ], 16 )) for i in range (0 , len (hex_str), 2 )) return result_str x1 = 0xA0A195500DCC0E5C x2 = 0x943E9588CFCF645D x3 = 0xAA32D3B2B7C50388 x4 = 0xA727C0574231D098 y1 = 0xE795A7603F90341F y2 = 0xC65BF3E99CAA093C y3 = 0xCF59BCC699A060E9 y4 = 0xA727C0574231E1F6 result1 = hex_xor_to_string(x1, y1) result2 = hex_xor_to_string(x2, y2) result3 = hex_xor_to_string(x3, y3) result4 = hex_xor_to_string(x4, y4) print ("Result 1:" , result1)print ("Result 2:" , result2)print ("Result 3:" , result3)print ("Result 4:" , result4)res = result1[::-1 ] + result2[::-1 ] + result3[::-1 ] + result4[::-1 ] print (res)
整个拼起来是字符串C:\2024GameSafeRace.token1
,应该是创建了一个文件,然后向这个文件写入了token1了,接着往下
loc_180007A20这个函数内部被加密了,猜测是对token1的解密过程,然后通过WriteFile写入C:\2024GameSafeRace.token1
中,并不是很像去分析这个函数,直接加载驱动看看能不能直接运行得到2024GameSafeRace.token1文件。
找到下没找到,回头看看CreateFileA函数,核查一下后面几个参数
1 FileA = CreateFileA((LPCSTR)(_RBP + 96 ), 0x40000000 u, 0 , 0 i64, 3u , 0x80 u, 0 i64);
看来是参数在作怪,CreateFileA函数传入OPEN_EXISTING 参数,如果没有指定文件,则函数会返回失败,那也好办,自己创建一个就好了。
C:\2024GameSafeRace.token1成功被写入
010打开找到token1:757F4749AEBB1891EF5AC2A9B5439CEA
token2的寻找就偏简单了,加载驱动后留意一下dbgView的打印信息就可以获取
组合一下就是token2:803f14a24d64f3e697957c252e3a5686
(二)解题过程 题目要求:
编写程序,运行时修改尽量少的内存,让两段token输出成功。(满分2分)
根据之前分析的token1,我们可以知道程序会在CreateFileA后解密token1然后写入到C:\2024GameSafeRace.token1
中,但是会因为CreateFileA参数OPEN_EXISTING
条件不满足而失败,所以我们只需要修改这个传参,改成OPEN_ALWAYS
,即可实现输出token1,那我们只需要hook CreateFileA
函数修改传参即可,但是有个问题,因为不是hack.exe本身调用CreateFileA
函数,而是hack.exe注入了一个dll到winlogon.exe,然后再winlogon.exe里调用CreateFileA
函数,所以我们可以考虑在注入前修改WriteProcessMemory
函数参数buffer,从而在注入前patch dll,或者编写代码直接注入winlogon.exe,hook CreateFileA函数修改传参,但是考虑到第二种方式可能不被允许,winlogon.exe毕竟是系统进程,题目应该是要我们通过patch dll的方式解题。
这里我们要patch 传参,通过ida找到传参的汇编代码
在winhex里找到对应所在文件偏移
也就是在0x7171处OPEN_EXISTING:0x3是要patch的地方,把这个参数修改成OPEN_ALWAYS:0x4即可。下面编写代码实现。
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 #include "pch.h" #include <windows.h> #include <shellapi.h> #include <detours.h> #include <tlhelp32.h> #include <stdlib.h> #include <stdio.h> #pragma comment(lib,"detours.lib" ) #define _KDEBUG #define DBGMGEBOX(fmt, ...) \ do { \ \ wsprintfA(out, fmt, __VA_ARGS__); \ MessageBoxA(NULL, out, "提示" , MB_OK); \ } while(0) char out[100 ];typedef BOOL (WINAPI* WriteProcessMemory_t) ( _In_ HANDLE hProcess, _In_ LPVOID lpBaseAddress, _In_reads_bytes_(nSize) LPCVOID lpBuffer, _In_ SIZE_T nSize, _Out_opt_ SIZE_T* lpNumberOfBytesWritten ) ;WriteProcessMemory_t TrueWriteProcessMemory = NULL ; BOOL WINAPI HookWriteProcessMemory ( _In_ HANDLE hProcess, _In_ LPVOID lpBaseAddress, _In_reads_bytes_(nSize) LPCVOID lpBuffer, _In_ SIZE_T nSize, _Out_opt_ SIZE_T* lpNumberOfBytesWritten ) { if (nSize == 4506624 && *((PUCHAR)lpBuffer + 0x7171 ) == 0x3 ) { *((PUCHAR)lpBuffer + 0x7171 ) = 0x4 ; DBGMGEBOX("Hook Success!\n" ); } return TrueWriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten); } BOOL APIENTRY DllMain (HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); TrueWriteProcessMemory = (WriteProcessMemory_t)DetourFindFunction("kernel32.dll" , "WriteProcessMemory" ); DetourAttach(&(PVOID&)TrueWriteProcessMemory, HookWriteProcessMemory); DetourTransactionCommit(); break ; case DLL_PROCESS_DETACH: DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourDetach(&(PVOID&)TrueWriteProcessMemory, HookWriteProcessMemory); DetourTransactionCommit(); break ; } return TRUE; }
成功输出token1文件:
然后是token2,既然是内核输出,那只能是在ace.sys里做点手脚,DIE查壳发现ace.sys的大部分代码都被加壳过了,静态代码不好看,只能先猜测token2的输出调用了DbgPrint或者DbgPrintEx,因为之前输出token2的时候开启了Verbose Kernel outPut,猜测之所以正常输出失败是因为DbgPrintEx的level值太低,仅将字符串传递给内核调试器,不执行输出操作。
hook DbgPrintEx函数看一眼传参。
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 #include <ntifs.h> #include <ntdef.h> #include <ntstatus.h> #include <ntddk.h> #include <stdarg.h> #include "R0Hook.h" #define dbgFilter "Kvancy:" typedef ULONG (*FuncPtr) (ULONG ComponentId, ULONG Level, PCSTR Format, ...) ;HOOK_MANAGER hookManager; ULONG myDbgPrintEx (ULONG ComponentId, ULONG Level, PCSTR Format, ...) { Unhook(&hookManager); FuncPtr func = (FuncPtr)hookManager.target; kPrint("%s DbgPrintEx ComponentId:%lu,Level:%lu\n" ,dbgFilter, ComponentId, Level); va_list args; va_start(args, Format); NTSTATUS s = func(ComponentId, Level, Format, args); va_end(args); ApplyHook(&hookManager); return s; } void DriverUnload (PDRIVER_OBJECT pDriver) { kPrint("%s DriverUnload\n" , dbgFilter); Unhook(&hookManager); } NTSTATUS DriverEntry ( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ) { DriverObject->DriverUnload = DriverUnload; PVOID dbgPrintEx = DbgPrintEx; InitializeHookManager(&hookManager, dbgPrintEx, myDbgPrintEx); ApplyHook(&hookManager); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "%s DriverEntry\n" ,dbgFilter); return STATUS_SUCCESS; }
发现加载ace驱动后,有大量的level:5的调试信息输出
也就是说,程序通过设置调试信息的重要级别来控制调试信息是否正常输出,于是可以提高level级别来输出token2,那么最简单的方式就是hook之后修改level后传回去,编写代码hook测试下。
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 #include <ntifs.h> #include <ntdef.h> #include <ntstatus.h> #include <ntddk.h> #include <stdarg.h> #include <stdio.h> #include "R0Hook.h" #define dbgFilter "Kvancy:" typedef ULONG (*FuncPtr) (ULONG ComponentId, ULONG Level, PCSTR Format, ...) ;HOOK_MANAGER hookManager; char buffer[1024 ]; ULONG myDbgPrintEx (ULONG ComponentId, ULONG Level, PCSTR Format, ...) { Unhook(&hookManager); FuncPtr func = (FuncPtr)hookManager.target; va_list args; va_start(args, Format); vsprintf (buffer, Format, args); va_end(args); NTSTATUS s = func(ComponentId, 0 , "%s" , buffer); ApplyHook(&hookManager); return s; } void DriverUnload (PDRIVER_OBJECT pDriver) { kPrint("Kvancy: DriverUnload\n" ); Unhook(&hookManager); } NTSTATUS DriverEntry ( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ) { DriverObject->DriverUnload = DriverUnload; PVOID dbgPrintEx = DbgPrintEx; InitializeHookManager(&hookManager, dbgPrintEx, myDbgPrintEx); ApplyHook(&hookManager); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Kvancy: DriverEntry\n" ); return STATUS_SUCCESS; }
成功输出token2,但是根据题目要求是不能修改系统模块代码的,也就是说hook内核函数的方法不能过这道题,还是得想想别的方法。现在已知的ace.sys的行为就是驱动会在被加载之后做了某些操作会使得系统持续调用DbgPrintEx来输出token2,但是ace.sys其实做了某种操作后就被卸载掉了,如下图所示。
可以想到就是说驱动启动了一个线程或者进程,让该任务持续输出token2,创建完随后再卸载自己并且不停止这个线程或者进程。先枚举进程看看有没有奇怪的进程出现。
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 VOID WriteToFile (PUNICODE_STRING FilePath, PCHAR Data) { OBJECT_ATTRIBUTES objAttr; IO_STATUS_BLOCK ioStatusBlock; HANDLE fileHandle; NTSTATUS status; UNICODE_STRING unicodeFilePath; RtlInitUnicodeString(&unicodeFilePath, FilePath->Buffer); InitializeObjectAttributes(&objAttr, &unicodeFilePath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL , NULL ); status = ZwCreateFile( &fileHandle, FILE_APPEND_DATA | SYNCHRONIZE, &objAttr, &ioStatusBlock, NULL , FILE_ATTRIBUTE_NORMAL, 0 , FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL , 0 ); if (NT_SUCCESS(status)) { size_t dataLength = strlen (Data); ZwWriteFile(fileHandle, NULL , NULL , NULL , &ioStatusBlock, Data, (ULONG)dataLength, NULL , NULL ); ZwClose(fileHandle); } else { DbgPrint("Failed to create file: %08X\n" , status); } } VOID EnumProcesses () { NTSTATUS status; PVOID buffer; ULONG bufferSize = 0x10000 ; ULONG returnLength; CHAR logBuffer[1024 ]; UNICODE_STRING filePath; RtlInitUnicodeString(&filePath, L"\\??\\C:\\Users\\15386\\Desktop\\1.txt" ); buffer = ExAllocatePoolWithTag(NonPagedPool, bufferSize, 'proc' ); if (!buffer) { DbgPrint("Failed to allocate buffer for process information\n" ); return ; } status = ZwQuerySystemInformation(SystemProcessInformation, buffer, bufferSize, &returnLength); if (status == STATUS_INFO_LENGTH_MISMATCH) { ExFreePool(buffer); bufferSize = returnLength; buffer = ExAllocatePoolWithTag(NonPagedPool, bufferSize, 'proc' ); if (!buffer) { DbgPrint("Failed to allocate larger buffer for process information\n" ); return ; } status = ZwQuerySystemInformation(SystemProcessInformation, buffer, bufferSize, &returnLength); } if (NT_SUCCESS(status)) { PSYSTEM_PROCESS_INFORMATION processInfo = (PSYSTEM_PROCESS_INFORMATION)buffer; while (TRUE) { if (processInfo->ImageName.Buffer) { _snprintf(logBuffer, sizeof (logBuffer), "Process ID: %lu, Name: %wZ\n" , (ULONG)(ULONG_PTR)processInfo->ProcessId, &processInfo->ImageName); } else { _snprintf(logBuffer, sizeof (logBuffer), "Process ID: %lu, Name: [System Process]\n" , (ULONG)(ULONG_PTR)processInfo->ProcessId); } WriteToFile(&filePath, logBuffer); if (processInfo->NextEntryOffset == 0 ) break ; processInfo = (PSYSTEM_PROCESS_INFORMATION)((PUCHAR)processInfo + processInfo->NextEntryOffset); } } ExFreePool(buffer); }
结果发现好像没有奇怪的进程被创建出来,那么有可能是驱动利用PsCreateSystemThread
创建了一个内核线程。hookPsCreateSystemThread
函数看看驱动加载时是否调用了这个函数。
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 #include <ntifs.h> #include <ntdef.h> #include <ntstatus.h> #include <ntddk.h> #include <stdarg.h> #include "R0Hook.h" #define dbgFilter "Kvancy:" typedef ULONG (*FuncPtr) ( PHANDLE ThreadHandle, ULONG DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle, PCLIENT_ID ClientId, PKSTART_ROUTINE StartRoutine, PVOID StartContext) ;HOOK_MANAGER hookManager; NTSTATUS myPsCreateSystemThread ( PHANDLE ThreadHandle, ULONG DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, HANDLE ProcessHandle, PCLIENT_ID ClientId, PKSTART_ROUTINE StartRoutine, PVOID StartContext ) { Unhook(&hookManager); FuncPtr func = (FuncPtr)hookManager.target; kPrint("%s myPsCreateSystemThread StartRoutine:%p\n" , dbgFilter, StartRoutine); NTSTATUS s = func(ThreadHandle, DesiredAccess, ObjectAttributes, ProcessHandle, ClientId, StartRoutine, StartContext); ApplyHook(&hookManager); return s; } void DriverUnload (PDRIVER_OBJECT pDriver) { kPrint("%s DriverUnload\n" , dbgFilter); Unhook(&hookManager); } NTSTATUS DriverEntry ( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath ) { DriverObject->DriverUnload = DriverUnload; PVOID dbgPrintEx = PsCreateSystemThread; InitializeHookManager(&hookManager, dbgPrintEx, myPsCreateSystemThread); ApplyHook(&hookManager); DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "%s DriverEntry\n" , dbgFilter); return STATUS_SUCCESS; }
发现在token2输出前确实有PsCreateSystemThread函数调用,虽然不确定是不是ace.sys创建的。在windbg里反汇编看看线程函数
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 0: kd> u FFFFBA0729013DB0 l 100 ffffba07`29013db0 488bc4 mov rax,rsp ffffba07`29013db3 48895808 mov qword ptr [rax+8],rbx ffffba07`29013db7 48897818 mov qword ptr [rax+18h],rdi ffffba07`29013dbb 4c897020 mov qword ptr [rax+20h],r14 ffffba07`29013dbf 55 push rbp ffffba07`29013dc0 488d68a1 lea rbp,[rax-5Fh] ffffba07`29013dc4 4881eca0000000 sub rsp,0A0h ffffba07`29013dcb 48bf4e93328b546b331e mov rdi,1E336B548B32934Eh ffffba07`29013dd5 49bed520794add1d6d4b mov r14,4B6D1DDD4A7920D5h ffffba07`29013ddf 0f57c0 xorps xmm0,xmm0 ffffba07`29013de2 488d4d37 lea rcx,[rbp+37h] ffffba07`29013de6 0f114537 movups xmmword ptr [rbp+37h],xmm0 ffffba07`29013dea e8d1030000 call ffffba07`290141c0 ffffba07`29013def 48b8a14f122fb3276d4b mov rax,4B6D27B32F124FA1h ffffba07`29013df9 4c8d45e7 lea r8,[rbp-19h] ffffba07`29013dfd 4889456f mov qword ptr [rbp+6Fh],rax ffffba07`29013e01 ba05000000 mov edx,5 ffffba07`29013e06 488b456f mov rax,qword ptr [rbp+6Fh] ffffba07`29013e0a 33c9 xor ecx,ecx ffffba07`29013e0c 488945e7 mov qword ptr [rbp-19h],rax ffffba07`29013e10 48897d6f mov qword ptr [rbp+6Fh],rdi ffffba07`29013e14 488b456f mov rax,qword ptr [rbp+6Fh] ffffba07`29013e18 488945ef mov qword ptr [rbp-11h],rax ffffba07`29013e1c 4c89756f mov qword ptr [rbp+6Fh],r14 ffffba07`29013e20 488b456f mov rax,qword ptr [rbp+6Fh] ffffba07`29013e24 48894517 mov qword ptr [rbp+17h],rax ffffba07`29013e28 48897d6f mov qword ptr [rbp+6Fh],rdi ffffba07`29013e2c 488b456f mov rax,qword ptr [rbp+6Fh] ffffba07`29013e30 660f6f45e7 movdqa xmm0,xmmword ptr [rbp-19h] ffffba07`29013e35 4889451f mov qword ptr [rbp+1Fh],rax ffffba07`29013e39 660fef4517 pxor xmm0,xmmword ptr [rbp+17h] ffffba07`29013e3e 488b0543330000 mov rax,qword ptr [ffffba07`29017188] ffffba07`29013e45 660f7f45e7 movdqa xmmword ptr [rbp-19h],xmm0 ffffba07`29013e4a ff15c8210000 call qword ptr [ffffba07`29016018] ffffba07`29013e50 33db xor ebx,ebx ffffba07`29013e52 48b8f0104b32dd1d6d4b mov rax,4B6D1DDD324B10F0h ffffba07`29013e5c 4c8d45f7 lea r8,[rbp-9] ffffba07`29013e60 4889456f mov qword ptr [rbp+6Fh],rax ffffba07`29013e64 ba05000000 mov edx,5 ffffba07`29013e69 488b456f mov rax,qword ptr [rbp+6Fh] ffffba07`29013e6d 33c9 xor ecx,ecx ffffba07`29013e6f 488945f7 mov qword ptr [rbp-9],rax ffffba07`29013e73 48897d6f mov qword ptr [rbp+6Fh],rdi ffffba07`29013e77 488b456f mov rax,qword ptr [rbp+6Fh] ffffba07`29013e7b 488945ff mov qword ptr [rbp-1],rax ffffba07`29013e7f 4c89756f mov qword ptr [rbp+6Fh],r14 ffffba07`29013e83 488b456f mov rax,qword ptr [rbp+6Fh] ffffba07`29013e87 48894527 mov qword ptr [rbp+27h],rax ffffba07`29013e8b 48897d6f mov qword ptr [rbp+6Fh],rdi ffffba07`29013e8f 488b456f mov rax,qword ptr [rbp+6Fh] ffffba07`29013e93 660f6f45f7 movdqa xmm0,xmmword ptr [rbp-9] ffffba07`29013e98 440fb64c1d37 movzx r9d,byte ptr [rbp+rbx+37h] ffffba07`29013e9e 4889452f mov qword ptr [rbp+2Fh],rax ffffba07`29013ea2 660fef4527 pxor xmm0,xmmword ptr [rbp+27h] ffffba07`29013ea7 488b05da320000 mov rax,qword ptr [ffffba07`29017188] ffffba07`29013eae 660f7f45f7 movdqa xmmword ptr [rbp-9],xmm0 ffffba07`29013eb3 ff155f210000 call qword ptr [ffffba07`29016018] ffffba07`29013eb9 48ffc3 inc rbx ffffba07`29013ebc 4883fb10 cmp rbx,10h ffffba07`29013ec0 7c90 jl ffffba07`29013e52 ffffba07`29013ec2 48b8df20794add1d6d4b mov rax,4B6D1DDD4A7920DFh ffffba07`29013ecc 4c8d4507 lea r8,[rbp+7] ffffba07`29013ed0 4889456f mov qword ptr [rbp+6Fh],rax ffffba07`29013ed4 ba05000000 mov edx,5 ffffba07`29013ed9 488b456f mov rax,qword ptr [rbp+6Fh] ffffba07`29013edd 33c9 xor ecx,ecx ffffba07`29013edf 48894507 mov qword ptr [rbp+7],rax ffffba07`29013ee3 48897d6f mov qword ptr [rbp+6Fh],rdi ffffba07`29013ee7 488b456f mov rax,qword ptr [rbp+6Fh] ffffba07`29013eeb 4889450f mov qword ptr [rbp+0Fh],rax ffffba07`29013eef 4c89756f mov qword ptr [rbp+6Fh],r14 ffffba07`29013ef3 488b456f mov rax,qword ptr [rbp+6Fh] ffffba07`29013ef7 48894547 mov qword ptr [rbp+47h],rax ffffba07`29013efb 48897d6f mov qword ptr [rbp+6Fh],rdi ffffba07`29013eff 488b456f mov rax,qword ptr [rbp+6Fh] ffffba07`29013f03 660f6f4507 movdqa xmm0,xmmword ptr [rbp+7] ffffba07`29013f08 4889454f mov qword ptr [rbp+4Fh],rax ffffba07`29013f0c 660fef4547 pxor xmm0,xmmword ptr [rbp+47h] ffffba07`29013f11 488b0570320000 mov rax,qword ptr [ffffba07`29017188] ffffba07`29013f18 660f7f4507 movdqa xmmword ptr [rbp+7],xmm0 ffffba07`29013f1d ff15f5200000 call qword ptr [ffffba07`29016018] ffffba07`29013f23 b9ce0a0000 mov ecx,0ACEh ffffba07`29013f28 e833e9ffff call ffffba07`29012860 ffffba07`29013f2d e9adfeffff jmp ffffba07`29013ddf ffffba07`29013f32 cc int 3 ffffba07`29013f33 cc int 3 ffffba07`29013f34 4152 push r10
导入到ida里看伪代码
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 void __noreturn sub_180005148 () { __m128i v0; __int64 i; __m128i v2; __int64 v3; __m128i v4; __m128i v5; __m128i v6; __m128i v7; __m128i v8; __m128i v9; __int128 v10; __m128i v11; while ( 1 ) { v10 = 0 i64; ((void (__fastcall *)(__int128 *))((char *)&loc_180005556 + 2 ))(&v10); v5.m128i_i64[0 ] = 0x4B6D27B32F124FA1 i64; v5.m128i_i64[1 ] = 0x1E336B548B32934E i64; v8.m128i_i64[0 ] = 0x4B6D1DDD4A7920D5 i64; v0 = _mm_load_si128(&v5); v8.m128i_i64[1 ] = 0x1E336B548B32934E i64; v5 = _mm_xor_si128(v0, v8); MEMORY[0x31305A8047353130 ](0 i64, 5 i64, &v5); for ( i = 0 i64; i < 16 ; ++i ) { v6.m128i_i64[0 ] = 0x4B6D1DDD324B10F0 i64; v6.m128i_i64[1 ] = 0x1E336B548B32934E i64; v9.m128i_i64[0 ] = 0x4B6D1DDD4A7920D5 i64; v2 = _mm_load_si128(&v6); v3 = *((unsigned __int8 *)&v10 + i); v9.m128i_i64[1 ] = 0x1E336B548B32934E i64; v6 = _mm_xor_si128(v2, v9); MEMORY[0x31305A8047353130 ](0 i64, 5 i64, &v6, v3); } v7.m128i_i64[0 ] = 0x4B6D1DDD4A7920DF i64; v7.m128i_i64[1 ] = 0x1E336B548B32934E i64; v11.m128i_i64[0 ] = 0x4B6D1DDD4A7920D5 i64; v4 = _mm_load_si128(&v7); v11.m128i_i64[1 ] = 0x1E336B548B32934E i64; v7 = _mm_xor_si128(v4, v11); MEMORY[0x31305A8047353130 ](0 i64, 5 i64, &v7); sub_180003BF8(2766 i64); } }
好像是做了一个字符串解密然后输出的操作,浅浅用python跑一下解析字符串验证猜想。
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 def hex_xor_to_string (a, b ): result = a ^ b hex_str = hex (result)[2 :] if len (hex_str) % 2 != 0 : hex_str = '0' + hex_str result_str = '' .join(chr (int (hex_str[i:i+2 ], 16 )) for i in range (0 , len (hex_str), 2 )) return result_str x1 = 0x4B6D27B32F124FA1 x2 = 0x1E336B548B32934E y1 = 0x4B6D1DDD4A7920D5 y2 = 0x1E336B548B32934E result1 = hex_xor_to_string(x1, y1) result2 = hex_xor_to_string(x2, y2) print ("Result 1:" , result1)print ("Result 2:" , result2)res = result1[::-1 ] + result2[::-1 ] print (res)
打印出了token
基本上确定了这个线程就是打印token的线程,现在就是要想怎么patch这个线程函数使得token能够输出出来。
)
这里有个mov edx,5
语句,将DbgPrintEx函数的level设置成5,可以考虑patch这个语句,将5改成0,那么只需要patch一个字节,共三处。但是又要怎么patch呢,首先不能通过现在这种方式hook PsCreateSystemThread函数调用来确定StartRoutine地址(题目要求不能修改系统模块代码),也就是说得想另外一个办法确定这个线程的地址,然后通过偏移来确定需要patch的地址。
那么怎么确定这个线程地址呢,如果通过ZwQuerySystemInformation
枚举内核模块然后枚举模块下的所有线程的话,已经卸载了的ace.sys模块还能被枚举到么?问了下GPT好像是不能的,还可以考虑用StartRoutine地址的后几位做特征,匹配所有线程的开始地址的后几位,但是这种方式又感觉怕遇到地址特征一模一样的,感觉还是不大行。又问GPT怎么寻找到某个内核线程,得到答复是除了ZwQuerySystemInformation
枚举,还有通过PsLookupThreadByThreadId
函数从进程id和线程id查找的。
那么线程id和进程id又从哪获取呢?因为之前hook过PsCreateSystemThread函数,翻阅文档找到了一个ClientId参数,这个参数指向接收新线程的客户端标识符的结构,即一个pid,一个tid,但是pid,tid应该都是系统分配的吧,能是一个固定值么?hook一下看看输出
诶,tid貌似是系统分配的,但是pid一直都是4,很奇怪,pid=4代表的是什么进程呢?之前刚好枚举过进程来找有没有新进程创建,现在正好能派上用场。
貌似是一个系统进程,GPT了一下发现原来如果驱动程序通过内核模式创建系统线程(使用PsCreateSystemThread
),这些线程通常会在系统进程下运行,PID为4。原来如此,驱动程序和进程是一个级别的,但是驱动程序创建的这个线程是在系统进程之下的,而不是属于驱动模块,只是线程起始地址隶属于模块地址空间的,驱动卸载并不影响线程的运行。
这样的话,我们要找的线程因为模块被卸载了,所以它不在所有模块地址空间内,只要枚举所有系统进程pid=4下的所有线程,然后通过判断线程的起始地址是否在所有模块地址之内,即可判断它是否是我们要找的线程。这下思路就通了,开始编写代码实现patch。
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 #include "header.h" PVOID MoudleBaseAddress[1024 ]; ULONG64 MoudleSize[1024 ]; ULONG ModuleCount = 0 ; ULONG Offset1 = 0x52 , Offset2 = 0xB5 , Offset3 = 0x125 ; BOOLEAN IsAddressInKnownModules (PVOID Address, PVOID* ModuleBaseAddresses, ULONG64* ModuleSize, ULONG ModuleCount) { for (size_t i = 0 ; i < ModuleCount; i++) { if (Address >= ModuleBaseAddresses[i] && Address < (ULONG64)ModuleBaseAddresses[i] + ModuleSize[i]) { return TRUE; } } return FALSE; } PVOID EnumSystemModulesForProcess (HANDLE TargetProcessId) { NTSTATUS status; ULONG bufferSize = 0x10000 ; PVOID processBuffer = NULL ; PVOID moduleBuffer = NULL ; ULONG returnLength; processBuffer = ExAllocatePoolWithTag(NonPagedPool, bufferSize, 'proc' ); if (!processBuffer) { DbgPrint("Failed to allocate buffer for process information\n" ); return ; } status = ZwQuerySystemInformation(SystemProcessInformation, processBuffer, bufferSize, &returnLength); if (status == STATUS_INFO_LENGTH_MISMATCH) { ExFreePool(processBuffer); bufferSize = returnLength; processBuffer = ExAllocatePoolWithTag(NonPagedPool, bufferSize, 'proc' ); if (!processBuffer) { DbgPrint("Failed to allocate larger buffer for process information\n" ); return ; } status = ZwQuerySystemInformation(SystemProcessInformation, processBuffer, bufferSize, &returnLength); } moduleBuffer = ExAllocatePoolWithTag(NonPagedPool, bufferSize, 'modl' ); if (!moduleBuffer) { DbgPrint("Failed to allocate buffer for module information\n" ); ExFreePool(processBuffer); return ; } status = ZwQuerySystemInformation(SystemModuleInformation, moduleBuffer, bufferSize, &returnLength); if (status == STATUS_INFO_LENGTH_MISMATCH) { ExFreePool(moduleBuffer); bufferSize = returnLength; moduleBuffer = ExAllocatePoolWithTag(NonPagedPool, bufferSize, 'modl' ); if (!moduleBuffer) { DbgPrint("Failed to allocate larger buffer for module information\n" ); ExFreePool(processBuffer); return ; } status = ZwQuerySystemInformation(SystemModuleInformation, moduleBuffer, bufferSize, &returnLength); } if (NT_SUCCESS(status)) { PSYSTEM_MODULE_INFORMATION moduleInfo = (PSYSTEM_MODULE_INFORMATION)moduleBuffer; ModuleCount = moduleInfo->ModulesCount; for (ULONG i = 0 ; i < moduleInfo->ModulesCount; i++) { PSYSTEM_MODULE_INFORMATION_ENTRY moduleEntry = &moduleInfo->Modules[i]; MoudleBaseAddress[i] = moduleEntry->Base; MoudleSize[i] = moduleEntry->Size; } } if (NT_SUCCESS(status)) { PSYSTEM_PROCESS_INFORMATION processInfo = (PSYSTEM_PROCESS_INFORMATION)processBuffer; while (TRUE) { if (processInfo->ProcessId == TargetProcessId) { PSYSTEM_THREAD_INFORMATION threadInfo = (PSYSTEM_THREAD_INFORMATION)(processInfo + 1 ); for (ULONG i = 0 ; i < processInfo->NumberOfThreads; i++) { if (!IsAddressInKnownModules(threadInfo[i].StartAddress, MoudleBaseAddress, MoudleSize, ModuleCount)) { DbgPrint("Find it:%p\n" ,threadInfo[i].StartAddress); return threadInfo[i].StartAddress; } } break ; } if (processInfo->NextEntryOffset == 0 ) break ; processInfo = (PSYSTEM_PROCESS_INFORMATION)((PUCHAR)processInfo + processInfo->NextEntryOffset); } } ExFreePool(processBuffer); ExFreePool(moduleBuffer); return 0 ; } VOID UnloadDriver (PDRIVER_OBJECT DriverObject) { KdPrint(("Driver Unloaded\n" )); } NTSTATUS DriverEntry (PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { DriverObject->DriverUnload = UnloadDriver; KdPrint(("Driver Loaded\n" )); HANDLE targetPid = (HANDLE)4 ; PVOID targetAddress = EnumSystemModulesForProcess(targetPid); UCHAR valueToWrite = 0x00 ; UCHAR valueToRead = 0x05 ; ULONG numHasChanged = 0x00 ; if (*(PUCHAR)((ULONG64)targetAddress + Offset1) == valueToRead) { *(PUCHAR)((ULONG64)targetAddress + Offset1) = valueToWrite; numHasChanged++; } if (*(PUCHAR)((ULONG64)targetAddress + Offset2) == valueToRead) { *(PUCHAR)((ULONG64)targetAddress + Offset2) = valueToWrite; numHasChanged++; } if (*(PUCHAR)((ULONG64)targetAddress + Offset3) == valueToRead) { *(PUCHAR)((ULONG64)targetAddress + Offset3) = valueToWrite; numHasChanged++; } DbgPrint("numHasChanged:%d\n" , numHasChanged); return STATUS_SUCCESS; }
成功输出token2
(三)解题过程 题目要求:
编写程序,运行时修改尽量少的内存,让shellcode 往自行指定的位置写入token1成功。(满分3分)
要求任意位置,也就是要修改CreateFileA函数的第一个参数的值,根据之前分析的C:\2024GameSafeRace.token1
字符串是由十六进制异或得到的,也就是下面这些
可以考虑的是patch这些十六进制数据,把异或的key改成0,然后密文改成明文即可,因为明文异或0还是明文,但是考虑到要尽量修改少量的内存,我们最好还是保持key不变,自定义密文解密到我们所要的文件地址。跑个python脚本解出新的密文,得到新的密文,接着找到密文所在文件的偏移然后patch即可,给出解题代码。
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 #include "pch.h" #include <windows.h> #include <shellapi.h> #include <detours.h> #include <tlhelp32.h> #include <stdlib.h> #include <stdio.h> #pragma comment(lib,"detours.lib" ) #define _KDEBUG #define DBGMGEBOX(fmt, ...) \ do { \ \ wsprintfA(out, fmt, __VA_ARGS__); \ MessageBoxA(NULL, out, "提示" , MB_OK); \ } while(0) char out[100 ];typedef BOOL (WINAPI* WriteProcessMemory_t) ( _In_ HANDLE hProcess, _In_ LPVOID lpBaseAddress, _In_reads_bytes_(nSize) LPCVOID lpBuffer, _In_ SIZE_T nSize, _Out_opt_ SIZE_T* lpNumberOfBytesWritten ) ;WriteProcessMemory_t TrueWriteProcessMemory = NULL ; BOOL WINAPI HookWriteProcessMemory ( _In_ HANDLE hProcess, _In_ LPVOID lpBaseAddress, _In_reads_bytes_(nSize) LPCVOID lpBuffer, _In_ SIZE_T nSize, _Out_opt_ SIZE_T* lpNumberOfBytesWritten ) { if (nSize == 4506624 && *((PUCHAR)lpBuffer + 0x7171 ) == 0x3 ) { *((PUCHAR)lpBuffer + 0x7171 ) = 0x4 ; *(PULONG64)((ULONG64)lpBuffer + 0x7082 ) = 0x94e7c2136acc0e5c ; *(PULONG64)((ULONG64)lpBuffer + 0x7093 ) = 0x8207c5d1af9f3860 ; *(PULONG64)((ULONG64)lpBuffer + 0x70F1 ) = 0xa905cca9edcb138c ; *(PULONG64)((ULONG64)lpBuffer + 0x7108 ) = 0xa753b8236c56809a ; DBGMGEBOX("Hook Success!\n" ); } return TrueWriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten); } BOOL APIENTRY DllMain (HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); TrueWriteProcessMemory = (WriteProcessMemory_t)DetourFindFunction("kernel32.dll" , "WriteProcessMemory" ); DetourAttach(&(PVOID&)TrueWriteProcessMemory, HookWriteProcessMemory); DetourTransactionCommit(); break ; case DLL_PROCESS_DETACH: DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourDetach(&(PVOID&)TrueWriteProcessMemory, HookWriteProcessMemory); DetourTransactionCommit(); break ; } return TRUE; }
注入hook成功后,成功在桌面的flag.txt
输出token1