xia0ji233 发表于 2024-4-22 00:54

腾讯游戏安全大赛2024决赛题解

决赛打五天,是真的顶级,但是五天比一年学的东西都多...

本题[解题附件](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/attachment.zip)自取

<!--more-->

## 2024腾讯游戏安全竞赛决赛题解

### 分析

#### 自行加载loader.sys, 找到用户名为administrator的KEY,作为答案提交(1分)

Key 和 User 默认读 `C:\card.txt`,如果找不到或者是错误,那么加载会失败,于是想到 hook NtCreateFile 获取到文件句柄,再 hook NtReadFile 找到文件内容写入的地址。

```C
#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__);
}

HANDLE FileHandler = NULL;

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 newcode2[] = {
    0x48,0xB8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//mov rax,xxx
    0xFF,0xE0//jmp rax
};
char oldcode2[] = {
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,
};

char* target;
char* target2;
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 = oldcode;
    }
    WPONx64(irql);
    return STATUS_SUCCESS;
}

NTSTATUS Unhook2() {
    KIRQL irql = WPOFFx64();
    for (int i = 0; i < sizeof(newcode2); i++) {
      target2 = oldcode2;
    }
    WPONx64(irql);
    return STATUS_SUCCESS;
}

NTSTATUS Hook() {
    KIRQL irql = WPOFFx64();
    for (int i = 0; i < sizeof(newcode); i++) {
      target = newcode;
    }
    WPONx64(irql);
    return STATUS_SUCCESS;
}

NTSTATUS Hook2() {
    KIRQL irql = WPOFFx64();
    for (int i = 0; i < sizeof(newcode2); i++) {
      target2 = newcode2;
    }
    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;



typedef NTSTATUS (* FuncPtr) (
    _Out_ PHANDLE FileHandle,
    _In_ ACCESS_MASK DesiredAccess,
    _In_ POBJECT_ATTRIBUTES ObjectAttributes,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _In_opt_ PLARGE_INTEGER AllocationSize,
    _In_ ULONG FileAttributes,
    _In_ ULONG ShareAccess,
    _In_ ULONG CreateDisposition,
    _In_ ULONG CreateOptions,
    _In_reads_bytes_opt_(EaLength) PVOID EaBuffer,
    _In_ ULONG EaLength
    );
typedef NTSTATUS (* FuncPtr2 )(
    _In_ HANDLE FileHandle,
    _In_opt_ HANDLE Event,
    _In_opt_ PIO_APC_ROUTINE ApcRoutine,
    _In_opt_ PVOID ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _Out_writes_bytes_(Length) PVOID Buffer,
    _In_ ULONG Length,
    _In_opt_ PLARGE_INTEGER ByteOffset,
    _In_opt_ PULONG Key
    );

ULONGmyCreateFile(_Out_ PHANDLE FileHandle,
    _In_ ACCESS_MASK DesiredAccess,
    _In_ POBJECT_ATTRIBUTES ObjectAttributes,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _In_opt_ PLARGE_INTEGER AllocationSize,
    _In_ ULONG FileAttributes,
    _In_ ULONG ShareAccess,
    _In_ ULONG CreateDisposition,
    _In_ ULONG CreateOptions,
    _In_reads_bytes_opt_(EaLength) PVOID EaBuffer,
    _In_ ULONG EaLength) {

    Unhook();
    FuncPtr func = (FuncPtr)target;

    NTSTATUS s = func(FileHandle,DesiredAccess,ObjectAttributes,IoStatusBlock,AllocationSize,FileAttributes,ShareAccess,CreateDisposition,CreateOptions,EaBuffer,EaLength);
    if (!wcscmp(ObjectAttributes->ObjectName->Buffer, L"\\??\\C:\\card.txt")) {
      kprintf(("call NtCreateFile(%p,%p,%S,%p,%p,%p,%p,%p,%p,%p,%p)\n"), FileHandle,DesiredAccess,ObjectAttributes->ObjectName->Buffer,IoStatusBlock,AllocationSize,FileAttributes,ShareAccess,CreateDisposition,CreateOptions,EaBuffer,EaLength);
      DbgBreakPoint();
      FileHandler = *FileHandle;
    }

    //DbgBreakPoint();
   
   
    Hook();

    return s;
}

ULONG myReadFile(
    _In_ HANDLE FileHandle,
    _In_opt_ HANDLE Event,
    _In_opt_ PIO_APC_ROUTINE ApcRoutine,
    _In_opt_ PVOID ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _Out_writes_bytes_(Length) PVOID Buffer,
    _In_ ULONG Length,
    _In_opt_ PLARGE_INTEGER ByteOffset,
    _In_opt_ PULONG Key) {

    Unhook2();
    FuncPtr2 func = (FuncPtr2)target2;
   
    if (FileHandler && FileHandler == FileHandle) {
      kprintf(("call NtReadFile(%p,%p,%p,%p,%p,%p,%p,%p,%p)\n"), FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, Buffer, Length, ByteOffset, Key);
      DbgBreakPoint();
      FileHandler = 0;
    }
    //DbgBreakPoint();
    NTSTATUS s = func(FileHandle,Event,ApcRoutine,ApcContext,IoStatusBlock,Buffer,Length,ByteOffset,Key);
   
    Hook2();

    return s;


}

void DriverUnload(PDRIVER_OBJECT pDriver) {
    kprintf(("Line %d:xia0ji233: start unload\n"), __LINE__);
    Unhook();
    Unhook2();
    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 = NtCreateFile;
    target2 = NtReadFile;
    kprintf(("Line %d:xia0ji233: NtCreateFile=%p NtReadFile=%p\n"), __LINE__, target,target2);
    g_Object = DriverObject;
    if (target&&target2) {
      for (int i = 0; i < sizeof(oldcode); i++) {
            oldcode = target;
            oldcode2 = target2;
      }
      *(UINT64*)(newcode + 2) = myCreateFile;
      *(UINT64*)(newcode2 + 2) = myReadFile;
      Hook();
      Hook2();
    }
    else {
      kprintf(("xia0ji233:hahaha"));
    }
    return 0;
}
```

跟到后面可以找到文件内容(ReadFile第六个参数),在文件内容处下硬件读取断点,可以找到这里文件内容被被写入两个寄存器,随后又写入另外的内存(RCX所指向的内存)。

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/1.png)

然后就直接 ret 了,这里将内存窗口转到 RCX 指向的地址,然后程序跳出来。

紧接着给末尾添 0 字节,然后将 r14 与 0x10 比较,小于跳出

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/2.png)

这一次输出失败了,于是考虑换长度但是改一个字符,发现在另外的位置写了零字节

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/3.png)

在跟的过程中,发现后面的一个内存就是长度

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/4.png)

然后接着跟,会跟到一个找 - 的代码

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/5.png)

很容易理解,因为 - 就是分隔 user 和 key 的,必然有一个遍历在找 - 的位置,那就直接跳到它找到了 - 的位置,发现有一个大跳转

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/6.png)

随后会把 - 所处的地址存入栈中

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/7.png)

紧接着跟,发现把User拷贝到下面的内存了

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/8.png)

随后将key也写入下方的内存

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/9.png)

然后开始循环 key,判断如果 -0x30 是否 >10,应该 key 只能是数字的判断。

于是我在循环这里下了一个软件断点,发现 RDI 会存储当前已遍历的十进制。

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/11.png)

例如现在已经遍历到了 405,那么 rdi=0x195=405。

经过数次的循环,

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/12.png)

可以发现 RDI=0xF17E203C,就是 4051574844,那么接下来就往下面跟看看跟 User 有什么样的关系。

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/13.png)

取出ACE之后算出一个值 0x0000000020450083,最后很 0x1003F 相乘,得到 00000000F17E203D,刚好是题目所给 key 的十六进制表示。

后面通过一定的调试分析,发现一个规律

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/14.png)

似乎它会把每一个字符加起来然后 `*0x1003F`,并且一定是 `int`。

先验证一遍ACE是否正确

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/15.png)

发现果然如此,那么照样子算出 key 为 `4007951923`。

#### 编写一个keygen,能生成对于任何用户名的KEY(1分)

根据上面的分析,不难写出 keygen 程序

