五步十阁 - 自动化脚本系列
type: channel
link: http://8.129.33.146/index.php/category/魔兽
items: wow某个内存数据的查找及使用
items: 某游戏自动化脚本系列 （八）可能是终章了
items: 某游戏自动化脚本系列 （七）基础功能封装到lua--2
items: 某游戏自动化脚本系列 （七）基础功能封装到lua--1
items: 某游戏自动化脚本系列 （六）再续--封装函数实现游戏lua_pcall调用和返回值获取
items: 某游戏自动化脚本系列 （六）续
items: 某游戏自动化脚本系列 （番外篇）聊聊数据分析
items: 某游戏自动化脚本系列 （六）基本功能测试
items: 某游戏自动化脚本系列 （五）注入带MFC窗口的Dll
items: 某游戏自动化脚本系列（四）第二期 PE节保护

wow某个内存数据的查找及使用
type: item
date: 2022-12-11T19:42:00+08:00
description: 因某个顾客老是说，需要判断是否已经登陆进游戏了，所以，就有了此文。关键信息查找因为是进行状态判断，其关键字应该和“ingame”之类的相关，通过外网一番翻找，发现如下字眼“it uses flags...check ths isplayerinworld lua function.”数据定位使用工具将游戏内存转储、PE修补后，将unpack后的文件拖入ida，静候30分钟自动分析完成后，在字符串界面搜索“isplayerinworld”，定位到如下信息：.rdata:00000001426E6DD8 ; DATA XREF: .data:0000000142C3F7E0↓o .rdata:00000001426E6DEB align 10h .rdata:00000001426E6DF0 aIsplayerinworl db 'IsPlayerInWorld',0 ; DATA XREF: .data:0000000142C3F7F0↓o <-- 点击此处引用跳转 .rdata:00000001426E6E00 aGetdungeoninfo db 'GetDungeonInfo',0 ; DATA XREF: .data:0000000142C3F800↓o .rdata:00000001426E6E0F align 10h .rdata:00000001426E6E10 aReportbug db 'ReportBug',0 ; DATA XREF: .data:0000000142C3F810↓o来到如下地方：.data:0000000142C3F7E0 dq offset aIslegacydiffic ; "IsLegacyDifficulty" .data:0000000142C3F7E8 dq offset sub_1415D6010 .data:0000000142C3F7F0 dq offset aIsplayerinworl ; "IsPlayerInWorld" .data:0000000142C3F7F8 dq offset sub_1415CE330 <-----该地址即为我们需要找的对应c lua函数 .data:0000000142C3F800 dq offset aGetdungeoninfo ; "GetDungeonInfo" .data:0000000142C3F808 dq offset sub_1415D54E0 .data:0000000142C3F810 dq offset aReportbug ; "ReportBug" .data:0000000142C3F818 dq offset sub_1415D60B0 .data:0000000142C3F820 dq offset aReportsuggesti ; "ReportSuggestion" .data:0000000142C3F828 dq offset sub_1415D6100跳转到函数 1415CE330：.text:00000001415CE330 sub_1415CE330 proc near ; DATA XREF: .data:0000000142C3F7F8↓o .text:00000001415CE330 ; .pdata:000000014334A7C8↓o .text:00000001415CE330 sub rsp, 28h //内部栈空间开辟 .text:00000001415CE334 movzx edx, cs:byte_1430A3134 //获取地址1430A3134处的值，类型为byte 也就是 byte status = *(byte*)0x1430A3134 .text:00000001415CE33B shr edx, 2 //status >>2 .text:00000001415CE33E and edx, 1 //status&1 <-----此处结果就是我们需要的值 .text:00000001415CE341 call sub_1402BBAB0 //lua 压栈函数，不管 .text:00000001415CE346 mov eax, 1 .text:00000001415CE34B add rsp, 28h .text:00000001415CE34F retn .text:00000001415CE34F sub_1415CE330 endp代码实现于是，有了如下代码：/// /// 角色是否已进入游戏 /// /// 进程句柄 /// 主模块基址 /// <returns>bool</returns> bool PlayerOnLine(HANDLE hand…

