腾讯游戏安全2023
第一次打腾讯游戏安全,成功进了决赛。
但决赛不会驱动,第一题也没完全做出来
初赛-PC客户端-解题过程
题目
1.获取flag
直接运行等程序的反调试结束后,暂停程序,对输出的密文下硬件断点。找到如下函数
估计是换表base64
跟踪输入的明文,下断点,成功在内存中找到明文
与解密出的结果一致
2.写入密文信息变为写入明文成功
经调试可得,明文应该只经过了上面已经找到的base64函数
因此,在函数头下断然后更改eip直接运行到return
又因为尽可能修改的内存少,选择dll远程注入,仅需修改寄存器和复写明文到输出即可。
套用远程注入模板
Inject.cpp
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
| #include <cstdio> #include <windows.h> #include <tlhelp32.h> #include <tchar.h>
int main() { HANDLE hProcessSnap; PROCESSENTRY32 pe32; hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hProcessSnap == INVALID_HANDLE_VALUE) { return -1; } pe32.dwSize = sizeof(PROCESSENTRY32); if (!Process32First(hProcessSnap, &pe32)) { CloseHandle(hProcessSnap); return -1; } do { if(wcscmp(pe32.szExeFile,L"contest.exe")==0) { HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID); char strCurrent[MAX_PATH]; GetCurrentDirectoryA(MAX_PATH,strCurrent); char strDllPath[MAX_PATH]; sprintf_s(strDllPath, "%s\\patch.dll", strCurrent); HMODULE hModule = GetModuleHandleA("kernel32.dll"); PVOID ProcAdd = GetProcAddress(hModule, "LoadLibraryA"); PVOID ParaAddr = VirtualAllocEx(hProcess, NULL, strlen(strDllPath) + 1, MEM_COMMIT, PAGE_READWRITE); WriteProcessMemory(hProcess, ParaAddr, strDllPath, strlen(strDllPath) + 1, NULL);
DWORD threadid = 0; HANDLE hThread = NULL; hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)ProcAdd, ParaAddr, 0, &threadid);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread); CloseHandle(hProcessSnap); return 0; } } while (Process32Next(hProcessSnap, &pe32)); CloseHandle(hProcessSnap); return -1; }
|
DLLmain.cpp
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
| BOOL APIENTRY DllMain(HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { AddVectoredExceptionHandler(1, ExceptionHandler); DWORD dwProcessID = GetCurrentProcessId(); THREADENTRY32 te32 = { sizeof(te32) }; HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (Thread32First(hThreadSnap, &te32)) { do { if (dwProcessID == te32.th32OwnerProcessID) { dwMainThreadId = te32.th32ThreadID; break; } } while (Thread32Next(hThreadSnap, &te32)); } CreateThread(NULL, NULL, breakpoint, (LPVOID)dwMainThreadId, NULL, NULL); break; } case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
|
设置硬件断点时要先挂起线程再进行设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| DWORD breakpoint(LPVOID lpThreadParameter) { HANDLE hThread = OpenThread(THREAD_ALL_ACCESS,TRUE,(DWORD)lpThreadParameter); SuspendThread(hThread); CONTEXT ctx; ctx.ContextFlags = CONTEXT_ALL; GetThreadContext(hThread, &ctx); ctx.Dr0 = PatchAddr; ctx.Dr7 = 0x405; SetThreadContext(hThread, &ctx); ResumeThread(hThread); CloseHandle(hThread); return 0; }
|
实现VEH 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
| LONG ExceptionHandler(EXCEPTION_POINTERS* ExceptionInfo) { if ((DWORD64)ExceptionInfo->ExceptionRecord->ExceptionAddress == PatchAddr && ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) { ExceptionInfo->ContextRecord->Rax = (DWORD64)0x0; const char* Input = (const char*)ExceptionInfo->ContextRecord->Rcx; const DWORD64 InputLen = ExceptionInfo->ContextRecord->Rdx; char* flag = (char*)ExceptionInfo->ContextRecord->R8; DWORD* OutputLen = (DWORD*)ExceptionInfo->ContextRecord->R9; for(int i = 0; i < InputLen; i++) { flag[i] = Input[i]; } *OutputLen = InputLen;
ExceptionInfo->ContextRecord->Rip += 0x168; return EXCEPTION_CONTINUE_EXECUTION; } return EXCEPTION_CONTINUE_SEARCH; }
|
测试结果
3.往入自行指定的不同的文件里写入明文信息
经调试可得,输出文件的名字写在内存中,因此直接修改对应进程内存即可
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
| #include <iostream> #include <Windows.h> #include <Psapi.h> #include <cstdio>
using namespace std; int main(){ byte buff[12] = {0}; int pId; printf("目的文件"); char file[100]; cin >> file; printf("输入进程ID:"); scanf("%d", &pId); for(int i = 1; i < 10; i++) { buff[i] = file[i-1]; }
buff[10] = '\0'; buff[0] = 0;
HANDLE handle=OpenProcess(PROCESS_ALL_ACCESS, 0, pId); DWORD64 PatchAddr; HMODULE moduleHandle; DWORD bytesNeeded; if (EnumProcessModules(handle, &moduleHandle, sizeof(moduleHandle), &bytesNeeded)) { DWORD64 baseAddress = (DWORD64)moduleHandle; PatchAddr = (DWORD64)baseAddress + 0x772F9; DWORD64 PatchAddr2 = (DWORD64)baseAddress + 0x772E8; while(1) { char buff2[12] = { 0 }; ReadProcessMemory(handle, (LPVOID)(PatchAddr+1), buff2, 5, NULL); if (buff2[0] == 0x63 && buff2[1] == 0x6f && buff2[2] == 0x6e) { WriteProcessMemory(handle, (LPVOID)PatchAddr, buff, 10, NULL); WriteProcessMemory(handle, (LPVOID)PatchAddr2, buff, 1, NULL); printf("修改成功"); break; } } } return 0; }
|
在任务管理器获取对应的线程id输入即可
测试结果
决赛-PC客户端-解题过程
第一题
简述一下思路
首先这个文件会不断的创建进程,用火绒剑可以扫得该程序往别的进程里面写入了WorkingServiceDll.dll文件
第一题要求杀掉这个,首先把dll给卸载了,再把这个程序给关掉
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
| #include <windows.h> #include <tlhelp32.h> #include <tchar.h> #define DEF_PROC_NAME (L"WorkingService.exe") #define DEF_DLL_NAME (L"WorkingServiceDll.dll") BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName) { BOOL bMore = FALSE, bFound = FALSE; HANDLE hSnapshot, hProcess, hThread; HMODULE hModule = NULL; MODULEENTRY32 me = { sizeof(me) }; LPTHREAD_START_ROUTINE pThreadProc; hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID); bMore = Module32First(hSnapshot, &me); for (; bMore; bMore = Module32Next(hSnapshot, &me)) { if (!_tcsicmp((LPCTSTR)me.szModule, szDllName) || !_tcsicmp((LPCTSTR)me.szExePath, szDllName)) { bFound = TRUE; break; } } if (!bFound) { CloseHandle(hSnapshot); return FALSE; } if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID))) { _tprintf(L"进程(%d) 打开失败[%d]\n", dwPID, GetLastError); return FALSE; } hModule = GetModuleHandle(L"kernel32.dll"); pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary"); hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); CloseHandle(hProcess); CloseHandle(hSnapshot); return TRUE; } int _tmain(int argc, TCHAR* argv[]) { while (true) { DWORD dwPID = 0xFFFFFFFF; HANDLE hSnapShot = INVALID_HANDLE_VALUE; PROCESSENTRY32 pe; pe.dwSize = sizeof(PROCESSENTRY32); hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL); Process32First(hSnapShot, &pe); do { dwPID = pe.th32ProcessID; EjectDll(dwPID, DEF_DLL_NAME); system("taskkill /f /t /im WorkingService.exe"); } while (Process32Next(hSnapShot, &pe)); } return 0; }
|
第二题要求让这个程序慢下来
本来是想hook他写入文件的函数的,但是断不到那个地址
然后又发现这个程序和初赛使用了同一个base64变表
于是和初赛一样VEH hook然后VEH在里面塞个sleep(10000)
第三题
程序执行了两步加密
一个是变表的base64,另一个是选取其中的几位异或上0x78
但是具体如何异或的因为时间不够没找到。
附加题
驱动
牡蛎达牡蛎,鸭蛋莫鸭蛋
赛后总结
这个算是我第一次通宵打的比赛,都没怎么睡 菜
加上决赛刚好于是清明得回家打不满3天于是有点着急了。
打比赛更重要的是良好的心态和相信自己会出的信念
是非常难忘的经历,学到了不少东西,也感谢在实验室一起通宵的寞叶师傅和xman师傅