```C
#include<stdio.h>
#include<string.h>
int main(){
        char user[]="xia0ji233";
        int n=strlen(user);
        unsigned key=0;
        for(int i=0;i<n;i++){
                key=(key+user)*0x1003F;
        }
        printf("%u\n",key);
}
```

运行即可获得 key 的输出。

#### 编写一个exp,在exp程序运行后,对于任意的用户名-key,Loader.sys均能正确启动(1分)

有这么几种方法:监控内核线程,在 shellcode 执行的时候拦截,把比较是否相等的代码patch掉。监控文件读写,在读文件的时候,判断如果是目标文件,让它返回正确的结果。

最后还是选择在文件处拦截,然后趁它读文件的时候遍历驱动模块改它代码,上面分析的 imul 关键指令在 `Loader.sys+0xafc1c4` 的位置

再调试一遍,决出关键一步,找到了 cmp 指令

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/16.png)

如图所示的内存分别为实际输入的数值和通过 user 计算得到的key的数值,随后取出相比较,不相等显然跳转到 Fail 分支,因此这里改成 NOP 让它不跳转任意情况下跳转成功。

此时的 sys 基地址为 `0xFFFFF806F82D0000`,与该指令相减得到 `0xa27e` 的偏移,只需把这两个字节改成 0x90 即可达到任意的 user key 可以成功加载驱动。

我先使用了hook的方式去劫持,确保劫持的函数没错,再通过修改劫持时机和方式让方法满足要求

```C
#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__);
}

HANDLE FileHandler = NULL;

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 newcode2[] = {
    0x48,0xB8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//mov rax,xxx
    0xFF,0xE0//jmp rax
};
char oldcode2[] = {
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,
};

char* target;
char* target2;
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 = oldcode;
    }
    WPONx64(irql);
    return STATUS_SUCCESS;
}

NTSTATUS Unhook2() {
    KIRQL irql = WPOFFx64();
    for (int i = 0; i < sizeof(newcode2); i++) {
      target2 = oldcode2;
    }
    WPONx64(irql);
    return STATUS_SUCCESS;
}

NTSTATUS Hook() {
    KIRQL irql = WPOFFx64();
    for (int i = 0; i < sizeof(newcode); i++) {
      target = newcode;
    }
    WPONx64(irql);
    return STATUS_SUCCESS;
}

NTSTATUS Hook2() {
    KIRQL irql = WPOFFx64();
    for (int i = 0; i < sizeof(newcode2); i++) {
      target2 = newcode2;
    }
    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;



typedef NTSTATUS (* FuncPtr) (
    _Out_ PHANDLE FileHandle,
    _In_ ACCESS_MASK DesiredAccess,
    _In_ POBJECT_ATTRIBUTES ObjectAttributes,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _In_opt_ PLARGE_INTEGER AllocationSize,
    _In_ ULONG FileAttributes,
    _In_ ULONG ShareAccess,
    _In_ ULONG CreateDisposition,
    _In_ ULONG CreateOptions,
    _In_reads_bytes_opt_(EaLength) PVOID EaBuffer,
    _In_ ULONG EaLength
    );
typedef NTSTATUS (* FuncPtr2 )(
    _In_ HANDLE FileHandle,
    _In_opt_ HANDLE Event,
    _In_opt_ PIO_APC_ROUTINE ApcRoutine,
    _In_opt_ PVOID ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _Out_writes_bytes_(Length) PVOID Buffer,
    _In_ ULONG Length,
    _In_opt_ PLARGE_INTEGER ByteOffset,
    _In_opt_ PULONG Key
    );

NTSTATUS EnumerateKernelThreads();

typedef NTSTATUS (*ZWQUERYSYSTEMINFORMATION)(ULONG, PVOID, ULONG, PULONG);
typedef struct _SYSTEM_PROCESS_INFORMATION {
    ULONG NextEntryOffset;
    ULONG NumberOfThreads;
    LARGE_INTEGER Reserved;
    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

ULONGmyCreateFile(_Out_ PHANDLE FileHandle,
    _In_ ACCESS_MASK DesiredAccess,
    _In_ POBJECT_ATTRIBUTES ObjectAttributes,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _In_opt_ PLARGE_INTEGER AllocationSize,
    _In_ ULONG FileAttributes,
    _In_ ULONG ShareAccess,
    _In_ ULONG CreateDisposition,
    _In_ ULONG CreateOptions,
    _In_reads_bytes_opt_(EaLength) PVOID EaBuffer,
    _In_ ULONG EaLength) {

    Unhook();
    FuncPtr func = (FuncPtr)target;

    NTSTATUS s = func(FileHandle,DesiredAccess,ObjectAttributes,IoStatusBlock,AllocationSize,FileAttributes,ShareAccess,CreateDisposition,CreateOptions,EaBuffer,EaLength);
    if (!wcscmp(ObjectAttributes->ObjectName->Buffer, L"\\??\\C:\\card.txt")) {
      kprintf(("call NtCreateFile(%p,%p,%S,%p,%p,%p,%p,%p,%p,%p,%p)\n"), FileHandle,DesiredAccess,ObjectAttributes->ObjectName->Buffer,IoStatusBlock,AllocationSize,FileAttributes,ShareAccess,CreateDisposition,CreateOptions,EaBuffer,EaLength);
      //DbgBreakPoint();
      FileHandler = *FileHandle;
    }

    //DbgBreakPoint();
   
   
    Hook();

    return s;
}

MDLWriteMemory(PVOID pBaseAddress, PVOID pWriteData, SIZE_T writeDataSize)
{
    PMDL pMdl = NULL;
    PVOID pNewAddress = NULL;
    pMdl = MmCreateMdl(NULL, pBaseAddress, writeDataSize);
    if (NULL == pMdl)
    {
      return FALSE;
    }
    MmBuildMdlForNonPagedPool(pMdl);
    pNewAddress = MmMapLockedPages(pMdl, KernelMode);
    if (NULL == pNewAddress)
    {
      IoFreeMdl(pMdl);
    }
    RtlCopyMemory(pNewAddress, pWriteData, writeDataSize);
    MmUnmapLockedPages(pNewAddress, pMdl);
    IoFreeMdl(pMdl);
    return TRUE;
}


VOID PatchInstr()
{
    LDR_DATA_TABLE_ENTRY *TE, *Tmp;
    TE = (LDR_DATA_TABLE_ENTRY*)g_Object->DriverSection;
    PLIST_ENTRY LinkList;
    ;
    int i = 0;
    LinkList = TE->InLoadOrderLinks.Flink;
    UNICODE_STRING name;
    RtlInitUnicodeString(&name,L"Loader.sys");
    ;
    while (LinkList != &TE->InLoadOrderLinks)
    {
      Tmp = (LDR_DATA_TABLE_ENTRY*)LinkList;
      
      if (RtlEqualUnicodeString(&Tmp->BaseDllName, &name,FALSE)) {
            kprintf(("DLLname:%S DLLBase=%p nowcode=%p\n"), Tmp->BaseDllName.Buffer,Tmp->DllBase,(ULONG64)(Tmp->DllBase) + 0xa27e);
            char buffer[] = { 0x90,0x90 };
            MDLWriteMemory((ULONG64)(Tmp->DllBase) + 0xa27e, buffer, 2);
            return;
      }
      LinkList = LinkList->Flink;
      i++;
    }


}

ULONG myReadFile(
    _In_ HANDLE FileHandle,
    _In_opt_ HANDLE Event,
    _In_opt_ PIO_APC_ROUTINE ApcRoutine,
    _In_opt_ PVOID ApcContext,
    _Out_ PIO_STATUS_BLOCK IoStatusBlock,
    _Out_writes_bytes_(Length) PVOID Buffer,
    _In_ ULONG Length,
    _In_opt_ PLARGE_INTEGER ByteOffset,
    _In_opt_ PULONG Key) {

    Unhook2();
    FuncPtr2 func = (FuncPtr2)target2;
   
    if (FileHandler && FileHandler == FileHandle) {
      kprintf(("call NtReadFile(%p,%p,%p,%p,%p,%p,%p,%p,%p)\n"), FileHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, Buffer, Length, ByteOffset, Key);
      kprintf(("buffer in %p\n"), Buffer);
      PatchInstr();
      FileHandler = 0;
    }
    //DbgBreakPoint();
    NTSTATUS s = func(FileHandle,Event,ApcRoutine,ApcContext,IoStatusBlock,Buffer,Length,ByteOffset,Key);
   
    Hook2();

    return s;


}

void DriverUnload(PDRIVER_OBJECT pDriver) {
    kprintf(("Line %d:xia0ji233: start unload\n"), __LINE__);
    Unhook();
    Unhook2();
    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 = NtCreateFile;
    target2 = NtReadFile;
    kprintf(("Line %d:xia0ji233: NtCreateFile=%p NtReadFile=%p\n"), __LINE__, target,target2);
    g_Object = DriverObject;
    if (target&&target2) {
      for (int i = 0; i < sizeof(oldcode); i++) {
            oldcode = target;
            oldcode2 = target2;
      }
      *(UINT64*)(newcode + 2) = myCreateFile;
      *(UINT64*)(newcode2 + 2) = myReadFile;
      Hook();
      Hook2();
    }
    else {
      kprintf(("xia0ji233:hahaha"));
    }
    return 0;
}

int Filter(ULONG Start)
{
    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;
      ULONG BASE = Tmp->DllBase;
      ULONG Size = Tmp->SizeOfImage;
      if (Start >= BASE && Start < BASE + Size) {
            return 0;
      }

      LinkList = LinkList->Flink;
      i++;
    }

    return 1;
}
```

