首页
社区
课程
招聘
[原创]腾讯游戏安全大赛题解
发表于: 2023-4-17 18:59 21246

[原创]腾讯游戏安全大赛题解

2023-4-17 18:59
21246

终于如愿参加一次腾讯游戏安全大赛,初赛也算是把所有题目都做出来了,进入决赛,但是自己太菜了,只会暴力NOP。

决赛题目就是一个占用率很高的服务进程,让我们分析解决,前几天都有事,最后一天 all in 写完的。

1.在64位Windows10系统上运行contest.exe, 找到明文的信息,作为答案提交(1分)。

答案:catchmeifyoucan

正常运行会在当前目录下的 contest.txt 去写入一个 ImVkImx9JG12OGtlImV+

CE打开,先通过CE的memory view找到一串一直在变化的内存。

虽然不确定,找到了 contest.txt 这个明文,但是 catchmeifyoucan 不确定它是不是flag,还得进一步确定,于是查找是谁访问了这个地址。

在第三项中,发现后面有一个读取 r10 所指示的内存的操作。

同样memory view查找 7FF713657190 地址,结果找到一串 base 表:

QRSTUVWXYZabcdefABCDEFGHIJKLMNOPwxyz0123456789+/ghijklmnopqrstuv

于是果断拿密文解密,发现得到了正确答案。

2.编写程序,运行时修改尽量少的contest.exe内存,让contest.exe 由写入密文信息变为写入明文信息成功。(满分2分)

Base64编码的一组中,三个原文对应四个密文。第一个密文和第四个密文只会被原文第一个字符和第三个字符影响。第二个字符会被第一个原文和第二个原文字符影响,第三个字符会被第二个原文和第三个原文字符影响。

对 base 表进行访问查询,找到三个地址,对三个地址分析。

查看第一项访问地址

右移两位进行查表,很明显就是对第一个密文的操作,因此把中间的操作全部NOP,只剩读取和写入操作。

再对第二个项目分析,很明显看到了对第四个密文的操作:

这里的 and 0x3f 就是取得最低的六位,很明显是最后一位,同样也把中间的指令全部NOP掉。

不仅如此,同时注意到赋值的时候,对 r14+1了,并且r14的值在后面+1。这里有一个很头疼的点就是明文密文长度不一致,三个原文对应四个密文,因此这里可以为这个点考虑起来了。如果我 r14 的值不给他 +1,直接给 r14 的值赋值,结果会怎样呢?也就是对应指令修改为:

发现输出文件的内容果然变短了。

并且此时,我们再处理中间的一位,就能得到明文输出了。

我们来看第三个项目:

这里同样把 BA5D-BA68的代码段NOP掉,做完之后,发现contest输出明文了。

总结一下:

编写dll进行一次性修改:

但是在写内存的时候,发现 VirtualProtect 一直调用失败,拿火绒剑扫了一下,发现 VirtualProtect 被下了钩子。

尝试修复一下这个钩子,写一个注入器和dll

DLL不能直接使用 VirtualProtect 去修改内存属性,所以我们需要在注入之前,使用 VirtualProtectEx 先修改内存权限,再通过 WriteProcessMemory函数修复程序在 API 处下的一个钩子,这里是 inline hook,因此直接遍历模块寻找 ZwProtectVirtualMemory 函数把钩子取消。

取消之后,即可使用远程线程注入的方式去注入dll,远程注入的思路就是开辟一块远程内存,写入dll路径,创建远程线程回调 LoadLibraryA 函数去加载 DLL。

DLL主要就是作上面分析的一些PATCH内存的操作。

下面是源码:

在一开始的时候我们在内存看到了 contest.txt,于是尝试直接修改,发现可以成功更改写入的文件,因此为了达到目的我们可以直接修改这里的内存,但是因为它一直在变化,因此可以查看什么写了这个内存。

主要是做了一个异或运算,直接 NOP即可。

需要注意在把指令patch掉之后,一定要写一下这里存储 Name 的地方,把 Name 替换为自己想要的名字即可。

但是发现 flag 写进去时而变化,究其原因还是因为flag内存一直在变化,因此patch修改flag的内存让它也不再变化即可。

