吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 250|回复: 1
上一主题 下一主题
收起左侧

[原创] 2024腾讯游戏安全PC初赛复现

[复制链接]
跳转到指定楼层
楼主
Kvancy 发表于 2024-9-23 13:56 回帖奖励
本帖最后由 Kvancy 于 2024-9-23 14:03 编辑

题目

(一)解题过程

拿到hack.exe,浅分析一下发现加了VM,并且有检测黑客工具的行为,检测到了之后即使关闭黑客程序也会影响程序正常运行,但是xdbg稍微改一下还是可以动调的,在xdbg里下一些可能的函数断点,我这里在这些地方下了断点

运行发现程序会多次在WriteProcessMemory下断下,hook一下观察传参

//dllmain.cpp
#include "pch.h"
#include <windows.h>
#include <shellapi.h>
#include <detours.h>
#include <tlhelp32.h>
#include <stdlib.h>
#include <stdio.h>
#pragma comment(lib,"detours.lib")
#define _KDEBUG
#define DBGMGEBOX(fmt, ...) \
    do { \
         /* 假设最大长度为1024,根据需要调整大小 */ \
        wsprintfA(out, fmt, __VA_ARGS__); \
        MessageBoxA(NULL, out, "提示", MB_OK); \
    } while(0)
char out[100];
typedef BOOL(WINAPI* WriteProcessMemory_t)(
    _In_ HANDLE hProcess,
    _In_ LPVOID lpBaseAddress,
    _In_reads_bytes_(nSize) LPCVOID lpBuffer,
    _In_ SIZE_T nSize,
    _Out_opt_ SIZE_T* lpNumberOfBytesWritten
    );
WriteProcessMemory_t TrueWriteProcessMemory = NULL;
BOOL
WINAPI
HookWriteProcessMemory(
    _In_ HANDLE hProcess,
    _In_ LPVOID lpBaseAddress,
    _In_reads_bytes_(nSize) LPCVOID lpBuffer,
    _In_ SIZE_T nSize,
    _Out_opt_ SIZE_T * lpNumberOfBytesWritten
)
{
    char fileName[12] = { 0 };
    sprintf(fileName, "out%d.txt", (int)hProcess % 1000);
    HANDLE hFile = CreateFile(fileName, FILE_APPEND_DATA, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        DBGMGEBOX("CreateFile Fail");
        return TrueWriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten);
    }
    SetFilePointer(hFile, 0, NULL, FILE_END);
    DWORD bytesWritten;
    BOOL result = WriteFile(hFile, lpBuffer, nSize, &bytesWritten, NULL);
    CloseHandle(hFile);
    DBGMGEBOX("findProcess WriteProcessMemory:%p,size:%d\n", hProcess,nSize);
    return TrueWriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten);
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH:

        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());

        TrueWriteProcessMemory = (WriteProcessMemory_t)DetourFindFunction("kernel32.dll", "WriteProcessMemory");
        DetourAttach(&(PVOID&)TrueWriteProcessMemory, HookWriteProcessMemory);

        DetourTransactionCommit();
        break;
    case DLL_PROCESS_DETACH:
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)TrueWriteProcessMemory, HookWriteProcessMemory);

        DetourTransactionCommit();
        break;
    }
    return TRUE;
}

这里输出了三个txt文件

其中out200.txt文件有明显的PE头

去除前面的字节,把文件丢到DIE里分析一下发现是dll64文件并且貌似没加壳,所以hack.exe通过WriteProcessMemory往某个进程写入了一个dll?怀疑是远程注入,至于做了什么,有可能跟token有关,继续分析。

ida64打开发现程序的dllMain入口还是被加密了

还是继续动调,随便找了个64位的可执行文件,拖到X64dbg里运行,直接用xdbg的注入方式将out200.dll注入进程,在

入口点下断点,并且对一些可疑的WINDOWS API下断观察进程行为

这里我对下面这几个API下了断点

运行,第一次成功在openProcess函数断下

观察传参窗口,找到了一个系统进程名称字符串winlogon.exe,而这里调用的是openProcess,疑似是对系统进程winlogon.exe做了一些操作。继续分析,运行到返回,回溯一层函数,找到一段没有被加密的代码

汇编代码不是很好看,根据偏移在IDA里反汇编看看