某游戏自动化脚本系列 （八）可能是终章了
type: item
date: 2021-09-16T01:13:00+08:00
description: 随着某雪在应用层（R3层）增加的花指令、反调试等乱七八糟手段越来越多，该游戏数据分析工作量也越来越大，到目前我一个人利用业余时间已经完全无法搞定了。目前遇到了几个坑。一.遍地花指令比如下面代码中的 0x81、0x9A、0x55、0x78：.text:00000001408BA4E6 call loc_1408C9C8F .text:00000001408BA4EB sub ecx, 9CF78B84h .text:00000001408BA4F1 add ebp, 0FFFFFF83h .text:00000001408BA4F4 add dh, 5Bh ; '[' .text:00000001408BA4F4 ; --------------------------------------------------------------------------- .text:00000001408BA4F7 db 81h .text:00000001408BA4F8 db 9Ah .text:00000001408BA4F9 db 55h ; U .text:00000001408BA4FA db 78h ; x .text:00000001408BA4FB ; --------------------------------------------------------------------------- .text:00000001408BA4FB .text:00000001408BA4FB loc_1408BA4FB: ; CODE XREF: function_FrameScript_ExecuteBuffer+F4↑j .text:00000001408BA4FB mov eax, cs:lua_taintexpected .text:00000001408BA501 test eax, eax以及形形色色的乱序跳转、jnb+jb、jnz+jz等跳转指令与jmp的等价替换....text:00000001408B7774 nop dword ptr [rax+00h] .text:00000001408B7778 nop dword ptr [rax+rax+00000000h] .text:00000001408B7780 jnz short near ptr loc_1408B77B0+5 .text:00000001408B7782 mov al, al .text:00000001408B7784 jz short near ptr loc_1408B77B0+5 .text:00000001408B7786 neg ch .text:00000001408B7788 add edi, 92885BD0h .text:00000001408B778E pop rbx .text:00000001408B778F add dl, 0BAh .text:00000001408B7792 js near ptr loc_1408BB673+2 .text:00000001408B7798 add ebp, 9029D1EFh .text:00000001408B779E add al, 11h .text:00000001408B77A1 sub al, 76h ; 'v' .text:00000001408B77A4 add ebp, 61h ; 'a' .text:00000001408B77A7 mov edi, 801CAECh .text:00000001408B77AD mov dh, 18h .text:00000001408B77B0 .text:00000001408B77B0 loc_1408B77B0: ; CODE XREF: sub_1408B7720+60↑j .text:00000001408B77B0 ; sub_1408B7720+64↑j .text:00000001408B77B0 imul ebp, [rsi-7A4BFFEDh], -37h .text:00000001408B77B7 jz loc_1408B7DBB .text:00000001408B77BD xor r8d, r8d .text:00000001408B77C0 mov rcx, r14 .text:00000001408B77C3 lea edx, [r8-1]在这种情况下，纯手工还原的工作量太大了。二、结构体错乱UI控件父子结构关系故意紊乱化。完成lua_state的global_table逆向分析，结果发现uiobject分散分布于lua-arrayObject和node节点中，各种崩溃…

某游戏自动化脚本系列 （七）基础功能封装到lua--2
type: item
date: 2021-08-10T22:50:00+08:00
description: 采用第三方库BlackBone实现主线程调用callBlackbone是windows操作系统下一款内存hack库，具体功能介绍移步其github页：https://github.com/DarthTon/Blackbone。一、BlackBone编译使用Visual Studio 2019完成编译，特别注意，C++标准需使用17版本。编译到静态库。二、使用静态库包含BlackBone.lib。在LuaFunc.cpp文件头部添加#include "..\BlackBone\BlackBone\Process\Process.h"并添加如下代码：uintptr_t g_gameBase = 0; //游戏主模块基址 blackbone::Process g_process; bool LuaFuncInit() { g_process.Attach(GetCurrentProcessId()); //g_process 初始化 g_gameBase = g_process.modules().GetMainModule().get()->baseAddress;//游戏主模块基址初始化 return true; }参考BlackBone源码中Sample项目示例：auto& remote = notepad.remote(); auto GetModuleHandleWPtr = notepad.modules().GetExport( L"kernel32.dll", "GetModuleHandleW" ); auto mainThread = notepad.threads().getMain(); //获取主线程ID //AsmFactory::GetAssembler()为AsmJit进行的封装 //AsmJit是一个完整的JIT(just In Time, 运行时刻)的针对C++语言的汇编器， //可以生成兼容x86和x64架构的原生代码，不仅支持整个x86/x64的指令集（包括传统的MMX和最新的AVX2指令集） //，而且提供了一套可以在编译时刻进行语义检查的API。 if (auto asmPtr = AsmFactory::GetAssembler(); asmPtr && mainThread && GetModuleHandleWPtr) { auto& a = *asmPtr; a.GenPrologue(); /// /// 原生函数调用 /// /// 函数指针 /// 函数参数列表 /// 函数调用约定 a.GenCall( static_cast<uintptr_t>(GetModuleHandleWPtr->procAddress), { nullptr }, cc_stdcall ); a.GenEpilogue(); uint64_t result = 0; /// /// 在任意存在的线程中执行代码 /// /// 需要执行的代码 /// 代码字节长度 /// 返回值 /// 目标线程 /// <returns>Status</returns> remote.ExecInAnyThread( a->make(), a->getCodeSize(), result, mainThread ); }根据上述示例及连接服务器函数原型，我们可以轻易写出如下代码：int ConnectToRealm(lua_State* L){ int n = lua_gettop(L); if (n < 1) return 0; unsigned int realmID = lua_tonumber(L, 1); //服务器ID if (g_gameBase + offset::LoginState_6) { if (utils::read<BYTE>(g_gameBase + offset::LoginState_6 + offset::LoginState_6_Offset)) { //根据ID获取ptr auto v2 = sub_141B6C8D0(g_gameBase + offset::LoginState_6, realmID); if (v2) { //主线程调用call //sub_141ACA530(v2 + 0x138, realmID, v2); if (auto asmPtr = blackbone::AsmFactory::GetAssembler(); asmPtr ) { auto& a = *asmPtr; a.GenPrologue(); a.GenCall(g_gameBase + offset::Login_ConnectToServer, { v2 + 0x138, …

