远程代码注入及DLL注入教程(InlineHook)---植物大战僵尸为例

远程代码注入及DLL注入教程

说明

​ 本人刚开始学习逆向,不知道有没有动力学下深去,这一块也没有详细的实战教学,学多少就上传多少,希望能给想学的朋友一点帮助吧,本教程想通过植物大战僵尸这一经典游戏来抛砖引玉,教大家如何编写一个单机辅助。

​ CE找数据的过程本教程不提供,本文着重讲述如何根据找到的数据实现无限阳光,无冷却,其他诸如秒杀僵尸,后台运行等均大同小异。

所需基础

CE找关键数据基址(网上教程很多)、基本汇编命令

涉及工具

VS、CheatEngine(CE,网上很多)、汇编转机器码工具(我在用的https://www.jb51.net/softs/629217.html)、DLL注入工具(网上很多)

开始

无限冷却

​ 假设第一个植物栏的冷却地址已找到,如图:
在这里插入图片描述
此时种下植物后暂停游戏,切到CE,选中冷却地址,右键-找出什么改写了这个地址,然后继续游戏,发现改写的代码:
远程代码注入及DLL注入教程(InlineHook)---植物大战僵尸为例_第1张图片
点击显示汇编代码:
远程代码注入及DLL注入教程(InlineHook)---植物大战僵尸为例_第2张图片
经过分析发现:如果还在冷却,那么在②处判断会跳走,把②处代码nop掉,让代码无论如何也会去Call③,发现植物已经实现无冷却(有很多种方法,比如把①修改为cmp eax,0),那么只需要如下代码即可实现:

BYTE noCD[2] = {0};
memset(noCD, 0x90, 2);//nop硬编码为0x90
WriteProcessMemory(hProcess, (LPVOID)(0x487296), noCD, 2, NULL);

无限阳光

像前面这种替换指令所用空间小于当前代码的方式,实现起来非常简单,但如果替换的汇编指令超过当前代码长度,将会导致后面指令被覆盖,引起崩溃,此时需要用到代码注入
选中找到的阳光值地址,右键-找出什么访问了这个地址
远程代码注入及DLL注入教程(InlineHook)---植物大战僵尸为例_第3张图片
可以看到有两处频繁访问,我们选择访问次数较少的第二条-显示反汇编程序。
在这里插入图片描述
那就在这里注入吧,前面已经知道,阳光值的地址为[[[基址006A9EC0]->偏移768]->偏移5560],所以要注入的汇编代码可以写成:

push eax
mov eax,[006A9EC0]
mov eax,[eax+768]
mov [eax+5560],0xFFF // 0xFFF为阳光值,别太小就行
pop eax
mov eax,[esi+00005560] //恢复被覆盖掉的代码

而在注入处发现eax被赋值,所以push eax 及 pop eax可以删掉(实际情况实际分析)。
因此,运行流程为:
代码到达"PlantsVsZombies.exe"+89825(0x489825)处时,JMP到空闲空间,执行我们注入的汇编代码,JMP到"PlantsVsZombies.exe"+8982B处继续向后执行,两种方法的示意图:
远程代码注入及DLL注入教程(InlineHook)---植物大战僵尸为例_第4张图片

远程代码注入

我们需要计算0x489825处 JMP的偏移,推导过程如下:

起跳地址(0x489825) + 5(JMP远跳占用固定5字节,0xE9 + 4字节偏移量) + 偏移量 = 目标地址

因此

偏移量 = 目标地址 - (起跳地址 + 5

目标地址需要我们手动申请,代码如下:

//第三个参数为申请大小
LPVOID virAddr = VirtualAllocEx(hProcess, NULL, 64, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

那么偏移jmpOffset为:

DWORD jmpOffset = (DWORD)(0x489825) - ((DWORD)virAddr + 5);

接下来组合0x489825处代码:

BYTE ByteOffset[4];
IntTo4Bytes(jmpOffset , ByteOffset); //int转4字节byte[]
BYTE JmpToNewCode[6] = {0};
JmpToNewCode[0] = 0xE9;         //jmp指令
memcpy(JmpToNewCode + 1, ByteOffset, 4);
JmpToNewCode[5] = 0x90;			//空余的1个字节用nop填充

到这一步时,我们先不要着急WriteProcessMemory写入,因为我们申请的virAddr中还没有写入我们的汇编代码,这时需要使用汇编代码转机器码工具(文中有下载地址),转成机器码,然后写入到virAddr中,如图:
远程代码注入及DLL注入教程(InlineHook)---植物大战僵尸为例_第5张图片

BYTE machinecode[32] = { 161,192,158,106,0,139,128,104,7,0,0,199,128,96,85,0,0,255,15,0,0,139,134,96,85,0,0 };//27

为什么是32字节呢,因为我们的汇编代码还没有JMP跳回0x48982B,需要再加上5字节的JMP 偏移量,计算方法和上面思路一样:

//起跳地址:virAddr + 27   目标地址:0x48982B
DWORD returnoffset = (DWORD)(0x48982B) - ((DWORD)virAddr + 27 + 5); 

组合注入的机器码:

BYTE ByteOffset[4];
IntTo4Bytes(returnoffset, ByteOffset);
BYTE ReturnCode[5] = { 0 };
ReturnCode[0] = 0xE9;         //jmp指令
memcpy(ReturnCode + 1, ByteOffset, 4); //组合jmp
memcpy(machinecode + 27, ReturnCode, 5);//组合完整机器码

在注入前,先把原来汇编指令保存下来,方便还原代码。

BYTE OldCode[6] = { 0 };
ReadProcessMemory(hProcess, (LPVOID)0x489825, OldCode, 6, NULL)

OK,把machinecode写入到virAddr中:

WriteProcessMemory(hProcess, virAddr, machinecode, 32, NULL);

最后再修改注入点(0x489825):

WriteProcessMemory(hProcess, (LPVOID)0x489825, JmpToNewCode, 6, NULL);

至此,代码注入功能基本完毕,编译,运行,测试完美无限阳光。
恢复原来功能只需要:

WriteProcessMemory(hProcess, (LPVOID)0x489825, OldCode, 6, NULL);

DLL注入

DLL注入相比代码注入少了手动开辟内存以及计算跳回偏移的过程,更加简单,步骤如下:
VS创建DLL工程,在DLL_PROCESS_ATTACH中码功能:
远程代码注入及DLL注入教程(InlineHook)---植物大战僵尸为例_第6张图片
内联代码:

BYTE OldJmp[6] = { 0 };
DWORD JmpBack = 0x48982B;

__declspec(naked) void InfinateSunshine()
{	
	// 不加 dword ptr ss 编译器会翻译成单字赋值,原因不明(求大佬解释)
	__asm
	{
		mov eax, dword ptr ss: [0x006A9EC0]
		mov eax, dword ptr ss : [eax + 0x00000768]
		mov dword ptr ss : [eax + 0x00005560], 0x00000FFF
		mov eax, dword ptr ss : [esi + 0x5560]
		jmp	JmpBack
	}
}

计算偏移方法和上面一致:

//计算偏移,InfinateSunshine 即为目标函数
DWORD offset = (DWORD)InfinateSunshine - (0x489825 + 5);
BYTE NewJmp[6] = {0};
NewJmp[0] = 0xE9;
BYTE ByteOffset[4];
IntTo4Bytes(offset, ByteOffset);
memcpy(NewJmp + 1, ByteOffset, 4);
NewJmp[5] = 0x90;

nRet = WriteProcessMemory(hProcess, (LPVOID)0x489825, NewJmp, 6, NULL);

编译后,用注入工具注入:
远程代码注入及DLL注入教程(InlineHook)---植物大战僵尸为例_第7张图片
成功。
但是,如果你再卸载掉DLL,会发现游戏崩溃,原因为在卸载DLL时不会自动还原0x489825 处的代码,因此需要再添加逻辑,卸载时复原代码。
远程代码注入及DLL注入教程(InlineHook)---植物大战僵尸为例_第8张图片远程代码注入及DLL注入教程(InlineHook)---植物大战僵尸为例_第9张图片

再分析

后来经过分析,发现无限阳光插入处的代码还可以再优化,因为插入处的代码只有阳光访问时才会到达,如何判断呢,可以先用CE代码注入写一个判断代码,和当前的阳光地址做对比,加入一段没有意义的代码并设置断点,看能否断下,如果没有断下,那说明注入处只会被阳光访问,如图:
远程代码注入及DLL注入教程(InlineHook)---植物大战僵尸为例_第10张图片
故[esi+00005560]一定为阳光地址,因此可以省掉使用基址加偏移的方式,注入处代码可以修改为更简单的:

mov [esi+00005560],0xffff  //确定为阳光唯一,直接赋值
mov eax,[esi+00005560] 	   //恢复被覆盖掉的代码

总结

代码注入及DLL注入的关键为
1、注入代码中不要破坏原先堆栈平衡,如果不确定寄存器里的值在我们使用之前是不是有用,最好先pushad再写自己的注入代码,最后popad
2、注入处的JMP偏移及返回的JMP偏移一定要计算准确,否则必出错,可以配合OD和CE进行调试检查。

你可能感兴趣的:(逆向学习)