好友
阅读权限10
听众
最后登录1970-1-1
|
本帖最后由 0xLuna 于 2026-3-19 22:40 编辑
前言
研究仅供学习交流目的,请勿用于任何违法用途
听说ARK上架了PC端,作为一个资深二游玩家,决定一探究竟
基本分析
下载游戏,第一件事就是查看游戏采用的保护方案。看文件夹名称就能略知一二了
ida64打开 GameAssembly.dll,发现text 段和 il2cpp 段均未加密。这两个段通常都会被加密运行时依赖 XXShell (xxxBase.dll ) 进行解密。看来可以尝试一些有趣的操作。参考链接:某PC沙盒游戏残血ACE反作弊ring3下的绕过分析
先看下这个模块 Arknights.exe,显然是调用了ArknightsBase的导出函数
我们找一个未接入axe的游戏,看一下是什么样子,1400A6B22调用了 UnityPlayer.dll 的导出函数 UnityMain这就对了,那么可以做出假设,把call指令的地址 ArknightsBase_1修改成UnityMain的地址这样axe就不工作了!
代码实现
dll注入使用Manual Map的注入方式 Create进程挂起,再将dll注入,然后恢复被挂起的线程dll代码,先写代码实验
main.h
[C++] 纯文本查看 复制代码 #pragma once
#include <Windows.h>
#include <stdio.h>
HMODULE UnityPlayer = LoadLibraryW(L"UnityPlayer.dll");
PBYTE Arknights = (PBYTE)GetModuleHandleW(NULL);
int (*UnityMain)(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd);
/*
Arknights.exe+A6B22 - FF15 02000000 EB08 0000000000000000 | call HookHandler
*/
BYTE HookShellCode[] = { 0xFF, 0x15, 0x02, 0x00, 0x00, 0x00, 0xEB, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
main.cpp[C++] 纯文本查看 复制代码 #include "main.h"
/*
优化控制 保证堆栈平衡
*/
#pragma optimize("t", on)
void __fastcall HookHandler(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
UnityMain(hInstance, hPrevInstance, lpCmdLine, nShowCmd);
}
#pragma optimize("", on)
void InstallHook(PVOID lpTargetAddress)
{
DWORD dwOldProtect = 0;
VirtualProtect(lpTargetAddress, sizeof(HookShellCode), PAGE_EXECUTE_READWRITE, &dwOldProtect);
*(DWORD64*)&HookShellCode[8] = (DWORD64)HookHandler;
RtlCopyMemory(lpTargetAddress, HookShellCode, sizeof(HookShellCode));
VirtualProtect(lpTargetAddress, sizeof(HookShellCode), dwOldProtect, &dwOldProtect);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
if (ul_reason_for_call != DLL_PROCESS_ATTACH)
return FALSE;
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)Arknights;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(Arknights + pDosHeader->e_lfanew);
if (pNtHeader->Signature == IMAGE_NT_SIGNATURE)
{
IMAGE_OPTIONAL_HEADER OptionalHeader = pNtHeader->OptionalHeader;
DWORD dwEntryPoint = OptionalHeader.AddressOfEntryPoint;
void (*Start)() = ((void(*)())((Arknights + dwEntryPoint)));
UnityMain = (int(*)(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd))(GetProcAddress(UnityPlayer, "UnityMain"));
InstallHook(PVOID((DWORD64)Start + 4));
}
return TRUE;
}
注入测试
手动Create进程,注入后发现游戏窗口被隐藏了,任务管理器查看游戏进程正在运行,看样子是有问题的。问题的答案显然是调用UnityMain未传入所需的参数调用UnityMain的时候,寄存器保存的是一些乱七八糟的值
找一个未接入axe的unity游戏,分析一下nShowCmd是哪来的
根据伪代码实现即可
改进代码
main.h
[C++] 纯文本查看 复制代码 #pragma once
#include <Windows.h>
#include <stdio.h>
HMODULE UnityPlayer = LoadLibraryW(L"UnityPlayer.dll");
PBYTE Arknights = (PBYTE)GetModuleHandleW(NULL);
WORD wShowWindow;
int (*UnityMain)(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd);
/*
Arknights.exe+A6B22 - FF15 02000000 EB08 0000000000000000 | call HookHandler
*/
BYTE HookShellCode[] = { 0xFF, 0x15, 0x02, 0x00, 0x00, 0x00, 0xEB, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
main.cpp[C++] 纯文本查看 复制代码 #include "main.h"
WORD GetShowWindowMode()
{
WORD result = 10;
STARTUPINFOW StartupInfo = { sizeof(StartupInfo) };
GetStartupInfoW(&StartupInfo);
if ((StartupInfo.dwFlags & 1) != 0)
return StartupInfo.wShowWindow;
return result;
}
/*
优化控制 保证堆栈平衡
*/
#pragma optimize("t", on)
void __fastcall HookHandler(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
nShowCmd = wShowWindow;
UnityMain(hInstance, hPrevInstance, lpCmdLine, nShowCmd);
}
#pragma optimize("", on)
void InstallHook(PVOID lpTargetAddress)
{
DWORD dwOldProtect = 0;
VirtualProtect(lpTargetAddress, sizeof(HookShellCode), PAGE_EXECUTE_READWRITE, &dwOldProtect);
*(DWORD64*)&HookShellCode[8] = (DWORD64)HookHandler;
RtlCopyMemory(lpTargetAddress, HookShellCode, sizeof(HookShellCode));
VirtualProtect(lpTargetAddress, sizeof(HookShellCode), dwOldProtect, &dwOldProtect);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
if (ul_reason_for_call != DLL_PROCESS_ATTACH)
return FALSE;
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)Arknights;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(Arknights + pDosHeader->e_lfanew);
if (pNtHeader->Signature == IMAGE_NT_SIGNATURE)
{
IMAGE_OPTIONAL_HEADER OptionalHeader = pNtHeader->OptionalHeader;
DWORD dwEntryPoint = OptionalHeader.AddressOfEntryPoint;
void (*Start)() = ((void(*)())((Arknights + dwEntryPoint)));
UnityMain = (int(*)(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd))(GetProcAddress(UnityPlayer, "UnityMain"));
wShowWindow = GetShowWindowMode();
InstallHook(PVOID((DWORD64)Start + 4));
}
return TRUE;
}
注入测试,这时候axe已经不工作了,用x64dbg附加没有问题
结论
加强安全组件运行时依赖可以提高剥离难度,希望有关游戏厂商能尽快修复问题 |
免费评分
-
查看全部评分
|