可以看到,先加载我写的驱动,后加载题目驱动,无论 user-key 正确与否,都加载成功

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/17.png)

于是这里把修改的函数套到文件读取里面去拦截,达到读该文件时遍历模块,找到指定模块则写入指令。

下面是真正的代码(编译文件为XSafe2.sys)

```C
#include <ntifs.h>
#include <ntdef.h>
#include <ntstatus.h>
#include <ntddk.h>
#define kprintf(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, format, ##__VA_ARGS__)

typedef struct _CALLBACK_ENTRY
{
    LIST_ENTRY CallbackList;
    OB_OPERATIONOperations;
    ULONG Active;
    PVOID Handle;
    POBJECT_TYPE ObjectType;
    POB_PRE_OPERATION_CALLBACKPreOperation;
    POB_POST_OPERATION_CALLBACK PostOperation;
    ULONG unknown;
} CALLBACK_ENTRY, *PCALLBACK_ENTRY;

typedef struct _LDR_DATA                         // 24 elements, 0xE0 bytes (sizeof)
{
    /*0x000*/   struct _LIST_ENTRY InLoadOrderLinks;                     // 2 elements, 0x10 bytes (sizeof)
    /*0x010*/   struct _LIST_ENTRY InMemoryOrderLinks;                   // 2 elements, 0x10 bytes (sizeof)
    /*0x020*/   struct _LIST_ENTRY InInitializationOrderLinks;         // 2 elements, 0x10 bytes (sizeof)
    /*0x030*/   VOID*      DllBase;
    /*0x038*/   VOID*      EntryPoint;
    /*0x040*/   ULONG32      SizeOfImage;
    /*0x044*/   UINT8      _PADDING0_;
    /*0x048*/   struct _UNICODE_STRING FullDllName;                      // 3 elements, 0x10 bytes (sizeof)
    /*0x058*/   struct _UNICODE_STRING BaseDllName;                      // 3 elements, 0x10 bytes (sizeof)
    /*0x068*/   ULONG32      Flags;
    /*0x06C*/   UINT16       LoadCount;
    /*0x06E*/   UINT16       TlsIndex;
    union                                                    // 2 elements, 0x10 bytes (sizeof)
    {
      /*0x070*/         struct _LIST_ENTRY HashLinks;                        // 2 elements, 0x10 bytes (sizeof)
      struct                                             // 2 elements, 0x10 bytes (sizeof)
      {
            /*0x070*/             VOID*      SectionPointer;
            /*0x078*/             ULONG32      CheckSum;
            /*0x07C*/             UINT8      _PADDING1_;
      };
    };
    union                                                    // 2 elements, 0x8 bytes (sizeof)
    {
      /*0x080*/         ULONG32      TimeDateStamp;
      /*0x080*/         VOID*      LoadedImports;
    };
    /*0x088*/   struct _ACTIVATION_CONTEXT* EntryPointActivationContext;
    /*0x090*/   VOID*      PatchInformation;
    /*0x098*/   struct _LIST_ENTRY ForwarderLinks;                     // 2 elements, 0x10 bytes (sizeof)
    /*0x0A8*/   struct _LIST_ENTRY ServiceTagLinks;                      // 2 elements, 0x10 bytes (sizeof)
    /*0x0B8*/   struct _LIST_ENTRY StaticLinks;                        // 2 elements, 0x10 bytes (sizeof)
    /*0x0C8*/   VOID*      ContextInformation;
    /*0x0D0*/   UINT64       OriginalBase;
    /*0x0D8*/   union _LARGE_INTEGER LoadTime;                           // 4 elements, 0x8 bytes (sizeof)
}LDR_DATA, *PLDR_DATA;

typedef struct _OBJECT_TYPE_INITIALIZER                                                                                                                                       // 25 elements, 0x70 bytes (sizeof)
{
    /*0x000*/   UINT16       Length;
    union                                                                                                                                                                     // 2 elements, 0x1 bytes (sizeof)
    {
      /*0x002*/         UINT8      ObjectTypeFlags;
      struct                                                                                                                                                                  // 7 elements, 0x1 bytes (sizeof)
      {
            /*0x002*/             UINT8      CaseInsensitive : 1;                                                                                                                                 // 0 BitPosition
            /*0x002*/             UINT8      UnnamedObjectsOnly : 1;                                                                                                                              // 1 BitPosition
            /*0x002*/             UINT8      UseDefaultObject : 1;                                                                                                                                  // 2 BitPosition
            /*0x002*/             UINT8      SecurityRequired : 1;                                                                                                                                  // 3 BitPosition
            /*0x002*/             UINT8      MaintainHandleCount : 1;                                                                                                                               // 4 BitPosition
            /*0x002*/             UINT8      MaintainTypeList : 1;                                                                                                                                  // 5 BitPosition
            /*0x002*/             UINT8      SupportsObjectCallbacks : 1;                                                                                                                           // 6 BitPosition
      };
    };
    /*0x004*/   ULONG32      ObjectTypeCode;
    /*0x008*/   ULONG32      InvalidAttributes;
    /*0x00C*/   struct _GENERIC_MAPPING GenericMapping;                                                                                                                                     // 4 elements, 0x10 bytes (sizeof)
    /*0x01C*/   ULONG32      ValidAccessMask;
    /*0x020*/   ULONG32      RetainAccess;
    /*0x024*/   enum _POOL_TYPE PoolType;
    /*0x028*/   ULONG32      DefaultPagedPoolCharge;
    /*0x02C*/   ULONG32      DefaultNonPagedPoolCharge;
    /*0x030*/   PVOID DumpProcedure;
    /*0x038*/   PVOID OpenProcedure;
    /*0x040*/   PVOID CloseProcedure;
    /*0x048*/   PVOID DeleteProcedure;
    /*0x050*/   PVOID ParseProcedure;
    /*0x058*/   PVOID SecurityProcedure;
    /*0x060*/   PVOID QueryNameProcedure;
    /*0x068*/   PVOID OkayToCloseProcedure;
}OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER;

typedef struct _EX_PUSH_LOCK               // 7 elements, 0x8 bytes (sizeof)
{
    union                                    // 3 elements, 0x8 bytes (sizeof)
    {
      struct                               // 5 elements, 0x8 bytes (sizeof)
      {
            /*0x000*/             UINT64       Locked : 1;         // 0 BitPosition
            /*0x000*/             UINT64       Waiting : 1;      // 1 BitPosition
            /*0x000*/             UINT64       Waking : 1;         // 2 BitPosition
            /*0x000*/             UINT64       MultipleShared : 1; // 3 BitPosition
            /*0x000*/             UINT64       Shared : 60;      // 4 BitPosition
      };
      /*0x000*/         UINT64       Value;
      /*0x000*/         VOID*      Ptr;
    };
};

typedef struct _MY_OBJECT_TYPE                   // 12 elements, 0xD0 bytes (sizeof)
{
    /*0x000*/   struct _LIST_ENTRY TypeList;            // 2 elements, 0x10 bytes (sizeof)
    /*0x010*/   struct _UNICODE_STRING Name;            // 3 elements, 0x10 bytes (sizeof)
    /*0x020*/   VOID*      DefaultObject;
    /*0x028*/   UINT8      Index;
    /*0x029*/   UINT8      _PADDING0_;
    /*0x02C*/   ULONG32      TotalNumberOfObjects;
    /*0x030*/   ULONG32      TotalNumberOfHandles;
    /*0x034*/   ULONG32      HighWaterNumberOfObjects;
    /*0x038*/   ULONG32      HighWaterNumberOfHandles;
    /*0x03C*/   UINT8      _PADDING1_;
    /*0x040*/   struct _OBJECT_TYPE_INITIALIZER TypeInfo; // 25 elements, 0x70 bytes (sizeof)
    /*0x0B0*/   struct _EX_PUSH_LOCK TypeLock;            // 7 elements, 0x8 bytes (sizeof)
    /*0x0B8*/   ULONG32      Key;
    /*0x0BC*/   UINT8      _PADDING2_;
    /*0x0C0*/   struct _LIST_ENTRY CallbackList;          // 2 elements, 0x10 bytes (sizeof)
}MY_OBJECT_TYPE, *PMY_OBJECT_TYPE;


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;
    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



PVOID obHandle;
DRIVER_INITIALIZE DriverEntry;

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;


MDLWriteMemory(PVOID pBaseAddress, PVOID pWriteData, SIZE_T writeDataSize)
{
    PMDL pMdl = NULL;
    PVOID pNewAddress = NULL;
    pMdl = MmCreateMdl(NULL, pBaseAddress, writeDataSize);
    if (NULL == pMdl)
    {
      return FALSE;
    }
    MmBuildMdlForNonPagedPool(pMdl);
    pNewAddress = MmMapLockedPages(pMdl, KernelMode);
    if (NULL == pNewAddress)
    {
      IoFreeMdl(pMdl);
    }
    RtlCopyMemory(pNewAddress, pWriteData, writeDataSize);
    MmUnmapLockedPages(pNewAddress, pMdl);
    IoFreeMdl(pMdl);
    return TRUE;
}


VOID PatchInstr()
{
    LDR_DATA_TABLE_ENTRY *TE, *Tmp;
    TE = (LDR_DATA_TABLE_ENTRY*)g_Object->DriverSection;
    PLIST_ENTRY LinkList;
    ;
    int i = 0;
    LinkList = TE->InLoadOrderLinks.Flink;
    UNICODE_STRING name;
    RtlInitUnicodeString(&name,L"Loader.sys");
    ;
    while (LinkList != &TE->InLoadOrderLinks)
    {
      Tmp = (LDR_DATA_TABLE_ENTRY*)LinkList;
      if (RtlEqualUnicodeString(&Tmp->BaseDllName, &name,FALSE)) {
            kprintf(("DLLname:%S DLLBase=%p nowcode=%p\n"), Tmp->BaseDllName.Buffer,Tmp->DllBase,(ULONG64)(Tmp->DllBase) + 0xa27e);
            char buffer[] = { 0x90,0x90 };
            MDLWriteMemory((ULONG64)(Tmp->DllBase) + 0xa27e, buffer, 2);
            return;
      }
      LinkList = LinkList->Flink;
      i++;
    }
}

// 文件回调
OB_PREOP_CALLBACK_STATUS FileObjectpreCall(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation)
{
    UNICODE_STRING DosName;
    PFILE_OBJECT fileo = OperationInformation->Object;
    HANDLE CurrentProcessId = PsGetCurrentProcessId();
    UNREFERENCED_PARAMETER(RegistrationContext);

    if (OperationInformation->ObjectType != *IoFileObjectType)
    {
      return OB_PREOP_SUCCESS;
    }

    // 过滤无效指针
    if (fileo->FileName.Buffer == NULL ||
      !MmIsAddressValid(fileo->FileName.Buffer) ||
      fileo->DeviceObject == NULL ||
      !MmIsAddressValid(fileo->DeviceObject))
    {
      return OB_PREOP_SUCCESS;
    }

    // 过滤无效路径
    if (!_wcsicmp(fileo->FileName.Buffer, L"\\Endpoint") ||
      !_wcsicmp(fileo->FileName.Buffer, L"?") ||
      !_wcsicmp(fileo->FileName.Buffer, L"\\.\\.") ||
      !_wcsicmp(fileo->FileName.Buffer, L"\\"))
    {
      return OB_PREOP_SUCCESS;
    }
   
    // 将对象转为DOS路径
    RtlVolumeDeviceToDosName(fileo->DeviceObject, &DosName);
   
   

    if (!wcscmp(fileo->FileName.Buffer, L"\\card.txt")) {
      PETHREAD pct=PsGetCurrentThread();
      PVOID addr=*(ULONG64*)((char*)pct + 0x450);
      PatchInstr();

      //EnumerateKernelThreads();
      DbgBreakPoint();
    }



    return OB_PREOP_SUCCESS;
}

VOID EnableObType(POBJECT_TYPE ObjectType)
{
    PMY_OBJECT_TYPE myobtype = (PMY_OBJECT_TYPE)ObjectType;
    myobtype->TypeInfo.SupportsObjectCallbacks = 1;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    UNREFERENCED_PARAMETER(driver);
    ObUnRegisterCallbacks(obHandle);
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    NTSTATUS status = STATUS_SUCCESS;
    PLDR_DATA ldr;

    kprintf(("hello xia0ji233\n"));
    g_Object = Driver;
    OB_CALLBACK_REGISTRATION obRegFileCallBack;
    OB_OPERATION_REGISTRATION opRegFileCallBack;

    // enable IoFileObjectType
    EnableObType(*IoFileObjectType);

    // bypass MmVerifyCallbackFunction
    ldr = (PLDR_DATA)Driver->DriverSection;
    ldr->Flags |= 0x20;

    // 初始化回调
    memset(&obRegFileCallBack, 0, sizeof(obRegFileCallBack));
    obRegFileCallBack.Version = ObGetFilterVersion();
    obRegFileCallBack.OperationRegistrationCount = 1;
    obRegFileCallBack.RegistrationContext = NULL;
    RtlInitUnicodeString(&obRegFileCallBack.Altitude, L"321000");
    obRegFileCallBack.OperationRegistration = &opRegFileCallBack;

    memset(&opRegFileCallBack, 0, sizeof(opRegFileCallBack));
    opRegFileCallBack.ObjectType = IoFileObjectType;
    opRegFileCallBack.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
    opRegFileCallBack.PreOperation = (POB_PRE_OPERATION_CALLBACK)&FileObjectpreCall;

    status = ObRegisterCallbacks(&obRegFileCallBack, &obHandle);
    if (!NT_SUCCESS(status))
    {
      kprintf(("注册回调错误 \n"));
      status = STATUS_UNSUCCESSFUL;
    }

    UNREFERENCED_PARAMETER(RegistryPath);
    Driver->DriverUnload = &UnDriver;
    return status;
}
```