__int64 __fastcall sub_1800063D0(_DWORD *Dst, DWORD dwProcessId)
{
  __m128i si128; // xmm0
  __m128i v6; // xmm0
  __int64 result; // rax
  __m128i v10; // xmm2
  size_t v14; // rbx
  __int64 v15; // rax
  int v16; // r14d
  HANDLE Toolhelp32Snapshot; // rsi
  HANDLE v18; // rax
  __int64 v21; // rbx
  CHAR Caption[16]; // [rsp+40h] [rbp+0h] BYREF

  _RBP = (unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64;
  if ( !dwProcessId )
  {
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x28) = 0xC65BF3E99CAA093Cui64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 8) = 0xC65BF3E99CAA093Cui64;
    *(_QWORD *)_RBP = 0xE795A71250E2465Aui64;
    si128 = _mm_load_si128((const __m128i *)_RBP);
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x20) = 0xE795A7603F90341Fui64;
    v6 = _mm_xor_si128(si128, *(__m128i *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x20));
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x20) = 0x84FAD5301FE45158ui64;
    *(__m128i *)_RBP = v6;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x28) = 0xBF19D3ADD5D97A59ui64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x1A0) = 0xE795A7603F90341Fui64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x30) = 0xBD1C9CA3F4C12EC9ui64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x38) = 0xA727C05763438E84ui64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x1A8) = 0xC65BF3E99CAA093Cui64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x1B0) = 0xCF59BCC699A060E9ui64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x1B8) = 0xA727C0574231E1F6ui64;
    __asm
    {
      vmovdqu ymm0, [rbp+210h+var_70]
      vpxor   ymm1, ymm0, ymmword ptr [rbp+210h+Text]
      vmovdqa ymmword ptr [rbp+210h+Text], ymm1
      vzeroupper
    }
    MessageBoxA(0i64, (LPCSTR)(_RBP + 32), (LPCSTR)((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64), 0);
LABEL_3:
    GetLastError();
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x20) = 0xE795A7603F90341Fui64;
    *(_QWORD *)_RBP = 0x88D6871250E2465Aui64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x28) = 0xC65BF3E99CAA093Cui64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 8) = 0xC65BF98DB9906C58ui64;
    *(__m128i *)_RBP = _mm_xor_si128(
                         _mm_load_si128((const __m128i *)_RBP),
                         *(__m128i *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x20));
    return sub_180006A00((void *)((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64));
  }
  if ( !(unsigned __int8)sub_180006FC0() )
  {
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x1A0) = 0xE795A7603F90341Fui64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x48) = 0x44F651D568826090i64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x1C0) = 0x52C9FCDC77FF5FC3i64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x20) = 0xDDD2E92971C27548ui64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x28) = 0xA43EB7C9E8CF4E1Cui64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x30) = 0xA62FD5B4C980079Cui64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x38) = 0xD55585772756849Aui64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x40) = 0x52C9FCDC7DDE2DACi64;
    v10 = _mm_load_si128((const __m128i *)(_RBP + 64));
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x1C8) = 0x44F651D568826090i64;
    _XMM2 = _mm_xor_si128(v10, *(__m128i *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x1C0));
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x1A8) = 0xC65BF3E99CAA093Cui64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x1B0) = 0xCF59BCC699A060E9ui64;
    *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x1B8) = 0xA727C0574231E1F6ui64;
    __asm
    {
      vmovdqu ymm0, [rbp+210h+var_70]
      vpxor   ymm1, ymm0, ymmword ptr [rbp+210h+Text]
      vmovdqa [rbp+210h+var_1D0], xmm2
      vmovdqa ymmword ptr [rbp+210h+Text], ymm1
      vzeroupper
    }
    sub_180006A00((void *)(_RBP + 32));
  }
  v14 = -1i64;
  if ( dwProcessId == -1 )
  {
    Dst[34] = GetCurrentProcessId();
    *((_QWORD *)Dst + 13) = -1i64;
  }
  else
  {
    Dst[34] = dwProcessId;
    v18 = OpenProcess(0x1FFFFFu, 0, dwProcessId);
    *((_QWORD *)Dst + 13) = v18;
    if ( !v18 )
    {
      *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x38) = 0xA727C0574231E1F6ui64;
      *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x20) = 0x84FAD53051F54450ui64;
      *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x1A0) = 0xE795A7603F90341Fui64;
      *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x28) = 0xA92981ACBCD97A59ui64;
      *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x30) = 0xCF59BCC699AA419Bui64;
      *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x1A8) = 0xC65BF3E99CAA093Cui64;
      *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x1B0) = 0xCF59BCC699A060E9ui64;
      *(_QWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x1B8) = 0xA727C0574231E1F6ui64;
      __asm
      {
        vmovdqu ymm0, [rbp+210h+var_70]
        vpxor   ymm1, ymm0, ymmword ptr [rbp+210h+Text]
        vmovdqa ymmword ptr [rbp+210h+Text], ymm1
        vzeroupper
      }
      sub_180006A00((void *)(_RBP + 32));//打印报错信息
      goto LABEL_3;
    }
  }
  Dst[30] = 0x1FFFFF;
  v15 = -1i64;
  do
    ++v15;
  while ( *((_BYTE *)Dst + v15) );
  if ( !v15 )
  {
    v16 = Dst[34];
    Toolhelp32Snapshot = CreateToolhelp32Snapshot(2u, 0);
    if ( Toolhelp32Snapshot != (HANDLE)-1i64 )
    {
      *(_DWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x60) = 304;
      memset((void *)(_RBP + 100), 0, 0x12Cui64);
      if ( Process32First(Toolhelp32Snapshot, (LPPROCESSENTRY32)(_RBP + 96)) )
      {
        while ( *(_DWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 0x68) != v16 )
        {
          if ( !Process32Next(Toolhelp32Snapshot, (LPPROCESSENTRY32)(_RBP + 96)) )
            goto LABEL_20;
        }
        do
          ++v14;
        while ( *(_BYTE *)(_RBP + 140 + v14) );
        memmove(Dst, (const void *)(_RBP + 140), v14);
      }
LABEL_20:
      CloseHandle(Toolhelp32Snapshot);
    }
  }
  *((_QWORD *)Dst + 18) = sub_1800068D0(Dst, Dst);
  v21 = 0i64;
  *(_DWORD *)(((unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64) + 8) = Dst[34];
  *(_QWORD *)_RBP = 0i64;
  EnumWindows(EnumFunc, (unsigned __int64)Caption & 0xFFFFFFFFFFFFFFE0ui64);
  result = *(_QWORD *)_RBP;
  if ( *(_QWORD *)_RBP )
    v21 = *(_QWORD *)_RBP;
  *((_QWORD *)Dst + 16) = v21;
  return result;
}

这段代码一次进行了获取进程pid,打开进程,遍历模块等操作,并且在函数失败后做了一些奇奇怪怪的东西,对一些地址赋上了一些64位的值,猜测是隐藏字符串来打印调试信息用的,再通过messagebox和outputDebugString给出调试信息,显示打开进程失败,猜测是因为hack.exe启动是管理员启动,这里失去了管理员权限。

分析完这个函数,继续回溯一层,运行到返回,定位到这个地方

继续根据偏移转到IDA里看反汇编

int sub_180001990()
{
  size_t v0; // rbx
  DWORD v1; // eax
  __m128i Dst; // [rsp+20h] [rbp-48h] BYREF
  __int64 v4; // [rsp+30h] [rbp-38h]
  __int64 v5; // [rsp+38h] [rbp-30h]
  __m128i Src; // [rsp+40h] [rbp-28h] BYREF

  Dst.m128i_i64[0] = 0xE795A7603F90341Fui64;
  Dst.m128i_i64[1] = 0xC65BF3E99CAA093Cui64;
  Src.m128i_i64[0] = 0x89FAC00F53FE5D68ui64;
  Src.m128i_i64[1] = 0xC65BF3E9F9D26C12ui64;
  Src = _mm_xor_si128(_mm_load_si128(&Src), Dst);
  v0 = -1i64;
  do
    ++v0;
  while ( Src.m128i_i8[v0] );
  memmove(dword_1800349A0, &Src, v0);
  Dst.m128i_i64[0] = 0i64;
  v4 = 0i64;
  v5 = 15i64;
  sub_180004770(&Dst, &Src, v0);
  v1 = sub_1800070A0(&Dst);
  sub_1800063D0(dword_1800349A0, v1);
  return atexit(sub_180020C90);
}

前面应该是一个加密的字符串操作,用python打印出字符串

def hex_xor_to_string(a, b):
    result = a ^ b
    hex_str = hex(result)[2:]
    if len(hex_str) % 2 != 0:
        hex_str = '0' + hex_str

    result_str = ''.join(chr(int(hex_str[i:i+2], 16)) for i in range(0, len(hex_str), 2))
    return result_str

x1 = 0xE795A7603F90341F
x2 = 0xC65BF3E99CAA093C
y1 = 0x89FAC00F53FE5D68
y2 = 0xC65BF3E9F9D26C12

result1 = hex_xor_to_string(x1, y1)
result2 = hex_xor_to_string(x2, y2)

print("Result 1:", result1)
print("Result 2:", result2)
#Result 1: nogolniw
#Result 2: exe.

得到的刚好是winlogon.exe字符串,然后程序将这个字符串转移到了dword_1800349A0全局变量中,目的应该是隐藏字符串,接着sub_180004770函数也是一个类似memmove操作,把这个字符串传到了Dst局部变量中,接着在sub_1800070A0中传入这个字符串,貌似是在根据字符串获取进程PID,接着调用sub_1800063D0函数根据pid打开进程,并将进程句柄存储到了某个地方

v18 = OpenProcess(0x1FFFFFu, 0, dwProcessId);
*((_QWORD *)Dst + 13) = v18;

随后return exit退出。