注入器不变,DLL 需要稍微改变一下。

常规思路就是找进程匹配名字直接杀,但是发现会再生,用火绒剑分析了一下,发现有两个进程,应该是相互守护关系。并且 exploror.exe 进程一直在产生新的 working service 进程。猜测应该是运行了程序之后对这个进程下了钩子,于是火绒剑扫描一下钩子:

上下图对比下图是没运行之前扫描的结果,可以发现对 ZwProtectVirtualMemory 下了钩子,和之前一样,先把这个钩子修复,然后再杀掉所有进程名为 WorkingService 的进程即可。

但是之后又发现一些很奇怪的事情,有时候可以完全杀掉,有时候杀不掉,究其原因,原来它对很多进程都进行了 hook 尝试,有次我打开了任务管理器没有考虑到就又复活了,因此选择遍历进程的这个模块,识别钩子并去掉。

去掉之后再循环寻找进程杀掉即可。

代码实现:

运行结果:

直接使用main1.cpp编译出来的代码运行即可结束。

但是有一定概率失败,不知道是什么问题,但是有时候可以完美运行,因此这里我使用了另一种方法:检测到被注入的进程,然后直接结束它们,之后马上杀掉服务进程。

实现起来也非常简单:

此方式编译main1-2.cpp文件的代码运行即可。

通过火绒剑分析行为,发现一秒内就会有大量的创建文件请求。

因此想到使用 CE 去查找到 contest 字符串,找到创建文件的指令,hook出来每次创建文件之后sleep即可。

但是由于pid一直变化,因此尝试先卸载钩子,保证pid不变化,使用 CE 调试,找到内存访问的指令。

注入的时候同样发现进程存在钩子,因此也先在注入器当中手动把钩子去掉。

找到读 contest 内存的代码段,把代码段拷贝到一个自己分配的内存上面,赋予可执行权限,不过在指令中间可以加入一个 sleep函数,让它得以休息。不过这里遇到一个问题,就是32位偏移貌似跳转不了,因此使用

的方式去调用休眠函数,这里的八字节整数直接填sleep函数的虚拟地址即可。

注入器代码:

DLL代码:

内存利用率下降了很多。

使用main2.cpp和injector.cpp编译出exe文件,先执行main2取消被钩住的进程,再使用injector注入dllmain.cpp编译出的动态链接库,达到效果。

同样做的时候发现了不稳定的因素,有时候可以使用,有时候不能使用,因此采取第二种方式来解决。

同样要先把所有钩子卸掉,最好的办法还是重启那些已经被钩住的程序。

这部分程序与之前 main1-2.cpp 中的区别仅仅是少了 system("taskkill /f /im WorkingService.exe"); 而已。

然后把上面的dll注入到两个进程中去,这一段代码没有变化。

通过火绒剑的事件分析可以发现,文件创建的频率明显下降很多(因为每次强制休息了500MS):

并且没有影响进程功能,CPU占用率也大大降低了。

首先就要分析它写入了哪四个文件了,从火绒剑的分析来看,应该是有四个线程在不同的写,因为很容易注意到,四个文件几乎是同时打开的。

对进程进行内存dump,拖入ida分析,发现一串base64的表,于是在CE中找到主要编码的函数。

通过参数分析找到了内存的位置,通过仔细观察,发现和初赛的flag:catchmeifyoucan 十分相似。

经过分析,发现它们存在互补关系

通过某次虚拟机炸了之后,意外得到了两份落地的文件

经过base64解码发现,其中一个是 catchmeifyoucan + id,一个是不可见的字符,而且刚好等于上面不可见字符的补集。

本来并没有什么头绪,但是发现了一个细节,在火绒剑当中

可以发现 contestxxxx.txt 的 xxxx 就是线程的 id,

大概分析了一下,应该是有两串content,根据线程id,选择到底是 ASCII 字符还是非ASCII 字符,然后base64后+TID。

经过多次测试之后发现,只会出现两种:两种互补,就像这样子:

