依星源码资源网,依星资源网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

【好消息,好消息,好消息】VIP会员可以发表文章赚积分啦 !
查看: 131|回复: 0

植物大战僵尸修改器制作--从入门到入土

[复制链接] 主动推送

1万

主题

1万

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
12061
发表于 2024-10-23 16:59:43 | 显示全部楼层 |阅读模式
植物大战僵尸修改器制作--从入门到入土
基础准备
示例游戏版本: 中文年度加强版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
查找对阳光修改的代码即可
阳光减少代码

植物大战僵尸修改器制作--从入门到入土

植物大战僵尸修改器制作--从入门到入土
阳光增加代码

植物大战僵尸修改器制作--从入门到入土

植物大战僵尸修改器制作--从入门到入土
基本过程:
  • 设置阳光值为9999
  • 修改阳光减少代码使得种植物不消耗阳光
  • 修改阳光增加代码使得阳光不变化(防止阳光过多导致溢出)

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技术

植物大战僵尸修改器制作--从入门到入土

植物大战僵尸修改器制作--从入门到入土
HOOK
hook的基本过程
  • 读取并保存目的地址原始代码
  • 申请空间(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;
}

参考资料

相关帖子

扫码关注微信公众号,及时获取最新资源信息!下载附件优惠VIP会员6折;永久VIP4折
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

免责声明:
1、本站提供的所有资源仅供参考学习使用,版权归原著所有,禁止下载本站资源参与商业和非法行为,请在24小时之内自行删除!
2、本站所有内容均由互联网收集整理、网友上传,并且以计算机技术研究交流为目的,仅供大家参考、学习,请勿任何商业目的与商业用途。
3、若您需要商业运营或用于其他商业活动,请您购买正版授权并合法使用。
4、论坛的所有内容都不保证其准确性,完整性,有效性,由于源码具有复制性,一经售出,概不退换。阅读本站内容因误导等因素而造成的损失本站不承担连带责任。
5、用户使用本网站必须遵守适用的法律法规,对于用户违法使用本站非法运营而引起的一切责任,由用户自行承担
6、本站所有资源来自互联网转载,版权归原著所有,用户访问和使用本站的条件是必须接受本站“免责声明”,如果不遵守,请勿访问或使用本网站
7、本站使用者因为违反本声明的规定而触犯中华人民共和国法律的,一切后果自己负责,本站不承担任何责任。
8、凡以任何方式登陆本网站或直接、间接使用本网站资料者,视为自愿接受本网站声明的约束。
9、本站以《2013 中华人民共和国计算机软件保护条例》第二章 “软件著作权” 第十七条为原则:为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。若有学员需要商用本站资源,请务必联系版权方购买正版授权!
10、本网站如无意中侵犯了某个企业或个人的知识产权,请来信【站长信箱312337667@qq.com】告之,本站将立即删除。
郑重声明:
本站所有资源仅供用户本地电脑学习源代码的内含设计思想和原理,禁止任何其他用途!
本站所有资源、教程来自互联网转载,仅供学习交流,不得商业运营资源,不确保资源完整性,图片和资源仅供参考,不提供任何技术服务。
本站资源仅供本地编辑研究学习参考,禁止未经资源商正版授权参与任何商业行为,违法行为!如需商业请购买各资源商正版授权
本站仅收集资源,提供用户自学研究使用,本站不存在私自接受协助用户架设游戏或资源,非法运营资源行为。
 
在线客服
点击这里给我发消息 点击这里给我发消息 点击这里给我发消息
售前咨询热线
312337667

微信扫一扫,私享最新原创实用干货

QQ|免责声明|小黑屋|依星资源网 ( 鲁ICP备2021043233号-3 )|网站地图

GMT+8, 2025-1-18 15:58

Powered by Net188.com X3.4

邮箱:312337667@qq.com 客服QQ:312337667(工作时间:9:00~21:00)

快速回复 返回顶部 返回列表