某游戏自动化脚本系列 （七）基础功能封装到lua--1
type: item
date: 2021-08-08T17:42:00+08:00
description: 由于游戏对 frame_excuteScript以及相关内容检测太严格，因此尽量使用原生C/C++函数来实现具体功能。由于有太多功能需要主线程调用，而使用消息回调的话，太过于繁琐，因此采用第三方框架来实现主线程插入。一、整体思路将各功能封装，注册到自己lua状态机中，然后通过lua脚本来实现具体功能。二、函数申明(后续逐步补充完善)extern "C" { #include "lua/lua.h" #include "lua/lualib.h" #include "lua/lauxlib.h" } //C Function //C-Lua Function /*登录相关*/ int GetLoginState(lua_State* L); //获取登录状态 int AccountLogin(lua_State* L); //账号密码登录 int GetToken(lua_State* L); int SubmitToken(lua_State* L); //输入安全令 int ConnectToRealm(lua_State* L); // int CharSelect(lua_State* L); // int EnterWorld(lua_State* L); // int IsConnecting(lua_State* L); int IsConnectedToServer(lua_State* L); /*状态相关*/ int IsInGame(lua_State* L); int IsInDoor(lua_State* L); int IsSwimming(lua_State* L); int IsFalling(lua_State* L); int IsInBattle(lua_State* L); int IsDead(lua_State* L); int IsGhost(lua_State* L); int IsAutoCasting(lua_State* L); int IsResting(lua_State* L); int IsDriking(lua_State* L); int IsCasting(lua_State* L); int IsChannel(lua_State* L); int IsMoving(lua_State* L); int IsHorsing(lua_State* L); int IsPetExist(lua_State* L); int IsTaxing(); /*按键*/ int CPostMessage(lua_State* L); int CSendMessage(lua_State* L); /*宏*/ int MacroCreat(lua_State* L); int MacroBind2Key(lua_State* L); int MacroRunByPressKey(lua_State* L); int MacroRun(lua_State* L); /*技能*/ int SpellGetMaxMinRange(lua_State* L); int SpellGetCoolDown(lua_State* L); int SpellCast(lua_State* L); int SpellIsKnown(lua_State* L); int SpellFindSlotById(lua_State* L); int SpellCastAoe(lua_State* L); int SpellGetNameRankById(lua_State* L); int SpellLearnByNameRank(lua_State* L); int SpellLearnById(lua_State* L); /*宠物*/ int PetSummon(lua_State* L); int PetRelease(lua_State* L); int PetAttack(lua_State* L); int PetMoveTo(lua_State* L); int PetFeed(lua_State* L); /*对象管理*/ int ObjFirst(lua_State* L); int ObjNext(lua_State* L); int ObjType(lua_State* L); int ObjGuid(lua_State* L); int ObjPtr2Guid(lua_State* L); int ObjGuid2Ptr(lua_State* L); int ObjLocalPlayerPtr(lua_State* L); int ObjLocalPlayerGUID(lua…

某游戏自动化脚本系列 （六）再续--封装函数实现游戏lua_pcall调用和返回值获取
type: item
date: 2021-08-01T16:42:00+08:00
description: 一、缘起之前文章某游戏自动化脚本系列（四）核心功能DLL实现的方法，需要在每次需要多个返回值的时候，在外层增加函数调用，但是这种方式侵入程度太高，容易被发现。二、思路通过反复研究lua 5.1.4源码，发现可以自己实现单独的api调用函数，lua源码如下：LUA_API int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) { struct CallS c; int status; ptrdiff_t func; lua_lock(L); api_checknelems(L, nargs+1); checkresults(L, nargs, nresults); if (errfunc == 0) func = 0; else { StkId o = index2adr(L, errfunc); api_checkvalidindex(L, o); func = savestack(L, o); } c.func = L->top - (nargs+1); /* function to be called */ c.nresults = nresults; status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); adjustresults(L, nresults); lua_unlock(L); return status; }errfunc可以先忽略，主要填充了 CallS结构，再调用luaD_pcall。与下面游戏lua_pcall伪代码对比。void __fastcall lua_pcall(__int64 pLua_State, int nargs, int nresults, unsigned int msgh) { __int64 v7; // r10 __int64 func; // rax void *v9; // rax __int64 v10; // r9 __int64 v11; // [rsp+30h] [rbp+0h] BYREF int v12; // [rsp+38h] [rbp+8h] if ( msgh ) { v9 = index2adr(pLua_State, msgh); v7 = *(_QWORD *)(pLua_State + 88); func = (__int64)v9 - v7; } else { v7 = *(_QWORD *)(pLua_State + 88); func = 0i64; } v10 = *(_QWORD *)(pLua_State + 40); v12 = nresults; v11 = v10 - 24i64 * (nargs + 1); luaD_pcall(pLua_State, (unsigned int)&f_call, (unsigned int)&v11, v11 - v7, func); JUMPOUT(0x140455D59i64); // 后面为混淆过的返回地址检查及其处理函数 }不难看出，我们需要做以下几件事情：所需调用的API入栈 通过lua源码#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s))发现，lua_setglobal是调用的lua_setfield，而游戏中LUA_GLOBALSINDEX的值可通过index2adr来确定。参数入栈（可能有多个） 通过调用lua_push*实现。调用lua_pcall 同时通过动态修改硬编码解决返回地址检查。返回值获取 通过调用lua_to*实现。三、弊端调用的时候，需要确定具体有几个返回值，略显麻烦（别个脚本的都不需要）。

某游戏自动化脚本系列 （六）续
type: item
date: 2021-07-31T23:29:00+08:00
description: 一、背包及背包物品遍历通过相关API及IDA静态分析，定位如下函数bool __fastcall sub_14171E500(__int64 a1, int a2, _QWORD *a3, int *a4, _DWORD *a5) { __int64 v8; // rdi __int64 localPlayerPtr; // rax bool v10; // cc int v12; // eax int v13; // eax int v14; // eax __int64 bagPtr; // rax unsigned __int128 bagGUID; // [rsp+20h] [rbp-18h] BYREF *a3 = 0i64; *a4 = -1; v8 = (int)a1; *a5 = 2; sub_1410E0EE0(a1); if ( !localPlayerPtr ) // localPlayerPtr 实际为sub_1410E0EE0(a1)返回值 return 0; *a4 = a2; if ( (int)v8 >= 0 ) { if ( (int)v8 >= 11 ) return *a3 && *a4 >= 0 && *a4 < *(_DWORD *)*a3; bagGUID = (unsigned int)v8 < 0xB && ((int)v8 < 4 || sub_141252550()) ? *((_OWORD *)&unk_142FA0450 + v8) : (unsigned __int128)0i64; bagPtr = ((__int64 (__fastcall *)(_QWORD, _QWORD, _QWORD, _QWORD))ObjGUID2Ptr)( &bagGUID, 4i64, "D:\\BuildServer\\WoW\\1\\work\\DedicatedCheckout\\Classic\\Source\\Ui\\ContainerFrame.cpp", 614i64); if ( bagPtr ) *a3 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)bagPtr + 0x350i64))(bagPtr); if ( (int)v8 < 4 ) return *a3 && *a4 >= 0 && *a4 < *(_DWORD *)*a3; goto LABEL_23; } *a3 = localPlayerPtr + 0x28640; // item-guid数组指针 switch ( (_DWORD)v8 ) { case 0xFFFFFFFB: v14 = *a4 + 0x4B; *a4 = v14; v10 = v14 <= 81; break; case 0xFFFFFFFD: v13 = *a4 + 0x5E; *a4 = v13; v10 = v13 <= 125; break; case 0xFFFFFFFE: v12 = *a4 + 0x2F; *a4 = v12; if ( v12 > 74 ) return 0; LABEL_23: *a5 = 3; return *a3 && *a4 >= 0 && *a4 < *(_DWORD *)*a3; case 0xFFFFFFFF: *a4 += 0x17; v10 = *a4 <= (int)(sub_1412544D0(*a3) + 23); break; default: return *a3 && *a4 >= 0 && *a4 < *(_DWORD *)*a3; } if ( !v10 ) return 0; return *a3 && *a4 >= 0 && *a4 < *(_DWORD *)*a3; }根据上述伪代码，大胆猜测：&unk_142FA0450 + v8为背包指针或者GUID；*a3 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)bagPtr + 0x350i64))(bagPtr)与背包中物品有关系。通过Cheat Engine验证，发现：&unk_142FA0450 + bagIndex为背包GUID。虚函数：bagPtr + 0x350为lea rax, qword ptr ds:[rcx+0x00001730]，观察 bagPtr+0x1730处值，发现*(bagPtr+0x1730+8)为背包物品GUID数组。考虑到游戏存在系统自带背包，在上述伪代码中，背包物品指针为localPlayerPtr + 0x28640因此，有了如下遍历函数：void DbgWin:…

某游戏自动化脚本系列 （番外篇）聊聊数据分析
type: item
date: 2021-07-30T22:14:00+08:00
description: 一、可用资源GitHubWoWWiki | FandomownedcoreWoW.tools | ExportTrinityCorerecastnavigationarticls show how to make bot二、工具dumpwowCheatEngineX64DbgIDA/ghidra...三、分析过程通过相关网站获取信息。使用dump工具修复并dump可执行文件。使用IDA静态分析可执行文件，使用CheatEngine进行动态调试。使用VS2019 编写C++（c#）程序，验证数据准确性。四、IDA分析示例情景：需要分析游戏中背包物品及其信息存储方式。通过查询API，发现GetContainerItemInfo返回了足够的内容：纹理、数量、ID等等。于是祭出IDA，导入可执行文件，通过字符串“GetContainerItemInfo”定位其对应的lua c函数。如下图所示：其中，sub_1417158F0就是接下来需要分析的函数。lua函数参数的传递，是使用其lua堆栈（lua_state指针及序号）来进行的。考虑到API GetContainerItemInfo的参数和返回值：ParametersbagID：Number - number of the bag the item is in, e.g. 0 for your backpackslot：Number - slot number of the bag item you want the info for.Returnstexture：String - the texture for the item in the specified bag slotitemCount：Number - the number of items in the specified bag slotlocked：Boolean - 1 if the item is locked by the server, nil otherwise.quality：Number - the numeric quality of the itemreadable：Boolean - 1 if the item can be "read" (as in a book)lootable：Boolean - 1 if the item is a temporary container containing items that can be looted; otherwise nil.itemLink：a hyperlink for the item.因此，对于其F5分析后的伪代码，有了如下结果：__int64 __fastcall sub_1417158F0(__int64 a1) { int Itemquality; // edi int slotIndex; // ebx int bagIndex; // eax __int64 v5; // rax __int64 itemPtr; // rbx unsigned int Itemtexture; // eax unsigned int v8; // ebp __int64 itemlocked; // rdx __int128 v10; // xmm1 __int128 v11; // xmm0 __int128 v12; // xmm1 __int64 v13; // r14 unsigned int v14; // eax unsigned int v15; // eax int v16; // edx int v17; // er8 int v18; // er9 __int64 v19; // rax unsigned __int8 Itemlootable; // al unsigned __int8 itemLink; // al __int64 result; // rax char v23; // [rsp+20h] [rbp-B8h] __int64 v24[6]; // [rsp+30h] [rbp-A8h] BYREF __int128 v25[4]; // [rsp+60h] [rbp-78h] BYREF __int64 v26; // [rsp+A0h] [rbp-38h] unsigned int v27; // [rsp+E8h] [rbp+10h] BYREF int v28; // [rsp+F0h] [rbp+18h] BYREF __int64 v29; // [rsp+F8h] [rbp+20h] BYREF if ( (unsigned int)sub_14045E150() && (Itemq…

某游戏自动化脚本系列 （六）基本功能测试
type: item
date: 2021-07-24T19:11:00+08:00
description: 一、模块隐藏class XHideDll { public: XHideDll(); ~XHideDll(); //static PVOID GetProcessPEB(); static void PrintModuleList(); static bool HideInLoadOrderLinks(unsigned long long dllBase); static bool HideInMemoryOrderLinks(unsigned long long dllBase); static bool HideInInitializationOrderLinks(unsigned long long dllBase); static void HideModule(unsigned long long hModule, bool DeleteAfter); static void LM_CreateFakeThread(unsigned __int64 Threadfunc, unsigned __int64 fakeAddr); };#include "XHideDll.h" #include "ntdll/ntdll.h" #include <TlHelp32.h> #include <intrin.h> #include <tchar.h> #include <stdio.h> #include <stdarg.h> #ifndef _WIN64 #pragma comment(lib,"ntdll/ntdll_x86.lib") #else #pragma comment(lib,"ntdll/ntdll_x64.lib") #endif #include <psapi.h> //#include <winternl.h> #pragma comment(lib, "Psapi.lib") #ifdef _DEBUG #define DbgPrint(_x_) Print _x_ #else #define DbgPrint(_x_) #endif typedef LPVOID WINAPI VIRTUALALLOC( _In_opt_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD flAllocationType, _In_ DWORD flProtect ); typedef void *MEMCPY( void *dest, const void *src, size_t count ); typedef bool UNLOADMODULE( HMODULE hModule, LPVOID lpNewBaseAddr, ULONG_PTR SizeOfImage ); typedef NTSTATUS WINAPI NTQUERYINFORMATIONPROCESS( _In_ HANDLE ProcessHandle, _In_ PROCESSINFOCLASS ProcessInformationClass, _Out_ PVOID ProcessInformation, _In_ ULONG ProcessInformationLength, _Out_opt_ PULONG ReturnLength ); typedef struct _LDR_MODULE { LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; PVOID BaseAddress; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; SHORT LoadCount; SHORT TlsIndex; LIST_ENTRY HashTableEntry; ULONG TimeDateStamp; } LDR_MODULE, *PLDR_MODULE; //PsGetCurrentProcess(); void Print(const LPTSTR fmt, ...) { va_list args; va_start(args, fmt); int len = _vsctprintf(fmt, args); LPTSTR buffer = (LPTSTR)new TCHAR[len + 1]; _vsntprintf_s(buffer, len + 1, …

某游戏自动化脚本系列 （五）注入带MFC窗口的Dll
type: item
date: 2021-07-22T22:50:00+08:00
description: 刘某人重新回来啦~带MFC窗口，以更方便数据遍历和功能测试。一、带MFC的Dll使用VS2019 创建带MFC的动态链接库（DLL），项目属性设置使用静态MFC。新建名为DbgWin的对话框界面。在DLL工程名cpp中（比如我DLL工程名叫GameCore，则目标文件为 GameCore.cpp）添加头文件 DbgWin.h 引用。并添加如下代码：DbgWin* m_dbgWin; DWORD WINAPI ShowDialog(LPARAM lp) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); //MFC宏，必须，确保资源正确加载 m_dbgWin = new DbgWin; m_dbgWin->DoModal(); //以模态方式创建窗口 m_dbgWin->ShowWindow(SW_SHOWNORMAL); //显示窗口，可hook消息处理函数，通过按键显示、隐藏窗口 return 0; }修改BOOL CGameCoreApp::InitInstance()函数为：BOOL CGameCoreApp::InitInstance() { CWinApp::InitInstance(); ::CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ShowDialog, NULL, NULL, NULL); return TRUE; }设计如下MFC界面：二、DLL注入#include <iostream> #include <Windows.h> #include <string.h> #include <thread> #include <chrono> int main() { char* commandLineCopy = NULL; std::string tmpString = "D:\\Program Files (x86)\\｛此处隐藏｝.exe"; commandLineCopy = (char*)tmpString.c_str(); int cdSize = strlen(commandLineCopy) + 1; // 创建进程并暂停 LPSTARTUPINFOA startInfo = new STARTUPINFOA(); PROCESS_INFORMATION processInfo = { 0 }; ZeroMemory(startInfo, sizeof(STARTUPINFOA)); startInfo->cb = sizeof(STARTUPINFOA); if (!CreateProcessA(NULL, (LPSTR)"D:\\Program Files (x86)\\｛此处隐藏｝.exe", NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, "D:\\Program Files (x86)\\｛此处隐藏｝", startInfo, &processInfo)) { delete startInfo; return false; } auto pid = processInfo.dwProcessId; //打开进程，获取进程句柄 HANDLE targetProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (targetProc == NULL) { return false; } std::string tmpChar = "H:\\2Project\\testMFCLibrary1\\x64\\Debug\\GameCore.dll"; char* pChar = (char*)tmpChar.c_str(); int dllLen = strlen(pChar) + 1; // 1.目标进程申请空间 LPVOID pDLLPath = VirtualAllocEx(targetProc, NULL, dllLen, MEM_COMMIT, PAGE_READWRITE); if (pDLLPath == NULL) { //qDebug() << "VirtualAllocEx error"; return false; } SIZE_T wLen = 0; // 2.将DLL路径写进目标进程内存空间 int ret = WriteProcessMemory(targetProc, pDLLPath, pChar, dllLen, &wLen); if (ret == 0) { //qDebug() << "WriteProcessMemory error"; return false; } …

某游戏自动化脚本系列（四）第二期 PE节保护
type: item
date: 2021-06-13T15:01:00+08:00
description: 某游戏使用了PE节保护技术,重新映射PE内存,致使PE节不可修改参考资料：1.[[原创]分享下wow PE section保护技术](https://bbs.pediy.com/thread-268057.htm)2.Self-Remapping-Code参考资料1中，是使用lua来实现的内层空间重新映射，自己对这块关注蛮久了，遂使用c/c++进行了实现。说明：本代码运行于R3层，无需进行驱动开发。一.结构体定义typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING, * PUNICODE_STRING; typedef struct _OBJECT_ATTRIBUTES { ULONG Length; HANDLE RootDirectory; PUNICODE_STRING ObjectName; ULONG Attributes; PVOID SecurityDescriptor; PVOID SecurityQualityOfService; } OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES; typedef enum _SECTION_INHERIT { ViewShare = 1, ViewUnmap = 2 } SECTION_INHERIT, * PSECTION_INHERIT;二.ntdll未导出函数声明及地址获取1.函数声明typedef DWORD(WINAPI* NtSuspendProcess)(HANDLE ProcessHandle); //进程暂停 typedef DWORD(WINAPI* NtResumeProcess)(HANDLE hProcess); //进程恢复 typedef NTSTATUS(NTAPI* NtCreateSection)( _Out_ PHANDLE SectionHandle, _In_ ACCESS_MASK DesiredAccess, _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes, _In_opt_ PLARGE_INTEGER MaximumSize, _In_ ULONG SectionPageProtection, _In_ ULONG AllocationAttributes, _In_opt_ HANDLE FileHandle ); typedef NTSTATUS (NTAPI* NtMapViewOfSection)( _In_ HANDLE SectionHandle, _In_ HANDLE ProcessHandle, _Inout_ PVOID* BaseAddress, _In_ ULONG_PTR ZeroBits, _In_ SIZE_T CommitSize, _Inout_opt_ PLARGE_INTEGER SectionOffset, _Inout_ PSIZE_T ViewSize, _In_ SECTION_INHERIT InheritDisposition, _In_ ULONG AllocationType, _In_ ULONG Win32Protect ); typedef NTSTATUS(NTAPI* NtUnmapViewOfSection)( _In_ HANDLE ProcessHandle, _In_opt_ PVOID BaseAddress ); typedef NTSTATUS(NTAPI* NtClose)( _In_ HANDLE Handle );2.函数地址获取NtSuspendProcess m_NtSuspendProcess; NtResumeProcess m_NtResumeProcess; NtCreateSection m_NtCreateSection; NtMapViewOfSection m_NtMapViewOfSection; NtUnmapViewOfSection m_NtUnmapViewOfSection; NtClose m_NtClose; HMODULE h_module = LoadLibrary("ntdll.dll"); if (h_module == 0) { printf("loadlibrary error.\n"); return 0; } m_NtResumeProcess = (NtResumeProcess)GetProcAddress(h_module, "NtResumeProcess"); …