逆向(一):HOOK

一、概念

具体的描述大家可以在网上找,在这里我就不再罗嗦了,大致意思为:通过改变代码的执行流程达到某种目的。

二、背景

谈及逆向分析,基本就是分析一个在市场上已经发布的软件程序,而在这种情况下,我们往往是没有源代码的,但又想实现一些特殊的功能,此时HOOK就该登场了。

三、实现

本文以某老旧款游戏(以下称“Game”)为例,实现人物无敌的功能,简单说明HOOK的一些事情。
图片为项目(以下称“MyProject”)运行后的界面:
在这里插入图片描述
1、输入进程ID:
因为进程间的内存彼此独立,在MyProject中做的事情,Game是无法直接访问到的,所以需要依据进程ID得到Game的句柄,进而继续其他操作,该步骤调用API即可

DWORD dwPID;// 打开任务管理器就能看到
// 具体参数的意思 请看官方文档 
HANDLE hGameProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);

2、关键逻辑:
以无敌功能为例,在一个游戏中,当人物受到攻击时,会触发一个类似于这样的函数

void beAct(int damage)
{
    // 其他业务代码
    hp = hp - damage;
    // 其他业务代码
}

只有这样才会导致血量发生变化,继而显现在游戏界面或人物信息的面板上,总之,这一句代码是一个十分基本的逻辑,所以无敌的思想就是不让这句代码执行,或者damage清零。
修改的时候还需要注意一个问题:有可能人家使用了面向对象的思想,人物和怪物属于同一个角色类class Role,所以在修改时还需要判断调用被攻击函数的指针是否是人物,否则会出现怪物也打不死的尴尬场面。
基于上面的分析,很显然已经不能通过逆向分析工具在原基础上进行修改了,否则破坏性太大。
3、实现:
分配新的内存空间,将无敌功能代码写在该内存,通过跳转到该内存来实现无敌。此时刚刚拿到的hGameProcess就派上用场了,因为你内存分配到MyProject里没用啊,Game访问不到

// 在Game的进程中分配内存,分配成功返回首地址,失败返回NULL
LPVOID lAdr = VirtualAllocEx(hGameProcess, NULL, 1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

内存分配好后,就该写代码了。问题又来了,Game是一个已经编译好的程序,是一个完全由机器码组成的文件,所以我们也只能直接在分配的内存中写入机器码(当然也是可以使用纯C++代码完成的,那个以后再说)

// 宏的值都是瞎写的 
#define PTR_ROLE       0xaaaaaaaa  // 人物指针 
#define JUMP_FROM_GAME 0xbbbbbbbb  // 从Game的这个地址处跳到分配的内存空间 
#define JUMP_TO_GAME   0xcccccccc  // 无敌功能执行完了还要回到Game并继续执行其他


char data[]
{
	0x81,0xFE,            //cpm esi
	0x00,0x00,0x00,0x00,  //即人物的地址 写出0方便后面使用下标修改  与前一句连接起来为 cmp esi 人物指针
	0x75,0x02,            //跳两个字节 即jne xxxxxxxx  根据具体执行指令所占字节长度修改02的值
	0x31,0xFF,            //xor edi,edi
	0x8B,0xD0,            //mov edx,eax
	0x29,0xFA,            //sub edx,edi
	0x39,0xCA,            //cmp edx,ecx
	0xE9,                 //jmp
	0x00,0x00,0x00,0x00   //要跳转回的地址 即要返回游戏原来本该继续执行的地址 
};

//jmp指令 jmp后面跟着的值=要跳转到的目标地址-当前jmp指令的地址-5 没有为什么 算法规定
//计算准备跳回原游戏地址的机器码 即data中最后一组0的值
int iJumpReturnAdr = JUMP_TO_GAME - ((int)lAdr + 0x10) - 5;
//把计算出的值 写进data中 
int* nCode = (int*)(data + 0x11);
nCode[0] = iJumpReturnAdr;

//同理 data中第一组的0的值也通过这样写入
nCode = (int*)(data + 0x2);
nCode[0] = PTR_ROLE;

//将无敌代码 写到新分配的内存中
WriteProcessMemory(hGameProcess, lAdr, data, sizeof(data), NULL);

//处理前面游戏准备跳过来的地方
int iJumpAdr = (int)lAdr - JUMP_FROM_GAME - 0x5;
nCode = (int*)(jumpCode + 0x1);
nCode[0] = iJumpAdr;

4、说明:
a、根据已经做出的逆向分析确定了这是面向对象,即人物和怪物同属一个类,beAct函数为成员函数,所以需要判断this指针;
b、跳转指令后面机器码的计算;
c、上述内容完成后,即可实现在MyProject的对话框中勾选“无敌”按钮,来控制是否需要无敌;

四、总结

大致流程图如下:
在这里插入图片描述
在这里插入图片描述

一个简单的无敌功能就通过HOOK实现了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值