该程序(附件中的XSafe2.sys)先加载,再加载Loader.sys同样可以任意user key加载成功并且不hook任何系统API和文件。

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/18.png)

#### 分析shellcode反复在读取哪个内存地址(2分)

shellcode加载之后,会检测双机调试

但是有一定的延迟,说明是主动检测的不是被动触发的,刚好遇上这个题,猜测是检测了一个调试器标志位。

不过这里感觉是得先确定一下 shellcode 的位置的,因为它带反调,不知道它怎么反调的,突然想到之前一位神仙把蓝屏代码删了导致电脑爆炸,于是我也想效仿一下看看可不可彳亍。

```C
#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;
#define MAX_BACKTRACE_DEPTH 20

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__);
}

HANDLE FileHandler = NULL;

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 = oldcode;
    }
    WPONx64(irql);
    return STATUS_SUCCESS;
}



NTSTATUS Hook() {
    KIRQL irql = WPOFFx64();
    for (int i = 0; i < sizeof(newcode); i++) {
      target = newcode;
    }
    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;



typedef NTSTATUS (* FuncPtr) (
    _In_ ULONG BugCheckCode,
    _In_ ULONG_PTR BugCheckParameter1,
    _In_ ULONG_PTR BugCheckParameter2,
    _In_ ULONG_PTR BugCheckParameter3,
    _In_ ULONG_PTR BugCheckParameter4
    );


ULONGmyKeBugCheckEx(_In_ ULONG BugCheckCode,
    _In_ ULONG_PTR BugCheckParameter1,
    _In_ ULONG_PTR BugCheckParameter2,
    _In_ ULONG_PTR BugCheckParameter3,
    _In_ ULONG_PTR BugCheckParameter4
) {
   

    Unhook();
    FuncPtr func = (FuncPtr)target;

    PVOID backtrace;
        USHORT capturedFrames = RtlCaptureStackBackTrace(0, MAX_BACKTRACE_DEPTH, backtrace, NULL);
    for (USHORT i = 0; i < capturedFrames; i++) {
      kprintf(("xia0ji233:Backtrace[%u]: %p\n"), i, backtrace);
    }

    kprintf(("calling KeBugCheckEx(%p,%p,%p,%p,%p)\n"),BugCheckCode,BugCheckParameter1,BugCheckParameter2,BugCheckParameter3,BugCheckParameter4);
    DbgBreakPoint();
    LARGE_INTEGER inTime;
        inTime.QuadPart = (LONGLONG) - 10 * 1000 * 1000 * 3600;
        KeDelayExecutionThread(KernelMode, FALSE, &inTime);

    ULONG64 s = func(BugCheckCode,BugCheckParameter1,BugCheckParameter2,BugCheckParameter3,BugCheckParameter4);
    Hook();

    return s;
}

MDLWriteMemory(PVOID pBaseAddress, PVOID pWriteData, SIZE_T writeDataSize)
{
    PMDL pMdl = NULL;
    PVOID pNewAddress = NULL;
    pMdl = MmCreateMdl(NULL, pBaseAddress, writeDataSize);
    if (NULL == pMdl)
    {
      return FALSE;
    }
    MmBuildMdlForNonPagedPool(pMdl);
    pNewAddress = MmMapLockedPages(pMdl, KernelMode);
    if (NULL == pNewAddress)
    {
      IoFreeMdl(pMdl);
    }
    RtlCopyMemory(pNewAddress, pWriteData, writeDataSize);
    MmUnmapLockedPages(pNewAddress, pMdl);
    IoFreeMdl(pMdl);
    return TRUE;
}
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);
    UNICODE_STRING unName = { 0 };
        RtlInitUnicodeString(&unName, L"KeBugCheckEx");
        target = ((ULONG64)MmGetSystemRoutineAddress(&unName))+5;
    kprintf(("Line %d:xia0ji233: KeBugCheckEx=%p\n"), __LINE__, target);
    g_Object = DriverObject;
    if (target) {
      for (int i = 0; i < sizeof(oldcode); i++) {
            oldcode = target;
      }
      *(UINT64*)(newcode + 2) = myKeBugCheckEx;
      Hook();

    }
    else {
      kprintf(("xia0ji233:hahaha"));
    }
    return 0;
}
```