根据逻辑分析,是计算TID的总和,然后根据位数,选择这一位到底是选择 ASCII 还是非ASCII字符。

根据之前进程的行为写出了以下的程序,这里需要CREATE_ALWAYS和FILE_FLAG_DELETE_ON_CLOSE两个标志位,以便于达到和之前进程一样的行为。

在附件中,编译 main3.cpp,运行可以直接达到效果,并且拥有很低的CPU占用率。

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
mov [r14],al
add r14,0
mov [r14],al
add r14,0
 
 
 
 
 
 
mov [r14],al
add r14,0
mov [r14],al
add r14,0
 
 
 
 
 
 
#include<windows.h>
#include<iostream>
#include<time.h>
#include<stdlib.h>
#include<TlHelp32.h>
DWORD old;
SIZE_T written;
DWORD FindProcess() {
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe32;
    pe32 = { sizeof(pe32) };
    BOOL ret = Process32First(hSnap, &pe32);
    while (ret)
    {
        if (!wcsncmp(pe32.szExeFile, L"contest.exe", 11)) {
            printf("Find contest.exe Process %d\n", pe32.th32ProcessID);
            return pe32.th32ProcessID;
        }
        ret = Process32Next(hSnap, &pe32);
    }
    return 0;
}
void InjectModule(DWORD ProcessId, const char* szPath)
{
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
    printf("进程句柄:%p\n", hProcess);
    LPVOID lpAddress = VirtualAllocEx(hProcess, NULL, 0x100, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    SIZE_T dwWriteLength = 0;
    WriteProcessMemory(hProcess, lpAddress, szPath, strlen(szPath), &dwWriteLength);
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibraryA, lpAddress, NULL, NULL);
    WaitForSingleObject(hThread, -1);
    VirtualFreeEx(hProcess, lpAddress, 0, MEM_RELEASE);
    CloseHandle(hProcess);
    CloseHandle(hThread);
}
void UNHOOK(DWORD ProcessId) {
    BYTE INS[] = {0x4C,0x8B,0xD1,0xB8,0x50};
    HANDLE ths = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessId);
    MODULEENTRY32 me;
    me.dwSize = sizeof(me);
    UINT64 addr=0;
    if (Module32First(ths, &me))
    {
        do
        {
            if (addr=(UINT64)GetProcAddress(me.hModule, "ZwProtectVirtualMemory"))
            {
                printf("addr:%p\n", addr);
                break;
            }
 
        } while (Module32Next(ths, &me));
    }
    CloseHandle(ths);
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
    VirtualProtectEx(hProcess, (void *)addr, 0x5, PAGE_EXECUTE_READWRITE, &old);
    WriteProcessMemory(hProcess, (void *)addr, INS, 0x5, &written);
    printf("written:%d\n", written);
    VirtualProtectEx(hProcess, (void*)addr, 0x5, old, &old);
    CloseHandle(hProcess);
}
int main() {
    DWORD ProcessId = FindProcess();
    while (!ProcessId) {
        printf("未找到contest程序,等待两秒中再试\n");
        Sleep(2000);
        ProcessId = FindProcess();
    }
    printf("尝试去除钩子...\n");
    UNHOOK(ProcessId);//去除钩子
    printf("开始注入进程...\n");
    InjectModule(ProcessId, "C:\\Users\\xia0ji233\\source\\repos\\T-contest\\x64\\Debug\\T-contest.dll");
    printf("注入完毕\n");
}
#include<windows.h>
#include<iostream>
#include<time.h>
#include<stdlib.h>
#include<TlHelp32.h>
DWORD old;
SIZE_T written;
DWORD FindProcess() {
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe32;
    pe32 = { sizeof(pe32) };
    BOOL ret = Process32First(hSnap, &pe32);
    while (ret)
    {
        if (!wcsncmp(pe32.szExeFile, L"contest.exe", 11)) {
            printf("Find contest.exe Process %d\n", pe32.th32ProcessID);
            return pe32.th32ProcessID;
        }
        ret = Process32Next(hSnap, &pe32);
    }
    return 0;
}
void InjectModule(DWORD ProcessId, const char* szPath)
{
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
    printf("进程句柄:%p\n", hProcess);
    LPVOID lpAddress = VirtualAllocEx(hProcess, NULL, 0x100, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    SIZE_T dwWriteLength = 0;
    WriteProcessMemory(hProcess, lpAddress, szPath, strlen(szPath), &dwWriteLength);
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibraryA, lpAddress, NULL, NULL);
    WaitForSingleObject(hThread, -1);
    VirtualFreeEx(hProcess, lpAddress, 0, MEM_RELEASE);
    CloseHandle(hProcess);
    CloseHandle(hThread);
}
void UNHOOK(DWORD ProcessId) {
    BYTE INS[] = {0x4C,0x8B,0xD1,0xB8,0x50};
    HANDLE ths = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessId);
    MODULEENTRY32 me;
    me.dwSize = sizeof(me);
    UINT64 addr=0;
    if (Module32First(ths, &me))
    {
        do
        {
            if (addr=(UINT64)GetProcAddress(me.hModule, "ZwProtectVirtualMemory"))
            {
                printf("addr:%p\n", addr);
                break;
            }
 
        } while (Module32Next(ths, &me));
    }
    CloseHandle(ths);
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
    VirtualProtectEx(hProcess, (void *)addr, 0x5, PAGE_EXECUTE_READWRITE, &old);
    WriteProcessMemory(hProcess, (void *)addr, INS, 0x5, &written);
    printf("written:%d\n", written);
    VirtualProtectEx(hProcess, (void*)addr, 0x5, old, &old);
    CloseHandle(hProcess);
}
int main() {
    DWORD ProcessId = FindProcess();
    while (!ProcessId) {
        printf("未找到contest程序,等待两秒中再试\n");
        Sleep(2000);
        ProcessId = FindProcess();
    }
    printf("尝试去除钩子...\n");
    UNHOOK(ProcessId);//去除钩子
    printf("开始注入进程...\n");
    InjectModule(ProcessId, "C:\\Users\\xia0ji233\\source\\repos\\T-contest\\x64\\Debug\\T-contest.dll");
    printf("注入完毕\n");
}
#include<Windows.h>
#include<time.h>
#include<stdio.h>
DWORD oldprot,ret;
PROC HookedFunction;
UINT64 Offset[3] = { 0xBA39 ,0xB9FD ,0xBA5D }, Len[3] = { 9,8,12 };//PATCH偏移和PATCH长度,这里皆patch为0x90(NOP)
BYTE Ins[] = {
    0x41,0x88,0x06,     //mov [r14],al
    0x90,               //nop
    0x49,0x83,0xC6,0x00 //add r14, 0
};
 
 
UINT64 InsOffset = 0xBA05,InsLen=sizeof(Ins);
SIZE_T num;
BYTE buf = 0x90;
BYTE NOP[] = { 0x90,0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 };
void patch() {
    UINT64 Base = (UINT64)GetModuleHandle(nullptr);
    for (int i = 0; i < 3; i++) {
        UINT64 addr = Base + Offset[i];
        VirtualProtect((void *)addr, Len[i], PAGE_EXECUTE_READWRITE, &oldprot);
        memcpy((void *)addr, NOP, Len[i]);
        VirtualProtect((void*)addr, Len[i], oldprot, &oldprot);
    }
    printf("NOP done\n");
    VirtualProtect((void*)(Base + InsOffset), InsLen,PAGE_EXECUTE_READWRITE, &oldprot);
    memcpy((void *)(Base + InsOffset), Ins, InsLen);
    VirtualProtect((void*)(Base + InsOffset), InsLen, oldprot, &oldprot);
    printf("Instruction Patch Done!\n");
}
 
 
BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        patch();
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
#include<Windows.h>
#include<time.h>
#include<stdio.h>
DWORD oldprot,ret;
PROC HookedFunction;
UINT64 Offset[3] = { 0xBA39 ,0xB9FD ,0xBA5D }, Len[3] = { 9,8,12 };//PATCH偏移和PATCH长度,这里皆patch为0x90(NOP)
BYTE Ins[] = {
    0x41,0x88,0x06,     //mov [r14],al
    0x90,               //nop
    0x49,0x83,0xC6,0x00 //add r14, 0
};
 
 
UINT64 InsOffset = 0xBA05,InsLen=sizeof(Ins);
SIZE_T num;
BYTE buf = 0x90;
BYTE NOP[] = { 0x90,0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 };
void patch() {
    UINT64 Base = (UINT64)GetModuleHandle(nullptr);
    for (int i = 0; i < 3; i++) {
        UINT64 addr = Base + Offset[i];
        VirtualProtect((void *)addr, Len[i], PAGE_EXECUTE_READWRITE, &oldprot);
        memcpy((void *)addr, NOP, Len[i]);
        VirtualProtect((void*)addr, Len[i], oldprot, &oldprot);
    }
    printf("NOP done\n");
    VirtualProtect((void*)(Base + InsOffset), InsLen,PAGE_EXECUTE_READWRITE, &oldprot);
    memcpy((void *)(Base + InsOffset), Ins, InsLen);
    VirtualProtect((void*)(Base + InsOffset), InsLen, oldprot, &oldprot);
    printf("Instruction Patch Done!\n");
}
 
 
BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        patch();
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
 
 
 
 
 