随后我在退出函数传参的时候看到了一个hProcess

一个全局变量,很有可能在其他地方对句柄进行了读取,交叉引用一下定位到如下函数

void sub_180007C10()
{
  HANDLE v1; // rcx
  void *v2; // rdx
  __int128 v3; // xmm0
  __int128 v4; // xmm1
  HANDLE FileA; // rbx
  __int64 v8; // rcx
  _BYTE *v9; // rdx
  unsigned __int64 v10; // rdx
  _QWORD *v11; // rcx
  char Buffer; // [rsp+60h] [rbp+0h] BYREF

  _RBP = (unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64;
  while ( byte_180032C00 )
  {
    if ( !byte_180034961 && !byte_180034960 )
      sub_1800041D0();
    v1 = hProcess;
    v2 = (void *)(qword_180034968 + 2766);
    *(_BYTE *)_RBP = 15;
    WriteProcessMemory(v1, v2, (LPCVOID)((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64), 1ui64, 0i64);
    byte_180032C00 = 0;
    *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64) + 0x20) = 0xAA32D3B2B7C50388ui64;
    *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64) + 0x10) = 0xA0A195500DCC0E5Cui64;
    *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64) + 0x18) = 0x943E9588CFCF645Dui64;
    v3 = *(_OWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64) + 0x10);
    *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64) + 0x28) = 0xA727C0574231D098ui64;
    v4 = *(_OWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64) + 0x20);
    *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64) + 0x80) = 0xE795A7603F90341Fui64;
    *(_OWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64) + 0x60) = v3;
    *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64) + 0x88) = 0xC65BF3E99CAA093Cui64;
    *(_OWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64) + 0x70) = v4;
    *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64) + 0x90) = 0xCF59BCC699A060E9ui64;
    *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64) + 0x98) = 0xA727C0574231E1F6ui64;
    __asm
    {
      vmovdqu ymm0, [rbp+0D0h+var_50]
      vpxor   ymm1, ymm0, ymmword ptr [rbp+0D0h+FileName]
      vmovdqa ymmword ptr [rbp+0D0h+FileName], ymm1
      vzeroupper
    }
    FileA = CreateFileA((LPCSTR)(_RBP + 96), 0x40000000u, 0, 0i64, 3u, 0x80u, 0i64);
    if ( FileA != (HANDLE)-1i64 )
    {
      ((void (__fastcall *)(unsigned __int64))loc_180007A20)(_RBP + 48);
      v8 = -1i64;
      if ( *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64) + 0x48) < 0x10ui64 )
      {
        do
          ++v8;
        while ( *(_BYTE *)(_RBP + 48 + v8) );
        v9 = (_BYTE *)(_RBP + 48);
      }
      else
      {
        v9 = *(_BYTE **)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64) + 0x30);
        do
          ++v8;
        while ( v9[v8] );
      }
      WriteFile(FileA, v9, v8, (LPDWORD)(_RBP + 8), 0i64);
      CloseHandle(FileA);
      v10 = *(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64) + 0x48);
      if ( v10 >= 0x10 )
      {
        v11 = *(_QWORD **)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64) + 0x30);
        if ( v10 + 1 >= 0x1000 )
        {
          v11 = (_QWORD *)*(v11 - 1);
          if ( (unsigned __int64)(*(_QWORD *)(((unsigned __int64)&Buffer & 0xFFFFFFFFFFFFFFE0ui64) + 0x30)
                                - (_QWORD)v11
                                - 8i64) > 0x1F )
            invalid_parameter_noinfo_noreturn();
        }
        j_j_free(v11);
      }
    }
  }
}

看到了有WriteProcessMemory写入hProcess内存操作,CreateFileA,WriteFile,打开和写入文件操作,但是并没有找到hProcess的赋值语句,也就是说这个进程句柄还不知道是谁的,猜测赋值被隐藏了,但是可以猜测可能是winlogon.exe进程句柄。byte_180032C00是一个全局的标志变量,强制函数只能执行一次,对应的是运行程序时仅一次的初始化操作。接着看一下CreateFileA函数,同样的文件名被隐藏了,python解析一下

def hex_xor_to_string(a, b):
    result = a ^ b
    hex_str = hex(result)[2:]
    if len(hex_str) % 2 != 0:
        hex_str = '0' + hex_str

    result_str = ''.join(chr(int(hex_str[i:i+2], 16)) for i in range(0, len(hex_str), 2))
    return result_str

x1 = 0xA0A195500DCC0E5C
x2 = 0x943E9588CFCF645D
x3 = 0xAA32D3B2B7C50388
x4 = 0xA727C0574231D098
y1 = 0xE795A7603F90341F
y2 = 0xC65BF3E99CAA093C
y3 = 0xCF59BCC699A060E9
y4 = 0xA727C0574231E1F6

result1 = hex_xor_to_string(x1, y1)
result2 = hex_xor_to_string(x2, y2)
result3 = hex_xor_to_string(x3, y3)
result4 = hex_xor_to_string(x4, y4)

print("Result 1:", result1)
print("Result 2:", result2)
print("Result 3:", result3)
print("Result 4:", result4)

res = result1[::-1] + result2[::-1] + result3[::-1] + result4[::-1]
print(res)

整个拼起来是字符串C:\2024GameSafeRace.token1,应该是创建了一个文件,然后向这个文件写入了token1了,接着往下

loc_180007A20这个函数内部被加密了,猜测是对token1的解密过程,然后通过WriteFile写入C:\2024GameSafeRace.token1中,并不是很像去分析这个函数,直接加载驱动看看能不能直接运行得到2024GameSafeRace.token1文件。

找到下没找到,回头看看CreateFileA函数,核查一下后面几个参数

FileA = CreateFileA((LPCSTR)(_RBP + 96), 0x40000000u, 0, 0i64, 3u, 0x80u, 0i64);

看来是参数在作怪,CreateFileA函数传入OPEN_EXISTING参数,如果没有指定文件,则函数会返回失败,那也好办,自己创建一个就好了。

C:\2024GameSafeRace.token1成功被写入

010打开找到token1:757F4749AEBB1891EF5AC2A9B5439CEA

token2的寻找就偏简单了,加载驱动后留意一下dbgView的打印信息就可以获取

组合一下就是token2:803f14a24d64f3e697957c252e3a5686

(二)解题过程

题目要求:

编写程序,运行时修改尽量少的内存,让两段token输出成功。(满分2分)

根据之前分析的token1,我们可以知道程序会在CreateFileA后解密token1然后写入到C:\2024GameSafeRace.token1中,但是会因为CreateFileA参数OPEN_EXISTING条件不满足而失败,所以我们只需要修改这个传参,改成OPEN_ALWAYS,即可实现输出token1,那我们只需要hook CreateFileA函数修改传参即可,但是有个问题,因为不是hack.exe本身调用CreateFileA函数,而是hack.exe注入了一个dll到winlogon.exe,然后再winlogon.exe里调用CreateFileA函数,所以我们可以考虑在注入前修改WriteProcessMemory函数参数buffer,从而在注入前patch dll,或者编写代码直接注入winlogon.exe,hook CreateFileA函数修改传参,但是考虑到第二种方式可能不被允许,winlogon.exe毕竟是系统进程,题目应该是要我们通过patch dll的方式解题。