这里去hook KeBugCheckEx,然后直接让它 sleep 一小时,防止它蓝我,我有更多时间可以去分析。

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/19.png)

直接拿捏住了蓝屏。

在后面的分析中发现了 GameSec.exe 的内存一直在被读,估计是在搜索进程,然后读取进程的内存,这一部分后面没有分析太出来。

并且在运行的时候发现会读一些 exe 文件的字符串,在 shellcode 开头 + 80 的位置,这里后续没继续分析了。

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/2_1.png)

#### 编写一个search程序,在Load驱动运行后找到内核内存空间中的shellcode,输出shellcode范围内的任意地址

随后想到去dump shellcode,这里注册回调的方式并不能成功拦截住,因此想到直接去 hook,判断线程起始位置是否在模块范围内,或者说在 Loader.sys 范围内,如果有都输出然后调试器看看内存像不像shellcode,无果,于是选择去跟一下,结果跟到一个类似shellcode的地方(很多浮点指令,看起来像垃圾指令的混淆),dump下来用 010 分析。

##### 方法1

通过内存地址的特征可以发现,前八位可以通过 VAD 的方式去获取,后四个位每次加载似乎都是固定的,因此只需要爆破两个字节用一个特征去匹配就行了,这里。

```C
#include "vad.h"
#include <ntifs.h>
#define kprintf(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, format, ##__VA_ARGS__)
// 定义VAD相对于EProcess头部偏移值
#define eprocess_offset_VadRoot 0x658
#define eprocess_offset_VadCount 0x668

VOID EnumVad(PMMVAD Root, PALL_VADS pBuffer, ULONG nCnt)
{
    if (!Root || !pBuffer || !nCnt)
    {
      return;
    }

    __try
    {
      if (nCnt > pBuffer->nCnt)
      {
            // 得到起始页与结束页
            ULONG64 endptr = (ULONG64)Root->Core.EndingVpnHigh;
            endptr = endptr << 32;

            ULONG64 startptr = (ULONG64)Root->Core.StartingVpnHigh;
            startptr = startptr << 32;

            // 得到根节点
            pBuffer->VadInfos.pVad = (ULONG_PTR)Root;

            // 起始页: startingVpn * 0x1000
            pBuffer->VadInfos.startVpn = (startptr | Root->Core.StartingVpn) << PAGE_SHIFT;

            // 结束页: EndVpn * 0x1000 + 0xfff
            pBuffer->VadInfos.endVpn = ((endptr | Root->Core.EndingVpn) << PAGE_SHIFT) + 0xfff;

            // VAD标志 928 = Mapped    1049088 = Private   ....
            pBuffer->VadInfos.flags = Root->Core.u1.Flags.flag;

            // 验证节点可读性
            if (MmIsAddressValid(Root->Subsection) && MmIsAddressValid(Root->Subsection->ControlArea))
            {
                if (MmIsAddressValid((PVOID)((Root->Subsection->ControlArea->FilePointer.Value >> 4) << 4)))
                {
                  pBuffer->VadInfos.pFileObject = ((Root->Subsection->ControlArea->FilePointer.Value >> 4) << 4);
                }
            }
            pBuffer->nCnt++;
      }

      if (MmIsAddressValid(Root->Core.VadNode.Left))
      {
            // 递归枚举左子树
            EnumVad((PMMVAD)Root->Core.VadNode.Left, pBuffer, nCnt);
      }

      if (MmIsAddressValid(Root->Core.VadNode.Right))
      {
            // 递归枚举右子树
            EnumVad((PMMVAD)Root->Core.VadNode.Right, pBuffer, nCnt);
      }
    }
    __except (1)
    {
    }
}

BOOLEAN EnumProcessVad(ULONG Pid, PALL_VADS pBuffer, ULONG nCnt)
{
    PEPROCESS Peprocess = 0;
    PRTL_AVL_TREE Table = NULL;
    PMMVAD Root = NULL;

    // 通过进程PID得到进程EProcess
    if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)Pid, &Peprocess)))
    {
      // 与偏移相加得到VAD头节点
      Table = (PRTL_AVL_TREE)((UCHAR*)Peprocess + eprocess_offset_VadRoot);
      if (!MmIsAddressValid(Table) || !eprocess_offset_VadRoot)
      {
            return FALSE;
      }

      __try
      {
            // 取出头节点
            Root = (PMMVAD)Table->Root;

            if (nCnt > pBuffer->nCnt)
            {
                // 得到起始页与结束页
                ULONG64 endptr = (ULONG64)Root->Core.EndingVpnHigh;
                endptr = endptr << 32;

                ULONG64 startptr = (ULONG64)Root->Core.StartingVpnHigh;
                startptr = startptr << 32;

                pBuffer->VadInfos.pVad = (ULONG_PTR)Root;

                // 起始页: startingVpn * 0x1000
                pBuffer->VadInfos.startVpn = (startptr | Root->Core.StartingVpn) << PAGE_SHIFT;

                // 结束页: EndVpn * 0x1000 + 0xfff
                pBuffer->VadInfos.endVpn = (endptr | Root->Core.EndingVpn) << PAGE_SHIFT;
                pBuffer->VadInfos.flags = Root->Core.u1.Flags.flag;

                if (MmIsAddressValid(Root->Subsection) && MmIsAddressValid(Root->Subsection->ControlArea))
                {
                  if (MmIsAddressValid((PVOID)((Root->Subsection->ControlArea->FilePointer.Value >> 4) << 4)))
                  {
                        pBuffer->VadInfos.pFileObject = ((Root->Subsection->ControlArea->FilePointer.Value >> 4) << 4);
                  }
                }
                pBuffer->nCnt++;
            }

            // 枚举左子树
            if (Table->Root->Left)
            {
                EnumVad((MMVAD*)Table->Root->Left, pBuffer, nCnt);
            }

            // 枚举右子树
            if (Table->Root->Right)
            {
                EnumVad((MMVAD*)Table->Root->Right, pBuffer, nCnt);
            }
      }
      __finally
      {
            ObDereferenceObject(Peprocess);
      }
    }
    else
    {
      return FALSE;
    }

    return TRUE;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    kprintf(("unload\n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    kprintf(("hello xia0ji233\n"));

    typedef struct
    {
      ULONG nPid;
      ULONG nSize;
      PALL_VADS pBuffer;
    }VADProcess;

    __try
    {
      VADProcess vad = { 0 };

      vad.nPid = 4;

      // 默认有1000个线程
      vad.nSize = sizeof(VAD_INFO) * 0x5000 + sizeof(ULONG);

      // 分配临时空间
      vad.pBuffer = (PALL_VADS)ExAllocatePool(PagedPool, vad.nSize);

      // 根据传入长度得到枚举数量
      ULONG nCount = (vad.nSize - sizeof(ULONG)) / sizeof(VAD_INFO);

      // 枚举VAD
      EnumProcessVad(vad.nPid, vad.pBuffer, nCount);
      
      uintptr_t addr;

      for (ULONG64 i = 0x0; i <65536; i++)
      {
            addr = vad.pBuffer->VadInfos.pVad & 0xffffffff00000000;
            addr = addr + 0x1000;
            addr = addr + (i<<16);
            if (MmIsAddressValid((PVOID)addr) && *((ULONG64 *)addr) == 0x7C8B483024748B48 )
            {
                kprintf(("shellcode found in %p\n"), addr);
                //DbgBreakPoint();
                goto end;
            }

      }
    }
    __except (1)

    {
      kprintf(("Something Error1\n"));
    }
end:
    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}
```