#include<Windows.h>
#include<time.h>
#include<stdio.h>
DWORD oldprot,ret;
PROC HookedFunction;
UINT64 Offset[3] = { 0xBA39 ,0xB9FD ,0xBA5D }, Len[3] = { 9,8,12 };//PATCH偏移和PATCH长度,这里皆patch为0x90(NOP)
BYTE Ins[] = {
    0x41,0x88,0x06,     //mov [r14],al
    0x90,               //nop
    0x49,0x83,0xC6,0x00 //add r14, 0
};
 
UINT64 InsOffset = 0xBA05,InsLen=sizeof(Ins);
SIZE_T num;
BYTE buf = 0x90;
BYTE NOP[] = { 0x90,0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 };
void patch() {
    UINT64 Base = (UINT64)GetModuleHandle(nullptr);
    for (int i = 0; i < 3; i++) {//把三个点位的指令NOP掉
        UINT64 addr = Base + Offset[i];
        VirtualProtect((void *)addr, Len[i], PAGE_EXECUTE_READWRITE, &oldprot);
        memcpy((void *)addr, NOP, Len[i]);
        VirtualProtect((void*)addr, Len[i], oldprot, &oldprot);
    }
    printf("NOP done\n");
    VirtualProtect((void*)(Base + InsOffset), InsLen,PAGE_EXECUTE_READWRITE, &oldprot);
    memcpy((void *)(Base + InsOffset), Ins, InsLen);//替换对应的指令
    VirtualProtect((void*)(Base + InsOffset), InsLen, oldprot, &oldprot);
    printf("Instruction Patch Done!\n");
}
 
