首页
社区
课程
招聘
[原创]腾讯游戏安全大赛2024初赛题解
发表于: 2024-4-15 01:17 10810

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

2024-4-15 01:17
10810

报名参加了一下2024的游戏安全竞赛,今天初赛结束,总体来说赛题质量还是非常高的。

解题附件下载

<!--more-->

先工具分析一下,发现加了 VM,动调发现对很多工具有检测,部分改名可以直接绕过,但是 CE 怎么改都会被检测,所以先上微步基本分析一下行为:

https://s.threatbook.com/report/file/1bc2f607b5e4707a70a32bb78ac72c9b895f00413ba4bd21229f6103757ca19f

注意到了有注入行为,一般会通过 WriteProcessMemory 这个 API 进行,于是编写 DLL 去hook看看情况。

这一块没什么进展,但是突然发现这个进程对其它进程有些操作

这不就是 token1 嘛,但是发现底下没有这个文件,尝试创建这个文件,发现直接就有内容写进去了,用命令可以打印出来。

于是 token1 就出来了,很神奇。 token1: 757F4749AEBB1891EF5AC2A9B5439CEA

对于token2,一直尝试做一些 API 的 hook 想看看它干了啥,这里我做了 MmCopyMemory 的 hook。

结果直接没有调用过。后面还同样的方法 hook 了其它的 API,诸如 KeStackAttachProcess 的,同样没有调用,于是陷入沉思,既然它没有把 token 写到 r3 那大概率在 r0 层。

同时在一次巧合中(开了 DbgView 的 verbose),发现 token 直接打印出来了。

那么 token2 就出来了 token2: 8b3f14a24d64f3e697957c252e3a5686

所以 flag 就是 flag{757F4749AEBB1891EF5AC2A9B5439CEA-8b3f14a24d64f3e697957c252e3a5686}

首先看看内核层的输出吧,因为它本来就可以输出,直接调用的 DbgPrintEx 函数,只不过某个 Level 无法正常被接受罢了,尝试 hook 一下,看看 Level

持续输出了 call DbgPrintEx(0000000000000000,0000000000000005,FFFFF1001067EB90) Level=5几乎不能输出任何内容了,因此尝试hook替换让它可以输出,但是这里 Hook 还是太麻烦了,于是我选择打下断点之后栈回溯一下看看情况

可以发现关键 call 之前,有对 edx 赋值为 5,那么直接修改这个指令,把 hook 解掉看看能否输出。

这里我选择手动改一下,一共发现了三个位置,特征都是差不多的,都把 mov edx,5 改成 mov edx,0

发现改完之后 token 成功输出了。

但是这里应该是驱动加载时候分配的内存写入的 shellcode,如果能知道地址,那么能去改掉这些指令,但是地址是我通过栈回溯找到的,如果没有hook掉系统 API,那么我根本不太可能去获取到shellcode的地址。于是想到它既然是不停地在打印的,必然创建了一个内核线程,那么我先遍历一下内核线程。

在中间我判断了一下地址是否为 0xFFFF 开头来判断是否为内核线程,然后打印出来之后,搜索通过栈回溯得到的 shellcode 的前几位。

很幸运地只能找到一个,多次实验之后发现它shellcode是不会变的(至少头几个字节),那么就可以匹配特征码去判断 shellcode 的地址。

并且可以手动计算一下 shellcode StartAddress 和 对应要修改的指令的偏移。

启动地址:0xFFFFBB0EDB013DB0

修改地址1:0xffffbb0edb013e01

修改地址2:0xffffbb0edb013e64

修改地址3:0xffffbb0edb013ed4

分别是 shellcode+0x51+1shellcode+0xb4+1shellcode+0x124+1 的位置。(+1是因为要改的操作数在指令的偏移处)

先编写一个遍历内核线程的程序,然后去特判它的特征码,来确定shellcode位置,最后再写入三个指令即可。

该文件编译产物为 XSafe2.sys,运行题目之后加载这个驱动可以让 token2 输出。

通过下图可以看到,驱动加载后搜索到了 shellcode 的地址,并通过修改内存让 token成功输出了,但是自己做的时候发现是有概率的,有时候可能搜不到这个线程,从截图可以看出反复加载了4次才成功找到 shellcode,但是注入成功之后也成功输出了 token2。

token1 的话可以采用新建 C:\2024GameSafeRace.token1 文件的方式让它将 token1 打印出来,究其原因没有成功输出出来是因为创建文件的时候没有让它在文件不存在时创建。

后面发现写文件的进程好像是 TaskMgr,并且 token1 是可以独立运行的,所以可以把虚拟机的测试模式关了,后面只需要分析这个三环程序即可。

用调试器附加,虽然外挂程序检测了调试器,但是通过改名可以绕过,断在创建文件的API上 CreateFileA

栈回溯一下发现创建是失败的(返回-1)。

要让它成功输出让它创建成功即可,同时观察栈我注意到了有个参数3,而参数通过查阅 CreateFileA 的参数说明可知

这也就解释了为什么创建一个文件能够成功写入,尝试把它修改成 1。

发现成功返回了,所以目的非常明确了,让它传的第五个参数改成1即可成功输出到文件中,而刚好注意到上面的一条指令有直接 mov xxx,3 的,把它改成1试试。

文件即使不存在也会创建并输出成功。

那么这里说一个可行的思路:同样注入一个 dll 给它,然后去遍历线程找到 shellcode 入口,计算偏移改掉这个指令,让 token3 成功输出。

先手动操作一遍:

此时可以发现线程入口是 0000019B070AAB48,要修改的指令地址为 0000019B070A4D6D

