<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    植物大戰僵尸修改器制作--從入門到入土

    VSole2023-08-18 09:35:43
    一
    基礎準備
    

    1.CheatEngine工具的基本使用

    推薦視頻你能學會的Cheat Engine零基礎入門教程(https://www.bilibili.com/video/BV1nR4y1u7PZ/?spm_id_from=333.999.0.0&vd_source=8c182d1e4a80cc9f34dfe996135c2c23),

    將ce官方給的闖關游戲通關即可。

    2.C/C++和匯編語言基礎

    附上匯編代碼轉換網站(https://defuse.ca/online-x86-assembler.htm#disassembly)。

    3.WIN32開發基礎

    了解WIN32命名規則,會使用GPT和查找微軟官方文檔即可。

    推薦通過看微軟官方文檔Win32 和 C++ 入門 能創建第一個windows程序即可。

    示例游戲版本: 中文年度加強版1.1.0.1056

    主要參考資料

    1.【補檔】豪哥植物大戰僵尸修改教程視頻合集(https://www.bilibili.com/video/BV1te4y1U7Jn?p=1&vd_source=8c182d1e4a80cc9f34dfe996135c2c23)

    2.C/C++全棧軟件安全課(調試、反調試、游戲反外掛、軟件逆向)持續更新中~~~~(https://www.bilibili.com/video/BV1By4y1r7Cq/?p=156&vd_source=8c182d1e4a80cc9f34dfe996135c2c23)

    3.逆向工程實戰 揭秘匯編/反匯編(win32+游戲逆向實戰)(https://www.bilibili.com/video/BV1Jb411p7fU/?p=25&vd_source=8c182d1e4a80cc9f34dfe996135c2c23)

    二
    基址偏移表
    

    部分參考: 公布我所找到的所有基址及各種功能實現方法

    基址 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

    關鍵函數和變量

    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

    設置卡槽植物函數

    //設置卡槽植物
    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會導致無法種植。

    修改方法:

    1.修改冷卻結束后恢復的速度,將inc指令修改為mov一個較大值 這個

    2.直接跳轉到冷卻值和冷卻上限比較成功的函數

    以方法2為例

    7E 16 對應匯編指令為 jle 0x18

    修改為jmp $+2 即 eb 00 (相對當前指令2字節后的指令)

    直接執行冷卻值達到冷卻上限后的函數(冷卻值清零,植物冷卻完畢可種植)。

    附上匯編代碼轉換網站(https://defuse.ca/online-x86-assembler.htm#disassembly)

    關鍵代碼

    //修改進程代碼區代碼 參數: 進程句柄 修改代碼起始地址 硬編碼指針 代碼字節數
    BOOL WriteProcessCodeMemory(HANDLE hProcess, LPVOID lpStartAddress, LPCVOID lpBuffer, SIZE_T nSize) {
        DWORD dwOldProtect;
        //取消頁保護
        if (!VirtualProtectEx(hProcess, lpStartAddress, nSize, PAGE_EXECUTE_READWRITE, &dwOldProtect)) {
            return FALSE;
        }
        BOOL bResult = WriteProcessMemory(hProcess, lpStartAddress, lpBuffer, nSize, NULL);//寫入代碼
        VirtualProtectEx(hProcess, lpStartAddress, nSize, dwOldProtect, &dwOldProtect);//開啟頁保護
        return bResult;
    }
    //無限冷卻
    BOOL Uncooled(HANDLE hProcess, DWORD BaseAddr) {
        unsigned char code[2] = { 0xeb,0x00 };
        return WriteProcessCodeMemory(hProcess, BaseAddr + 0x9ce02, code, 2);//jle 0x18修改為jmp $+2
    }
    //恢復冷卻
    BOOL RecoveryCooling(HANDLE hProcess, DWORD BaseAddr) {
        unsigned char OriginalCode[2] = { 0x7E ,0x16 };//jmp $+2恢復為jle 0x18
        return WriteProcessCodeMemory(hProcess, BaseAddr + 0x9ce02, OriginalCode, 2);
    }
    


    六
    無限陽光
    

    前文已經給出了陽光的地址 基址為0x355E0C 偏移+868 +5578

    查找對陽光修改的代碼即可。

    陽光減少代碼

    陽光增加代碼

    基本過程:

    1.設置陽光值為9999

    2.修改陽光減少代碼使得種植物不消耗陽光

    3.修改陽光增加代碼使得陽光不變化(防止陽光過多導致溢出)

    //修改進程代碼區代碼 參數: 進程句柄 修改代碼起始地址 硬編碼指針 代碼字節數
    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的基本過程

    1.讀取并保存目的地址原始代碼

    2.申請空間(PVZ游戲進程空間)用于存儲原始代碼 hook代碼 jmp返回代碼

    3.向申請的空間中寫入原始代碼 hook代碼 jmp返回代碼

    4.修改目的地址的代碼為jmp HookCode

    5.返回HookCode首地址 用于解除hook

    值得一提的是jmp指令后跟的偏移值是以jmp的下一條指令首地址計算

    jmp指令偏移值=目的地址-(jmp指令首地址+5) 這里的5是jmp指令本身的長度 +5便是下一條指令

    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;
    }
    

    除霧代碼

    //除霧 注意保留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注入容易被檢測到

    基本過程:

    1.打開進程獲取進程句柄

    2.在目標進程中申請空間用于存儲dll路徑名

    3.將dll路徑名寫入申請的空間中

    4.創建遠程線程,執行LoadLibrary函數(加載dll)

    5.目標進程加載dll后自動執行dll的DllMain函數

    //創建遠程線程方式向指定進程注入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等問題。

    基本過程:

    1.在目標進程申請內存,將需要卸載的dll模塊名稱寫入該內存

    2.通過枚舉模塊來查找指定模塊

    3.成功查找到dll模塊則創建遠程線程執行FreeLibrary函數卸載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;
    }
    

    關鍵dll函數

    這里使用了三種方法。

    注意: 不要將代碼寫入switch(reason)之外,否則可能會導致多次執行。

    #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; 
    }
    

    執行結果

    失敗代碼

    這是寫dll函數時遇到的問題 如果直接用 mov ecx,[BaseAddr+0x355E0C]會導致代碼執行失敗,推測是這條指令訪存過慢所以無效。

    建議mov ecx,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
        }
    

    遠程線程代碼注入(推薦)

    和遠程線程dll注入類似,CreateRemoteThread函數要求的函數原型是:

    DWORD WINAPI ThreadProc(
      _In_ LPVOID lpParameter//使用CreateThread函數傳遞的參數 該參數是一個指向其他數據的指針,當然也可以強轉為其他類型直接使用
    );
    

    基本過程:

    1.打開進程

    2.定義注入代碼(函數)

    3.在目標進程中申請空間并寫入注入代碼

    4.創建遠程線程執行注入代碼(函數)

    5.執行完畢釋放空間

    //以創建遠程線程方式種植植物
    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注入版

    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 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;
    }
    


    十
    完整代碼
    #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) {
        DWORD choice = 0;
        unsigned int num = 0;
        DWORD fogAddr = 0;
        unsigned int x, y, Type;
       while(1) {
            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;
            }
        }
    }
    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代碼

    #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; 
    }
    
    dworddll注入
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    初探DLL注入
    2023-02-06 10:35:24
    DLL注入是指向運行中的其它進程強制插入特定的DLL文件。從技術細節來說,DLL注入命令其它進程自行調用LoadLibrary()API,加載用戶指定的DLL文件。從上圖可以看到,test.dll已被強制插入進程。加載到某一進程中的test.dll與已經加載到某一進程中的dll一樣,擁有訪問notepad.exe進程內存的權限。DLL被加載到進程后會自動運行DLLMain()函數,用戶可以把想執行的代碼放到DLLMain()函數,每當加載DLL時,添加的代碼就會得到執行。利用這種特性可以修復程序Bug以及添加新功能
    反射式DLL注入實現
    2022-05-13 15:59:21
    反射式dll注入與常規dll注入類似,而不同的地方在于反射式dll注入技術自己實現了一個reflective loader()函數來代替LoadLibaryA()函數去加載dll,示意圖如下圖所示。藍色的線表示與用常規dll注入相同的步驟,紅框中的是reflective loader()函數行為,也是下面重點描述的地方。
    在Windows大部分應用都是基于消息機制,他們都擁有一個消息過程函數,根據不同消息完成不同功能,windows通過鉤子機制來截獲和監視系統中的這些消息。一般鉤子分局部鉤子與全局鉤子,局部鉤子一般用于某個線程,而全局鉤子一般通過dll文件實現相應的鉤子函數。
    全局鉤子注入在Windows大部分應用都是基于消息機制,他們都擁有一個消息過程函數,根據不同消息完成不同功能,windows通過鉤子機制來截獲和監視系統中的這些消息。一般鉤子分局部鉤子與全局鉤子,局部鉤子一般用于某個線程,而全局鉤子一般通過dll文件實現相應的鉤子函數。
    本文從 dll 注入技術、msf migrate 模塊剖析、檢測思路等方面展開說明。
    Dll注入
    2021-11-08 14:57:41
    最近太忙啦XDM,又在做一些列的分析復現工作量有點大,更新要慢一點了。一致,也不會覆蓋其他的進程信息。
    簡介這次實驗是在WIN7 X86系統上進程,使用的編譯器是VS2017。所謂的DLL注入,其實就是在其他的進程中把我們編寫的DLL加載進去。所以DLL注入的核心就是把要注入DLL的路徑寫到目標進程中,然后在目標進程中調用LoadLibrary函數,并且指定參數為保存了DLL路徑的地址。要實現DLL注入,首先就要創建一個用來注入DLL
    Kaspersky AVP.exe DLL 劫持
    2022-07-21 22:53:31
    Kaspersky AVP.exe 中的 DLL 注入允許本地管理員在不知道 Kaspersky 密碼的情況
    Windows注入的一些方式
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类