void patchname() {
    UINT64 Base = (UINT64)GetModuleHandle(nullptr);
    UINT64 Offset1 = 0xC8F3,Offset2=0xC5C6,NameOffset=0x772FA,Len1=4,Len2=5,flagOffset = 0x772E9;
    char NewName[] = "test.txt";//新文件名
    char flag[] = "catchmeifyoucan";
    VirtualProtect((void*)(Base + Offset1), Len1, PAGE_EXECUTE_READWRITE, &oldprot);
    memcpy((void*)(Base + Offset1), NOP, Len1);//指令Nop掉防止写的时机不对发生变化
    VirtualProtect((void*)(Base + Offset1), Len1, oldprot, &oldprot);
 
    VirtualProtect((void*)(Base + Offset2), Len2, PAGE_EXECUTE_READWRITE, &oldprot);
    memcpy((void*)(Base + Offset2), NOP, Len2);//指令Nop掉防止写的时机不对发生变化
    VirtualProtect((void*)(Base + Offset2), Len2, oldprot, &oldprot);
 
    VirtualProtect((void*)(Base + NameOffset), sizeof(NewName), PAGE_EXECUTE_READWRITE, &oldprot);
    memcpy((void*)(Base + NameOffset), NewName, sizeof(NewName));//把名字写到内存中
    VirtualProtect((void*)(Base + NameOffset), sizeof(NewName), oldprot, &oldprot);
 
    VirtualProtect((void*)(Base + flagOffset), sizeof(flag), PAGE_EXECUTE_READWRITE, &oldprot);
    memcpy((void*)(Base + flagOffset), flag, sizeof(flag));//把flag写到内存中
    VirtualProtect((void*)(Base + flagOffset), sizeof(flag), oldprot, &oldprot);
 
 
    printf("Change Name Success\n");
}
 
BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        patch();
        patchname();
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
#include<Windows.h>
#include<time.h>
#include<stdio.h>
DWORD oldprot,ret;
PROC HookedFunction;
UINT64 Offset[3] = { 0xBA39 ,0xB9FD ,0xBA5D }, Len[3] = { 9,8,12 };//PATCH偏移和PATCH长度,这里皆patch为0x90(NOP)
BYTE Ins[] = {
    0x41,0x88,0x06,     //mov [r14],al
    0x90,               //nop
    0x49,0x83,0xC6,0x00 //add r14, 0
};
 
UINT64 InsOffset = 0xBA05,InsLen=sizeof(Ins);
SIZE_T num;
BYTE buf = 0x90;
BYTE NOP[] = { 0x90,0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 };
void patch() {
    UINT64 Base = (UINT64)GetModuleHandle(nullptr);
    for (int i = 0; i < 3; i++) {//把三个点位的指令NOP掉
        UINT64 addr = Base + Offset[i];
        VirtualProtect((void *)addr, Len[i], PAGE_EXECUTE_READWRITE, &oldprot);
        memcpy((void *)addr, NOP, Len[i]);
        VirtualProtect((void*)addr, Len[i], oldprot, &oldprot);
    }
    printf("NOP done\n");
    VirtualProtect((void*)(Base + InsOffset), InsLen,PAGE_EXECUTE_READWRITE, &oldprot);
    memcpy((void *)(Base + InsOffset), Ins, InsLen);//替换对应的指令
    VirtualProtect((void*)(Base + InsOffset), InsLen, oldprot, &oldprot);
    printf("Instruction Patch Done!\n");
}
 
void patchname() {
    UINT64 Base = (UINT64)GetModuleHandle(nullptr);
    UINT64 Offset1 = 0xC8F3,Offset2=0xC5C6,NameOffset=0x772FA,Len1=4,Len2=5,flagOffset = 0x772E9;
    char NewName[] = "test.txt";//新文件名
    char flag[] = "catchmeifyoucan";
    VirtualProtect((void*)(Base + Offset1), Len1, PAGE_EXECUTE_READWRITE, &oldprot);
    memcpy((void*)(Base + Offset1), NOP, Len1);//指令Nop掉防止写的时机不对发生变化
    VirtualProtect((void*)(Base + Offset1), Len1, oldprot, &oldprot);
 
    VirtualProtect((void*)(Base + Offset2), Len2, PAGE_EXECUTE_READWRITE, &oldprot);
    memcpy((void*)(Base + Offset2), NOP, Len2);//指令Nop掉防止写的时机不对发生变化
    VirtualProtect((void*)(Base + Offset2), Len2, oldprot, &oldprot);
 
    VirtualProtect((void*)(Base + NameOffset), sizeof(NewName), PAGE_EXECUTE_READWRITE, &oldprot);
    memcpy((void*)(Base + NameOffset), NewName, sizeof(NewName));//把名字写到内存中
    VirtualProtect((void*)(Base + NameOffset), sizeof(NewName), oldprot, &oldprot);
 
    VirtualProtect((void*)(Base + flagOffset), sizeof(flag), PAGE_EXECUTE_READWRITE, &oldprot);
    memcpy((void*)(Base + flagOffset), flag, sizeof(flag));//把flag写到内存中
    VirtualProtect((void*)(Base + flagOffset), sizeof(flag), oldprot, &oldprot);
 
 
    printf("Change Name Success\n");
}
 
BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        patch();
        patchname();
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
 
 
 
 
 