在 StartRoutine - 0x5ddb 的位置上,而且它的地址很明显,只要在堆上就符合条件,但是为了保险还是取一定长度的特征码去比较。

那么据此写一个针对 Taskmgr 的注入器(二进制文件为 Xinject.exe):

然后根据以上分析结果写一个改变这个代码的 DLL(二进制文件为 XHack.dll):

运行 hack.exe 之后,运行 Xinject.exe 即可达到如下效果:

注入结束之后可以看到马上创建了文件。

第二题就完整地实现了。

附件说明:

其实还是 hook 这个地方,改它的写入文件名即可,这里我又注意到

往下顺着看,它把RAX写到了 RBP+0x20 的位置,但是最后又对它写了第五个参数,因此基本可以认为这个指令是无用的,尝试去进程分配一块内存,在这个地方将 RCX 的值赋值给它即可达成任意位置的写入。

这个指令位置在 StartRoutine - 0x5ddb - 0x67 处。

用下面的代码编译出 XHack.dll (运行时需要改名为这个,用第二题的注入器,附件中的文件名为 XHack3.dll)

先分配一个内存,写入地址,再构造 mov rcx,xxx 指令,最后替换到指定位置即可。

内容也非常完美没有被改变

4,5如上所示,源代码均在本文中提供,在打包的可执行文件中:

总共的附件说明:

三天没打满,中间一天抽空去拿了个ACM省赛。

然后第二天又接到调剂复试通知赶路去了。

不过最后好在还是赶着最后完成了赛题 233。

https://www.ctyun.cn/zhishi/p-233867

// dllmain.cpp : 定义 DLL 应用程序的入口点。 hack.dll
#include "pch.h"
#include <Windows.h>
#include <stdio.h>
#include<Psapi.h>
typedef BOOL (*Func)( HANDLE hProcess,
     LPVOID lpBaseAddress,
     LPCVOID lpBuffer,
     SIZE_T nSize,
     SIZE_T* lpNumberOfBytesWritten
);
Func OriginFunc = NULL;
 
BYTE HookCode[] = {
    0x48,0xB8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //mov rax , xxx
    0xFF,0xE0                                          //jmp rax
};
BYTE OriginCode[0x50];
SIZE_T HookLen = sizeof(HookCode);
DWORD saved=0;
WCHAR FILENAME[MAX_PATH];
BOOL HackWriteProcessMemory(HANDLE hProcess,
    LPVOID lpBaseAddress,
    LPCVOID lpBuffer,
    SIZE_T nSize,
    SIZE_T* lpNumberOfBytesWritten
) {
     
    VirtualProtect(OriginFunc, HookLen, PAGE_EXECUTE_READWRITE, &saved);
    memcpy(OriginFunc, OriginCode,HookLen);             //unhook
    printf("Call WriteProcessMemory(%p,%p,%p,%d,%p)",hProcess,lpBaseAddress,lpBuffer,nSize,lpNumberOfBytesWritten);
    GetModuleFileNameEx(hProcess, NULL, FILENAME, MAX_PATH);
    wprintf(L"ProcessName=%s\n", FILENAME);
    BOOL ret=OriginFunc(hProcess, lpBaseAddress, lpBuffer, nSize,lpNumberOfBytesWritten);
    memcpy(OriginFunc, HookCode, HookLen);               //rehook
    VirtualProtect(OriginFunc, HookLen, saved, &saved);
    return ret;
}
 
void hack() {
    OriginFunc = WriteProcessMemory;
    VirtualProtect(OriginFunc, HookLen, PAGE_EXECUTE_READWRITE, &saved);
    memcpy(OriginCode, OriginFunc,HookLen); //saved
    *(__int64*)(HookCode + 2) = (__int64)HackWriteProcessMemory;//build
    memcpy(OriginFunc, HookCode, HookLen);  //Hook
    VirtualProtect(OriginFunc, HookLen, saved, &saved);
    printf("Hook done\n");
}
 
 
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH: {
        AllocConsole();
        freopen("CONOUT$", "w", stdout);
        hack();
    }
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
// dllmain.cpp : 定义 DLL 应用程序的入口点。 hack.dll
#include "pch.h"
#include <Windows.h>
#include <stdio.h>
#include<Psapi.h>
typedef BOOL (*Func)( HANDLE hProcess,
     LPVOID lpBaseAddress,
     LPCVOID lpBuffer,
     SIZE_T nSize,
     SIZE_T* lpNumberOfBytesWritten
);
Func OriginFunc = NULL;
 
BYTE HookCode[] = {
    0x48,0xB8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //mov rax , xxx
    0xFF,0xE0                                          //jmp rax
};
BYTE OriginCode[0x50];
SIZE_T HookLen = sizeof(HookCode);
DWORD saved=0;
WCHAR FILENAME[MAX_PATH];
BOOL HackWriteProcessMemory(HANDLE hProcess,
    LPVOID lpBaseAddress,
    LPCVOID lpBuffer,
    SIZE_T nSize,
    SIZE_T* lpNumberOfBytesWritten
) {
     
    VirtualProtect(OriginFunc, HookLen, PAGE_EXECUTE_READWRITE, &saved);
    memcpy(OriginFunc, OriginCode,HookLen);             //unhook
    printf("Call WriteProcessMemory(%p,%p,%p,%d,%p)",hProcess,lpBaseAddress,lpBuffer,nSize,lpNumberOfBytesWritten);
    GetModuleFileNameEx(hProcess, NULL, FILENAME, MAX_PATH);
    wprintf(L"ProcessName=%s\n", FILENAME);
    BOOL ret=OriginFunc(hProcess, lpBaseAddress, lpBuffer, nSize,lpNumberOfBytesWritten);
    memcpy(OriginFunc, HookCode, HookLen);               //rehook
    VirtualProtect(OriginFunc, HookLen, saved, &saved);
    return ret;
}
 
