-
-
[原创]2023年腾讯游戏安全竞赛PC端初赛复现
-
发表于: 2024-7-29 22:12 5751
-
题目描述:
小红是一个PC客户端安全爱好者。有一天她发现一台机器上有一个未知的程序名为contest.exe,这个程序会在当前同个目录下的”contest.txt” 目录里每秒重复写入一次密文的信息。她想了解这个程序究竟写入了什么,并试试能否反过来控制这个程序按自己的意图工作。
评分标准:
(1)在64位Windows10系统上运行contest.exe, 找到明文的信息,作为答案提交(1分)。
(2)编写程序,运行时修改尽量少的contest.exe内存,让contest.exe 由写入密文信息变为写入明文信息成功。(满分2分)
(3)编写程序,运行时修改尽量少的contest.exe内存,让contest.exe 往入自行指定的不同的文件里写入明文信息成功。(满分3分)
(4)文档编写,详细描述(1)-(3)解题过程,详述提供的解题程序的演示方法。做到清晰易懂,操作可以复现结果。(满2分)
(5)提供(2)和(3)解题演示所用的源代码。要求编码工整风格优雅(1分)、注释详尽(1分)。
解题要求:
(1)编写代码,通过注入任何dll或者shellcode或者跨进程修改内存的方式来patch contest.exe的内存,但shellcode不能调用任何系统API。
(2)不得删改contest.exe的文件本身。不得使用任何文件和磁盘相关手段(比如同名文件、设置文件权限占坑等方式)阻止或者破坏contest.exe的执行。
(3)此题编程中不可使用内核驱动程序。
(4)必须使用64位Win10系统解题。
DIE查壳发现是带了VMP的,发现可以拖到x64dbg里调试,因为运行的时候每秒都会向文件中写入字符串信息,所以可以猜测用到了哪些函数
观察符号列表,找到contest.exe模块的导入函数
只找到了Sleep函数,猜测其他函数是通过loadLibraryA来间接使用,先在Sleep函数下断
确实断下来了,栈回溯找到父函数
在这个函数里调用了Sleep函数,现在就需要判断这个函数是不是直接存在于死循环体
因为按照猜测程序应该是有一个死循环体,然后在其中调用了Sleep函数,但是可能有多层函数调用需要多次栈回溯
就像这样的代码模型:
这里通过运行到返回发现程序不停止,说明这个函数已经直接存在于死循环体,那么其他关键文件操作函数也应该存在于这个函数的附近,
对附近所有Call指令下断,清空txt文件,根据回显发现在这两个call之间实现了文件写入
继续调试发现了是在00007FF7E9D2DA37这个函数内实现了文件内容写入,但是不是每次调用这个函数都会向文件写入,而是每两次调用写入一次文件,暂时不知道为什么,步进分析该函数
到了00007FF7E9D2BCB0这个函数,继续对这个函数内所有call指令下断点分析争取找到具体文件操作函数
下完断点,总共有十多个call指令,依次运行分析
第一个call指令运行完的返回值是一个字符串ZyAhZyk4YSgzfS4gZyA7
,猜测是明文或者某个加解密的密钥,继续运行分析
第二个call指令的函数传参的第一个参数rcx也是一个字符串catchmeifyoucan
,emm,这个很明显应该就是明文了,步进函数分析这个rbx函数是个什么东西
找到了一个疑似base64加密的表QRSTUVWXYZabcdefABCDEFGHIJKLMNOPwxyz0123456789+/ghijklmnopqrstuv
,试着将明文和这个表进行一个base64变表加密
cyberchef跑一下一下发现确实是密文,那明文就确定了,就是catchmeifyoucan
,继续分析
第四个call函数的第一个传参RCX是CreateFileA地址,那就应该是创建文件了,题目要求向不同的文件里写入明文信息,那么只要确定文件名所在的参数位置就可以了,观察传参
第6个参数是contest.txt
,应该就是文件名地址了,继续分析
第七个call指令运行完文件就被写入了,然后rcx也是WriteFileA函数地址,所以这里面应该就是一个写入文件,第七个参数是写入字符串内容,根据搜索找到WriteFileA函数定义
根据传参顺序和写入的字节数是DWORD类型推断第八个参数的低四字节可能是要写入的字节数,这里对应0x14
有了这些我们就可以初步编写代码,思路就是尝试hook创建文件和写入文件的两个call,传入需要的参数即可
这里最开始是想通过VirtualProtect函数和写入ShellCode来实现hook,但是在调试的时候发现了报错
显示没有写入的权限,那只能先加载dll进去调试一番
根据调试结果发现,在virtualProtect时返回了NULL,步进分析
到这发现了一个奇怪的jmp指令,和附近的其他函数形成鲜明对比,猜测作者在这动了手脚,hook了ZwProtectVirtualMemory函数,根据附近汇编代码取消钩子后运行,成功跑出结果
可以通过注入器UnHook ZwProtectVirtualMemory函数,然后再进行注入
注入器代码:
dll代码:
void
run()
{
while
(1)
{
fopen
(````);
fwrite
(````);
fclose
(```);
A();
}
}
void
A()
{
Sleep(```);
}
void
run()
{
while
(1)
{
fopen
(````);
fwrite
(````);
fclose
(```);
A();
}
}
void
A()
{
Sleep(```);
}
BOOL
WriteFile(
HANDLE
hFile,
// 需要写入数据的文件句柄
LPCVOID
lpBuffer,
// 指向要写入的数据缓冲区的指针
DWORD
nNumberOfBytesToWrite,
// 要写入的字节数
LPDWORD
lpNumberOfBytesWritten,
// 用于接收实际写入的字节数
LPOVERLAPPED lpOverlapped
// 指向OVERLAPPED结构的指针,用于异步操作
);
BOOL
WriteFile(
HANDLE
hFile,
// 需要写入数据的文件句柄
LPCVOID
lpBuffer,
// 指向要写入的数据缓冲区的指针
DWORD
nNumberOfBytesToWrite,
// 要写入的字节数
LPDWORD
lpNumberOfBytesWritten,
// 用于接收实际写入的字节数
LPOVERLAPPED lpOverlapped
// 指向OVERLAPPED结构的指针,用于异步操作
);
#include <Windows.h>
#include <TlHelp32.h>
#include <stdio.h>
#include <tchar.h>
wchar_t
dllPath[] = L
"C:\\Users\\15386\\Desktop\\腾旭游戏安全竞赛赛题\\23年第一轮\\wp\\Tencent2023.dll"
;
wchar_t
exeName[] = L
"contest.exe"
;
DWORD64
UnHookAddr = 0x00007FFBC1F90990;
// ZwProtectVirtualMemory地址
BYTE
UnHookShellCode[] = {
0x4C, 0x8B, 0xD1, 0xB8, 0x50, 0x00, 0x00, 0x00, 0xF6, 0x04, 0x25, 0x08, 0x03, 0xFE, 0x7F, 0x01,
0x75, 0x03, 0x0F, 0x05, 0xC3, 0xCD, 0x2E, 0xC3
};
/* Unhook还原ZwProtectVirtualMemory
00007FFBC1F90990 < | 4C:8BD1 | mov r10,rcx |
00007FFBC1F90993 | B8 50000000 | mov eax,50 | 50:'P'
00007FFBC1F90998 | F60425 0803FE7F 01 | test byte ptr ds:[7FFE0308],1 |
00007FFBC1F909A0 | 75 03 | jne ntdll.7FFBC1F909A5 |
00007FFBC1F909A2 | 0F05 | syscall |
00007FFBC1F909A4 | C3 | ret |
00007FFBC1F909A5 | CD 2E | int 2E |
00007FFBC1F909A7 | C3 | ret |
*/
BOOL
InjectDll(
DWORD
dwPID,
wchar_t
* szDllPath)
{
HANDLE
hProcess = NULL, hThread = NULL;
HMODULE
hMod = NULL;
LPVOID
pRemoteBuf = NULL;
DWORD
dwBufSize = (
DWORD
)(_tcslen(szDllPath) + 1) *
sizeof
(
TCHAR
);
LPTHREAD_START_ROUTINE pThreadProc;
if
(!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
{
printf
(
"OpenProcess(%d) failed!!! [%d]\n"
, dwPID, GetLastError());
return
FALSE;
}
//UnHook
DWORD
oldProtect = 0;
if
(!VirtualProtectEx(hProcess, (
LPVOID
)UnHookAddr,
sizeof
(UnHookShellCode), PAGE_EXECUTE_READWRITE, &oldProtect))
{
printf
(
"VirtualProtectEx failed!!! [%d]\n"
, GetLastError());
return
FALSE;
}
if
(!WriteProcessMemory(hProcess, (
LPVOID
)UnHookAddr, UnHookShellCode,
sizeof
(UnHookShellCode), 0))
{
printf
(
"UnHook failed!!! [%d]\n"
, GetLastError());
return
FALSE;
}
printf
(
"UnHook virtualProtect success!!!\n"
);
if
(!(pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE)))
{
printf
(
"VirtualAllocEx %d falied!!! [%d]\n"
, hProcess,GetLastError());
return
FALSE;
}
if
(!WriteProcessMemory(hProcess, pRemoteBuf, (
LPVOID
)szDllPath, dwBufSize, NULL))
{
printf
(
"WriteProcess %d Memory fail [%d]\n"
,hProcess, GetLastError());
return
FALSE;
}
hMod = GetModuleHandle(L
"kernel32.dll"
);
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod,
"LoadLibraryW"
);
hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
return
TRUE;
}
DWORD
ProcessFind(
wchar_t
* Exename)
{
HANDLE
hProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if
(!hProcess)
{
return
FALSE;
}
PROCESSENTRY32 info;
info.dwSize =
sizeof
(PROCESSENTRY32);
if
(!Process32First(hProcess, &info))
{
return
FALSE;
}
while
(
true
)
{
if
(
memcmp
(info.szExeFile, Exename, _tcslen(Exename)) == 0)
{
return
info.th32ProcessID;
}
if
(!Process32Next(hProcess, &info))
{
return
FALSE;
}
}
return
FALSE;
}
int
main()
{
DWORD64
pid = ProcessFind(exeName);
if
(InjectDll(pid, dllPath))
{
printf
(
"InjectDll success\n"
);
}
system
(
"pause"
);
}
#include <Windows.h>
#include <TlHelp32.h>
#include <stdio.h>
#include <tchar.h>
wchar_t
dllPath[] = L
"C:\\Users\\15386\\Desktop\\腾旭游戏安全竞赛赛题\\23年第一轮\\wp\\Tencent2023.dll"
;
wchar_t
exeName[] = L
"contest.exe"
;
DWORD64
UnHookAddr = 0x00007FFBC1F90990;
// ZwProtectVirtualMemory地址
BYTE
UnHookShellCode[] = {
0x4C, 0x8B, 0xD1, 0xB8, 0x50, 0x00, 0x00, 0x00, 0xF6, 0x04, 0x25, 0x08, 0x03, 0xFE, 0x7F, 0x01,
0x75, 0x03, 0x0F, 0x05, 0xC3, 0xCD, 0x2E, 0xC3
};
/* Unhook还原ZwProtectVirtualMemory
00007FFBC1F90990 < | 4C:8BD1 | mov r10,rcx |
00007FFBC1F90993 | B8 50000000 | mov eax,50 | 50:'P'
00007FFBC1F90998 | F60425 0803FE7F 01 | test byte ptr ds:[7FFE0308],1 |
00007FFBC1F909A0 | 75 03 | jne ntdll.7FFBC1F909A5 |
00007FFBC1F909A2 | 0F05 | syscall |
00007FFBC1F909A4 | C3 | ret |
00007FFBC1F909A5 | CD 2E | int 2E |
00007FFBC1F909A7 | C3 | ret |
*/
BOOL
InjectDll(
DWORD
dwPID,
wchar_t
* szDllPath)
{
HANDLE
hProcess = NULL, hThread = NULL;
HMODULE
hMod = NULL;
LPVOID
pRemoteBuf = NULL;
DWORD
dwBufSize = (
DWORD
)(_tcslen(szDllPath) + 1) *
sizeof
(
TCHAR
);
LPTHREAD_START_ROUTINE pThreadProc;
if
(!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
{
printf
(
"OpenProcess(%d) failed!!! [%d]\n"
, dwPID, GetLastError());
return
FALSE;
}
//UnHook
DWORD
oldProtect = 0;
if
(!VirtualProtectEx(hProcess, (
LPVOID
)UnHookAddr,
sizeof
(UnHookShellCode), PAGE_EXECUTE_READWRITE, &oldProtect))
{
printf
(
"VirtualProtectEx failed!!! [%d]\n"
, GetLastError());
return
FALSE;
}
if
(!WriteProcessMemory(hProcess, (
LPVOID
)UnHookAddr, UnHookShellCode,
sizeof
(UnHookShellCode), 0))
{
printf
(
"UnHook failed!!! [%d]\n"
, GetLastError());
return
FALSE;
}
printf
(
"UnHook virtualProtect success!!!\n"
);
if
(!(pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE)))
{
printf
(
"VirtualAllocEx %d falied!!! [%d]\n"
, hProcess,GetLastError());
return
FALSE;
}
if
(!WriteProcessMemory(hProcess, pRemoteBuf, (
LPVOID
)szDllPath, dwBufSize, NULL))
{
printf
(
"WriteProcess %d Memory fail [%d]\n"
,hProcess, GetLastError());
return
FALSE;
}
hMod = GetModuleHandle(L
"kernel32.dll"
);
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod,
"LoadLibraryW"
);
hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
return
TRUE;
}
DWORD
ProcessFind(
wchar_t
* Exename)
{
HANDLE
hProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
- [原创]2024年腾讯游戏安全竞赛PC初赛复现 6023
- [原创]2023年腾讯游戏安全PC决赛复现 3958
- 2022腾讯游戏安全PC端初赛复现 7098
- [原创]2023年腾讯游戏安全竞赛PC端初赛复现 5752