003 辅助框架的代码实现
文章目錄
- Stu
- CPublic公共函數(shù)
- MemOper 內(nèi)存操作
- GlobalData 全局?jǐn)?shù)據(jù)
接著上一篇文章,我們來聊關(guān)于整個(gè)輔助框架的代碼實(shí)現(xiàn)
Stu
按照常規(guī)的思路,我們來構(gòu)想一下接下來的開發(fā)過程。首先我們需要找到人物的數(shù)據(jù),然后編寫代碼的時(shí)候設(shè)計(jì)一個(gè)人物數(shù)據(jù)的結(jié)構(gòu)體;接著周圍遍歷的數(shù)據(jù),又要設(shè)計(jì)一個(gè)周圍遍歷的結(jié)構(gòu)體;后續(xù)用到每一個(gè)新的數(shù)據(jù),都要設(shè)計(jì)一個(gè)結(jié)構(gòu)體來存放。
等寫了兩三個(gè)游戲以后就會(huì)發(fā)現(xiàn),很多代碼都在重復(fù)寫。那么這就引出了一個(gè)數(shù)據(jù)管理的問題,怎么樣用最高效的方式去管理這些數(shù)據(jù)。
其實(shí)很多對(duì)象有一部分屬性都是通用的,比如說名字,等級(jí)和ID;這樣我們就可以設(shè)計(jì)一個(gè)通用的對(duì)象結(jié)構(gòu)體,這個(gè)對(duì)象結(jié)構(gòu)體存儲(chǔ)所有的游戲數(shù)據(jù)對(duì)象,然后用一個(gè)type字段來對(duì)對(duì)象進(jìn)行區(qū)分。
對(duì)象結(jié)構(gòu)如下:
struct _stuObj {int m_StuType; //0 人物 //--------------------------------公用-------------------------------------------------wstring m_Name; //名字 DWORD m_Obj; //對(duì)象DWORD m_ID; //ID//輸出調(diào)試信息void OutputDebugInfo(); }先暫時(shí)設(shè)計(jì)這些屬性,后面需要再進(jìn)行添加;再設(shè)置一個(gè)枚舉,用于區(qū)分_stuObj的數(shù)據(jù)類型。
//stu類型 enum EType {//角色Em_Role,//對(duì)象Em_Object,//物品Em_Item,//地面物品 Em_GroundItem,//技能Em_Skill,//已接任務(wù)Em_GetedTask,//可接任務(wù)Em_CanGetTask, };OutputDebugInfo函數(shù)實(shí)現(xiàn)如下:
//輸出單個(gè)對(duì)象的調(diào)試信息 void _stuObj::OutputDebugInfo() {__try{switch (m_StuType){//輸出角色信息case Em_Role:break;//輸出對(duì)象信息case Em_Object:break;//輸出物品信息case Em_Item:break;//輸出地面物品信息case Em_GroundItem:break;//輸出任務(wù)信息case Em_GetedTask:break;//輸出技能信息case Em_Skill:break;default:break;}}__except (1){OutputDebugStringA("輸出stuobj信息錯(cuò)誤");} }現(xiàn)在我們已經(jīng)有了一個(gè)對(duì)象結(jié)構(gòu)體,接著再設(shè)計(jì)一個(gè)對(duì)象結(jié)構(gòu)體的集合。
//對(duì)象結(jié)構(gòu)體集合 struct _stuObjs {//所有的對(duì)象集合vector<_stuObj> m_data;public:void OutputDebugInfo();//通過名字列表獲取對(duì)象_stuObj GetDataByName(wstring name);//通過名字列表獲取多個(gè)對(duì)象_stuObjs GetDataByNames(vector<wstring> names); };vector<_stuObj> m_data這個(gè)對(duì)象結(jié)構(gòu)體的集合用于保存所有的對(duì)象。方便后面數(shù)據(jù)存儲(chǔ)。然后再實(shí)現(xiàn)三個(gè)函數(shù),OutputDebugInfo函數(shù)實(shí)現(xiàn)如下:
//輸出多個(gè)對(duì)象的調(diào)試信息 void _stuObjs::OutputDebugInfo() {for (auto it = begin(m_data); it != end(m_data); it++){it->OutputDebugInfo();} }由于我們已經(jīng)實(shí)現(xiàn)了單個(gè)對(duì)象的OutputDebugInfo函數(shù),所以多個(gè)對(duì)象的OutputDebugInfo只需要調(diào)用單個(gè)對(duì)象的輸出函數(shù)就可以了。
接著還需要實(shí)現(xiàn)一個(gè)函數(shù),通過名字獲取對(duì)象。
//通過名字獲取對(duì)象 _stuObj _stuObjs::GetDataByName(wstring name) {for (auto it = begin(m_data); it != end(m_data); it++){if (it->m_Name==name){return *it;}}return _stuObj(); }需要實(shí)現(xiàn)這個(gè)函數(shù)的場景是方便我們進(jìn)行某些功能測試。比如我找到了所有的技能遍歷的數(shù)據(jù),然后現(xiàn)在需要測試釋放技能call,釋放技能call一般是傳入技能ID,但如果用ID的話,那么我每次都需要去查看我需要釋放的ID到底是多少,這樣的話就會(huì)顯得比較麻煩。但如果封裝了這個(gè)函數(shù)就可以直接傳入技能名字。
然后再寫一個(gè)通過名字取多個(gè)對(duì)象的方法
//通過名字列表獲取多個(gè)對(duì)象 _stuObjs _stuObjs::GetDataByNames(vector<wstring> names) {_stuObjs values;for (auto it = begin(m_data); it != end(m_data); it++){for (auto nit = begin(names); nit != end(names); nit++){if (*nit == it->m_Name){values.m_data.push_back(*it);break;}}}return values; }CPublic公共函數(shù)
這個(gè)類存放整個(gè)項(xiàng)目都需要用到的公共函數(shù),首先是兩個(gè)封裝好的輸出調(diào)試信息
//************************************************************ // 函數(shù)名稱: __OutputDebugStringW // 函數(shù)說明: 打印調(diào)試信息 // 作 者: GuiShou // 時(shí) 間: 2020/3/11 // 參 數(shù): pstrFormat // 返 回 值: void //************************************************************ void __OutputDebugStringW(const wchar_t* pstrFormat, ...) {TCHAR szBuffer[1024] = { 0 };va_list argList;va_start(argList, pstrFormat);_vstprintf_s(szBuffer, pstrFormat, argList);va_end(argList);OutputDebugString(szBuffer); }//************************************************************ // 函數(shù)名稱: __OutputDebugStringA // 函數(shù)說明: 打印調(diào)試信息 // 作 者: GuiShou // 時(shí) 間: 2020/3/11 // 參 數(shù): pstrFormat // 返 回 值: void //************************************************************ void __OutputDebugStringA(const char* pstrFormat, ...) {CHAR szBuffer[1024] = { 0 };va_list argList;va_start(argList, pstrFormat);vsprintf_s(szBuffer, pstrFormat, argList);va_end(argList);OutputDebugStringA(szBuffer); }接著,由于x64只有一種調(diào)用約定,所以我們可以封裝一個(gè)函數(shù)來對(duì)所有的功能call進(jìn)行調(diào)用。例如我們找到的功能call有兩個(gè)參數(shù)時(shí),就可以傳入?yún)?shù)直接調(diào)用下面的函數(shù)
QWORD GameCall2(QWORD RCX, QWORD RDX, QWORD calladdr) {__try{//定義函數(shù)指針typedef UINT64(*PFnFuncCall)(QWORD RCX, QWORD RDX);PFnFuncCall FuncCall = (PFnFuncCall)(calladdr);return FuncCall(RCX, RDX);}__except (1){__OutputDebugStringA("通用CALL2 出錯(cuò)\n");}return 0; }這樣就可以重復(fù)利用,而且不用寫匯編文件。下面封裝的函數(shù)也一樣,適用于不同參數(shù)的call。
QWORD GameCall3(QWORD RCX, QWORD RDX, QWORD R8, QWORD calladdr) {__try{//定義函數(shù)指針typedef UINT64(*PFnFuncCall)(QWORD RCX, QWORD RDX, QWORD R8);PFnFuncCall FuncCall = (PFnFuncCall)(calladdr);return FuncCall(RCX, RDX, R8);}__except (1){__OutputDebugStringA("通用CALL3 出錯(cuò)\n");}return 0; }QWORD GameCall4(QWORD RCX, QWORD RDX, QWORD R8, QWORD R9, QWORD calladdr) {__try {typedef UINT64(*PFnFuncCall)(QWORD RCX, QWORD RDX, QWORD R8, QWORD R9);PFnFuncCall FuncCall = (PFnFuncCall)(calladdr);return FuncCall(RCX, RDX, R8, R9);}__except (1) {__OutputDebugStringA("通用CALL4 出錯(cuò)\n");}return 0; }QWORD GameCall5(QWORD RCX, QWORD RDX, QWORD R8, QWORD R9, QWORD Rsp20, QWORD calladdr) {__try{typedef UINT64(*PFnFuncCall)(QWORD RCX, QWORD RDX, QWORD R8, QWORD R9, QWORD Rsp20);PFnFuncCall FuncCall = (PFnFuncCall)(calladdr);return FuncCall(RCX, RDX, R8, R9, Rsp20);}__except (1){__OutputDebugStringA("通用CALL5 出錯(cuò)\n");}return 0; }QWORD GameCall6(QWORD RCX, QWORD RDX, QWORD R8, QWORD R9, QWORD Rsp20, QWORD Rsp28, QWORD calladdr) {__try{typedef UINT64(*PFnFuncCall)(QWORD RCX, QWORD RDX, QWORD R8, QWORD R9, QWORD Rsp20, QWORD Rsp28);PFnFuncCall FuncCall = (PFnFuncCall)(calladdr);return FuncCall(RCX, RDX, R8, R9, Rsp20, Rsp28);}__except (1){__OutputDebugStringA("通用CALL6 出錯(cuò)\n");}return 0; }MemOper 內(nèi)存操作
這個(gè)類封裝所有數(shù)據(jù)類型的取內(nèi)容操作,目的是提高代碼可讀性
#include "pch.h" #include "MemOper.h"BYTE ReadBYTE(QWORD pBase) {__try{if (IsBadReadPtr((void*)pBase, sizeof(BYTE)) == 0){return *(BYTE*)pBase;}}__except(1){__OutputDebugStringA("ReadBYTE Error Addr:%x", pBase);}return 0; }BYTE ReadBYTE(DWORD pBase) {__try{if (IsBadReadPtr((void*)pBase, sizeof(BYTE)) == 0){return *(BYTE*)pBase;}}__except (1){__OutputDebugStringA("ReadBYTE Error Addr:%x", pBase);}return 0; }WORD ReadWord(QWORD pBase) {__try{if (IsBadReadPtr((void*)pBase, sizeof(WORD)) == 0){return *(WORD*)pBase;}}__except (1){__OutputDebugStringA("ReadWord Error Addr:%x", pBase);}return 0; }WORD ReadWord(DWORD pBase) {__try{if (IsBadReadPtr((void*)pBase, sizeof(WORD)) == 0){return *(WORD*)pBase;}}__except (1){__OutputDebugStringA("ReadWord Error Addr:%x", pBase);}return 0; }QWORD ReadQword(QWORD pBase) {__try{if (IsBadReadPtr((void*)pBase, sizeof(QWORD)) == 0){return *(QWORD*)pBase;}}__except (1){__OutputDebugStringA("ReadQword Error Addr:%x", pBase);}return 0; }DWORD ReadDword(QWORD pBase) {__try{if (IsBadReadPtr((void*)pBase, sizeof(QWORD)) == 0){return *(DWORD*)pBase;}}__except (1){__OutputDebugStringA("ReadDword Error Addr:%x", pBase);return 0;}return 0; }DWORD ReadDword(DWORD pBase) {__try{if (IsBadReadPtr((void*)pBase, sizeof(DWORD)) == 0){return *(DWORD*)pBase;}}__except (1){__OutputDebugStringA("ReadDword Error Addr:%x", pBase);return 0;}return 0; }int ReadInt(QWORD pBase) {__try{if (IsBadReadPtr((void*)pBase, sizeof(int)) == 0){return *(int*)pBase;}}__except (1){__OutputDebugStringA("ReadInt Error Addr:%x", pBase);}return 0; }float ReadFloat(QWORD pBase) {__try{if (IsBadReadPtr((void*)pBase, sizeof(float)) == 0){return *(float*)pBase;}}__except(1){__OutputDebugStringA("ReadFloat Error Addr:%x", pBase);}return 0; }float ReadFloat(DWORD pBase) {__try{if (IsBadReadPtr((void*)pBase, sizeof(float)) == 0){return *(float*)pBase;}}__except (1){__OutputDebugStringA("ReadFloat Error Addr:%x", pBase);}return 0; }void WriteFloat(QWORD pBase,float Value) {__try{if (IsBadWritePtr((void*)pBase, sizeof(float)) == 0){*(float*)pBase = Value;}}__except (1){__OutputDebugStringA("WriteFloat Error Addr:%x", pBase);}}wchar_t* ReadWChar(QWORD pBase) {__try{if (IsBadReadPtr((void*)pBase, sizeof(wchar_t)) == 0){return (wchar_t*)pBase;}}__except (1){__OutputDebugStringA("WriteFloat Error Addr:%x", pBase);}return L""; }char* ReadChar(QWORD pBase) {__try{if (IsBadReadPtr((void*)pBase, sizeof(char)) == 0){return (char*)pBase;}}__except (1){__OutputDebugStringA("ReadChar Error Addr:%x", pBase);}return ""; }GlobalData 全局?jǐn)?shù)據(jù)
這個(gè)里面存放游戲需要用到的全局?jǐn)?shù)據(jù)
.h文件
//全局的游戲模塊地址 extern QWORD g_GameAddr;.cpp文件
QWORD g_GameAddr = 0;void GetGameModuleAddr() {g_GameAddr = (QWORD)GetModuleHandleA("MMOGame-Win64-Shipping.exe"); }然后在界面初始化的時(shí)候調(diào)用一次,后續(xù)就可以一直使用游戲的模塊基地址了。
剩下的GameData和GameFunction模塊存放游戲數(shù)據(jù)代碼和游戲功能代碼,我們等需要用到的時(shí)候在添加。
Github:https://github.com/TonyChen56/GameReverseNote
完整代碼:https://download.csdn.net/download/qq_38474570/79498815
}
__except (1)
{
__OutputDebugStringA(“ReadChar Error Addr:%x”, pBase);
}
}
### GlobalData 全局?jǐn)?shù)據(jù)這個(gè)里面存放游戲需要用到的全局?jǐn)?shù)據(jù).h文件```c++ //全局的游戲模塊地址 extern QWORD g_GameAddr;.cpp文件
QWORD g_GameAddr = 0;void GetGameModuleAddr() {g_GameAddr = (QWORD)GetModuleHandleA("MMOGame-Win64-Shipping.exe"); }然后在界面初始化的時(shí)候調(diào)用一次,后續(xù)就可以一直使用游戲的模塊基地址了。
剩下的GameData和GameFunction模塊存放游戲數(shù)據(jù)代碼和游戲功能代碼,我們等需要用到的時(shí)候在添加。
Github:https://github.com/TonyChen56/GameReverseNote
完整代碼:https://download.csdn.net/download/qq_38474570/79498815
總結(jié)
以上是生活随笔為你收集整理的003 辅助框架的代码实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 002 辅助框架的设计
- 下一篇: 004 人物数据查找和代码编写