void hack() {
    OriginFunc = WriteProcessMemory;
    VirtualProtect(OriginFunc, HookLen, PAGE_EXECUTE_READWRITE, &saved);
    memcpy(OriginCode, OriginFunc,HookLen); //saved
    *(__int64*)(HookCode + 2) = (__int64)HackWriteProcessMemory;//build
    memcpy(OriginFunc, HookCode, HookLen);  //Hook
    VirtualProtect(OriginFunc, HookLen, saved, &saved);
    printf("Hook done\n");
}
 
 
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH: {
        AllocConsole();
        freopen("CONOUT$", "w", stdout);
        hack();
    }
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
#include <ntifs.h>
#include <ntdef.h>
#include <ntstatus.h>
#include <ntddk.h>
#define MAX_BACKTRACE_DEPTH 20
#define SYMBOL L"\\??\\xia0ji2333"
#define kprintf(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, format, ##__VA_ARGS__)
 
UINT64 BaseAddr=NULL, DLLSize=0;
 
NTSTATUS CreateDevice(PDEVICE_OBJECT driver) {
    NTSTATUS status;
    UNICODE_STRING MyDriver;
    PDEVICE_OBJECT device = NULL;
    RtlInitUnicodeString(&MyDriver, L"\\DEVICE\\xia0ji233");
    status = IoCreateDevice(
        driver,
        sizeof(driver->DeviceExtension),
        &MyDriver,
        FILE_DEVICE_UNKNOWN,
        FILE_DEVICE_SECURE_OPEN,
        FALSE,
        &device
    );
    if (status == STATUS_SUCCESS) {
        UNICODE_STRING Sym;
        RtlInitUnicodeString(&Sym, SYMBOL);
        status = IoCreateSymbolicLink(&Sym, &MyDriver);
        if (status == STATUS_SUCCESS) {
            kprintf(("Line %d:xia0ji233: symbol linked success\n"), __LINE__);
        }
        else {
            kprintf(("Line %d:xia0ji233: symbol linked failed status=%x\n"), __LINE__, status);
        }
    }
    else {
        kprintf(("Line %d:xia0ji233: create device fail status=%x\n"), __LINE__, status);
    }
}
 
void DeleteDevice(PDRIVER_OBJECT pDriver) {
    kprintf(("Line %d:xia0ji233: start delete device\n"), __LINE__);
    if (pDriver->DeviceObject) {
        UNICODE_STRING Sym;
        RtlInitUnicodeString(&Sym, SYMBOL);//CreateFile
        kprintf(("Line %d:xia0ji233: Delete Symbol\n"), __LINE__);
        IoDeleteSymbolicLink(&Sym);
        kprintf(("Line %d:xia0ji233: Delete Device\n"), __LINE__);
        IoDeleteDevice(pDriver->DeviceObject);
    }
    kprintf(("Line %d:xia0ji233: end delete device\n"), __LINE__);
}
 
 
 
char newcode[] = {
    0x48,0xB8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//mov rax,xxx
    0xFF,0xE0//jmp rax
};
char oldcode[] = {
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,
};
 
char *target;
 
KIRQL WPOFFx64()
{
    KIRQL irql = KeRaiseIrqlToDpcLevel();
    UINT64 cr0 = __readcr0();
    cr0 &= 0xfffffffffffeffff;
    __writecr0(cr0);
    _disable();
    return irql;
}
 
void WPONx64(KIRQL irql)
{
    UINT64 cr0 = __readcr0();
    cr0 |= 0x10000;
    _enable();
    __writecr0(cr0);
    KeLowerIrql(irql);
}
 
NTSTATUS Unhook() {
    KIRQL irql = WPOFFx64();
    for (int i = 0; i < sizeof(newcode); i++) {
        target[i] = oldcode[i];
    }
    WPONx64(irql);
    return STATUS_SUCCESS;
}
 
NTSTATUS Hook() {
    KIRQL irql = WPOFFx64();
    for (int i = 0; i < sizeof(newcode); i++) {
        target[i] = newcode[i];
    }
    WPONx64(irql);
    return STATUS_SUCCESS;
}
typedef NTSTATUS(*Copy)(PVOID, MM_COPY_ADDRESS, SIZE_T, ULONG, SIZE_T *);
 
