腾讯游戏安全2023

腾讯游戏安全2023

第一次打腾讯游戏安全,成功进了决赛。

但决赛不会驱动,第一题也没完全做出来

image-20230427004311389

初赛-PC客户端-解题过程

题目

image-20230427004804030

1.获取flag

直接运行等程序的反调试结束后,暂停程序,对输出的密文下硬件断点。找到如下函数

image-20230409161647096

估计是换表base64

跟踪输入的明文,下断点,成功在内存中找到明文

image-20230409161754232

与解密出的结果一致

image-20230409161829251

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
{
//通过进程名来查找contest.exe
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];

// 拼接dll路径
sprintf_s(strDllPath, "%s\\patch.dll", strCurrent);
HMODULE hModule = GetModuleHandleA("kernel32.dll");

// 获取LoadLibraryA函数地址
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: {
// 注册VEH异常
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;

// 修改Rip跳过函数
ExceptionInfo->ContextRecord->Rip += 0x168;
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}

测试结果

image-20230409163408542

3.往入自行指定的不同的文件里写入明文信息

经调试可得,输出文件的名字写在内存中,因此直接修改对应进程内存即可

image-20230409163609946

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';
//将异或0x40改为异或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;
// 使用baseAddress进行进一步操作
//修改为目标文件名
PatchAddr = (DWORD64)baseAddress + 0x772F9;
//将异或0x45改为异或0
DWORD64 PatchAddr2 = (DWORD64)baseAddress + 0x772E8;
while(1) {
char buff2[12] = { 0 };
ReadProcessMemory(handle, (LPVOID)(PatchAddr+1), buff2, 5, NULL);
//检测只有当开头为contest时才进行修改
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输入即可

测试结果

image-20230409163751394

决赛-PC客户端-解题过程

第一题

image-20230427004841647

简述一下思路

首先这个文件会不断的创建进程,用火绒剑可以扫得该程序往别的进程里面写入了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") //定义要卸载对应dll的进程名
#define DEF_DLL_NAME (L"WorkingServiceDll.dll") //定义要卸载的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;

//1.
//dwPID=notepad进程ID
//使用TH32CS_SNAPMODULE参数
//获取加载到notepad进程的DLL名称
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);

//此函数检索与进程相关联的第一个模块的信息
bMore = Module32First(hSnapshot, &me);

for (; bMore; bMore = Module32Next(hSnapshot, &me)) //bMore用于判断该进程的模块快照是否还有,bFound用于判断是否找到了我们想要卸载的dll模块
{
if (!_tcsicmp((LPCTSTR)me.szModule, szDllName) || !_tcsicmp((LPCTSTR)me.szExePath, szDllName))
{
bFound = TRUE;
break;
}
}

if (!bFound)
{
CloseHandle(hSnapshot);
return FALSE;
}
//2. 通过进程PID获取进程句柄
if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
{
_tprintf(L"进程(%d) 打开失败[%d]\n", dwPID, GetLastError);
return FALSE;
}
//3. 获取FreeLibrary函数的地址
hModule = GetModuleHandle(L"kernel32.dll");
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary");
//4.创建线程来执行FreeLibrary(modBaseAddr要卸载的dll模块基址)
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; //初始化PID为0xFFFFFFFF
HANDLE hSnapShot = INVALID_HANDLE_VALUE; //初始化快照句柄为INVALID_HANDLE_VALUE
PROCESSENTRY32 pe; //定义一个存放 快照进程信息 的一个结构体
//1.获取当前系统进程快照
pe.dwSize = sizeof(PROCESSENTRY32);
hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL);
//2.枚举全部进程
Process32First(hSnapShot, &pe);
do
{
dwPID = pe.th32ProcessID;
EjectDll(dwPID, DEF_DLL_NAME); //卸载DLL
system("taskkill /f /t /im WorkingService.exe");
} while (Process32Next(hSnapShot, &pe)); //循环查找下一个进程
}
return 0;
}

第二题要求让这个程序慢下来

本来是想hook他写入文件的函数的,但是断不到那个地址

然后又发现这个程序和初赛使用了同一个base64变表

于是和初赛一样VEH hook然后VEH在里面塞个sleep(10000)

第三题

程序执行了两步加密

一个是变表的base64,另一个是选取其中的几位异或上0x78

但是具体如何异或的因为时间不够没找到。

附加题

驱动

牡蛎达牡蛎,鸭蛋莫鸭蛋

赛后总结

这个算是我第一次通宵打的比赛,都没怎么睡

加上决赛刚好于是清明得回家打不满3天于是有点着急了。

打比赛更重要的是良好的心态和相信自己会出的信念

是非常难忘的经历,学到了不少东西,也感谢在实验室一起通宵的寞叶师傅和xman师傅