2023年11月26日发(作者:)

⽹络游戏逆向分析-9-⾃动更新基址

⽹络游戏逆向分析-9-⾃动更新基址

基址在每次更新之后都会修改,这个⽐较⿇烦,不然每次都得重新找,⾮常消耗体⼒和时间。

⾃动更新基址原理

搜索游戏进程的内存,然后把硬编码依次和内存⾥⾯数据进⾏匹配,匹配到了之后就返回地址,地址附近就是基址了,通过加减来得到基址。

这⾥要扯到⼀些关于硬编码和机器指令的问题了,从整个计算机来看实际上要跑的东西在CPU上,只能识别01,但是为了后⾯的多种多样功

能,通过对01的组合来实现了机器指令,CPU可以直接通过这个01的指令来进⾏不同的操作,这个指令就叫做机器指令也可以说是硬编

码(也就是硬件上的编码)根据CPU的不同⽽不同,⽽我们常⽤的汇编指令,是唯⼀⼀个可以和机器指令⼀⼀对应的东西,因为如果直接⽤机器指

令对于开发来说⾮常⾮常⿇烦。所以我们常⽤的是汇编语⾔,⽽我们通过⼀些Ollydbgxdbg这些东西,都是通过CPU⾥⽤的机器指令翻译成的汇

编指令,这个流程的⼯具叫做反汇编引擎,⽐如说:

这个ollydbg⾥⾯的1是内存地址,2是机器码的内容,3是汇编指令的内容。实际上运⾏的是内存地址⾥⾯存放的机器码,只不过这个调试器帮我们

翻译成了汇编指令,然后我们修改汇编指令的时候也帮我们修改了机器码这样⼦。

⾃动更新基址思路

所以这⾥我们可以参考机器码,我们把整个内存的机器码读出来,然后通过机器码⽐对得到对应的有关地址的机器码指令,然后转成字符串,读取

得到基址。

开始

这⾥我们随便⽤⼀段东西把:

需要注意的是,这⾥的机器码尽量多弄⼀下,这样来达到机器码是唯⼀的别的地⽅不能会重复。

逻辑都在代码⾥⾯了:

#include"UpdateAddr.h"

BOOL ByteToChar(BYTE* ByteArray, char* CharArray, int ByteLen)

{

//ByteArray是字节数组

//CharArray是字符数组

//ByteLen 是字节数组长度

for (int i = 0; i < ByteLen; i++)

{

wsprintfA(&CharArray[i * 2], "%02X", ByteArray[i]);

}

return TRUE;

}

BOOL CmpMachineStr(char* TempReadMachineCodeStr,char* MachineCodeStr, int MachineCodeStrLen)

{

// TempReadMachineCodeStr 读取的机器码字符串

// MachineCodeStr 特征机器码字符串

//MachineCodeStrLen特征机器码字符串长度

for (int i = 0; i < MachineCodeStrLen; i++)

{

if (TempReadMachineCodeStr[i] != MachineCodeStr[i])

return FALSE;

}

return TRUE;

}

BOOL ScanProcess(HANDLE HandleProcess, DWORD BeginAddr, DWORD EndAddr, char* MachineCodeStr, int MachineCodeStrLen)

{

//HandleProcess是进程的句柄,BeginAddr是起始内存地址,EndAddr是结束内存地址,MachineCode是机器码的字符串表达形式

//MachineCodeLen是机器码字符串长度。

int Flag = 0;

//每次读取0x1000个机器码的内容进⾏⽐较。

BYTE TempReadMachineCode[0x1000] = { 0 };

for (DWORD TempBeginAddr = BeginAddr; TempBeginAddr < EndAddr - 0x1000; TempBeginAddr += (0x1000 - MachineCodeStrLen))

{

//将机器码缓冲区⽤0填充

memset(TempReadMachineCode, 0x0, 0x1000);

//0x1000个机器码到byte缓冲数组⾥。

BOOL RetReadProcessMemory = ReadProcessMemory(HandleProcess,(LPVOID)TempBeginAddr,TempReadMachineCode, 0x1000, NULL);

if (RetReadProcessMemory == 0)

continue;

//byte字节数组转换成字符串

char TempReadMachineCodeStr[0x2001]={ 0 };

ByteToChar(TempReadMachineCode, TempReadMachineCodeStr, 0x1000);

//开始⽐较

for (int i = 0; i < 0x2001 - MachineCodeStrLen;i++)

{

BOOL ret = CmpMachineStr(TempReadMachineCodeStr + i,MachineCodeStr,MachineCodeStrLen);

if (ret == TRUE)

{

cout << "找到了地址为";

printf("%Xn", TempBeginAddr + i / 2);

Flag = 1;

}

}

}

if (Flag == 0)

cout << "未找到" << endl;

for (int i = 0; i < MachineCodeStrLen; i++)

{

if (MachineCodeStr[i] == '?')

continue;

if (TempReadMachineCodeStr[i] != MachineCodeStr[i])

return FALSE;

}

return TRUE;

}

完善2

添加⼀个⽂件来⽅便读取:

BOOL ReadCodeFile()

{

FILE* fp;

errno_t errnoFile = fopen_s(&fp, "", "r");

if (errnoFile != 0)

return FALSE;

fclose(fp);

delete[]TempStrBuff;

return TRUE;

}

完善5

编写Main函数将内容通过地址+偏移,然后读取:

int main()

{

HANDLE HandleProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 1144);

if (!HandleProcess)

{

cout << "打开进程失败" << endl;

return 0;

}

vector AllFileStr;

//读取⽂件中的字符串

ReadStrFile(AllFileStr);

//分割⽂件中的字符串

vector> AllFileStrToPartition;

split(AllFileStrToPartition, AllFileStr);

for (int i = 0; i < (); i++)

{

DWORD BaseAddr = 0;

DWORD DataBaseAddr = 0;

DWORD Context = 0;

顺带说⼀句,⽹游逆向⽆限期延迟更新了,因为这个游戏⽐较⽼了研究起来也没有价值,还有个原因是技术就这么写,对发包函数的处理,以及搜

索数据的处理,然后往上找基址。这个只能在应⽤层玩,⼀些更⾼难度的⽐如反调试,加壳脱壳,Windows内核就可以轻松解决掉。所以后⾯准备