PDRIVER_OBJECT g_Object = NULL;
typedef struct _LDR_DATA_TABLE_ENTRY {
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    PVOID EntryPoint;//驱动的进入点 DriverEntry 
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;//驱动的满路径 
    UNICODE_STRING BaseDllName;//不带路径的驱动名字 
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;
    union {
        LIST_ENTRY HashLinks;
        struct {
            PVOID SectionPointer;
            ULONG CheckSum;
        };
    };
    union {
        struct {
            ULONG TimeDateStamp;
        };
        struct {
            PVOID LoadedImports;
        };
    };
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
 
VOID bianliqudongmokuai(PUNICODE_STRING name, UINT64* pBaseAddr,UINT64* pSize)
{
    LDR_DATA_TABLE_ENTRY *TE, *Tmp;
    TE = (LDR_DATA_TABLE_ENTRY*)g_Object->DriverSection;
    PLIST_ENTRY LinkList;
    ;
    int i = 0;
    LinkList = TE->InLoadOrderLinks.Flink; 
    while (LinkList != &TE->InLoadOrderLinks)
    {
        Tmp = (LDR_DATA_TABLE_ENTRY*)LinkList;
        if (RtlCompareUnicodeString(&Tmp->BaseDllName, name, FALSE))
        {
        }
        else
        {
            kprintf(("Found Module!\n"));
            *pBaseAddr = (UINT64)(Tmp->DllBase);
            *pSize = (UINT64)(Tmp->SizeOfImage);
        }
        LinkList = LinkList->Flink;
        i++;
    }
 
 
}
 
 
NTSTATUS
    myMmCopyMemory(
    _In_ PVOID TargetAddress,
    _In_ MM_COPY_ADDRESS SourceAddress,
    _In_ SIZE_T NumberOfBytes,
    _In_ ULONG Flags,
    _Out_ PSIZE_T NumberOfBytesTransferred
) {
 
    if (!BaseAddr) {
        UNICODE_STRING name;
        RtlInitUnicodeString(&name, L"ace.sys");
        bianliqudongmokuai(&name,&BaseAddr,&DLLSize);
        if (!BaseAddr) {
            goto end;
        }
    }
 
 
    PVOID backtrace[MAX_BACKTRACE_DEPTH];
    USHORT capturedFrames = RtlCaptureStackBackTrace(0, MAX_BACKTRACE_DEPTH, backtrace, NULL);
    UINT64 addr = BaseAddr;
    UINT64 size = DLLSize;
    int flag = 0;
 
    for (USHORT i = 0; i < capturedFrames; i++)
    {
        if (backtrace[i] >= addr && backtrace[i] <= addr + size) {
            flag = 1;
        }
    }
    if (flag) {
        kprintf(("xia0ji233: calls MmCopyMemory(%p,%p,%d,%p,%p)\n"), TargetAddress, SourceAddress, NumberOfBytes, Flags, NumberOfBytesTransferred);
        kprintf(("Here is data: "));
        for (INT64 i = 0; i < NumberOfBytes; i++) {
            kprintf(("%02x "), *((unsigned char*)SourceAddress.VirtualAddress + i));
        }
        kprintf(("\n"));
    }
 
 
    end:
    Unhook();
    Copy func = (Copy)target;
    NTSTATUS s = func(TargetAddress, SourceAddress, NumberOfBytes, Flags, NumberOfBytesTransferred);
    Hook();
 
    return s;
}
 
void DriverUnload(PDRIVER_OBJECT pDriver) {
    kprintf(("Line %d:xia0ji233: start unload\n"), __LINE__);
    Unhook();
    DeleteDevice(pDriver);
}
 
 
NTSTATUS DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath
) {
    DriverObject->DriverUnload = DriverUnload;
    CreateDevice(DriverObject);
    kprintf(("Line %d:xia0ji233: RegistryPath = %S\n"), __LINE__, RegistryPath->Buffer);
    target = MmCopyMemory;
    kprintf(("Line %d:xia0ji233: MmCopyMemory=%p\n"), __LINE__, target);
    g_Object = DriverObject;
    if (target) {
        for (int i = 0; i < sizeof(oldcode); i++) {
            oldcode[i] = target[i];
        }
        *(UINT64*)(newcode + 2) = myMmCopyMemory;
        Hook();
    }
    else {
        kprintf(("xia0ji233:hahaha"));
    }
    return 0;
}
#include <ntifs.h>
#include <ntdef.h>
#include <ntstatus.h>
#include <ntddk.h>
#define MAX_BACKTRACE_DEPTH 20
#define SYMBOL L"\\??\\xia0ji2333"
#define kprintf(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, format, ##__VA_ARGS__)
 
UINT64 BaseAddr=NULL, DLLSize=0;
 
NTSTATUS CreateDevice(PDEVICE_OBJECT driver) {
    NTSTATUS status;
    UNICODE_STRING MyDriver;
    PDEVICE_OBJECT device = NULL;
    RtlInitUnicodeString(&MyDriver, L"\\DEVICE\\xia0ji233");
    status = IoCreateDevice(
        driver,
        sizeof(driver->DeviceExtension),
        &MyDriver,
        FILE_DEVICE_UNKNOWN,
        FILE_DEVICE_SECURE_OPEN,
        FALSE,
        &device
    );
    if (status == STATUS_SUCCESS) {
        UNICODE_STRING Sym;
        RtlInitUnicodeString(&Sym, SYMBOL);
        status = IoCreateSymbolicLink(&Sym, &MyDriver);
        if (status == STATUS_SUCCESS) {
            kprintf(("Line %d:xia0ji233: symbol linked success\n"), __LINE__);
        }
        else {
            kprintf(("Line %d:xia0ji233: symbol linked failed status=%x\n"), __LINE__, status);
        }
    }
    else {
        kprintf(("Line %d:xia0ji233: create device fail status=%x\n"), __LINE__, status);
    }
}
 
void DeleteDevice(PDRIVER_OBJECT pDriver) {
    kprintf(("Line %d:xia0ji233: start delete device\n"), __LINE__);
    if (pDriver->DeviceObject) {
        UNICODE_STRING Sym;
        RtlInitUnicodeString(&Sym, SYMBOL);//CreateFile
        kprintf(("Line %d:xia0ji233: Delete Symbol\n"), __LINE__);
        IoDeleteSymbolicLink(&Sym);
        kprintf(("Line %d:xia0ji233: Delete Device\n"), __LINE__);
        IoDeleteDevice(pDriver->DeviceObject);
    }
    kprintf(("Line %d:xia0ji233: end delete device\n"), __LINE__);
}
 
 
 
char newcode[] = {
    0x48,0xB8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//mov rax,xxx
    0xFF,0xE0//jmp rax
};
char oldcode[] = {
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,
};
 
char *target;
 
