欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-14 11:12:33
...

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

说明

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

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

所需基础

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

涉及工具

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

开始

无限冷却

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

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

无限阳光

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

远程代码注入

我们需要计算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)---植物大战僵尸为例

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)---植物大战僵尸为例
内联代码:

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)---植物大战僵尸为例
成功。
但是,如果你再卸载掉DLL,会发现游戏崩溃,原因为在卸载DLL时不会自动还原0x489825 处的代码,因此需要再添加逻辑,卸载时复原代码。
远程代码注入及DLL注入教程(InlineHook)---植物大战僵尸为例远程代码注入及DLL注入教程(InlineHook)---植物大战僵尸为例

总结

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