#include<windows.h>
#include<iostream>
#include<time.h>
#include<stdlib.h>
#include<TlHelp32.h>
#define EXP L"explorer"
#define PROCESSNAME L"WorkingService"
DWORD old;
SIZE_T written;
 
void UNHOOK() {
    DWORD ProcessId=0;
    BYTE INS[] = { 0x4C,0x8B,0xD1,0xB8,0x50 },buf;
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe32;
    pe32 = { sizeof(pe32) };
    BOOL ret = Process32First(hSnap, &pe32);
    while (ret)
    {
        if (TRUE) {
            ProcessId=pe32.th32ProcessID;
            HANDLE ths = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessId);
            if (!ths) {
                ret = Process32Next(hSnap, &pe32);
                continue;
            }
            MODULEENTRY32 me;
            me.dwSize = sizeof(me);
            UINT64 addr = 0;
            if (Module32First(ths, &me))
            {
                do
                {
                    if (addr = (UINT64)GetProcAddress(me.hModule, "ZwProtectVirtualMemory"))
                    {
                        break;
                    }
                } while (Module32Next(ths, &me));
            }
 
            CloseHandle(ths);
            HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
            if (!hProcess||addr==0) {
                ret = Process32Next(hSnap, &pe32);
                continue;
            }
            ReadProcessMemory(hProcess, (void *)addr, &buf, 1, &written);
            if (buf == 0xE9) {//识别到inline hook的标志
                VirtualProtectEx(hProcess, (void*)addr, 0x5, PAGE_EXECUTE_READWRITE, &old);
                WriteProcessMemory(hProcess, (void*)addr, INS, 0x5, &written);
                printf("Process %d,hook addr:%p\n",ProcessId, addr);
                printf("written:%d\n", written);
                VirtualProtectEx(hProcess, (void*)addr, 0x5, old, &old);
            }
            CloseHandle(hProcess);
        }
        ret = Process32Next(hSnap, &pe32);
    }
}
 
DWORD FindProcess() {
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe32;
    pe32 = { sizeof(pe32) };
    BOOL ret = Process32First(hSnap, &pe32);
    while (ret)
    {
        if (!wcsncmp(pe32.szExeFile, PROCESSNAME, lstrlenW(PROCESSNAME))) {
            printf("Find WorkingService.exe Process %d\n", pe32.th32ProcessID);
            return pe32.th32ProcessID;
        }
        ret = Process32Next(hSnap, &pe32);
    }
    return 0;
}
int main() {
    DWORD ProcessId = 0;
    UNHOOK();
    do {
        ProcessId = FindProcess();
        printf("pid:%p\n", ProcessId);
        if (!ProcessId) {
            break;
        }
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
        printf("hProcess:%p\n", hProcess);
        if (!hProcess) {
            break;
        }
        TerminateProcess(hProcess, 0);
    } while (1);
    printf("Terminate OK\n");
    system("pause");
}
#include<windows.h>
#include<iostream>
#include<time.h>
#include<stdlib.h>
#include<TlHelp32.h>
#define EXP L"explorer"
#define PROCESSNAME L"WorkingService"
DWORD old;
SIZE_T written;
 