KIRQL WPOFFx64()
{
    KIRQL irql = KeRaiseIrqlToDpcLevel();
    UINT64 cr0 = __readcr0();
    cr0 &= 0xfffffffffffeffff;
    __writecr0(cr0);
    _disable();
    return irql;
}
 
void WPONx64(KIRQL irql)
{
    UINT64 cr0 = __readcr0();
    cr0 |= 0x10000;
    _enable();
    __writecr0(cr0);
    KeLowerIrql(irql);
}
 
NTSTATUS Unhook() {
    KIRQL irql = WPOFFx64();
    for (int i = 0; i < sizeof(newcode); i++) {
        target[i] = oldcode[i];
    }
    WPONx64(irql);
    return STATUS_SUCCESS;
}
 
NTSTATUS Hook() {
    KIRQL irql = WPOFFx64();
    for (int i = 0; i < sizeof(newcode); i++) {
        target[i] = newcode[i];
    }
    WPONx64(irql);
    return STATUS_SUCCESS;
}
typedef NTSTATUS(*Copy)(PVOID, MM_COPY_ADDRESS, SIZE_T, ULONG, SIZE_T *);
 
PDRIVER_OBJECT g_Object = NULL;
typedef struct _LDR_DATA_TABLE_ENTRY {
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    PVOID EntryPoint;//驱动的进入点 DriverEntry 
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;//驱动的满路径 
    UNICODE_STRING BaseDllName;//不带路径的驱动名字 
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;
    union {
        LIST_ENTRY HashLinks;
        struct {
            PVOID SectionPointer;
            ULONG CheckSum;
        };
    };
    union {
        struct {
            ULONG TimeDateStamp;
        };
        struct {
            PVOID LoadedImports;
        };
    };
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
 
VOID bianliqudongmokuai(PUNICODE_STRING name, UINT64* pBaseAddr,UINT64* pSize)
{
    LDR_DATA_TABLE_ENTRY *TE, *Tmp;
    TE = (LDR_DATA_TABLE_ENTRY*)g_Object->DriverSection;
    PLIST_ENTRY LinkList;
    ;
    int i = 0;
    LinkList = TE->InLoadOrderLinks.Flink; 
    while (LinkList != &TE->InLoadOrderLinks)
    {
        Tmp = (LDR_DATA_TABLE_ENTRY*)LinkList;
        if (RtlCompareUnicodeString(&Tmp->BaseDllName, name, FALSE))
        {
        }
        else
        {
            kprintf(("Found Module!\n"));
            *pBaseAddr = (UINT64)(Tmp->DllBase);
            *pSize = (UINT64)(Tmp->SizeOfImage);
        }
        LinkList = LinkList->Flink;
        i++;
    }
 
 
}
 
 
NTSTATUS
    myMmCopyMemory(
    _In_ PVOID TargetAddress,
    _In_ MM_COPY_ADDRESS SourceAddress,
    _In_ SIZE_T NumberOfBytes,
    _In_ ULONG Flags,
    _Out_ PSIZE_T NumberOfBytesTransferred
) {
 
    if (!BaseAddr) {
        UNICODE_STRING name;
        RtlInitUnicodeString(&name, L"ace.sys");
        bianliqudongmokuai(&name,&BaseAddr,&DLLSize);
        if (!BaseAddr) {
            goto end;
        }
    }
 
 
    PVOID backtrace[MAX_BACKTRACE_DEPTH];
    USHORT capturedFrames = RtlCaptureStackBackTrace(0, MAX_BACKTRACE_DEPTH, backtrace, NULL);
    UINT64 addr = BaseAddr;
    UINT64 size = DLLSize;
    int flag = 0;
 
    for (USHORT i = 0; i < capturedFrames; i++)
    {
        if (backtrace[i] >= addr && backtrace[i] <= addr + size) {
            flag = 1;
        }
    }
    if (flag) {
        kprintf(("xia0ji233: calls MmCopyMemory(%p,%p,%d,%p,%p)\n"), TargetAddress, SourceAddress, NumberOfBytes, Flags, NumberOfBytesTransferred);
        kprintf(("Here is data: "));
        for (INT64 i = 0; i < NumberOfBytes; i++) {
            kprintf(("%02x "), *((unsigned char*)SourceAddress.VirtualAddress + i));
        }
        kprintf(("\n"));
    }
 
 
    end:
    Unhook();
    Copy func = (Copy)target;
    NTSTATUS s = func(TargetAddress, SourceAddress, NumberOfBytes, Flags, NumberOfBytesTransferred);
    Hook();
 
    return s;
}
 
void DriverUnload(PDRIVER_OBJECT pDriver) {
    kprintf(("Line %d:xia0ji233: start unload\n"), __LINE__);
    Unhook();
    DeleteDevice(pDriver);
}
 
 
NTSTATUS DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath
) {
    DriverObject->DriverUnload = DriverUnload;
    CreateDevice(DriverObject);
    kprintf(("Line %d:xia0ji233: RegistryPath = %S\n"), __LINE__, RegistryPath->Buffer);
    target = MmCopyMemory;
    kprintf(("Line %d:xia0ji233: MmCopyMemory=%p\n"), __LINE__, target);
    g_Object = DriverObject;
    if (target) {
        for (int i = 0; i < sizeof(oldcode); i++) {
            oldcode[i] = target[i];
        }
        *(UINT64*)(newcode + 2) = myMmCopyMemory;
        Hook();
    }
    else {
        kprintf(("xia0ji233:hahaha"));
    }
    return 0;
}
#include <ntifs.h>
#include <ntdef.h>
#include <ntstatus.h>
#include <ntddk.h>
#define MAX_BACKTRACE_DEPTH 20
#define SYMBOL L"\\??\\xia0ji2333"
#define kprintf(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, format, ##__VA_ARGS__)
 