这里我们要patch 传参,通过ida找到传参的汇编代码

在winhex里找到对应所在文件偏移

也就是在0x7171处OPEN_EXISTING:0x3是要patch的地方,把这个参数修改成OPEN_ALWAYS:0x4即可。下面编写代码实现。

//dllmain.cpp
#include "pch.h"
#include <windows.h>
#include <shellapi.h>
#include <detours.h>
#include <tlhelp32.h>
#include <stdlib.h>
#include <stdio.h>
#pragma comment(lib,"detours.lib")
#define _KDEBUG
#define DBGMGEBOX(fmt, ...) \
    do { \
         /* 假设最大长度为1024,根据需要调整大小 */ \
        wsprintfA(out, fmt, __VA_ARGS__); \
        MessageBoxA(NULL, out, "提示", MB_OK); \
    } while(0)
char out[100];
typedef BOOL(WINAPI* WriteProcessMemory_t)(
    _In_ HANDLE hProcess,
    _In_ LPVOID lpBaseAddress,
    _In_reads_bytes_(nSize) LPCVOID lpBuffer,
    _In_ SIZE_T nSize,
    _Out_opt_ SIZE_T* lpNumberOfBytesWritten
    );
WriteProcessMemory_t TrueWriteProcessMemory = NULL;
BOOL
WINAPI
HookWriteProcessMemory(
    _In_ HANDLE hProcess,
    _In_ LPVOID lpBaseAddress,
    _In_reads_bytes_(nSize) LPCVOID lpBuffer,
    _In_ SIZE_T nSize,
    _Out_opt_ SIZE_T* lpNumberOfBytesWritten
)
{
    if (nSize == 4506624 && *((PUCHAR)lpBuffer + 0x7171) == 0x3)
    {
        *((PUCHAR)lpBuffer + 0x7171) = 0x4;
        DBGMGEBOX("Hook Success!\n");
    }
    return TrueWriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten);
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH:

        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());

        TrueWriteProcessMemory = (WriteProcessMemory_t)DetourFindFunction("kernel32.dll", "WriteProcessMemory");
        DetourAttach(&(PVOID&)TrueWriteProcessMemory, HookWriteProcessMemory);

        DetourTransactionCommit();
        break;
    case DLL_PROCESS_DETACH:
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)TrueWriteProcessMemory, HookWriteProcessMemory);

        DetourTransactionCommit();
        break;
    }
    return TRUE;
}

成功输出token1文件:

然后是token2,既然是内核输出,那只能是在ace.sys里做点手脚,DIE查壳发现ace.sys的大部分代码都被加壳过了,静态代码不好看,只能先猜测token2的输出调用了DbgPrint或者DbgPrintEx,因为之前输出token2的时候开启了Verbose Kernel outPut,猜测之所以正常输出失败是因为DbgPrintEx的level值太低,仅将字符串传递给内核调试器,不执行输出操作。

hook DbgPrintEx函数看一眼传参。

#include <ntifs.h>
#include <ntdef.h>
#include <ntstatus.h>
#include <ntddk.h>
#include <stdarg.h>
#include "R0Hook.h"
#define dbgFilter "Kvancy:"
typedef ULONG(*FuncPtr) (ULONG ComponentId, ULONG Level, PCSTR Format, ...);
HOOK_MANAGER hookManager;
ULONG myDbgPrintEx(ULONG ComponentId, ULONG Level, PCSTR Format, ...) {
    Unhook(&hookManager);
    FuncPtr func = (FuncPtr)hookManager.target;
    kPrint("%s DbgPrintEx ComponentId:%lu,Level:%lu\n",dbgFilter, ComponentId, Level);
    va_list args;
    va_start(args, Format);
    NTSTATUS s = func(ComponentId, Level, Format, args);
    va_end(args);
    ApplyHook(&hookManager);
    return s;
}

void DriverUnload(PDRIVER_OBJECT pDriver) {
    kPrint("%s DriverUnload\n", dbgFilter);
    Unhook(&hookManager);
}

NTSTATUS DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath
) {
    DriverObject->DriverUnload = DriverUnload;
    PVOID dbgPrintEx = DbgPrintEx;
    InitializeHookManager(&hookManager, dbgPrintEx, myDbgPrintEx);
    ApplyHook(&hookManager);
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "%s DriverEntry\n",dbgFilter);
    return STATUS_SUCCESS;
}

发现加载ace驱动后,有大量的level:5的调试信息输出

也就是说,程序通过设置调试信息的重要级别来控制调试信息是否正常输出,于是可以提高level级别来输出token2,那么最简单的方式就是hook之后修改level后传回去,编写代码hook测试下。

#include <ntifs.h>
#include <ntdef.h>
#include <ntstatus.h>
#include <ntddk.h>
#include <stdarg.h>
#include <stdio.h>
#include "R0Hook.h"
#define dbgFilter "Kvancy:"
typedef ULONG(*FuncPtr) (ULONG ComponentId, ULONG Level, PCSTR Format, ...);
HOOK_MANAGER hookManager;
char buffer[1024]; 
ULONG myDbgPrintEx(ULONG ComponentId, ULONG Level, PCSTR Format, ...) {
    Unhook(&hookManager);
    FuncPtr func = (FuncPtr)hookManager.target;
    va_list args;
    va_start(args, Format);
    vsprintf(buffer, Format, args);
    va_end(args);
    NTSTATUS s = func(ComponentId, 0, "%s", buffer);//修改level为0
    ApplyHook(&hookManager);
    return s;
}

void DriverUnload(PDRIVER_OBJECT pDriver) {
    kPrint("Kvancy: DriverUnload\n");
    Unhook(&hookManager);
}

NTSTATUS DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath
) {
    DriverObject->DriverUnload = DriverUnload;
    PVOID dbgPrintEx = DbgPrintEx;
    InitializeHookManager(&hookManager, dbgPrintEx, myDbgPrintEx);
    ApplyHook(&hookManager);
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "Kvancy: DriverEntry\n");
    return STATUS_SUCCESS;
}

成功输出token2,但是根据题目要求是不能修改系统模块代码的,也就是说hook内核函数的方法不能过这道题,还是得想想别的方法。现在已知的ace.sys的行为就是驱动会在被加载之后做了某些操作会使得系统持续调用DbgPrintEx来输出token2,但是ace.sys其实做了某种操作后就被卸载掉了,如下图所示。

可以想到就是说驱动启动了一个线程或者进程,让该任务持续输出token2,创建完随后再卸载自己并且不停止这个线程或者进程。先枚举进程看看有没有奇怪的进程出现。