vad.h

```C
#pragma once
#include <ntifs.h>

typedef struct _MM_GRAPHICS_VAD_FLAGS      // 15 elements, 0x4 bytes (sizeof)
{
    /*0x000*/   ULONG32      Lock : 1;                   // 0 BitPosition                  
    /*0x000*/   ULONG32      LockContended : 1;          // 1 BitPosition                  
    /*0x000*/   ULONG32      DeleteInProgress : 1;       // 2 BitPosition                  
    /*0x000*/   ULONG32      NoChange : 1;               // 3 BitPosition                  
    /*0x000*/   ULONG32      VadType : 3;                // 4 BitPosition                  
    /*0x000*/   ULONG32      Protection : 5;             // 7 BitPosition                  
    /*0x000*/   ULONG32      PreferredNode : 6;          // 12 BitPosition                  
    /*0x000*/   ULONG32      PageSize : 2;               // 18 BitPosition                  
    /*0x000*/   ULONG32      PrivateMemoryAlwaysSet : 1; // 20 BitPosition                  
    /*0x000*/   ULONG32      WriteWatch : 1;             // 21 BitPosition                  
    /*0x000*/   ULONG32      FixedLargePageSize : 1;   // 22 BitPosition                  
    /*0x000*/   ULONG32      ZeroFillPagesOptional : 1;// 23 BitPosition                  
    /*0x000*/   ULONG32      GraphicsAlwaysSet : 1;      // 24 BitPosition                  
    /*0x000*/   ULONG32      GraphicsUseCoherentBus : 1; // 25 BitPosition                  
    /*0x000*/   ULONG32      GraphicsPageProtection : 3; // 26 BitPosition                  
}MM_GRAPHICS_VAD_FLAGS, * PMM_GRAPHICS_VAD_FLAGS;
typedef struct _MM_PRIVATE_VAD_FLAGS         // 15 elements, 0x4 bytes (sizeof)
{
    /*0x000*/   ULONG32      Lock : 1;                   // 0 BitPosition                  
    /*0x000*/   ULONG32      LockContended : 1;          // 1 BitPosition                  
    /*0x000*/   ULONG32      DeleteInProgress : 1;       // 2 BitPosition                  
    /*0x000*/   ULONG32      NoChange : 1;               // 3 BitPosition                  
    /*0x000*/   ULONG32      VadType : 3;                // 4 BitPosition                  
    /*0x000*/   ULONG32      Protection : 5;             // 7 BitPosition                  
    /*0x000*/   ULONG32      PreferredNode : 6;          // 12 BitPosition                  
    /*0x000*/   ULONG32      PageSize : 2;               // 18 BitPosition                  
    /*0x000*/   ULONG32      PrivateMemoryAlwaysSet : 1; // 20 BitPosition                  
    /*0x000*/   ULONG32      WriteWatch : 1;             // 21 BitPosition                  
    /*0x000*/   ULONG32      FixedLargePageSize : 1;   // 22 BitPosition                  
    /*0x000*/   ULONG32      ZeroFillPagesOptional : 1;// 23 BitPosition                  
    /*0x000*/   ULONG32      Graphics : 1;               // 24 BitPosition                  
    /*0x000*/   ULONG32      Enclave : 1;                // 25 BitPosition                  
    /*0x000*/   ULONG32      ShadowStack : 1;            // 26 BitPosition                  
}MM_PRIVATE_VAD_FLAGS, * PMM_PRIVATE_VAD_FLAGS;


typedef struct _MMVAD_FLAGS            // 9 elements, 0x4 bytes (sizeof)
{
    /*0x000*/   ULONG32      Lock : 1;             // 0 BitPosition                  
    /*0x000*/   ULONG32      LockContended : 1;    // 1 BitPosition                  
    /*0x000*/   ULONG32      DeleteInProgress : 1; // 2 BitPosition                  
    /*0x000*/   ULONG32      NoChange : 1;         // 3 BitPosition                  
    /*0x000*/   ULONG32      VadType : 3;          // 4 BitPosition                  
    /*0x000*/   ULONG32      Protection : 5;       // 7 BitPosition                  
    /*0x000*/   ULONG32      PreferredNode : 6;    // 12 BitPosition               
    /*0x000*/   ULONG32      PageSize : 2;         // 18 BitPosition               
    /*0x000*/   ULONG32      PrivateMemory : 1;    // 20 BitPosition               
}MMVAD_FLAGS, * PMMVAD_FLAGS;

typedef struct _MM_SHARED_VAD_FLAGS            // 11 elements, 0x4 bytes (sizeof)
{
    /*0x000*/   ULONG32      Lock : 1;                     // 0 BitPosition                  
    /*0x000*/   ULONG32      LockContended : 1;            // 1 BitPosition                  
    /*0x000*/   ULONG32      DeleteInProgress : 1;         // 2 BitPosition                  
    /*0x000*/   ULONG32      NoChange : 1;               // 3 BitPosition                  
    /*0x000*/   ULONG32      VadType : 3;                  // 4 BitPosition                  
    /*0x000*/   ULONG32      Protection : 5;               // 7 BitPosition                  
    /*0x000*/   ULONG32      PreferredNode : 6;            // 12 BitPosition                  
    /*0x000*/   ULONG32      PageSize : 2;               // 18 BitPosition                  
    /*0x000*/   ULONG32      PrivateMemoryAlwaysClear : 1; // 20 BitPosition                  
    /*0x000*/   ULONG32      PrivateFixup : 1;             // 21 BitPosition                  
    /*0x000*/   ULONG32      HotPatchAllowed : 1;          // 22 BitPosition                  
}MM_SHARED_VAD_FLAGS, * PMM_SHARED_VAD_FLAGS;

typedef struct _MMVAD_FLAGS2             // 7 elements, 0x4 bytes (sizeof)
{
    /*0x000*/   ULONG32      FileOffset : 24;      // 0 BitPosition                  
    /*0x000*/   ULONG32      Large : 1;            // 24 BitPosition               
    /*0x000*/   ULONG32      TrimBehind : 1;         // 25 BitPosition               
    /*0x000*/   ULONG32      Inherit : 1;            // 26 BitPosition               
    /*0x000*/   ULONG32      NoValidationNeeded : 1; // 27 BitPosition               
    /*0x000*/   ULONG32      PrivateDemandZero : 1;// 28 BitPosition               
    /*0x000*/   ULONG32      Spare : 3;            // 29 BitPosition               
}MMVAD_FLAGS2, * PMMVAD_FLAGS2;

typedef struct _MMVAD_SHORT
{
    RTL_BALANCED_NODE VadNode;
    UINT32 StartingVpn;               /*0x18*/
    UINT32 EndingVpn;               /*0x01C*/
    UCHAR StartingVpnHigh;
    UCHAR EndingVpnHigh;
    UCHAR CommitChargeHigh;
    UCHAR SpareNT64VadUChar;
    INT32 ReferenceCount;
    EX_PUSH_LOCK PushLock;            /*0x028*/
    struct
    {
      union
      {
            ULONG_PTR flag;
            MM_PRIVATE_VAD_FLAGS PrivateVadFlags;                        /*0x030*/
            MMVAD_FLAGSVadFlags;
            MM_GRAPHICS_VAD_FLAGS GraphicsVadFlags;
            MM_SHARED_VAD_FLAGS   SharedVadFlags;
      }Flags;

    }u1;

    PVOID EventList;                        /*0x038*/

}MMVAD_SHORT, * PMMVAD_SHORT;

typedef struct _MMADDRESS_NODE
{
    ULONG64 u1;
    struct _MMADDRESS_NODE* LeftChild;
    struct _MMADDRESS_NODE* RightChild;
    ULONG64 StartingVpn;
    ULONG64 EndingVpn;
}MMADDRESS_NODE, * PMMADDRESS_NODE;

typedef struct _MMEXTEND_INFO   // 2 elements, 0x10 bytes (sizeof)
{
    /*0x000*/   UINT64       CommittedSize;
    /*0x008*/   ULONG32      ReferenceCount;
    /*0x00C*/   UINT8      _PADDING0_;
}MMEXTEND_INFO, * PMMEXTEND_INFO;
struct _SEGMENT
{
    struct _CONTROL_AREA* ControlArea;
    ULONG TotalNumberOfPtes;
    ULONG SegmentFlags;
    ULONG64 NumberOfCommittedPages;
    ULONG64 SizeOfSegment;
    union
    {
      struct _MMEXTEND_INFO* ExtendInfo;
      void* BasedAddress;
    }u;
    ULONG64 SegmentLock;
    ULONG64 u1;
    ULONG64 u2;
    PVOID* PrototypePte;
    ULONGLONG ThePtes;
};

typedef struct _EX_FAST_REF
{
    union
    {
      PVOID Object;
      ULONG_PTR RefCnt : 3;
      ULONG_PTR Value;
    };
} EX_FAST_REF, * PEX_FAST_REF;

typedef struct _CONTROL_AREA                      // 17 elements, 0x80 bytes (sizeof)
{
    /*0x000*/   struct _SEGMENT* Segment;
    union                                       // 2 elements, 0x10 bytes (sizeof)
    {
      /*0x008*/         struct _LIST_ENTRY ListHead;            // 2 elements, 0x10 bytes (sizeof)
      /*0x008*/         VOID* AweContext;
    };
    /*0x018*/   UINT64       NumberOfSectionReferences;
    /*0x020*/   UINT64       NumberOfPfnReferences;
    /*0x028*/   UINT64       NumberOfMappedViews;
    /*0x030*/   UINT64       NumberOfUserReferences;
    /*0x038*/   ULONG32 u;                     // 2 elements, 0x4 bytes (sizeof)   
    /*0x03C*/   ULONG32 u1;                  // 2 elements, 0x4 bytes (sizeof)   
    /*0x040*/   struct _EX_FAST_REF FilePointer;            // 3 elements, 0x8 bytes (sizeof)   
    // 4 elements, 0x8 bytes (sizeof)   
}CONTROL_AREA, * PCONTROL_AREA;

typedef struct _SUBSECTION_
{
    struct _CONTROL_AREA* ControlArea;

}SUBSECTION, * PSUBSECTION;

typedef struct _MMVAD
{
    MMVAD_SHORT Core;
    union               /*0x040*/
    {
      UINT32 LongFlags2;
      //现在用不到省略
      MMVAD_FLAGS2 VadFlags2;

    }u2;
    PSUBSECTION Subsection;               /*0x048*/
    PVOID FirstPrototypePte;      /*0x050*/
    PVOID LastContiguousPte;      /*0x058*/
    LIST_ENTRY ViewLinks;         /*0x060*/
    PEPROCESS VadsProcess;          /*0x070*/
    PVOID u4;                     /*0x078*/
    PVOID FileObject;               /*0x080*/
}MMVAD, * PMMVAD;

typedef struct _RTL_AVL_TREE         // 1 elements, 0x8 bytes (sizeof)
{
    /*0x000*/   struct _RTL_BALANCED_NODE* Root;
}RTL_AVL_TREE, * PRTL_AVL_TREE;

typedef struct _VAD_INFO_
{
    ULONG_PTR pVad;
    ULONG_PTR startVpn;
    ULONG_PTR endVpn;
    ULONG_PTR pFileObject;
    ULONG_PTR flags;
}VAD_INFO, * PVAD_INFO;

typedef struct _ALL_VADS_
{
    ULONG nCnt;
    VAD_INFO VadInfos;
}ALL_VADS, * PALL_VADS;

typedef struct _MMSECTION_FLAGS                        // 27 elements, 0x4 bytes (sizeof)
{
    /*0x000*/   UINT32       BeingDeleted : 1;                     // 0 BitPosition                  
    /*0x000*/   UINT32       BeingCreated : 1;                     // 1 BitPosition                  
    /*0x000*/   UINT32       BeingPurged : 1;                      // 2 BitPosition                  
    /*0x000*/   UINT32       NoModifiedWriting : 1;                // 3 BitPosition                  
    /*0x000*/   UINT32       FailAllIo : 1;                        // 4 BitPosition                  
    /*0x000*/   UINT32       Image : 1;                            // 5 BitPosition                  
    /*0x000*/   UINT32       Based : 1;                            // 6 BitPosition                  
    /*0x000*/   UINT32       File : 1;                           // 7 BitPosition                  
    /*0x000*/   UINT32       AttemptingDelete : 1;               // 8 BitPosition                  
    /*0x000*/   UINT32       PrefetchCreated : 1;                  // 9 BitPosition                  
    /*0x000*/   UINT32       PhysicalMemory : 1;                   // 10 BitPosition                  
    /*0x000*/   UINT32       ImageControlAreaOnRemovableMedia : 1; // 11 BitPosition                  
    /*0x000*/   UINT32       Reserve : 1;                        // 12 BitPosition                  
    /*0x000*/   UINT32       Commit : 1;                           // 13 BitPosition                  
    /*0x000*/   UINT32       NoChange : 1;                         // 14 BitPosition                  
    /*0x000*/   UINT32       WasPurged : 1;                        // 15 BitPosition                  
    /*0x000*/   UINT32       UserReference : 1;                  // 16 BitPosition                  
    /*0x000*/   UINT32       GlobalMemory : 1;                     // 17 BitPosition                  
    /*0x000*/   UINT32       DeleteOnClose : 1;                  // 18 BitPosition                  
    /*0x000*/   UINT32       FilePointerNull : 1;                  // 19 BitPosition                  
    /*0x000*/   ULONG32      PreferredNode : 6;                  // 20 BitPosition                  
    /*0x000*/   UINT32       GlobalOnlyPerSession : 1;             // 26 BitPosition                  
    /*0x000*/   UINT32       UserWritable : 1;                     // 27 BitPosition                  
    /*0x000*/   UINT32       SystemVaAllocated : 1;                // 28 BitPosition                  
    /*0x000*/   UINT32       PreferredFsCompressionBoundary : 1;   // 29 BitPosition                  
    /*0x000*/   UINT32       UsingFileExtents : 1;               // 30 BitPosition                  
    /*0x000*/   UINT32       PageSize64K : 1;                      // 31 BitPosition                  
}MMSECTION_FLAGS, * PMMSECTION_FLAGS;

typedef struct _SECTION                        // 9 elements, 0x40 bytes (sizeof)
{
    /*0x000*/   struct _RTL_BALANCED_NODE SectionNode;       // 6 elements, 0x18 bytes (sizeof)
    /*0x018*/   UINT64       StartingVpn;
    /*0x020*/   UINT64       EndingVpn;
    /*0x028*/   union {
      PCONTROL_AREA   ControlArea;
      PVOID   FileObject;

    }u1;                   // 4 elements, 0x8 bytes (sizeof)
    /*0x030*/   UINT64       SizeOfSection;
    /*0x038*/   union {
      ULONG32 LongFlags;
      MMSECTION_FLAGS Flags;
    }u;                  // 2 elements, 0x4 bytes (sizeof)
    struct                                       // 3 elements, 0x4 bytes (sizeof)
    {
      /*0x03C*/         ULONG32      InitialPageProtection : 12; // 0 BitPosition                  
      /*0x03C*/         ULONG32      SessionId : 19;             // 12 BitPosition                  
      /*0x03C*/         ULONG32      NoValidationNeeded : 1;   // 31 BitPosition                  
    };
}SECTION, * PSECTION;
```