UINT64 BaseAddr=NULL, DLLSize=0;
 
 
void DeleteDevice(PDRIVER_OBJECT pDriver) {
    kprintf(("Line %d:xia0ji233: start delete device\n"), __LINE__);
    if (pDriver->DeviceObject) {
        UNICODE_STRING Sym;
        RtlInitUnicodeString(&Sym, SYMBOL);//CreateFile
        kprintf(("Line %d:xia0ji233: Delete Symbol\n"), __LINE__);
        IoDeleteSymbolicLink(&Sym);
        kprintf(("Line %d:xia0ji233: Delete Device\n"), __LINE__);
        IoDeleteDevice(pDriver->DeviceObject);
    }
    kprintf(("Line %d:xia0ji233: end delete device\n"), __LINE__);
}
 
 
 
char newcode[] = {
    0x48,0xB8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//mov rax,xxx
    0xFF,0xE0//jmp rax
};
char oldcode[] = {
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,
};
 
char *target;
 
KIRQL WPOFFx64()
{
    KIRQL irql = KeRaiseIrqlToDpcLevel();
    UINT64 cr0 = __readcr0();
    cr0 &= 0xfffffffffffeffff;
    __writecr0(cr0);
    _disable();
    return irql;
}
 
void WPONx64(KIRQL irql)
{
    UINT64 cr0 = __readcr0();
    cr0 |= 0x10000;
    _enable();
    __writecr0(cr0);
    KeLowerIrql(irql);
}
 
NTSTATUS Unhook() {
    KIRQL irql = WPOFFx64();
    for (int i = 0; i < sizeof(newcode); i++) {
        target[i] = oldcode[i];
    }
    WPONx64(irql);
    return STATUS_SUCCESS;
}
 
NTSTATUS Hook() {
    KIRQL irql = WPOFFx64();
    for (int i = 0; i < sizeof(newcode); i++) {
        target[i] = newcode[i];
    }
    WPONx64(irql);
    return STATUS_SUCCESS;
}
 
 
PDRIVER_OBJECT g_Object = NULL;
typedef struct _LDR_DATA_TABLE_ENTRY {
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    PVOID EntryPoint;//驱动的进入点 DriverEntry 
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;//驱动的满路径 
    UNICODE_STRING BaseDllName;//不带路径的驱动名字 
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;
    union {
        LIST_ENTRY HashLinks;
        struct {
            PVOID SectionPointer;
            ULONG CheckSum;
        };
    };
    union {
        struct {
            ULONG TimeDateStamp;
        };
        struct {
            PVOID LoadedImports;
        };
    };
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
 
VOID bianliqudongmokuai(PUNICODE_STRING name, UINT64* pBaseAddr,UINT64* pSize)
{
    LDR_DATA_TABLE_ENTRY *TE, *Tmp;
    TE = (LDR_DATA_TABLE_ENTRY*)g_Object->DriverSection;
    PLIST_ENTRY LinkList;
    ;
    int i = 0;
    LinkList = TE->InLoadOrderLinks.Flink; 
    while (LinkList != &TE->InLoadOrderLinks)
    {
        Tmp = (LDR_DATA_TABLE_ENTRY*)LinkList;
        if (RtlCompareUnicodeString(&Tmp->BaseDllName, name, FALSE))
        {
        }
        else
        {
            kprintf(("Found Module!\n"));
            *pBaseAddr = (UINT64)(Tmp->DllBase);
            *pSize = (UINT64)(Tmp->SizeOfImage);
        }
        LinkList = LinkList->Flink;
        i++;
    }
 
 
}
 
typedef ULONG(*FuncPtr) (ULONG ComponentId,ULONG Level, PCSTR Format, ...);
 
ULONG  myDbgPrintEx( ULONG ComponentId,ULONG Level,PCSTR Format, ... ) {
 
 
 
    Unhook();
    FuncPtr func = (FuncPtr)target;
    kprintf(("call DbgPrintEx(%p,%p,%p)"), ComponentId, Level, Format);
    DbgBreakPoint();
    NTSTATUS s = func(ComponentId,Level,Format);
     
    Hook();
 
    return s;
}
 
void DriverUnload(PDRIVER_OBJECT pDriver) {
    kprintf(("Line %d:xia0ji233: start unload\n"), __LINE__);
    Unhook();
    DeleteDevice(pDriver);
}
 
 
NTSTATUS DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath
) {
    DriverObject->DriverUnload = DriverUnload;
    kprintf(("Line %d:xia0ji233: RegistryPath = %S\n"), __LINE__, RegistryPath->Buffer);
    target = DbgPrintEx;
    kprintf(("Line %d:xia0ji233: DbgPrintEx=%p\n"), __LINE__, target);
    g_Object = DriverObject;
    if (target) {
        for (int i = 0; i < sizeof(oldcode); i++) {
            oldcode[i] = target[i];
        }
        *(UINT64*)(newcode + 2) = myDbgPrintEx;
        Hook();
    }
    else {
        kprintf(("xia0ji233:hahaha"));
    }
    return 0;
}
#include <ntifs.h>
#include <ntdef.h>
#include <ntstatus.h>
#include <ntddk.h>
#define MAX_BACKTRACE_DEPTH 20
#define SYMBOL L"\\??\\xia0ji2333"
#define kprintf(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, format, ##__VA_ARGS__)
 