VOID WriteToFile(PUNICODE_STRING FilePath, PCHAR Data)
{
    OBJECT_ATTRIBUTES objAttr;
    IO_STATUS_BLOCK ioStatusBlock;
    HANDLE fileHandle;
    NTSTATUS status;
    UNICODE_STRING unicodeFilePath;

    RtlInitUnicodeString(&unicodeFilePath, FilePath->Buffer);

    InitializeObjectAttributes(&objAttr, &unicodeFilePath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);

    status = ZwCreateFile(
        &fileHandle,
        FILE_APPEND_DATA | SYNCHRONIZE,
        &objAttr,
        &ioStatusBlock,
        NULL,
        FILE_ATTRIBUTE_NORMAL,
        0,
        FILE_OPEN_IF,
        FILE_SYNCHRONOUS_IO_NONALERT,
        NULL,
        0
    );

    if (NT_SUCCESS(status)) {
        size_t dataLength = strlen(Data);
        ZwWriteFile(fileHandle, NULL, NULL, NULL, &ioStatusBlock, Data, (ULONG)dataLength, NULL, NULL);
        ZwClose(fileHandle);
    }
    else {
        DbgPrint("Failed to create file: %08X\n", status);
    }
}

VOID EnumProcesses()
{
    NTSTATUS status;
    PVOID buffer;
    ULONG bufferSize = 0x10000; // Initial buffer size, can grow if needed
    ULONG returnLength;
    CHAR logBuffer[1024];

    UNICODE_STRING filePath;
    RtlInitUnicodeString(&filePath, L"\\??\\C:\\Users\\15386\\Desktop\\1.txt");

    buffer = ExAllocatePoolWithTag(NonPagedPool, bufferSize, 'proc');
    if (!buffer) {
        DbgPrint("Failed to allocate buffer for process information\n");
        return;
    }

    status = ZwQuerySystemInformation(SystemProcessInformation, buffer, bufferSize, &returnLength);

    if (status == STATUS_INFO_LENGTH_MISMATCH) {
        ExFreePool(buffer);
        bufferSize = returnLength;
        buffer = ExAllocatePoolWithTag(NonPagedPool, bufferSize, 'proc');
        if (!buffer) {
            DbgPrint("Failed to allocate larger buffer for process information\n");
            return;
        }
        status = ZwQuerySystemInformation(SystemProcessInformation, buffer, bufferSize, &returnLength);
    }

    if (NT_SUCCESS(status)) {
        PSYSTEM_PROCESS_INFORMATION processInfo = (PSYSTEM_PROCESS_INFORMATION)buffer;
        while (TRUE) {
            if (processInfo->ImageName.Buffer) {
                _snprintf(logBuffer, sizeof(logBuffer), "Process ID: %lu, Name: %wZ\n", (ULONG)(ULONG_PTR)processInfo->ProcessId, &processInfo->ImageName);
            }
            else {
                _snprintf(logBuffer, sizeof(logBuffer), "Process ID: %lu, Name: [System Process]\n", (ULONG)(ULONG_PTR)processInfo->ProcessId);
            }

            WriteToFile(&filePath, logBuffer);

            if (processInfo->NextEntryOffset == 0)
                break;
            processInfo = (PSYSTEM_PROCESS_INFORMATION)((PUCHAR)processInfo + processInfo->NextEntryOffset);
        }
    }

    ExFreePool(buffer);
}

结果发现好像没有奇怪的进程被创建出来,那么有可能是驱动利用PsCreateSystemThread创建了一个内核线程。hookPsCreateSystemThread函数看看驱动加载时是否调用了这个函数。

#include <ntifs.h>
#include <ntdef.h>
#include <ntstatus.h>
#include <ntddk.h>
#include <stdarg.h>
#include "R0Hook.h"
#define dbgFilter "Kvancy:"
typedef ULONG(*FuncPtr) (
    PHANDLE            ThreadHandle,
    ULONG              DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    HANDLE             ProcessHandle,
    PCLIENT_ID         ClientId,
    PKSTART_ROUTINE    StartRoutine,
    PVOID              StartContext);
HOOK_MANAGER hookManager;

NTSTATUS myPsCreateSystemThread(
    PHANDLE            ThreadHandle,
    ULONG              DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    HANDLE             ProcessHandle,
    PCLIENT_ID         ClientId,
    PKSTART_ROUTINE    StartRoutine,
    PVOID              StartContext
)
{
    Unhook(&hookManager);
    FuncPtr func = (FuncPtr)hookManager.target;
    kPrint("%s myPsCreateSystemThread StartRoutine:%p\n", dbgFilter, StartRoutine);
    NTSTATUS s = func(ThreadHandle, DesiredAccess, ObjectAttributes, ProcessHandle, ClientId, StartRoutine, StartContext);
    ApplyHook(&hookManager);
    return s;
}

void DriverUnload(PDRIVER_OBJECT pDriver) {
    kPrint("%s DriverUnload\n", dbgFilter);
    Unhook(&hookManager);
}

NTSTATUS DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath
) {
    DriverObject->DriverUnload = DriverUnload;
    PVOID dbgPrintEx = PsCreateSystemThread;
    InitializeHookManager(&hookManager, dbgPrintEx, myPsCreateSystemThread);
    ApplyHook(&hookManager);
    DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "%s DriverEntry\n", dbgFilter);
    return STATUS_SUCCESS;
}

发现在token2输出前确实有PsCreateSystemThread函数调用,虽然不确定是不是ace.sys创建的。在windbg里反汇编看看线程函数