先加载 loader 再加载 search,成功输出 shellcode 的地址,特征码匹配前八个字节,在自己的环境只输出了一个地址,如果输出多个地址可以考虑加长特征码。

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/20.png)

##### 方法2

通过获取到线程结构得到它的线程上下文,输出 RIP 的值应该也行,通过PCHUNTER看到shellcode运行的线程

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/21.png)

通过分析可知 shellcode 执行的线程具有如下特点:

与 TID=12 的线程入口相同

保持运行状态

据此可以筛选得到这个线程,通过线程结构体可以找到它的栈

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/22.png)

多次运行发现栈中存在 `GameSec.exe` 这个字符串。并且在它 - 0x28 的位置有一个地址,那个地址前八位和shellcode一致,所以同样可以爆破+特征码匹配。

```cpp
#include<ntifs.h>
#include <ntddk.h>
#include <ntstrsafe.h>

#define kprintf(format, ...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, format, ##__VA_ARGS__)
#define MAX_BACKTRACE_DEPTH 20
ULONG64 num = 0;
NTSTATUS EnumerateKernelThreads();

typedef NTSTATUS (*ZWQUERYSYSTEMINFORMATION)(ULONG, PVOID, ULONG, PULONG);

VOID DRIVERUNLOAD(_In_ struct _DRIVER_OBJECT* DriverObject)
{
        kprintf(("unload\n"));

}


NTSTATUS EnumerateKernelThreads() {


    PETHREAD T12 = NULL;
    PETHREAD T;
    PsLookupThreadByThreadId(12, &T12);
    kprintf(("T12=%p\n"), T12);
    ULONG64 Start = *(ULONG64 *)((ULONG64)T12 + 0x620);
    HANDLE TargetThread = 0;
    for (int i = 16; i < 0x20000; i+=4) {
      T = NULL;
      PsLookupThreadByThreadId(i, &T);
      if (T) {
            ULONG64 Startaddr = *(ULONG64 *)((ULONG64)T + 0x620);
            if (Startaddr == Start) {
                kprintf(("Found Thread=%p pThread=%p\n"), i,T);
         
                TargetThread = i;
                break;
            }
         
      }
    }


    if (T != NULL) {
      ULONG64 StackBase = 0;
      ULONG64 StackLimit = 0;
      StackBase= *(ULONG64 *)((ULONG64)T + 0x38);
      StackLimit=*(ULONG64 *)((ULONG64)T + 0x30);
      kprintf(("Stackbase=%p StackLimit=%p\n"), StackBase, StackLimit);
      for (ULONG64 addr = StackBase-0x10 ; addr > StackLimit; addr -= 8) {
            if (!strcmp(addr, "GameSec.exe")) {
                kprintf(("Found string in %p\n"), addr);
                uintptr_t address;
                for (ULONG64 i = 0x0; i <65536; i++)
                {
                  address = (*(ULONG64*)(addr-0x28)) & 0xffffffff00000000;
                  address = address + 0x1000;
                  address = address + (i<<16);
                  if (MmIsAddressValid((PVOID)address) && *((ULONG64 *)address) == 0x7C8B483024748B48 )
                  {
                        kprintf(("shellcode found in %p\n"), address);
                        //DbgBreakPoint();
                        break;
                  }

                }
                break;
            }

      }

      
    }


    return STATUS_SUCCESS;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{

        kprintf(("hello xia0ji233\n"));

    EnumerateKernelThreads();
        pDriver->DriverUnload = DRIVERUNLOAD;
        return STATUS_SUCCESS;
}
```