UINT64 BaseAddr=NULL, DLLSize=0;
 
 
void DeleteDevice(PDRIVER_OBJECT pDriver) {
    kprintf(("Line %d:xia0ji233: start delete device\n"), __LINE__);
    if (pDriver->DeviceObject) {
        UNICODE_STRING Sym;
        RtlInitUnicodeString(&Sym, SYMBOL);//CreateFile
        kprintf(("Line %d:xia0ji233: Delete Symbol\n"), __LINE__);
        IoDeleteSymbolicLink(&Sym);
        kprintf(("Line %d:xia0ji233: Delete Device\n"), __LINE__);
        IoDeleteDevice(pDriver->DeviceObject);
    }
    kprintf(("Line %d:xia0ji233: end delete device\n"), __LINE__);
}
 
 
 
char newcode[] = {
    0x48,0xB8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//mov rax,xxx
    0xFF,0xE0//jmp rax
};
char oldcode[] = {
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,
};
 
char *target;
 
KIRQL WPOFFx64()
{
    KIRQL irql = KeRaiseIrqlToDpcLevel();
    UINT64 cr0 = __readcr0();
    cr0 &= 0xfffffffffffeffff;
    __writecr0(cr0);
    _disable();
    return irql;
}
 
void WPONx64(KIRQL irql)
{
    UINT64 cr0 = __readcr0();
    cr0 |= 0x10000;
    _enable();
    __writecr0(cr0);
    KeLowerIrql(irql);
}
 
NTSTATUS Unhook() {
    KIRQL irql = WPOFFx64();
    for (int i = 0; i < sizeof(newcode); i++) {
        target[i] = oldcode[i];
    }
    WPONx64(irql);
    return STATUS_SUCCESS;
}
 
NTSTATUS Hook() {
    KIRQL irql = WPOFFx64();
    for (int i = 0; i < sizeof(newcode); i++) {
        target[i] = newcode[i];
    }
    WPONx64(irql);
    return STATUS_SUCCESS;
}
 
 
PDRIVER_OBJECT g_Object = NULL;
typedef struct _LDR_DATA_TABLE_ENTRY {
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    PVOID EntryPoint;//驱动的进入点 DriverEntry 
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;//驱动的满路径 
    UNICODE_STRING BaseDllName;//不带路径的驱动名字 
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;
    union {
        LIST_ENTRY HashLinks;
        struct {
            PVOID SectionPointer;
            ULONG CheckSum;
        };
    };
    union {
        struct {
            ULONG TimeDateStamp;
        };
        struct {
            PVOID LoadedImports;
        };
    };
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
 
VOID bianliqudongmokuai(PUNICODE_STRING name, UINT64* pBaseAddr,UINT64* pSize)
{
    LDR_DATA_TABLE_ENTRY *TE, *Tmp;
    TE = (LDR_DATA_TABLE_ENTRY*)g_Object->DriverSection;
    PLIST_ENTRY LinkList;
    ;
    int i = 0;
    LinkList = TE->InLoadOrderLinks.Flink; 
    while (LinkList != &TE->InLoadOrderLinks)
    {
        Tmp = (LDR_DATA_TABLE_ENTRY*)LinkList;
        if (RtlCompareUnicodeString(&Tmp->BaseDllName, name, FALSE))
        {
        }
        else
        {
            kprintf(("Found Module!\n"));
            *pBaseAddr = (UINT64)(Tmp->DllBase);
            *pSize = (UINT64)(Tmp->SizeOfImage);
        }
        LinkList = LinkList->Flink;
        i++;
    }
 
 
}
 
typedef ULONG(*FuncPtr) (ULONG ComponentId,ULONG Level, PCSTR Format, ...);
 
ULONG  myDbgPrintEx( ULONG ComponentId,ULONG Level,PCSTR Format, ... ) {
 
 
 
    Unhook();
    FuncPtr func = (FuncPtr)target;
    kprintf(("call DbgPrintEx(%p,%p,%p)"), ComponentId, Level, Format);
    DbgBreakPoint();
    NTSTATUS s = func(ComponentId,Level,Format);
     
    Hook();
 
    return s;
}
 
void DriverUnload(PDRIVER_OBJECT pDriver) {
    kprintf(("Line %d:xia0ji233: start unload\n"), __LINE__);
    Unhook();
    DeleteDevice(pDriver);
}
 
 
NTSTATUS DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath
) {
    DriverObject->DriverUnload = DriverUnload;
    kprintf(("Line %d:xia0ji233: RegistryPath = %S\n"), __LINE__, RegistryPath->Buffer);
    target = DbgPrintEx;
    kprintf(("Line %d:xia0ji233: DbgPrintEx=%p\n"), __LINE__, target);
    g_Object = DriverObject;
    if (target) {
        for (int i = 0; i < sizeof(oldcode); i++) {
            oldcode[i] = target[i];
        }
        *(UINT64*)(newcode + 2) = myDbgPrintEx;
        Hook();
    }
    else {
        kprintf(("xia0ji233:hahaha"));
    }
    return 0;
}
#include <ntddk.h>
#define kprintf(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, format, ##__VA_ARGS__)
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath);
VOID UnloadDriver(PDRIVER_OBJECT DriverObject);
NTSTATUS EnumerateKernelThreads();
 
