Article directory
Basic preparation
-
Basic usage of CheatEngine tool
Recommended Video Cheat Engine Zero-Basic Introductory Tutorial You Can Learn
Just clear the level-breaking game officially given by ce
-
Basics of C/C++ and Assembly Language
-
Basics of WIN32 Development
Understand the WIN32 naming rules, use GPT and find Microsoft official documents
It is recommended to read Microsoft's official documents Win32 and C++ Getting Started to be able to create the first windows program
Sample game version: Chinese annual enhanced version 1.1.0.1056
main reference
-
[Replenishment] Haoge Plants vs. Zombies Modification Tutorial Video Collection
-
Compilation/disassembly of reverse engineering actual combat (win32+ game reverse actual combat)
base offset table
Base address offset of common variables
Part of the reference: publish all the base addresses I found and the implementation methods of various functions
Base address 0x00355E0C
Sunshine +868 +5578
Money +950 +50
Flower fertilizer +950 +220
Chocolate +950 +250
Tree fertilizer +950 +258
Tree height +950 +11C
Insecticide +950 +224
Number of card slots +868 +15C +24
Card slot bar +868 +15C +5C After that, each plant bar is separated by 0x50
The current cooling value of the plant is +868 +15C +4C After that, the cooling interval of each plant is 0x50
The upper limit of plant cooling value +868 +15C +50 After that, the upper limit of cooling value of each plant is separated by 0x50
Current number of plants +868 +D4
Plant Planting Function EBP +868
Current number of zombies +868 +B8
Zombie planting function EBP +868 +178
regular items
Use CE to search according to the change of the variable, and then scan the pointer to find the available base address after finding it
The actual value of sunshine memory = game display value
Wisdom tree height memory actual value = game display value
Actual value of money memory = game display value/10
Flower fertilizer, insecticide, chocolate, tree fertilizer memory actual value = game display value + 1000
Key Functions and Variables
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);
}
card slot plant
Ten card slots, each card slot corresponds to a plant, you can find the address of the card slot according to the change of nuts in card slot 1 (the leftmost card slot) in Nut Bowling 2, and then look for the base address
Specific method: The initial value is unknown, if the plant in slot 1 is the same as the plant in the new slot 1 (original slot 2), scan the unchanged value, otherwise scan the changed value
The offset between card slots can be seen by browsing the memory area of card slot 1, which is 0x50
Nut Plant Card Slot No.:
common nuts 3
Exploding Nuts 49
Giant Nut 50
Set the card slot plant function
//设置卡槽植物
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);
}
Plant without cooling
Specific method: Only for one card slot, the initial value is unknown, it will continue to change after planting, and will not change after cooling, repeatedly scan and find the base address, check the corresponding memory area and compare the plant number to find that the offset between the card slots is 0x50
Cooling features: The cooling value in the plantable state is 0, and the cooling value continues to increase after planting. After reaching the cooling upper limit, the cooling value is reset to zero, and the plant can be planted again
Note: directly setting the cooling value to 0 will result in failure to plant
Modification method:
- Modify the speed of recovery after the cooling is over, and modify the inc command to a larger value of mov.
- Jump directly to the function where the cooling value and cooling upper limit are compared successfully
Take method 2 as an example
7E 16 corresponds to the assembly instruction jle 0x18
Change to jmp $+2 ie eb 00 (command after 2 bytes relative to the current command)
Directly execute the function after the cooling value reaches the cooling upper limit (the cooling value is cleared, and the plants can be planted after cooling down)
Attach the assembly code conversion website
key code
//修改进程代码区代码 参数: 进程句柄 修改代码起始地址 硬编码指针 代码字节数
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);
}
infinite sunshine
The previous article has given the address base address of Sunshine as 0x355E0C offset +868 +5578
Just find the code that modifies the sun
sun reduction code
sunshine increase code
Basic process:
-
Set the sunlight value to 9999
-
Modify the sunlight reduction code so that planting does not consume sunlight
-
Modify the sunlight increase code so that the sunlight does not change (to prevent overflow caused by too much sunlight)
//修改进程代码区代码 参数: 进程句柄 修改代码起始地址 硬编码指针 代码字节数
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;
}
Fog Perspective
Fundamental
Specific method: In the thick fog of the survival mode, the initial value is unknown. Judging by the changes caused by planting and eradicating street lamp flowers in the fog area, it can be found that it is 4 bytes of data. The value represents the concentration of fog, 255 represents dense fog, and 0 Represents no fog, and then find the code to modify the fog value
Find the Fog Address
[External link picture transfer failed, the source site may have an anti-theft link mechanism, it is recommended to save the picture and upload it directly (img-Pl7X1l0X-1690904306130) (6 Find the address of dense fog.png)]
Dense Fog Modification Code
mov [ecx], edx This line of code modifies the fog value, which can be changed to mov [ecx], 0
Note that the hard codes are 0xc7, 0x01, 0x00, 0x00, 0x00, 0x00. Since the code is too long, the code cannot be directly modified, so the hook technology is used here.
HOOK
The basic process of hook
- Read and save the original code of the destination address
- The application space (PVZ game process space) is used to store the original code hook code jmp return code
- Write the original code hook code jmp return code into the applied space
- Modify the code of the destination address to jmp HookCode
- Return the first address of HookCode to release the hook
It is worth mentioning that the offset value following the jmp instruction is calculated based on the first address of the next instruction of jmp
jmp instruction offset value = destination address - (jmp instruction first address + 5) where 5 is the length of the jmp instruction itself + 5 is the next instruction
offset=desAddr-(jmpAddr+5)
//修改进程代码区代码 参数: 进程句柄 修改代码起始地址 硬编码指针 代码字节数
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;
}
Defog code
//除雾 注意保留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);
}
The instruction before hook is mov [ecx],edx add ecx,04
After the hook, the instruction is changed to jmp
hookcode The first 5 bytes of the newly allocated space are exactly the original code, followed by the hook code and jmp return code
grow plants
Fundamental
The program is to execute the function of planting plants and then execute the function of increasing the number of plants
First find the number of plants on the lawn, the initial value is 0, as the number of plants increases, the base address is 0x355E0C offset +868 +D4
Then find out what modified the number of plants, and then plant a plant after the breakpoint
After disconnecting, check the return address in the call stack to find the planting function
This function was initially implemented by injecting dll from remote threads. Although injecting dll is relatively simple, it is not universal. This is just an introduction. It is recommended to use remote code injection.
remote thread injection dll function
The remote thread is that the current process creates a thread in the target process and executes specific code (this code must be in the target process instead of the current process)
The dll is injected because the dll executes the dll's DllMain function when it is loaded by a process or thread. Through this feature, we can achieve some special functions
Pros: Easy to implement
Cons: dll injection is easily detected
Basic process:
- Open a process to get a process handle
- Apply for space in the target process to store the dll path name
- Write the dll path name into the requested space
- Create a remote thread and execute the LoadLibrary function (loading dll)
- After the target process loads the dll, it automatically executes the DllMain function of the dll
//创建远程线程方式向指定进程注入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;
}
Remote thread unload dll function
Many tutorials only show how to inject dll, but do not demonstrate how to unload
If you only inject without unloading, the specific function will not be executed the next time you inject (because the dll has already been loaded), it is inconvenient to debug and update the dll in real time and other issues
Basic process:
- Apply for memory in the target process, and write the name of the dll module that needs to be uninstalled into the memory
- Find the specified module by enumerating modules
- If the dll module is successfully found, a remote thread is created to execute the FreeLibrary function to unload the 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[i], szModuleName, MAX_PATH) > 0)
{
// 获取模块句柄
if (wcsicmp(szModuleName, lpDllName) == 0)
{
DesModule = hModules[i];
}
}
}
//没有查找到模块
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;
}
key dll functions
Three methods are used here
Note: Do not write code outside of switch(reason) , otherwise it may cause multiple executions
#include<windows.h>
#include<stdio.h>
//调用函数
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!\n");
// 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;
}
Results of the
failure code
This is a problem encountered when writing dll functions. If you use mov ecx directly, [BaseAddr+0x355E0C] will cause the code to fail to execute. It is speculated that this instruction is too slow to access memory so it is invalid
It is recommended to achieve the goal by operating the register after mov ecx and BaseAddr
__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
}
Remote thread code injection (recommended)
Similar to remote thread dll injection, the function prototype required by the CreateRemoteThread function is
DWORD WINAPI ThreadProc(
_In_ LPVOID lpParameter//使用CreateThread函数传递的参数 该参数是一个指向其他数据的指针,当然也可以强转为其他类型直接使用
);
Basic process:
- open process
- Define injection code (function)
- Apply for space in the target process and write injection code
- Create a remote thread to execute the injected code (function)
- Release space after execution
//以创建远程线程方式种植植物
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;
}
Plant Zombies
Fundamental
Similar to the idea of planting plants
First find the address of the number of zombies by planting zombies in the brainstorming
Then find the code to increase the number of zombies
Then find the planting zombie call by looking at the call stack and parameters
The parameter should also be xy type ebp (note there is no -1)
The x value of the zombie planting function is above a call, which is a switch structure with no parameters, so the x value has not been modified
Plant zombie function – dll injection version
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;
}
Remote code injection version
//以创建远程线程方式种植僵尸
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;
}
complete program code
#include<stdio.h>
#include<windows.h>
#include <tlhelp32.h>
#include <string.h>
#include <shlwapi.h>
#include <psapi.h>
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[i], szModName, MAX_PATH)) {
//函数成功返回字符串长度,注意第四个参数的单位为字符而非字节
if (wcsstr(szModName, moduleName)) {
//查找模块名,若成功则返回子串第一次出现的指针
MODULEINFO modInfo = {
0 };
if (GetModuleInformation(hProcess, hMods[i], &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[i], szModuleName, MAX_PATH) > 0)
{
// 获取模块句柄
if (wcsicmp(szModuleName, lpDllName) == 0)
{
DesModule = hModules[i];
}
}
}
//没有查找到模块
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) {
BYTE choice = 0;
unsigned int num = 0;
DWORD fogAddr = 0;
unsigned int x, y, Type;
do {
system("cls");
printf("\t\t\t\tWelcome to PVZ Modifier!\n");
printf("\t\t\t\t\t0.退出\n");
printf("\t\t\t\t\t1.修改阳光数\n");
printf("\t\t\t\t\t2.修改金钱数\n");
printf("\t\t\t\t\t3.修改智慧树高\n");
printf("\t\t\t\t\t4.修改巧克力数\n");
printf("\t\t\t\t\t5.修改树肥\n");
printf("\t\t\t\t\t6.修改花肥\n");
printf("\t\t\t\t\t7.修改杀虫剂\n");
printf("\t\t\t\t\t8.无限冷却\n");
printf("\t\t\t\t\t9.恢复冷却\n");
printf("\t\t\t\t\t10.无限阳光\n");
printf("\t\t\t\t\t11.恢复阳光消耗\n");
printf("\t\t\t\t\t12.除雾\n");
printf("\t\t\t\t\t13.恢复雾\n");
printf("\t\t\t\t\t14.种植植物\n");
printf("\t\t\t\t\t15.生成僵尸\n");
printf("\t\t\t\tPlease choose your option:[ ]\b\b");
scanf("%d", &choice);
switch(choice){
case 0:
return;
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
printf("\t\t\t\tPlease 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;
}
printf("\t\t\t\t请选择是否继续:(y/Y or n/N): ");
getchar();
} while (scanf("%c", &choice) && (choice == 'y' || choice == 'Y'));
}
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 code
#include<windows.h>
#include<stdio.h>
//调用函数
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!\n");
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;
}
References
- You can learn the Cheat Engine zero-based introductory tutorial
- [Replenishment] Haoge Plants vs. Zombies Modification Tutorial Video Collection
- Announce all the base addresses and various function realization methods I found
- C/C++ full-stack software security course (debugging, anti-debugging, game anti-cheat, software reverse) is being continuously updated~~~~
- Compilation/disassembly of reverse engineering actual combat (win32+ game reverse actual combat)
- [Original] Several common DLL injection techniques
- C++ method of calling dll
- A Preliminary Study on DLL Injection
- Dll injection remote thread injection