void UNHOOK() {
    DWORD ProcessId=0;
    BYTE INS[] = { 0x4C,0x8B,0xD1,0xB8,0x50 },buf;
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe32;
    pe32 = { sizeof(pe32) };
    BOOL ret = Process32First(hSnap, &pe32);
    while (ret)
    {
        if (TRUE) {
            ProcessId=pe32.th32ProcessID;
            HANDLE ths = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, ProcessId);
            if (!ths) {
                ret = Process32Next(hSnap, &pe32);
                continue;
            }
            MODULEENTRY32 me;
            me.dwSize = sizeof(me);
            UINT64 addr = 0;
            if (Module32First(ths, &me))
            {
                do
                {
                    if (addr = (UINT64)GetProcAddress(me.hModule, "ZwProtectVirtualMemory"))
                    {
                        break;
                    }
                } while (Module32Next(ths, &me));
            }
 
            CloseHandle(ths);
            HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
            if (!hProcess||addr==0) {
                ret = Process32Next(hSnap, &pe32);
                continue;
            }
            ReadProcessMemory(hProcess, (void *)addr, &buf, 1, &written);
            if (buf == 0xE9) {//识别到inline hook的标志
                VirtualProtectEx(hProcess, (void*)addr, 0x5, PAGE_EXECUTE_READWRITE, &old);

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 11
支持
分享
最新回复 (15)
雪    币: 2303
活跃值: (3318)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
附件没了
2023-4-17 19:07
0
雪    币: 10650
活跃值: (5804)
能力值: ( LV15,RANK:523 )
在线值:
发帖
回帖
粉丝
3
mb_wpitiize 附件没了
其实代码都贴在markdown里面了,只不过提交writeup的时候要求同时提供源代码文件就用这种说法啦
2023-4-17 19:19
0
雪    币: 183
活跃值: (347)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4
这个我也报名做了,但是因为班里事有点多,决赛没做完。我的解决思路决赛的第1个,结束进程,我用的办法是比较暴力的,枚举系统进程,判断进程名是否相同,相同则结束,并且当进程为explorer.exe遍历线程和模块如果有WorkingServiceDll并且该模块内有线程执行则直接结束即可。降低CPU占用我本来想的是注入dll劫持SleepEx修改增加延迟时间,后来发现没屌用还是顺时高,因为时间原因没后续研究,第三个没搞
2023-4-17 20:48
0
雪    币: 26588
活跃值: (63252)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
5


初赛题目存档


https://gslab.qq.com/html/competition/2023/race-pre.htm

上传的附件:
2023-4-18 11:19
0
雪    币: 26588
活跃值: (63252)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
6

决赛题目存档

https://gslab.qq.com/html/competition/2023/race-final.htm

上传的附件:
2023-4-18 11:22
0
雪    币: 4105
活跃值: (5807)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
7
2023-4-18 13:18
0
雪    币: 40
活跃值: (682)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8

决赛的flag并不是随机抽取 而是每个线程都在拿着这块玩意xor 线程启动间隔越长 秘文就越乱 把其他暂停了 然后把flag那回复下 base64那下段你就发现每次只有没xor和全部xor后两种结果 他们故意多线程不加锁造成这种现象 
线程数量取决于逻辑核数 群里看到一堆人瞎猜还有硬刚vm的我把答案发出来后直接心态炸了

最后于 2023-4-19 09:01 被Dlnn编辑 ,原因:
2023-4-19 09:00
0
雪    币: 2948
活跃值: (30846)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
感谢分享
2023-4-19 09:05
1
雪    币: 8387
活跃值: (4961)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
10
哎,我一直以为这最后一问还原算法要硬钢VM,然后去搞附加题了
2023-4-20 15:53
0
雪    币: 4711
活跃值: (4219)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
大道在我 哎,我一直以为这最后一问还原算法要硬钢VM,然后去搞附加题了
这个游戏安全大赛你们在那发现的  接到通知的
2023-4-20 17:05
0
雪    币: 8387
活跃值: (4961)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
12
木志本柯 这个游戏安全大赛你们在那发现的 接到通知的
每年上半年固定时间举行 百度就有官网
2023-4-20 17:12
0
雪    币: 10650
活跃值: (5804)
能力值: ( LV15,RANK:523 )
在线值:
发帖
回帖
粉丝
13
Dlnn 决赛的flag并不是随机抽取&nbsp;而是每个线程都在拿着这块玩意xor&nbsp;线程启动间隔越长&nbsp;秘文就越乱&nbsp;把其他暂停了&nbsp; ...
心态炸了,因为就写了一天最后一个小时,VM刚不出来,只能去猜了
2023-4-21 10:33
0
雪    币: 877
活跃值: (1148)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
flag明文地址的前一个字节就是异或的值,改成0就不会变化了
2023-4-21 10:45
0
雪    币: 2119
活跃值: (1890)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
15
2023-5-3 15:29
0
雪    币: 177
活跃值: (278)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
创建进程那块你看了吗
2023-8-14 17:05
0
游客
登录 | 注册 方可回帖
返回
//