0: kd> u FFFFBA0729013DB0 l 100
ffffba07`29013db0 488bc4          mov     rax,rsp
ffffba07`29013db3 48895808        mov     qword ptr [rax+8],rbx
ffffba07`29013db7 48897818        mov     qword ptr [rax+18h],rdi
ffffba07`29013dbb 4c897020        mov     qword ptr [rax+20h],r14
ffffba07`29013dbf 55              push    rbp
ffffba07`29013dc0 488d68a1        lea     rbp,[rax-5Fh]
ffffba07`29013dc4 4881eca0000000  sub     rsp,0A0h
ffffba07`29013dcb 48bf4e93328b546b331e mov rdi,1E336B548B32934Eh
ffffba07`29013dd5 49bed520794add1d6d4b mov r14,4B6D1DDD4A7920D5h
ffffba07`29013ddf 0f57c0          xorps   xmm0,xmm0
ffffba07`29013de2 488d4d37        lea     rcx,[rbp+37h]
ffffba07`29013de6 0f114537        movups  xmmword ptr [rbp+37h],xmm0
ffffba07`29013dea e8d1030000      call    ffffba07`290141c0
ffffba07`29013def 48b8a14f122fb3276d4b mov rax,4B6D27B32F124FA1h
ffffba07`29013df9 4c8d45e7        lea     r8,[rbp-19h]
ffffba07`29013dfd 4889456f        mov     qword ptr [rbp+6Fh],rax
ffffba07`29013e01 ba05000000      mov     edx,5
ffffba07`29013e06 488b456f        mov     rax,qword ptr [rbp+6Fh]
ffffba07`29013e0a 33c9            xor     ecx,ecx
ffffba07`29013e0c 488945e7        mov     qword ptr [rbp-19h],rax
ffffba07`29013e10 48897d6f        mov     qword ptr [rbp+6Fh],rdi
ffffba07`29013e14 488b456f        mov     rax,qword ptr [rbp+6Fh]
ffffba07`29013e18 488945ef        mov     qword ptr [rbp-11h],rax
ffffba07`29013e1c 4c89756f        mov     qword ptr [rbp+6Fh],r14
ffffba07`29013e20 488b456f        mov     rax,qword ptr [rbp+6Fh]
ffffba07`29013e24 48894517        mov     qword ptr [rbp+17h],rax
ffffba07`29013e28 48897d6f        mov     qword ptr [rbp+6Fh],rdi
ffffba07`29013e2c 488b456f        mov     rax,qword ptr [rbp+6Fh]
ffffba07`29013e30 660f6f45e7      movdqa  xmm0,xmmword ptr [rbp-19h]
ffffba07`29013e35 4889451f        mov     qword ptr [rbp+1Fh],rax
ffffba07`29013e39 660fef4517      pxor    xmm0,xmmword ptr [rbp+17h]
ffffba07`29013e3e 488b0543330000  mov     rax,qword ptr [ffffba07`29017188]
ffffba07`29013e45 660f7f45e7      movdqa  xmmword ptr [rbp-19h],xmm0
ffffba07`29013e4a ff15c8210000    call    qword ptr [ffffba07`29016018]
ffffba07`29013e50 33db            xor     ebx,ebx
ffffba07`29013e52 48b8f0104b32dd1d6d4b mov rax,4B6D1DDD324B10F0h
ffffba07`29013e5c 4c8d45f7        lea     r8,[rbp-9]
ffffba07`29013e60 4889456f        mov     qword ptr [rbp+6Fh],rax
ffffba07`29013e64 ba05000000      mov     edx,5
ffffba07`29013e69 488b456f        mov     rax,qword ptr [rbp+6Fh]
ffffba07`29013e6d 33c9            xor     ecx,ecx
ffffba07`29013e6f 488945f7        mov     qword ptr [rbp-9],rax
ffffba07`29013e73 48897d6f        mov     qword ptr [rbp+6Fh],rdi
ffffba07`29013e77 488b456f        mov     rax,qword ptr [rbp+6Fh]
ffffba07`29013e7b 488945ff        mov     qword ptr [rbp-1],rax
ffffba07`29013e7f 4c89756f        mov     qword ptr [rbp+6Fh],r14
ffffba07`29013e83 488b456f        mov     rax,qword ptr [rbp+6Fh]
ffffba07`29013e87 48894527        mov     qword ptr [rbp+27h],rax
ffffba07`29013e8b 48897d6f        mov     qword ptr [rbp+6Fh],rdi
ffffba07`29013e8f 488b456f        mov     rax,qword ptr [rbp+6Fh]
ffffba07`29013e93 660f6f45f7      movdqa  xmm0,xmmword ptr [rbp-9]
ffffba07`29013e98 440fb64c1d37    movzx   r9d,byte ptr [rbp+rbx+37h]
ffffba07`29013e9e 4889452f        mov     qword ptr [rbp+2Fh],rax
ffffba07`29013ea2 660fef4527      pxor    xmm0,xmmword ptr [rbp+27h]
ffffba07`29013ea7 488b05da320000  mov     rax,qword ptr [ffffba07`29017188]
ffffba07`29013eae 660f7f45f7      movdqa  xmmword ptr [rbp-9],xmm0
ffffba07`29013eb3 ff155f210000    call    qword ptr [ffffba07`29016018]
ffffba07`29013eb9 48ffc3          inc     rbx
ffffba07`29013ebc 4883fb10        cmp     rbx,10h
ffffba07`29013ec0 7c90            jl      ffffba07`29013e52
ffffba07`29013ec2 48b8df20794add1d6d4b mov rax,4B6D1DDD4A7920DFh
ffffba07`29013ecc 4c8d4507        lea     r8,[rbp+7]
ffffba07`29013ed0 4889456f        mov     qword ptr [rbp+6Fh],rax
ffffba07`29013ed4 ba05000000      mov     edx,5
ffffba07`29013ed9 488b456f        mov     rax,qword ptr [rbp+6Fh]
ffffba07`29013edd 33c9            xor     ecx,ecx
ffffba07`29013edf 48894507        mov     qword ptr [rbp+7],rax
ffffba07`29013ee3 48897d6f        mov     qword ptr [rbp+6Fh],rdi
ffffba07`29013ee7 488b456f        mov     rax,qword ptr [rbp+6Fh]
ffffba07`29013eeb 4889450f        mov     qword ptr [rbp+0Fh],rax
ffffba07`29013eef 4c89756f        mov     qword ptr [rbp+6Fh],r14
ffffba07`29013ef3 488b456f        mov     rax,qword ptr [rbp+6Fh]
ffffba07`29013ef7 48894547        mov     qword ptr [rbp+47h],rax
ffffba07`29013efb 48897d6f        mov     qword ptr [rbp+6Fh],rdi
ffffba07`29013eff 488b456f        mov     rax,qword ptr [rbp+6Fh]
ffffba07`29013f03 660f6f4507      movdqa  xmm0,xmmword ptr [rbp+7]
ffffba07`29013f08 4889454f        mov     qword ptr [rbp+4Fh],rax
ffffba07`29013f0c 660fef4547      pxor    xmm0,xmmword ptr [rbp+47h]
ffffba07`29013f11 488b0570320000  mov     rax,qword ptr [ffffba07`29017188]
ffffba07`29013f18 660f7f4507      movdqa  xmmword ptr [rbp+7],xmm0
ffffba07`29013f1d ff15f5200000    call    qword ptr [ffffba07`29016018]
ffffba07`29013f23 b9ce0a0000      mov     ecx,0ACEh
ffffba07`29013f28 e833e9ffff      call    ffffba07`29012860
ffffba07`29013f2d e9adfeffff      jmp     ffffba07`29013ddf
ffffba07`29013f32 cc              int     3
ffffba07`29013f33 cc              int     3
ffffba07`29013f34 4152            push    r10

导入到ida里看伪代码

void __noreturn sub_180005148()
{
  __m128i v0; // xmm0
  __int64 i; // rbx
  __m128i v2; // xmm0
  __int64 v3; // r9
  __m128i v4; // xmm0
  __m128i v5; // [rsp+30h] [rbp-19h] BYREF
  __m128i v6; // [rsp+40h] [rbp-9h] BYREF
  __m128i v7; // [rsp+50h] [rbp+7h] BYREF
  __m128i v8; // [rsp+60h] [rbp+17h]
  __m128i v9; // [rsp+70h] [rbp+27h]
  __int128 v10; // [rsp+80h] [rbp+37h] BYREF
  __m128i v11; // [rsp+90h] [rbp+47h]

  while ( 1 )
  {
    v10 = 0i64;
    ((void (__fastcall *)(__int128 *))((char *)&loc_180005556 + 2))(&v10);
    v5.m128i_i64[0] = 0x4B6D27B32F124FA1i64;
    v5.m128i_i64[1] = 0x1E336B548B32934Ei64;
    v8.m128i_i64[0] = 0x4B6D1DDD4A7920D5i64;
    v0 = _mm_load_si128(&v5);
    v8.m128i_i64[1] = 0x1E336B548B32934Ei64;
    v5 = _mm_xor_si128(v0, v8);
    MEMORY[0x31305A8047353130](0i64, 5i64, &v5);
    for ( i = 0i64; i < 16; ++i )
    {
      v6.m128i_i64[0] = 0x4B6D1DDD324B10F0i64;
      v6.m128i_i64[1] = 0x1E336B548B32934Ei64;
      v9.m128i_i64[0] = 0x4B6D1DDD4A7920D5i64;
      v2 = _mm_load_si128(&v6);
      v3 = *((unsigned __int8 *)&v10 + i);
      v9.m128i_i64[1] = 0x1E336B548B32934Ei64;
      v6 = _mm_xor_si128(v2, v9);
      MEMORY[0x31305A8047353130](0i64, 5i64, &v6, v3);
    }
    v7.m128i_i64[0] = 0x4B6D1DDD4A7920DFi64;
    v7.m128i_i64[1] = 0x1E336B548B32934Ei64;
    v11.m128i_i64[0] = 0x4B6D1DDD4A7920D5i64;
    v4 = _mm_load_si128(&v7);
    v11.m128i_i64[1] = 0x1E336B548B32934Ei64;
    v7 = _mm_xor_si128(v4, v11);
    MEMORY[0x31305A8047353130](0i64, 5i64, &v7);
    sub_180003BF8(2766i64);
  }
}

好像是做了一个字符串解密然后输出的操作,浅浅用python跑一下解析字符串验证猜想。

def hex_xor_to_string(a, b):
    result = a ^ b
    hex_str = hex(result)[2:]
    if len(hex_str) % 2 != 0:
        hex_str = '0' + hex_str

    result_str = ''.join(chr(int(hex_str[i:i+2], 16)) for i in range(0, len(hex_str), 2))
    return result_str

x1 = 0x4B6D27B32F124FA1
x2 = 0x1E336B548B32934E
y1 = 0x4B6D1DDD4A7920D5
y2 = 0x1E336B548B32934E

result1 = hex_xor_to_string(x1, y1)
result2 = hex_xor_to_string(x2, y2)

print("Result 1:", result1)
print("Result 2:", result2)

res = result1[::-1] + result2[::-1]
print(res)

打印出了token基本上确定了这个线程就是打印token的线程,现在就是要想怎么patch这个线程函数使得token能够输出出来。

这里有个mov edx,5语句,将DbgPrintEx函数的level设置成5,可以考虑patch这个语句,将5改成0,那么只需要patch一个字节,共三处。但是又要怎么patch呢,首先不能通过现在这种方式hook PsCreateSystemThread函数调用来确定StartRoutine地址(题目要求不能修改系统模块代码),也就是说得想另外一个办法确定这个线程的地址,然后通过偏移来确定需要patch的地址。

那么怎么确定这个线程地址呢,如果通过ZwQuerySystemInformation枚举内核模块然后枚举模块下的所有线程的话,已经卸载了的ace.sys模块还能被枚举到么?问了下GPT好像是不能的,还可以考虑用StartRoutine地址的后几位做特征,匹配所有线程的开始地址的后几位,但是这种方式又感觉怕遇到地址特征一模一样的,感觉还是不大行。又问GPT怎么寻找到某个内核线程,得到答复是除了ZwQuerySystemInformation枚举,还有通过PsLookupThreadByThreadId函数从进程id和线程id查找的。

那么线程id和进程id又从哪获取呢?因为之前hook过PsCreateSystemThread函数,翻阅文档找到了一个ClientId参数,这个参数指向接收新线程的客户端标识符的结构,即一个pid,一个tid,但是pid,tid应该都是系统分配的吧,能是一个固定值么?hook一下看看输出


诶,tid貌似是系统分配的,但是pid一直都是4,很奇怪,pid=4代表的是什么进程呢?之前刚好枚举过进程来找有没有新进程创建,现在正好能派上用场。

貌似是一个系统进程,GPT了一下发现原来如果驱动程序通过内核模式创建系统线程(使用PsCreateSystemThread),这些线程通常会在系统进程下运行,PID为4。原来如此,驱动程序和进程是一个级别的,但是驱动程序创建的这个线程是在系统进程之下的,而不是属于驱动模块,只是线程起始地址隶属于模块地址空间的,驱动卸载并不影响线程的运行。

这样的话,我们要找的线程因为模块被卸载了,所以它不在所有模块地址空间内,只要枚举所有系统进程pid=4下的所有线程,然后通过判断线程的起始地址是否在所有模块地址之内,即可判断它是否是我们要找的线程。这下思路就通了,开始编写代码实现patch。

#include "header.h"

PVOID MoudleBaseAddress[1024];
ULONG64 MoudleSize[1024];
ULONG ModuleCount = 0;
ULONG Offset1 = 0x52, Offset2 = 0xB5, Offset3 = 0x125;

BOOLEAN IsAddressInKnownModules(PVOID Address, PVOID* ModuleBaseAddresses, ULONG64* ModuleSize, ULONG ModuleCount)
{
    for (size_t i = 0; i < ModuleCount; i++)
    {
        if (Address >= ModuleBaseAddresses[i] && Address < (ULONG64)ModuleBaseAddresses[i] + ModuleSize[i])
        {
            return TRUE;
        }
    }
    return FALSE;
}

PVOID EnumSystemModulesForProcess(HANDLE TargetProcessId)
{
    NTSTATUS status;
    ULONG bufferSize = 0x10000;
    PVOID processBuffer = NULL;
    PVOID moduleBuffer = NULL;
    ULONG returnLength;

    // 查询进程信息
    processBuffer = ExAllocatePoolWithTag(NonPagedPool, bufferSize, 'proc');
    if (!processBuffer) {
        DbgPrint("Failed to allocate buffer for process information\n");
        return;
    }

    status = ZwQuerySystemInformation(SystemProcessInformation, processBuffer, bufferSize, &returnLength);
    if (status == STATUS_INFO_LENGTH_MISMATCH) {
        ExFreePool(processBuffer);
        bufferSize = returnLength;
        processBuffer = ExAllocatePoolWithTag(NonPagedPool, bufferSize, 'proc');
        if (!processBuffer) {
            DbgPrint("Failed to allocate larger buffer for process information\n");
            return;
        }
        status = ZwQuerySystemInformation(SystemProcessInformation, processBuffer, bufferSize, &returnLength);
    }

    // 查询模块信息
    moduleBuffer = ExAllocatePoolWithTag(NonPagedPool, bufferSize, 'modl');
    if (!moduleBuffer) {
        DbgPrint("Failed to allocate buffer for module information\n");
        ExFreePool(processBuffer);
        return;
    }

    status = ZwQuerySystemInformation(SystemModuleInformation, moduleBuffer, bufferSize, &returnLength);
    if (status == STATUS_INFO_LENGTH_MISMATCH) {
        ExFreePool(moduleBuffer);
        bufferSize = returnLength;
        moduleBuffer = ExAllocatePoolWithTag(NonPagedPool, bufferSize, 'modl');
        if (!moduleBuffer) {
            DbgPrint("Failed to allocate larger buffer for module information\n");
            ExFreePool(processBuffer);
            return;
        }
        status = ZwQuerySystemInformation(SystemModuleInformation, moduleBuffer, bufferSize, &returnLength);
    }

    // 遍历模块信息
    if (NT_SUCCESS(status)) {
        PSYSTEM_MODULE_INFORMATION moduleInfo = (PSYSTEM_MODULE_INFORMATION)moduleBuffer;
        ModuleCount = moduleInfo->ModulesCount;
        for (ULONG i = 0; i < moduleInfo->ModulesCount; i++) {
            PSYSTEM_MODULE_INFORMATION_ENTRY moduleEntry = &moduleInfo->Modules[i];
            MoudleBaseAddress[i] = moduleEntry->Base;
            MoudleSize[i] = moduleEntry->Size;
        }
    }

    // 遍历进程信息
    if (NT_SUCCESS(status)) {
        PSYSTEM_PROCESS_INFORMATION processInfo = (PSYSTEM_PROCESS_INFORMATION)processBuffer;
        while (TRUE) {
            if (processInfo->ProcessId == TargetProcessId) {
                PSYSTEM_THREAD_INFORMATION threadInfo = (PSYSTEM_THREAD_INFORMATION)(processInfo + 1);
                for (ULONG i = 0; i < processInfo->NumberOfThreads; i++) {
                    if (!IsAddressInKnownModules(threadInfo[i].StartAddress, MoudleBaseAddress, MoudleSize, ModuleCount))
                    {
                        DbgPrint("Find it:%p\n",threadInfo[i].StartAddress);
                        return threadInfo[i].StartAddress;
                    }
                }
                break;
            }
            if (processInfo->NextEntryOffset == 0)
                break;
            processInfo = (PSYSTEM_PROCESS_INFORMATION)((PUCHAR)processInfo + processInfo->NextEntryOffset);
        }
    }

    // 清理分配的内存
    ExFreePool(processBuffer);
    ExFreePool(moduleBuffer);
    return 0;
}

VOID UnloadDriver(PDRIVER_OBJECT DriverObject) {
    KdPrint(("Driver Unloaded\n"));
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
    DriverObject->DriverUnload = UnloadDriver;
    KdPrint(("Driver Loaded\n"));

    HANDLE targetPid = (HANDLE)4; // 系统进程的 PID
    PVOID targetAddress = EnumSystemModulesForProcess(targetPid);

    UCHAR valueToWrite = 0x00; // 要写入的字节值
    UCHAR valueToRead = 0x05; // 要写入的字节值
    ULONG numHasChanged = 0x00;
    if (*(PUCHAR)((ULONG64)targetAddress + Offset1) == valueToRead)
    {
        *(PUCHAR)((ULONG64)targetAddress + Offset1) = valueToWrite;
        numHasChanged++;
    }
    if (*(PUCHAR)((ULONG64)targetAddress + Offset2) == valueToRead)
    {
        *(PUCHAR)((ULONG64)targetAddress + Offset2) = valueToWrite;
        numHasChanged++;
    }
    if (*(PUCHAR)((ULONG64)targetAddress + Offset3) == valueToRead)
    {
        *(PUCHAR)((ULONG64)targetAddress + Offset3) = valueToWrite;
        numHasChanged++;
    }
    DbgPrint("numHasChanged:%d\n", numHasChanged);
    return STATUS_SUCCESS;
}

成功输出token2

(三)解题过程

题目要求:

编写程序,运行时修改尽量少的内存,让shellcode 往自行指定的位置写入token1成功。(满分3分)

要求任意位置,也就是要修改CreateFileA函数的第一个参数的值,根据之前分析的C:\2024GameSafeRace.token1字符串是由十六进制异或得到的,也就是下面这些

可以考虑的是patch这些十六进制数据,把异或的key改成0,然后密文改成明文即可,因为明文异或0还是明文,但是考虑到要尽量修改少量的内存,我们最好还是保持key不变,自定义密文解密到我们所要的文件地址。跑个python脚本解出新的密文,得到新的密文,接着找到密文所在文件的偏移然后patch即可,给出解题代码。

//dllmain.cpp
#include "pch.h"
#include <windows.h>
#include <shellapi.h>
#include <detours.h>
#include <tlhelp32.h>
#include <stdlib.h>
#include <stdio.h>
#pragma comment(lib,"detours.lib")
#define _KDEBUG
#define DBGMGEBOX(fmt, ...) \
    do { \
         /* 假设最大长度为1024,根据需要调整大小 */ \
        wsprintfA(out, fmt, __VA_ARGS__); \
        MessageBoxA(NULL, out, "提示", MB_OK); \
    } while(0)
char out[100];
typedef BOOL(WINAPI* WriteProcessMemory_t)(
    _In_ HANDLE hProcess,
    _In_ LPVOID lpBaseAddress,
    _In_reads_bytes_(nSize) LPCVOID lpBuffer,
    _In_ SIZE_T nSize,
    _Out_opt_ SIZE_T* lpNumberOfBytesWritten
    );
WriteProcessMemory_t TrueWriteProcessMemory = NULL;
BOOL
WINAPI
HookWriteProcessMemory(
    _In_ HANDLE hProcess,
    _In_ LPVOID lpBaseAddress,
    _In_reads_bytes_(nSize) LPCVOID lpBuffer,
    _In_ SIZE_T nSize,
    _Out_opt_ SIZE_T* lpNumberOfBytesWritten
)
{
    if (nSize == 4506624 && *((PUCHAR)lpBuffer + 0x7171) == 0x3)
    {
        *((PUCHAR)lpBuffer + 0x7171) = 0x4;
        *(PULONG64)((ULONG64)lpBuffer + 0x7082) = 0x94e7c2136acc0e5c;//
        *(PULONG64)((ULONG64)lpBuffer + 0x7093) = 0x8207c5d1af9f3860;
        *(PULONG64)((ULONG64)lpBuffer + 0x70F1) = 0xa905cca9edcb138c;
        *(PULONG64)((ULONG64)lpBuffer + 0x7108) = 0xa753b8236c56809a;
        //C:\Users\15386\Desktop\flag.txt
        DBGMGEBOX("Hook Success!\n");
    }
    return TrueWriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten);
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH:

        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());

        TrueWriteProcessMemory = (WriteProcessMemory_t)DetourFindFunction("kernel32.dll", "WriteProcessMemory");
        DetourAttach(&(PVOID&)TrueWriteProcessMemory, HookWriteProcessMemory);

        DetourTransactionCommit();
        break;
    case DLL_PROCESS_DETACH:
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)TrueWriteProcessMemory, HookWriteProcessMemory);

        DetourTransactionCommit();
        break;
    }
    return TRUE;
}

注入hook成功后,成功在桌面的flag.txt输出token1

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
liangqz 发表于 2024-9-23 16:39
就喜欢这种喜欢看,又看不懂的感觉
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-9-23 21:02

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表