先运行 Loader.sys,再运行EmurateThread.sys,可以成功输出shellcode的地址。

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/23.png)

##### 方法3

随后我发现线程结构体中的 TrapFrame 有点东西,通过一段时间的运行之后,发现它的一些寄存器中会带上点东西

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/24.png)

这里我选 Rdx,取前8位,暴力搜索4位(65536,可接受范围内)匹配。

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/25.png)

先运行 Loader.sys 再运行 Search3.sys,可以直接得到 shellcode 的地址。

##### 方法4

观察到 ETHTREAD 结构体中有个指针指向了距离shellcode比较近的位置

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/26.png)

获取这个指针的前八位,然后爆破,匹配特征码。

![](https://xia0ji233.pro/2024/04/22/tencent-race-2024-pre-final/27.png)

先运行Loader.sys,再运行 Search4.sys,即可获得shellcode的地址。

##### 方法5

挂起线程,此时会将线程上下文保存在栈顶中,再去遍历一遍栈,获得RIP指针,这里判断只需要拿 RSP 即可,当 -0x400==addr(+0x180是RSP相对于上下文结构体的偏移,0x400是context上下文大小)时,取出 RIP 即可。

先运行 Loader.sys,在运行search5.sys,即可输出shellcode。

geesehoward 发表于 2024-4-22 09:47

大神之所以是大神,就是先要有NB的技术去分析问题,更要有耐心去分析和解决问题,这么多代码,单是C的都没耐心看完,还要分析一大堆汇编指令,还要写脚本,写测试程序等等,我连一行行仔细看的耐心都没有,这就是差距啊

hans7 发表于 2024-4-23 00:19

我能看懂的不多,但当我看到这段代码的时候

```cpp
#include<stdio.h>
#include<string.h>
int main(){
    char user[]="xia0ji233";
    int n=strlen(user);
    unsigned key=0;
    for(int i=0;i<n;i++){
      key=(key+user)*0x1003F;
    }
    printf("%u\n",key);
}
```

这是字符串哈希的自然溢出。多年没碰算法的我的DNA动了……

ltgb 发表于 2024-4-22 06:35

沙发 大佬厉害

pmhker 发表于 2024-4-22 08:12

太高深,完全看不懂膜拜一下

顺着路走下去 发表于 2024-4-22 08:13

太强了,学习学习,希望有朝一日可以赶上大佬的脚步

tyq2003 发表于 2024-4-22 08:15

这些东西,不了解的人看起来真的是眼花缭乱!学习了,谢谢分享

djtc 发表于 2024-4-22 08:32

我的乖乖,我纯往下拉都拉了半天

melloo 发表于 2024-4-22 08:53

沙发 大佬厉害 感谢大佬分享

smartfind 发表于 2024-4-22 09:06

感谢大佬分享~

hope9423 发表于 2024-4-22 09:19

不明觉厉,楼主前途无量

backaxe 发表于 2024-4-22 09:27

不明觉厉
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 腾讯游戏安全大赛2024决赛题解