植物大战僵尸修改器制作--从入门到入土
基础准备示例游戏版本: 中文年度加强版1.1.0.1056 主要参考资料 基址偏移表部分参考: 公布我所找到的所有基址及各种功能实现方法 基址 0x00355E0C 阳光 +868 +5578 金钱 +950 +50 花肥 +950 +220 巧克力 +950 +250 树肥 +950 +258 树高 +950 +11C 杀虫剂 +950 +224 卡槽数 +868 +15C +24 卡槽栏 +868 +15C +5C 此后每个植物栏相隔0x50 植物当前冷却值 +868 +15C +4C 此后每个植物冷却相隔0x50 植物冷却值上限 +868 +15C +50 此后每个植物冷却上限相隔0x50 植物当前数量 +868 +D4 植物种植函数EBP +868 僵尸当前数量 +868 +B8 僵尸种植函数EBP +868 +178 常规项目根据变量的变化使用CE寻找,找到之后再通过指针扫描寻找可用的基址 阳光 内存实际值=游戏显示值 智慧树高度 内存实际值=游戏显示值 金钱 内存实际值=游戏显示值/10 花肥,杀虫剂,巧克力,树肥 内存实际值=游戏显示值+1000 关键函数和变量 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| enum Type {
Sunlight, Money, TreeHeight, Chocolate, TreeFood, FlowerFood, Insecticide
};
//定义映射表用于保存各项偏移值
unsigned int offsetTable[10] = { 0x5578,0x50,0x11c,0x250,0x258,0x220,0x224 };
//获取某些项目的值
unsigned int getSomething(HANDLE handle, DWORD BaseAddr,unsigned int type) {
unsigned int num = 0;
DWORD addr = BaseAddr + 0x00355E0C;
ReadProcessMemory(handle, addr, &addr, sizeof(DWORD), NULL);
if (type == Sunlight)
addr += 0x868;
else
addr += 0x950;
ReadProcessMemory(handle, (LPVOID)addr, &addr, sizeof(DWORD), NULL);
addr += offsetTable[type];
ReadProcessMemory(handle, (LPVOID)addr, &num, sizeof(DWORD), 0);
return num;
}
//设置某些项目的值
void setSomething(HANDLE handle, DWORD BaseAddr,unsigned int type, unsigned int num) {
DWORD addr = BaseAddr + 0x00355E0C;
ReadProcessMemory(handle, addr, &addr, sizeof(DWORD), NULL);
if (type == Sunlight)
addr += 0x868;
else
addr += 0x950;
ReadProcessMemory(handle, (LPVOID)addr, &addr, sizeof(DWORD), NULL);
addr += offsetTable[type];
WriteProcessMemory(handle, (LPVOID)addr, &num, sizeof(DWORD), 0);
}
| 卡槽植物十个卡槽,每个卡槽对应一个植物,可以在坚果保龄球2中根据卡槽1(最左边的卡槽)的坚果变化来找到卡槽的地址,之后再寻找基址 具体方法: 初值未知,如果卡槽1的植物和新的卡槽1(原卡槽2)的植物相同,则扫不变的值,否则扫变化的值 卡槽之间的偏移可以通过浏览卡槽1内存区域看出,为0x50 坚果植物卡槽编号: 普通坚果 3 爆炸坚果 49 巨型坚果 50 设置卡槽植物函数 1
2
3
4
5
6
7
8
9
10
11
| //设置卡槽植物
BOOL SetPlantCard(HANDLE hProcess,DWORD BaseAddr,DWORD nCard,DWORD plantType) {
DWORD cardAddr = BaseAddr + 0x355E0C;
ReadProcessMemory(hProcess, cardAddr, &cardAddr, sizeof(DWORD), NULL);
cardAddr += 0x868;
ReadProcessMemory(hProcess, cardAddr, &cardAddr, sizeof(DWORD), NULL);
cardAddr += 0x15C;
ReadProcessMemory(hProcess, cardAddr, &cardAddr, sizeof(DWORD), NULL);
cardAddr += 0x5C+nCard*0x50;//卡槽偏移
return WriteProcessMemory(hProcess, cardAddr, &plantType, sizeof(DWORD), NULL);
}
| 种植无冷却具体方法: 仅针对一个卡槽,初始值未知,种植后持续变化,冷却完毕后不变,反复扫描并查找基址,查看对应内存区域再对照植物编号可以发现卡槽间的偏移为0x50 冷却特点: 可种植状态冷却值为0,种植后冷却值持续增长,到达冷却上限后,冷却值清零,植物重新可种植 注意: 直接将冷却值置0会导致无法种植 修改方法: - 修改冷却结束后恢复的速度,将inc指令修改为mov一个较大值 这个
- 直接跳转到冷却值和冷却上限比较成功的函数
以方法2为例 7E 16 对应汇编指令为 jle 0x18 修改为jmp $+2 即 eb 00 (相对当前指令2字节后的指令) 直接执行冷却值达到冷却上限后的函数(冷却值清零,植物冷却完毕可种植)
植物大战僵尸修改器制作--从入门到入土
关键代码 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| //修改进程代码区代码 参数: 进程句柄 修改代码起始地址 硬编码指针 代码字节数
BOOL WriteProcessCodeMemory(HANDLE hProcess, LPVOID lpStartAddress, LPCVOID lpBuffer, SIZE_T nSize) {
DWORD dwOldProtect;
//取消页保护
if (!VirtualProtectEx(hProcess, lpStartAddress, nSize, PAGE_EXECUTE_READWRITE, &dwOldProtect)) {
return FALSE;
}
BOOL bResult = WriteProcessMemory(hProcess, lpStartAddress, lpBuffer, nSize, NULL);//写入代码
VirtualProtectEx(hProcess, lpStartAddress, nSize, dwOldProtect, &dwOldProtect);//开启页保护
return bResult;
}
//无限冷却
BOOL Uncooled(HANDLE hProcess, DWORD BaseAddr) {
unsigned char code[2] = { 0xeb,0x00 };
return WriteProcessCodeMemory(hProcess, BaseAddr + 0x9ce02, code, 2);//jle 0x18修改为jmp $+2
}
//恢复冷却
BOOL RecoveryCooling(HANDLE hProcess, DWORD BaseAddr) {
unsigned char OriginalCode[2] = { 0x7E ,0x16 };//jmp $+2恢复为jle 0x18
return WriteProcessCodeMemory(hProcess, BaseAddr + 0x9ce02, OriginalCode, 2);
}
| 无限阳光前文已经给出了阳光的地址 基址为0x355E0C 偏移+868 +5578 查找对阳光修改的代码即可 阳光减少代码
植物大战僵尸修改器制作--从入门到入土
阳光增加代码
植物大战僵尸修改器制作--从入门到入土
基本过程: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| //修改进程代码区代码 参数: 进程句柄 修改代码起始地址 硬编码指针 代码字节数
BOOL WriteProcessCodeMemory(HANDLE hProcess, LPVOID lpStartAddress, LPCVOID lpBuffer, SIZE_T nSize) {
DWORD dwOldProtect;
//取消页保护
if (!VirtualProtectEx(hProcess, lpStartAddress, nSize, PAGE_EXECUTE_READWRITE, &dwOldProtect)) {
return FALSE;
}
BOOL bResult = WriteProcessMemory(hProcess, lpStartAddress, lpBuffer, nSize, NULL);//写入代码
VirtualProtectEx(hProcess, lpStartAddress, nSize, dwOldProtect, &dwOldProtect);//开启页保护
return bResult;
}
//无限阳光,锁定阳光为9999
BOOL UnlimitedSun(HANDLE hProcess, DWORD BaseAddr) {
unsigned char Code[3] = { 0x29,0xdb,0 };//cmp ebx,eax 修改为sub ebx,ebx and ecx,0x32修改为and ecx,0
BOOL flag;
flag = setSomething(hProcess, BaseAddr, Sunlight, 9999);//修改阳光
flag &= WriteProcessCodeMemory(hProcess, BaseAddr + 0x27690, Code, 2);//修改阳光减少代码
flag &= WriteProcessCodeMemory(hProcess, BaseAddr + 0x3C0AB, &Code[2], 1);//修改阳光增加代码
return flag;
}
//恢复阳光消耗
BOOL RecoverySunConsume(HANDLE hProcess, DWORD BaseAddr) {
unsigned char OriginalCode[3] = { 0x3B,0xD8,0x32 };//sub ebx,ebx恢复为cmp ebx,eax and ecx,0恢复为and ecx,0x32
BOOL flag = WriteProcessCodeMemory(hProcess, BaseAddr + 0x27690, OriginalCode, 2);//恢复阳光减少代码
flag &= WriteProcessCodeMemory(hProcess, BaseAddr + 0x3C0AB, &OriginalCode[2], 1);//恢复阳光增加代码
return flag;
}
| 浓雾透视基本原理具体方法: 在生存模式浓雾进行,初值未知,通过在雾区种植和铲除路灯花引起的变化来判断,最终可以发现是4字节数据,数值代表雾的浓度,255代表浓雾,0代表没雾,再查找修改雾值的代码 寻找浓雾地址
植物大战僵尸修改器制作--从入门到入土
浓雾修改代码 mov [ecx],edx这行代码修改了雾值,可以改为mov [ecx],0 注意硬编码为0xc7,0x01,0x00,0x00,0x00,0x00 由于较长无法直接修改代码,所以这里选择使用hook技术
植物大战僵尸修改器制作--从入门到入土
HOOKhook的基本过程 - 读取并保存目的地址原始代码
- 申请空间(PVZ游戏进程空间)用于存储原始代码 hook代码 jmp返回代码
- 向申请的空间中写入原始代码 hook代码 jmp返回代码
- 修改目的地址的代码为jmp HookCode
- 返回HookCode首地址 用于解除hook
值得一提的是jmp指令后跟的偏移值是以jmp的下一条指令首地址计算 jmp指令偏移值=目的地址-(jmp指令首地址+5) 这里的5是jmp指令本身的长度 +5便是下一条指令 offset=desAddr-(jmpAddr+5) 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
| //修改进程代码区代码 参数: 进程句柄 修改代码起始地址 硬编码指针 代码字节数
BOOL WriteProcessCodeMemory(HANDLE hProcess, LPVOID lpStartAddress, LPCVOID lpBuffer, SIZE_T nSize) {
DWORD dwOldProtect;
//取消页保护
if (!VirtualProtectEx(hProcess, lpStartAddress, nSize, PAGE_EXECUTE_READWRITE, &dwOldProtect)) {
return FALSE;
}
BOOL bResult = WriteProcessMemory(hProcess, lpStartAddress, lpBuffer, nSize, NULL);//写入代码
VirtualProtectEx(hProcess, lpStartAddress, nSize, dwOldProtect, &dwOldProtect);//开启页保护
return bResult;
}
//hook指定地址,申请新空间保存原始代码并写入hookcode,返回申请空间的地址
LPVOID SetHook(HANDLE hProcess, LPVOID desAddr, LPCVOID hookCode, SIZE_T hookCodeSize, SIZE_T origCodeSize) {
BYTE origCode[10] = { 0 }, jmpCode[5] = { 0xE9,0,0,0,0 };
//1. 读取并保存原始代码
if (!ReadProcessMemory(hProcess, desAddr, origCode, origCodeSize, NULL))
return NULL;
//2. 申请空间用于存储原始代码,hook代码,jmp返回代码
LPVOID allocAddr = VirtualAllocEx(hProcess, NULL, hookCodeSize + origCodeSize + 5, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!allocAddr)
return NULL;
//3. 向申请空间写入原始代码,hook代码,jmp返回代码 jmp xxx 偏移为目的地址-jmp下一条指令地址
*(DWORD*)(jmpCode + 1) = (DWORD)desAddr + 5 - ((DWORD)allocAddr + hookCodeSize + origCodeSize + 5);//hook返回地址的偏移
if (!WriteProcessCodeMemory(hProcess, allocAddr, origCode, origCodeSize) //写入原始代码
|| !WriteProcessCodeMemory(hProcess, (DWORD)allocAddr + origCodeSize, hookCode, hookCodeSize)//写入hook代码
|| !WriteProcessCodeMemory(hProcess, (DWORD)allocAddr + origCodeSize + hookCodeSize, jmpCode, 5))//写入jmpcode
{
VirtualFreeEx(hProcess, allocAddr, 0, MEM_RELEASE);//写入失败则释放空间
return NULL;
}
//4. 修改目的地址处的代码 jmp xxx偏移 原始代码后才是需要执行的hook代码
*(DWORD*)(jmpCode + 1) = ((DWORD)allocAddr + origCodeSize) - ((DWORD)desAddr + 5);
WriteProcessCodeMemory(hProcess, desAddr, jmpCode, 5);//在源地址处写入跳转代码
if (origCodeSize > 5)//原始代码长度大于5时nop多余字节
{
BYTE nopCode[5] = { 0x90,0x90,0x90,0x90,0x90 };
if (!WriteProcessCodeMemory(hProcess, (DWORD)desAddr + 5, nopCode, origCodeSize - 5))
{
VirtualFreeEx(hProcess, allocAddr, 0, MEM_RELEASE);//写入nopcode失败则释放空间并返回
return NULL;
}
}
//5. hook成功则返回hookCode所在地址
return allocAddr;
}
//取消hook指定地址,写回原始代码并释放申请空间
BOOL UnHook(HANDLE hProcess, LPVOID desAddr, SIZE_T origCodeSize, LPVOID allocAddr) {
BYTE origCode[10] = { 0 };
//1. 从申请空间中读出原始代码
if (!ReadProcessMemory(hProcess, allocAddr, origCode, origCodeSize, NULL))
return FALSE;
//2. 将原始代码写回目的地址
if (!WriteProcessCodeMemory(hProcess, desAddr, origCode, origCodeSize))
return FALSE;
//3. 释放申请空间
if (!VirtualFreeEx(hProcess, allocAddr, 0, MEM_RELEASE))
return FALSE;
return TRUE;
}
| 除雾代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| //除雾 注意保留hook代码首地址
LPVOID DeFogByHook(HANDLE hProcess, LPVOID BaseAddr) {
unsigned char hookCode[9] = {
0xc7,0x01,0x00,0x00,0x00,0x00, //mov [ecx],0
0x83,0xc1,0x04 //add ecx,0x4
};
//写入hook代码进行hook
return SetHook(hProcess, (DWORD)BaseAddr + 0x26173, hookCode, sizeof(hookCode), 5);
}
//恢复雾
BOOL RecoveryFogByUnHook(HANDLE hProcess, LPVOID BaseAddr, LPVOID allocAddr) {
return UnHook(hProcess, (DWORD)BaseAddr + 0x26173, 5, allocAddr);
}
|
hook前 指令为mov [ecx],edx add ecx,04
植物大战僵尸修改器制作--从入门到入土
hook后 指令被修改为jmp
植物大战僵尸修改器制作--从入门到入土
hookcode 新分配空间前5个字节正是原始代码 之后是hook代码和jmp返回代码
植物大战僵尸修改器制作--从入门到入土
种植植物基本原理程序是执行种植植物的函数后再执行增加植物数量的功能 首先查找草坪上的植物数量,初值0,随着种植个数增加 基址0x355E0C 偏移+868 +D4 再查找是什么修改了植物数量,下断点之后再种植一个植物 断下后查看调用堆栈中的返回地址,即可找到种植函数
植物大战僵尸修改器制作--从入门到入土
这个功能最初使用远程线程注入dll来实现,注入dll虽然比较简单但是却并不通用,在此仅做介绍,比较推荐使用远程代码注入的方式实现 远程线程注入dll函数远程线程是当前进程在目标进程中创建一个线程并执行特定代码(这段代码必须在目标进程中而不是当前进程中) 注入dll是因为dll在被进程或线程加载时执行dll的DllMain函数,通过这一特点我们可以实现一些特殊功能 优点: 便于实现 缺点: dll注入容易被检测到 基本过程: - 打开进程获取进程句柄
- 在目标进程中申请空间用于存储dll路径名
- 将dll路径名写入申请的空间中
- 创建远程线程,执行LoadLibrary函数(加载dll)
- 目标进程加载dll后自动执行dll的DllMain函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| //创建远程线程方式向指定进程注入dll
BOOL InjectDllByRemoteThread(DWORD desProcId,WCHAR* dllPath) {
//打开进程获取进程句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, desProcId);
if (!hProcess)
return FALSE;
//申请空间
DWORD pathSize = (wcslen(dllPath) + 1) * 2;
LPVOID newMemAddr = VirtualAllocEx(hProcess, 0, pathSize, MEM_COMMIT, PAGE_READWRITE);
if (!newMemAddr)
return FALSE;
//写入dll路径
if (!WriteProcessMemory(hProcess, newMemAddr, dllPath, pathSize, NULL))
{
VirtualFreeEx(hProcess, newMemAddr, 0, MEM_RELEASE);
return FALSE;
}
//创建远程线程
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryW, newMemAddr, 0, NULL);
if (!hThread)
{
VirtualFreeEx(hProcess, newMemAddr, 0, MEM_RELEASE);
return FALSE;
}
WaitForSingleObject(hThread, INFINITE);//等待线程信号,保证成功注入
//回收资源
VirtualFreeEx(hProcess, newMemAddr, 0, MEM_RELEASE);
CloseHandle(hThread);
CloseHandle(hProcess);
//返回成功
return TRUE;
}
| 远程线程卸载dll函数很多教程只给出了如何注入dll,没有演示如何卸载 如果只注入不卸载会导致下次再注入时不会执行特定函数(由于dll已经被加载过) 不方便实时调试更新dll等问题 基本过程: - 在目标进程申请内存,将需要卸载的dll模块名称写入该内存
- 通过枚举模块来查找指定模块
- 成功查找到dll模块则创建远程线程执行FreeLibrary函数卸载dll
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
| BOOL UnLoadDllByRemoteThread(DWORD dwProcessId, LPCWSTR lpDllName)
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (hProcess == NULL)
return FALSE;
// 在目标进程中申请一块内存,并将需要卸载的DLL模块的名称写入该内存
LPVOID lpRemoteDllName = VirtualAllocEx(hProcess, NULL, (wcslen(lpDllName) + 1) * sizeof(WCHAR), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (lpRemoteDllName == NULL)
{
CloseHandle(hProcess);
return FALSE;
}
if (!WriteProcessMemory(hProcess, lpRemoteDllName, lpDllName, (wcslen(lpDllName) + 1) * sizeof(WCHAR), NULL))
{
VirtualFreeEx(hProcess, lpRemoteDllName, 0, MEM_RELEASE);
CloseHandle(hProcess);
return FALSE;
}
//查找dll模块
HMODULE hModules[1024],DesModule=NULL;
DWORD dwSize = 0;
if (!EnumProcessModules(hProcess, hModules, sizeof(hModules), &dwSize))
{
VirtualFreeEx(hProcess, lpRemoteDllName, 0, MEM_RELEASE);
CloseHandle(hProcess);
return FALSE;
}
// 遍历模块列表,查找需要卸载的DLL模块
for (DWORD i = 0; i < (dwSize / sizeof(HMODULE)); i++)
{
WCHAR szModuleName[MAX_PATH] = { 0 };
if (GetModuleFileNameExW(hProcess, hModules, szModuleName, MAX_PATH) > 0)
{
// 获取模块句柄
if (wcsicmp(szModuleName, lpDllName) == 0)
{
DesModule = hModules;
}
}
}
//没有查找到模块
if (!DesModule) {
VirtualFreeEx(hProcess, lpRemoteDllName, 0, MEM_RELEASE);
CloseHandle(hProcess);
return FALSE;
}
// 在目标进程中创建远程线程,执行FreeLibrary函数
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)FreeLibrary, DesModule, 0, NULL);
if (hThread == NULL)
{
VirtualFreeEx(hProcess, lpRemoteDllName, 0, MEM_RELEASE);
CloseHandle(hProcess);
return FALSE;
}
// 等待线程执行完成
WaitForSingleObject(hThread, INFINITE);
// 关闭句柄
CloseHandle(hThread);
VirtualFreeEx(hProcess, lpRemoteDllName, 0, MEM_RELEASE);
CloseHandle(hProcess);
return TRUE;
}
| 关键dll函数这里使用了三种方法 注意: 不要将代码写入switch(reason)之外,否则可能会导致多次执行 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
| #include
#include
//调用函数
BOOL GrowPlant(DWORD BaseAddr, DWORD x, DWORD y, DWORD TypePlant) {
LPVOID PlantFunc = BaseAddr + 0x18D70;
__asm {
pushad
push -1 //-1
push TypePlant //植物类型
mov eax, y //y
push x //x
mov ecx, BaseAddr
mov ecx, [ecx+0x355E0C]
mov ecx, [ecx + 0x868]
push ecx //植物种植ebp
call PlantFunc
popad
}
return TRUE;
}
BOOL WINAPI DllMain(HMODULE hInstance, DWORD fdwReason, LPVOID lpReserved) {
DWORD BaseAddr = GetModuleHandle(NULL);
DWORD pid = GetCurrentProcessId();
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
LPVOID PlantFunc = BaseAddr + 0x18D70;
DWORD ebpAddr = BaseAddr+0x355E0C,num=0;
ReadProcessMemory(hProcess, ebpAddr, &ebpAddr, sizeof(DWORD), NULL);
ebpAddr += 0x868;
ReadProcessMemory(hProcess, ebpAddr, &ebpAddr, sizeof(DWORD), NULL);//必须使用带hProcess参数的才能正确读取到地址,NULL不可以
DWORD x = 1, y = 1, TypePlant = 16;
//注意不要写到switch外,否则可能会一次种多株植物,猜测是dll被多个线程加载导致的
switch (fdwReason)
{
case DLL_PROCESS_ATTACH: //当进程加载dll模块时执行
//MessageBoxW(0, L"ProcessAttach!", L"window2", 0);
//1.直接通过使用ReadProcessMemory函数读取内存获取ebp参数
__asm {
pushad
push - 1 //-1
push TypePlant //植物类型
mov eax, y //y
push x //x
push ebpAddr //ebp
call PlantFunc
popad
}
//2.通过利用寄存器获取ebp(推荐)
x = 3, y = 2, TypePlant = 18;
__asm {
pushad
push - 1 //-1
push TypePlant //植物类型
mov eax, y //y
push x //x
mov ecx, BaseAddr
mov ecx, [ecx+0x355E0C]
mov ecx, [ecx + 0x868]
push ecx
call PlantFunc
popad
}
//3. 通过调用函数(推荐)
GrowPlant(BaseAddr,7,3,23);
break;
//case DLL_THREAD_ATTACH:
// printf("ThreadAttach!");
// break;
//case DLL_THREAD_DETACH:
// if (lpReserved == NULL)
// {
// FreeLibrary(hInstance);
// }
break;
case DLL_PROCESS_DETACH: //当进程卸载dll模块时执行
MessageBoxW(0, L"ProcessDeTachDll!", L"window2", 0);
break;
}
return TRUE;
}
|
执行结果
植物大战僵尸修改器制作--从入门到入土
失败代码这是写dll函数时遇到的问题 如果直接用 mov ecx,[BaseAddr+0x355E0C]会导致代码执行失败,推测是这条指令访存过慢所以无效 建议mov ecx,BaseAddr之后通过对寄存器操作达到目的 1
2
3
4
5
6
7
8
9
10
11
12
13
| __asm {
pushad
push - 1 //-1
push TypePlant //植物类型
mov eax, y //y
push x //x
mov ecx,[BaseAddr+ 0x355E0C]//这样不行,推测是访存过慢
mov ecx,[ecx+0x868]
mov num, ecx
push ecx
call PlantFunc
popad
}
| 远程线程代码注入(推荐)和远程线程dll注入类似,CreateRemoteThread函数要求的函数原型是 1
2
3
| DWORD WINAPI ThreadProc(
_In_ LPVOID lpParameter//使用CreateThread函数传递的参数 该参数是一个指向其他数据的指针,当然也可以强转为其他类型直接使用
);
|
基本过程: - 打开进程
- 定义注入代码(函数)
- 在目标进程中申请空间并写入注入代码
- 创建远程线程执行注入代码(函数)
- 执行完毕释放空间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
| //以创建远程线程方式种植植物
BOOL GrowPlantByInjectCode(DWORD dwProcessId,DWORD BaseAddr,DWORD x,DWORD y,DWORD PlantType)
{
BOOL bSuccess = FALSE;
//1. 打开进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (hProcess != NULL)
{
//2. 定义注入代码(函数)
BYTE InjectCode[50] = { //汇编指令 //修正点偏移
0x55, //0 push ebp
0x89, 0xE5, //1 mov ebp,esp
0x60, //3 pushad
0x68, 0xFF, 0xFF, 0xFF, 0xFF, //4 push -1
0x68, 0x00, 0x00, 0x00, 0x00, //9 push PlantType //10
0xB8, 0x00, 0x00, 0x00, 0x00, //14 mov eax,y //15
0x68, 0x00, 0x00, 0x00, 0x00, //19 push x //20
0xB9, 0x00, 0x00, 0x00, 0x00, //24 mov ecx,BaseAddr //25
0x8B, 0x89, 0x0C, 0x5E, 0x35, 0x00, //29 mov ecx,[ecx+0x355E0C]
0x8B, 0x89, 0x68, 0x08, 0x00, 0x00, //35 mov ecx,[ecx+0x868]
0x51, //41 push ecx
0xE8, 0x00, 0x00, 0x00, 0x00, //42 call PlantFunc //43 //被调方平栈
0x61, //47 popad
0xC9, //48 leave
0xC3 //49 ret
};
//3. 申请空间用于存储代码
DWORD dwCodeSize = 50, desFunc = BaseAddr + 0x18D70;
LPVOID lpRemoteCodeMem = VirtualAllocEx(hProcess, NULL, dwCodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
//4. 修正参数
*(DWORD*)&InjectCode[10] = PlantType;
*(DWORD*)&InjectCode[15] = y;
*(DWORD*)&InjectCode[20] = x;
*(DWORD*)&InjectCode[25] = BaseAddr;
*(DWORD*)&InjectCode[43] = desFunc-((DWORD)lpRemoteCodeMem+42+5) ;
//call指令与jmp类似,相对于当前指令的下一条指令计算偏移,offset=des-(source+5),减去call自身长度5
if (lpRemoteCodeMem != NULL)
{
SIZE_T dwBytesWritten = 0;
//5. 注入代码
if (WriteProcessMemory(hProcess, lpRemoteCodeMem, InjectCode, dwCodeSize, &dwBytesWritten) &&
dwBytesWritten == dwCodeSize)
{
//6. 创建远程线程执行代码
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpRemoteCodeMem,NULL, 0, NULL);
if (hThread != NULL)
{
//7. 等待线程信号
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
bSuccess = TRUE;
}
}
//8. 执行完后释放空间
VirtualFreeEx(hProcess, lpRemoteCodeMem, 0, MEM_RELEASE);
}
CloseHandle(hProcess);
}
return bSuccess;
}
| 种植僵尸基本原理与种植植物思路类似 首先在头脑风暴中通过种植僵尸来找到僵尸数量地址 然后找到僵尸数量增加代码 再通过查看调用堆栈和参数找到种植僵尸call 参数应该也是x y type ebp (注意没有-1) 僵尸种植函数的x值在一个call上方,这个call是个switch结构,没有参数,所以x值也没被修改
植物大战僵尸修改器制作--从入门到入土
种植僵尸函数--dll注入版1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| BOOL GrowZombie(DWORD BaseAddr, DWORD x, DWORD y, DWORD ZombieType) {
LPVOID PlantZombieFunc = BaseAddr + 0x35390;
__asm {
pushad
push x
push ZombieType
mov eax,y
mov ecx,BaseAddr
mov ecx,[ecx+0x355E0C]
mov ecx,[ecx+0x868]
mov ecx,[ecx+0x178] //ebp
call PlantZombieFunc
popad
}
return TRUE;
}
| 远程代码注入版1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
| //以创建远程线程方式种植僵尸
BOOL GrowZombieByRemoteThread(DWORD dwProcessId,DWORD BaseAddr, DWORD x, DWORD y, DWORD ZombieType) {
BOOL bSuccess = FALSE;
//1. 打开进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (hProcess != NULL)
{
//2. 定义注入代码(函数)
BYTE InjectCode[50] = {
0x55, //0 push ebp
0x89, 0xE5, //1 mov ebp,esp
0x60, //3 pushad
0x68, 0x00, 0x00, 0x00, 0x00, //4 push x
0x68, 0x00, 0x00, 0x00, 0x00, //9 push ZombieType
0xB8, 0x00, 0x00, 0x00, 0x00, //14 mov eax,y
0xB9, 0x00, 0x00, 0x00, 0x00, //19 mov ecx,BaseAddr
0x8B, 0x89, 0x0C, 0x5E, 0x35, 0x00, //24 mov ecx,[ecx+0x355E0C]
0x8B, 0x89, 0x68, 0x08, 0x00, 0x00, //30 mov ecx,[ecx+0x868]
0x8B, 0x89, 0x78, 0x01, 0x00, 0x00, //36 mov ecx,[ecx+0x178]
0xE8, 0x00, 0x00, 0x00, 0x00, //42 call PlantZombieFunc
0x61, //47 popad
0xC9, //48 leave
0xC3 //49 ret
};
//3. 申请空间用于存储代码
DWORD dwCodeSize = 50, desFunc = BaseAddr + 0x35390; //种植僵尸函数
LPVOID lpRemoteCodeMem = VirtualAllocEx(hProcess, NULL, dwCodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
//4. 修正参数
*(DWORD*)&InjectCode[5] = x;
*(DWORD*)&InjectCode[10] = ZombieType;
*(DWORD*)&InjectCode[15] = y;
*(DWORD*)&InjectCode[20] = BaseAddr;
*(DWORD*)&InjectCode[43] = desFunc - ((DWORD)lpRemoteCodeMem + 42 + 5);//call指令与jmp类似,相对于当前指令的下一条指令计算偏移,要减去call长度5
if (lpRemoteCodeMem != NULL)
{
SIZE_T dwBytesWritten = 0;
//5. 注入代码
if (WriteProcessMemory(hProcess, lpRemoteCodeMem, InjectCode, dwCodeSize, &dwBytesWritten) &&
dwBytesWritten == dwCodeSize)
{
//6. 创建远程线程执行代码
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpRemoteCodeMem, NULL, 0, NULL);
if (hThread != NULL)
{
//7. 等待线程信号
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
bSuccess = TRUE;
}
}
//8. 执行完后释放空间
VirtualFreeEx(hProcess, lpRemoteCodeMem, 0, MEM_RELEASE);
}
CloseHandle(hProcess);
}
return bSuccess;
}
| 完整代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
| #include
#include
#include
#include
#include
#include
enum Type {
Sunlight, Money, TreeHeight, Chocolate, TreeFood, FlowerFood, Insecticide
};
unsigned int offsetTable[10] = { 0x5578,0x50,0x11c,0x250,0x258,0x220,0x224 };
// 根据进程名获取进程ID
DWORD GetProcessIdByName(const wchar_t* processName) {
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);// 创建一个进程快照
if (snapshot == INVALID_HANDLE_VALUE) {
return 0;// 如果创建失败,返回 0
}
// 定义一个 PROCESSENTRY32 结构体,用于存储进程信息
PROCESSENTRY32 processEntry = { 0 };
processEntry.dwSize = sizeof(PROCESSENTRY32); //必须初始化,否则调用Process32First会失败
if (!Process32First(snapshot, &processEntry)) {
CloseHandle(snapshot);
return 0;// 如果获取第一个进程信息失败,关闭进程快照句柄并返回 0
}
// 遍历进程列表
do {
wchar_t currentProcessName[MAX_PATH]; // 获取当前进程的名称
wcscpy_s(currentProcessName, MAX_PATH, processEntry.szExeFile); //szExeFile存储了进程对应可执行文件的名称
if (wcscmp(currentProcessName, processName) == 0) {
CloseHandle(snapshot); // 如果当前进程名称和指定的进程名称相同,返回进程 ID
return processEntry.th32ProcessID;
}
} while (Process32Next(snapshot, &processEntry)); //获取快照中下一个进程的信息
// 如果遍历完整个进程列表都没有找到指定进程,关闭进程快照句柄并返回 0
CloseHandle(snapshot);
return 0;
}
//根据进程模块名获取基址
LPVOID GetModuleBaseAddress(DWORD processId, LPCWSTR moduleName) {
LPVOID lpBaseAddress = NULL;
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId); // 打开进程句柄
if (hProcess != NULL) {
// 枚举进程中的所有模块
HMODULE hMods[1024];
DWORD cbNeeded;
if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) {
DWORD dwModuleCount = cbNeeded / sizeof(HMODULE);// 计算模块数量
// 获取指定模块的信息
for (DWORD i = 0; i < dwModuleCount; i++) {
TCHAR szModName[MAX_PATH];
//获取指定模块的完整路径名
if (GetModuleFileNameEx(hProcess, hMods, szModName, MAX_PATH)) {//函数成功返回字符串长度,注意第四个参数的单位为字符而非字节
if (wcsstr(szModName, moduleName)) {//查找模块名,若成功则返回子串第一次出现的指针
MODULEINFO modInfo = { 0 };
if (GetModuleInformation(hProcess, hMods, &modInfo, sizeof(MODULEINFO))) {//获取模块信息并保存到modInfo中
lpBaseAddress = modInfo.lpBaseOfDll;//模块基地址
break;
}
}
}
}
}
CloseHandle(hProcess); // 关闭进程句柄
}
return lpBaseAddress;
}
//修改进程代码区代码 参数: 进程句柄 修改代码起始地址 硬编码指针 代码字节数
BOOL WriteProcessCodeMemory(HANDLE hProcess, LPVOID lpStartAddress, LPCVOID lpBuffer, SIZE_T nSize) {
DWORD dwOldProtect;
//取消页保护
if (!VirtualProtectEx(hProcess, lpStartAddress, nSize, PAGE_EXECUTE_READWRITE, &dwOldProtect)) {
return FALSE;
}
BOOL bResult = WriteProcessMemory(hProcess, lpStartAddress, lpBuffer, nSize, NULL);//写入代码
VirtualProtectEx(hProcess, lpStartAddress, nSize, dwOldProtect, &dwOldProtect);//开启页保护
return bResult;
}
//hook指定地址,申请新空间保存原始代码并写入hookcode,返回申请空间的地址
LPVOID SetHook(HANDLE hProcess, LPVOID desAddr, LPCVOID hookCode, SIZE_T hookCodeSize, SIZE_T origCodeSize) {
BYTE origCode[10] = { 0 }, jmpCode[5] = { 0xE9,0,0,0,0 };
//1. 读取并保存原始代码
if (!ReadProcessMemory(hProcess, desAddr, origCode, origCodeSize, NULL))
return NULL;
//2. 申请空间用于存储原始代码,hook代码,jmp返回代码
LPVOID allocAddr = VirtualAllocEx(hProcess, NULL, hookCodeSize + origCodeSize + 5, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!allocAddr)
return NULL;
//3. 向申请空间写入原始代码,hook代码,jmp返回代码 jmp xxx 偏移为目的地址-jmp下一条指令地址
*(DWORD*)(jmpCode + 1) = (DWORD)desAddr + 5 - ((DWORD)allocAddr + hookCodeSize + origCodeSize + 5);//hook返回地址的偏移
if (!WriteProcessCodeMemory(hProcess, allocAddr, origCode, origCodeSize) //写入原始代码
|| !WriteProcessCodeMemory(hProcess, (DWORD)allocAddr + origCodeSize, hookCode, hookCodeSize)//写入hook代码
|| !WriteProcessCodeMemory(hProcess, (DWORD)allocAddr + origCodeSize + hookCodeSize, jmpCode, 5))//写入jmpcode
{
VirtualFreeEx(hProcess, allocAddr, 0, MEM_RELEASE);//写入失败则释放空间
return NULL;
}
//4. 修改目的地址处的代码 jmp xxx偏移 原始代码后才是需要执行的hook代码
*(DWORD*)(jmpCode + 1) = ((DWORD)allocAddr + origCodeSize) - ((DWORD)desAddr + 5);
WriteProcessCodeMemory(hProcess, desAddr, jmpCode, 5);//在源地址处写入跳转代码
if (origCodeSize > 5)//原始代码长度大于5时nop多余字节
{
BYTE nopCode[5] = { 0x90,0x90,0x90,0x90,0x90 };
if (!WriteProcessCodeMemory(hProcess, (DWORD)desAddr + 5, nopCode, origCodeSize - 5))
{
VirtualFreeEx(hProcess, allocAddr, 0, MEM_RELEASE);//写入nopcode失败则释放空间并返回
return NULL;
}
}
//5. hook成功则返回hookCode所在地址
return allocAddr;
}
//取消hook指定地址,写回原始代码并释放申请空间
BOOL UnHook(HANDLE hProcess, LPVOID desAddr, SIZE_T origCodeSize, LPVOID allocAddr) {
BYTE origCode[10] = { 0 };
//1. 从申请空间中读出原始代码
if (!ReadProcessMemory(hProcess, allocAddr, origCode, origCodeSize, NULL))
return FALSE;
//2. 将原始代码写回目的地址
if (!WriteProcessCodeMemory(hProcess, desAddr, origCode, origCodeSize))
return FALSE;
//3. 释放申请空间
if (!VirtualFreeEx(hProcess, allocAddr, 0, MEM_RELEASE))
return FALSE;
return TRUE;
}
//获取某些项目的值
unsigned int getSomething(HANDLE handle, DWORD BaseAddr, unsigned int type) {
unsigned int num = 0;
DWORD addr = BaseAddr + 0x00355E0C;
ReadProcessMemory(handle, (LPVOID)addr, &addr, sizeof(DWORD), NULL);
if (type == Sunlight)
addr += 0x868;
else
addr += 0x950;
ReadProcessMemory(handle, (LPVOID)addr, &addr, sizeof(DWORD), NULL);
addr += offsetTable[type];
ReadProcessMemory(handle, (LPVOID)addr, &num, sizeof(DWORD), 0);
return num;
}
//设置某些项目的值
BOOL setSomething(HANDLE handle, DWORD BaseAddr, unsigned int type, unsigned int num) {
DWORD addr = BaseAddr + 0x00355E0C;
ReadProcessMemory(handle, addr, &addr, sizeof(DWORD), NULL);
if (type == Sunlight)
addr += 0x868;
else
addr += 0x950;
ReadProcessMemory(handle, (LPVOID)addr, &addr, sizeof(DWORD), NULL);
addr += offsetTable[type];
return WriteProcessMemory(handle, (LPVOID)addr, &num, sizeof(DWORD), 0);
}
//无限冷却
BOOL Uncooled(HANDLE hProcess, DWORD BaseAddr) {
unsigned char code[2] = { 0xeb,0x00 };
return WriteProcessCodeMemory(hProcess, BaseAddr + 0x9ce02, code, 2);//jle 0x18修改为jmp $+2
}
//恢复冷却
BOOL RecoveryCooling(HANDLE hProcess, DWORD BaseAddr) {
unsigned char OriginalCode[2] = { 0x7E ,0x16 };//jmp $+2修改为jle 0x18
return WriteProcessCodeMemory(hProcess, BaseAddr + 0x9ce02, OriginalCode, 2);
}
//无限阳光,锁定阳光为9999
BOOL UnlimitedSun(HANDLE hProcess, DWORD BaseAddr) {
unsigned char Code[3] = { 0x29,0xdb,0 };//cmp ebx,eax 修改为sub ebx,ebx and ecx,0x32修改为and ecx,0
BOOL flag;
flag = setSomething(hProcess, BaseAddr, Sunlight, 9999);//修改阳光
flag &= WriteProcessCodeMemory(hProcess, BaseAddr + 0x27690, Code, 2);//修改阳光减少代码
flag &= WriteProcessCodeMemory(hProcess, BaseAddr + 0x3C0AB, &Code[2], 1);//修改阳光增加代码
return flag;
}
//恢复阳光消耗
BOOL RecoverySunConsume(HANDLE hProcess, DWORD BaseAddr) {
unsigned char OriginalCode[3] = { 0x3B,0xD8,0x32 };//sub ebx,ebx恢复为cmp ebx,eax and ecx,0恢复为and ecx,0x32
BOOL flag = WriteProcessCodeMemory(hProcess, BaseAddr + 0x27690, OriginalCode, 2);//恢复阳光减少代码
flag &= WriteProcessCodeMemory(hProcess, BaseAddr + 0x3C0AB, &OriginalCode[2], 1);//恢复阳光增加代码
return flag;
}
//除雾
LPVOID DeFogByHook(HANDLE hProcess, LPVOID BaseAddr) {
unsigned char hookCode[9] = {
0xc7,0x01,0x00,0x00,0x00,0x00, //mov [ecx],0
0x83,0xc1,0x04 //add ecx,0x4
};
//写hook代码进行hook
return SetHook(hProcess, (DWORD)BaseAddr + 0x26173, hookCode, sizeof(hookCode), 5);
}
//恢复雾
BOOL RecoveryFogByUnHook(HANDLE hProcess, LPVOID BaseAddr, LPVOID allocAddr) {
return UnHook(hProcess, (DWORD)BaseAddr + 0x26173, 5, allocAddr);
}
//创建远程线程向指定进程注入dll
BOOL InjectDllByRemoteThread(DWORD desProcId,WCHAR* dllPath) {
//打开进程获取进程句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, desProcId);
if (!hProcess)
return FALSE;
//申请空间
DWORD pathSize = (wcslen(dllPath) + 1) * 2;
LPVOID newMemAddr = VirtualAllocEx(hProcess, 0, pathSize, MEM_COMMIT, PAGE_READWRITE);
if (!newMemAddr)
return FALSE;
//写入dll路径
if (!WriteProcessMemory(hProcess, newMemAddr, dllPath, pathSize, NULL))
{
VirtualFreeEx(hProcess, newMemAddr, 0, MEM_RELEASE);
return FALSE;
}
//创建远程线程
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryW, newMemAddr, 0, NULL);
if (!hThread)
{
VirtualFreeEx(hProcess, newMemAddr, 0, MEM_RELEASE);
return FALSE;
}
WaitForSingleObject(hThread, INFINITE);//等待线程信号,保证成功注入
//回收资源
VirtualFreeEx(hProcess, newMemAddr, 0, MEM_RELEASE);
CloseHandle(hThread);
CloseHandle(hProcess);
//返回成功
return TRUE;
}
//创建远程线程释放指定进程dll
BOOL UnLoadDllByRemoteThread(DWORD dwProcessId, LPCWSTR lpDllName)
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (hProcess == NULL)
return FALSE;
// 在目标进程中申请一块内存,并将需要卸载的DLL模块的名称写入该内存
LPVOID lpRemoteDllName = VirtualAllocEx(hProcess, NULL, (wcslen(lpDllName) + 1) * sizeof(WCHAR), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (lpRemoteDllName == NULL)
{
CloseHandle(hProcess);
return FALSE;
}
if (!WriteProcessMemory(hProcess, lpRemoteDllName, lpDllName, (wcslen(lpDllName) + 1) * sizeof(WCHAR), NULL))
{
VirtualFreeEx(hProcess, lpRemoteDllName, 0, MEM_RELEASE);
CloseHandle(hProcess);
return FALSE;
}
//查找dll模块
HMODULE hModules[1024],DesModule=NULL;
DWORD dwSize = 0;
if (!EnumProcessModules(hProcess, hModules, sizeof(hModules), &dwSize))
{
VirtualFreeEx(hProcess, lpRemoteDllName, 0, MEM_RELEASE);
CloseHandle(hProcess);
return FALSE;
}
// 遍历模块列表,查找需要卸载的DLL模块
for (DWORD i = 0; i < (dwSize / sizeof(HMODULE)); i++)
{
WCHAR szModuleName[MAX_PATH] = { 0 };
if (GetModuleFileNameExW(hProcess, hModules, szModuleName, MAX_PATH) > 0)
{
// 获取模块句柄
if (wcsicmp(szModuleName, lpDllName) == 0)
{
DesModule = hModules;
}
}
}
//没有查找到模块
if (!DesModule) {
VirtualFreeEx(hProcess, lpRemoteDllName, 0, MEM_RELEASE);
CloseHandle(hProcess);
return FALSE;
}
// 在目标进程中创建远程线程,执行FreeLibrary函数
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)FreeLibrary, DesModule, 0, NULL);
if (hThread == NULL)
{
VirtualFreeEx(hProcess, lpRemoteDllName, 0, MEM_RELEASE);
CloseHandle(hProcess);
return FALSE;
}
// 等待线程执行完成
WaitForSingleObject(hThread, INFINITE);
// 关闭句柄
CloseHandle(hThread);
VirtualFreeEx(hProcess, lpRemoteDllName, 0, MEM_RELEASE);
CloseHandle(hProcess);
return TRUE;
}
//以创建远程线程方式种植植物
BOOL GrowPlantByInjectCode(DWORD dwProcessId,DWORD BaseAddr,DWORD x,DWORD y,DWORD PlantType)
{
BOOL bSuccess = FALSE;
//1. 打开进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (hProcess != NULL)
{
//2. 定义注入代码(函数)
BYTE InjectCode[50] = { //汇编指令 //修正点偏移
0x55, //0 push ebp
0x89, 0xE5, //1 mov ebp,esp
0x60, //3 pushad
0x68, 0xFF, 0xFF, 0xFF, 0xFF, //4 push -1
0x68, 0x00, 0x00, 0x00, 0x00, //9 push PlantType //10
0xB8, 0x00, 0x00, 0x00, 0x00, //14 mov eax,y //15
0x68, 0x00, 0x00, 0x00, 0x00, //19 push x //20
0xB9, 0x00, 0x00, 0x00, 0x00, //24 mov ecx,BaseAddr //25
0x8B, 0x89, 0x0C, 0x5E, 0x35, 0x00, //29 mov ecx,[ecx+0x355E0C]
0x8B, 0x89, 0x68, 0x08, 0x00, 0x00, //35 mov ecx,[ecx+0x868]
0x51, //41 push ecx
0xE8, 0x00, 0x00, 0x00, 0x00, //42 call PlantFunc //43 //被调方平栈
0x61, //47 popad
0xC9, //48 leave
0xC3 //49 ret
};
//3. 申请空间用于存储代码
DWORD dwCodeSize = 50, desFunc = BaseAddr + 0x18D70;
LPVOID lpRemoteCodeMem = VirtualAllocEx(hProcess, NULL, dwCodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
//4. 修正参数
*(DWORD*)&InjectCode[10] = PlantType;
*(DWORD*)&InjectCode[15] = y;
*(DWORD*)&InjectCode[20] = x;
*(DWORD*)&InjectCode[25] = BaseAddr;
*(DWORD*)&InjectCode[43] = desFunc-((DWORD)lpRemoteCodeMem+42+5) ;
//call指令与jmp类似,相对于当前指令的下一条指令计算偏移,offset=des-(source+5),减去call自身长度5
if (lpRemoteCodeMem != NULL)
{
SIZE_T dwBytesWritten = 0;
//5. 注入代码
if (WriteProcessMemory(hProcess, lpRemoteCodeMem, InjectCode, dwCodeSize, &dwBytesWritten) &&
dwBytesWritten == dwCodeSize)
{
//6. 创建远程线程执行代码
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpRemoteCodeMem,NULL, 0, NULL);
if (hThread != NULL)
{
//7. 等待线程信号
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
bSuccess = TRUE;
}
}
//8. 执行完后释放空间
VirtualFreeEx(hProcess, lpRemoteCodeMem, 0, MEM_RELEASE);
}
CloseHandle(hProcess);
}
return bSuccess;
}
//以创建远程线程方式种植僵尸
BOOL GrowZombieByInjectCode(DWORD dwProcessId,DWORD BaseAddr, DWORD x, DWORD y, DWORD ZombieType) {
BOOL bSuccess = FALSE;
//1. 打开进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (hProcess != NULL)
{
//2. 定义注入代码(函数)
BYTE InjectCode[50] = {
0x55, //0 push ebp
0x89, 0xE5, //1 mov ebp,esp
0x60, //3 pushad
0x68, 0x00, 0x00, 0x00, 0x00, //4 push x
0x68, 0x00, 0x00, 0x00, 0x00, //9 push ZombieType
0xB8, 0x00, 0x00, 0x00, 0x00, //14 mov eax,y
0xB9, 0x00, 0x00, 0x00, 0x00, //19 mov ecx,BaseAddr
0x8B, 0x89, 0x0C, 0x5E, 0x35, 0x00, //24 mov ecx,[ecx+0x355E0C]
0x8B, 0x89, 0x68, 0x08, 0x00, 0x00, //30 mov ecx,[ecx+0x868]
0x8B, 0x89, 0x78, 0x01, 0x00, 0x00, //36 mov ecx,[ecx+0x178]
0xE8, 0x00, 0x00, 0x00, 0x00, //42 call PlantZombieFunc
0x61, //47 popad
0xC9, //48 leave
0xC3 //49 ret
};
//3. 申请空间用于存储代码
DWORD dwCodeSize = 50, desFunc = BaseAddr + 0x35390; //种植僵尸函数
LPVOID lpRemoteCodeMem = VirtualAllocEx(hProcess, NULL, dwCodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
//4. 修正参数
*(DWORD*)&InjectCode[5] = x;
*(DWORD*)&InjectCode[10] = ZombieType;
*(DWORD*)&InjectCode[15] = y;
*(DWORD*)&InjectCode[20] = BaseAddr;
*(DWORD*)&InjectCode[43] = desFunc - ((DWORD)lpRemoteCodeMem + 42 + 5);//call指令与jmp类似,相对于当前指令的下一条指令计算偏移,要减去call长度5
if (lpRemoteCodeMem != NULL)
{
SIZE_T dwBytesWritten = 0;
//5. 注入代码
if (WriteProcessMemory(hProcess, lpRemoteCodeMem, InjectCode, dwCodeSize, &dwBytesWritten) &&
dwBytesWritten == dwCodeSize)
{
//6. 创建远程线程执行代码
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpRemoteCodeMem, NULL, 0, NULL);
if (hThread != NULL)
{
//7. 等待线程信号
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
bSuccess = TRUE;
}
}
//8. 执行完后释放空间
VirtualFreeEx(hProcess, lpRemoteCodeMem, 0, MEM_RELEASE);
}
CloseHandle(hProcess);
}
return bSuccess;
}
//设置卡槽植物
BOOL SetPlantCard(HANDLE hProcess,DWORD BaseAddr,DWORD nCard,DWORD plantType) {
DWORD cardAddr = BaseAddr + 0x355E0C;
ReadProcessMemory(hProcess, cardAddr, &cardAddr, sizeof(DWORD), NULL);
cardAddr += 0x868;
ReadProcessMemory(hProcess, cardAddr, &cardAddr, sizeof(DWORD), NULL);
cardAddr += 0x15C;
ReadProcessMemory(hProcess, cardAddr, &cardAddr, sizeof(DWORD), NULL);
cardAddr += 0x5C+nCard*0x50;//卡槽偏移
return WriteProcessMemory(hProcess, cardAddr, &plantType, sizeof(DWORD), NULL);
}
//选择菜单
void choiceMenu(HANDLE hProcess,DWORD Pid, LPVOID BaseAddr) {
DWORD choice = 0;
unsigned int num = 0;
DWORD fogAddr = 0;
unsigned int x, y, Type;
while(1) {
system("cls");
printf(" Welcome to PVZ Modifier!");
printf(" 0.退出");
printf(" 1.修改阳光数");
printf(" 2.修改金钱数");
printf(" 3.修改智慧树高");
printf(" 4.修改巧克力数");
printf(" 5.修改树肥");
printf(" 6.修改花肥");
printf(" 7.修改杀虫剂");
printf(" 8.无限冷却");
printf(" 9.恢复冷却");
printf(" 10.无限阳光");
printf(" 11.恢复阳光消耗");
printf(" 12.除雾");
printf(" 13.恢复雾");
printf(" 14.种植植物");
printf(" 15.生成僵尸");
printf(" Please choose your option:[ ]");
scanf("%d", &choice);
switch(choice){
case 0:
return;
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
printf(" Please input Num:");
scanf("%d", &num);
setSomething(hProcess, BaseAddr, choice - 1, num);
break;
case 8:
Uncooled(hProcess, BaseAddr);
break;
case 9:
RecoveryCooling(hProcess, BaseAddr);
break;
case 10:
UnlimitedSun(hProcess,BaseAddr);
break;
case 11:
RecoverySunConsume(hProcess, BaseAddr);
break;
case 12:
fogAddr=(DWORD)DeFogByHook(hProcess, BaseAddr);
break;
case 13:
RecoveryFogByUnHook(hProcess, BaseAddr,fogAddr );
break;
case 14:
printf("请输入X Y PlantType: ");
scanf("%d%d%d", &x, &y, &Type);
GrowPlantByInjectCode(Pid, BaseAddr,x,y,Type );
break;
case 15:
printf("请输入X Y ZombieType: ");
scanf("%d%d%d", &x, &y, &Type);
GrowZombieByInjectCode(Pid, BaseAddr, x, y, Type);
break;
}
}
}
int main() {
//获取进程pid
DWORD Pid = GetProcessIdByName(L"PlantsVsZombies.exe");
//打开进程,获取进程句柄
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE,Pid);
//获取进程基址
DWORD BaseAddr=GetModuleBaseAddress(Pid, L"PlantsVsZombies.exe");
choiceMenu(hProcess, Pid, BaseAddr);
//dll注入
//InjectDllByRemoteThread(Pid, L"E:\MyProject\vsProjects\Project1\Debug\DllPlant3.dll");
//int op = 1;
//printf("输入0卸载dll:");
//scanf("%d", &op);
//if(op==0)
// UnLoadDllByRemoteThread(Pid, L"E:\MyProject\vsProjects\Project1\Debug\DllPlant3.dll");//加载完dll之后释放掉
CloseHandle(hProcess);
return 0;
}
|
DLL代码 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
| #include
#include
//调用函数
BOOL GrowPlant(DWORD BaseAddr, DWORD x, DWORD y, DWORD TypePlant) {
LPVOID PlantFunc = BaseAddr + 0x18D70;
__asm {
pushad
push -1 //-1
push TypePlant //植物类型
mov eax, y //y
push x //x
mov ecx, BaseAddr
mov ecx, [ecx+0x355E0C]
mov ecx, [ecx + 0x868]
push ecx
call PlantFunc
popad
}
return TRUE;
}
BOOL GrowZombie(DWORD BaseAddr, DWORD x, DWORD y, DWORD ZombieType) {
LPVOID PlantZombieFunc = BaseAddr + 0x35390;
__asm {
pushad
push x
push ZombieType
mov eax,y
mov ecx,BaseAddr
mov ecx,[ecx+0x355E0C]
mov ecx,[ecx+0x868]
mov ecx,[ecx+0x178] //ebp
call PlantZombieFunc
popad
}
return TRUE;
}
BOOL WINAPI DllMain(HMODULE hInstance, DWORD fdwReason, LPVOID lpReserved) {
DWORD BaseAddr = GetModuleHandle(NULL);
DWORD pid = GetCurrentProcessId();
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
MessageBoxW(0, L"ProcessAttachDll!", L"window2", 0);
GrowPlant(BaseAddr,5,3,23);
GrowZombie(BaseAddr, 6, 2, 23);
break;
/* case DLL_THREAD_ATTACH:
printf("ThreadAttach!");
break;
case DLL_THREAD_DETACH:
if (lpReserved == NULL)
{
FreeLibrary(hInstance);
}
break;*/
case DLL_PROCESS_DETACH:
MessageBoxW(0, L"ProcessDeTachDll!", L"window2", 0);
break;
}
return TRUE;
}
| 参考资料
|