typedef NTSTATUS (*ZWQUERYSYSTEMINFORMATION)(ULONG, PVOID, ULONG, PULONG);
typedef struct _SYSTEM_PROCESS_INFORMATION {
    ULONG NextEntryOffset;
    ULONG NumberOfThreads;
    LARGE_INTEGER Reserved[3];
    LARGE_INTEGER CreateTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER KernelTime;
    UNICODE_STRING ImageName;
    ULONG BasePriority;
    HANDLE ProcessId;
    HANDLE InheritedFromProcessId;
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
typedef struct _SYSTEM_THREAD_INFORMATION {
    LARGE_INTEGER KernelTime;
    LARGE_INTEGER UserTime;
    LARGE_INTEGER CreateTime;
    ULONG WaitTime;
    PVOID StartAddress;
    CLIENT_ID ClientId;
    ULONG Priority;
    LONG BasePriority;
    ULONG ContextSwitchCount;
    LONG State;
    LONG WaitReason;
} SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION;
typedef enum _SYSTEM_INFORMATION_CLASS {
    SystemProcessInformation = 5
} SYSTEM_INFORMATION_CLASS;
#define SystemModuleInformation 11
 
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
    UNREFERENCED_PARAMETER(RegistryPath);
    DriverObject->DriverUnload = UnloadDriver;
    kprintf(("Driver Loaded\n"));
    return EnumerateKernelThreads();
}
 
VOID UnloadDriver(PDRIVER_OBJECT DriverObject) {
    UNREFERENCED_PARAMETER(DriverObject);
    kprintf(("Driver Unloaded\n"));
}
 
NTSTATUS EnumerateKernelThreads() {
    UNICODE_STRING routineName;
    RtlInitUnicodeString(&routineName, L"ZwQuerySystemInformation");
    ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)MmGetSystemRoutineAddress(&routineName);
    if (!ZwQuerySystemInformation) {
        return STATUS_UNSUCCESSFUL;
    }
    ULONG returnLength = 0;
    ZwQuerySystemInformation(SystemProcessInformation, NULL, 0, &returnLength);
    PVOID buffer = ExAllocatePool(NonPagedPool, returnLength);
    if (!buffer) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    NTSTATUS status = ZwQuerySystemInformation(SystemProcessInformation, buffer, returnLength, &returnLength);
    if (!NT_SUCCESS(status)) {
        ExFreePool(buffer);
        return status;
    }
    PSYSTEM_PROCESS_INFORMATION current = (PSYSTEM_PROCESS_INFORMATION)buffer;
    while (TRUE) {
        PSYSTEM_THREAD_INFORMATION threadInfo = (PSYSTEM_THREAD_INFORMATION)(current + 1);
        for (ULONG i = 0; i < current->NumberOfThreads; i++) {
 
            if (((UINT64)(threadInfo->StartAddress) & 0xFFFF000000000000) == 0xFFFF000000000000) {
                 kprintf(("Thread StartAddress: %p\n"), threadInfo->StartAddress);
            }
             
            threadInfo++;
        }
 
        if (current->NextEntryOffset == 0)
            break;
         
        current = (PSYSTEM_PROCESS_INFORMATION)((PUCHAR)current + current->NextEntryOffset);
    }
 
    ExFreePool(buffer);
    return STATUS_SUCCESS;
}
#include <ntddk.h>
#define kprintf(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, format, ##__VA_ARGS__)
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath);
VOID UnloadDriver(PDRIVER_OBJECT DriverObject);
NTSTATUS EnumerateKernelThreads();
 
typedef NTSTATUS (*ZWQUERYSYSTEMINFORMATION)(ULONG, PVOID, ULONG, PULONG);
typedef struct _SYSTEM_PROCESS_INFORMATION {
    ULONG NextEntryOffset;
    ULONG NumberOfThreads;
    LARGE_INTEGER Reserved[3];
    LARGE_INTEGER CreateTime;
    LARGE_INTEGER UserTime;

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

最后于 2024-4-15 10:05 被xi@0ji233编辑 ,原因:
收藏
免费 6
支持
分享
最新回复 (8)
雪    币: 6166
活跃值: (4937)
能力值: ( LV10,RANK:160 )
在线值:
发帖
回帖
粉丝
2
分析有纰漏,不一定注入taskmgr。

内核遍历线程判断入口地址的方法,不对。

正常来说你应该拿到所有驱动模块的起始地址+大小,然后判断入口地址是不是在某个驱动模块地址内,

这里的逻辑应该是入口地址不在任何一个驱动模块内存范围内。
2024-4-15 08:33
0
雪    币: 3070
活跃值: (30876)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2024-4-15 09:41
1
雪    币: 10850
活跃值: (5874)
能力值: ( LV15,RANK:533 )
在线值:
发帖
回帖
粉丝
4
淡然他徒弟 分析有纰漏,不一定注入taskmgr。 内核遍历线程判断入口地址的方法,不对。 正常来说你应该拿到所有驱动模块的起始地址+大小,然后判断入口地址是不是在某个驱动模块地址内, 这里的逻 ...
感谢指正,有部分的确分析得不是很到位
2024-4-15 10:05
0
雪    币: 1918
活跃值: (6285)
能力值: ( LV7,RANK:118 )
在线值:
发帖
回帖
粉丝
5
微步秒了
2024-4-15 10:10
0
雪    币: 5
活跃值: (288)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
厉害
2024-4-15 10:47
0
雪    币: 2927
活跃值: (5243)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
7
原来"让token1输出"指的是改成CREATE_NEW。。
我还以为哪里藏了个printf(
2024-4-15 16:07
0
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8

那个,如果你注入hook了读写内存的api,并且尝试dumpshellcode,阻止他销毁pe头等行为,能直接分析出所有他的操作,其中包括了r3外挂跟傀儡进程间是如何通讯监测傀儡进程是否被杀死的,能够直接定位傀儡进程,这样就不用猜是taskmgr了。

最后于 2024-4-19 22:19 被绝赞编辑 ,原因:
2024-4-19 21:55
0
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
微步还是吊,我是抓到shellcode,然后看到他有个通过hash来拿api初始化内存映射模块的操作,里面有ntcreatfile才发现的
2024-4-19 21:58
0
游客
登录 | 注册 方可回帖
返回
//