本次實驗內容:通過陽光增加的值為切入點,找到自動收集陽光的關鍵判斷並實現自動收集陽光,首先我們猜測當陽光出現后,我們是否會去點擊,這個過程必然是由一個判斷和一個時鍾周期事件來控制的,那么當我們點擊下落的陽光以后,則該判斷條件實現,會執行收集陽光的CALL,否則的話繼續執行陽光下落的過場動畫,這正是正向開發的一種開發手段,此時我們也僅僅是猜測,接下來我們將去驗證這個大膽的想法。
為了找到陽光自動收集的關鍵跳轉,我們需要以陽光增加作為切入點,為啥以它作為切入點呢?我們可以這樣思考,當我們點擊陽光后陽光增加了,說明已經完成了判斷,下一步就是寫入變量從而增加陽光,那么我們先來找到陽光的動態地址,並在該動態地址上按下F6鍵查找寫入,然后回到游戲等待陽光出現並點擊陽光,此時CE會出現以下代碼,我們只需要記下00430A11
這個內存地址,然后直接關閉CE。
接着打開X64dbg附加到游戲進程,附加完成后,游戲會被X64dbg暫停運行,此時我們直接按下F12讓游戲運行起來,然后按下Ctrl + G
輸入00430A11
跳轉到剛才找到的代碼位置,過去以后直接F2下一個斷點。
此時我們需要逆向思考一個問題add dword ptr ds:[eax+0x5560],ecx
這條指令是在我們陽光被點擊后執行的,也就是說我們已經點擊了陽光現在開始賦值了,那判斷陽光是否被回收肯定是在這條指令之前出現,所以我們向上找,觀察代碼我們不難看出執行add dword ptr ds:[eax+0x5560],ecx
指令之前有一個無條件跳轉jmp 0x00430A0E
跳過來的。
繼續向上查找跳轉來源,可知在jmp跳轉之前有一個je 0x004309EF
跳轉,經過測試這個地方具體控制陽光是否增加,在向上找就到段首了,此處代碼中並沒有出現自動收集陽光的關鍵跳轉,因此推斷這里應該是一個控制陽光是否增加的子過程(子過程:過程中調用的過程,稱為子過程),所以我們繼續回朔到上一層。
為了能夠回朔到上一層,我們需要取消陽光遞增處的斷點,並在段尾00430AB3
處下一個F2斷點防止程序跑飛,回到游戲等待陽光的出現,然后X64dbg就會斷下,斷下后直接取消00430AB3
處的斷點,執行到Ret處即可返回到上一層。
返回到上一層以后,可以看到我們正是在call <plantsvszombies.sub_4309D0>
這里出來的,而上方就有一個jne plantsvszombies.4313FD
關鍵跳,此處的關鍵跳轉也並不是控制是否回收陽光的關鍵跳轉,而此處的代碼量比較少,因此判斷此處還是一個子過程,我們繼續回溯到上一層。
我們直接單步F8運行到返回,並出這個CALL,出CALL以后會看到call <plantsvszombies.sub_430E40>
沒錯!我們正是從這個子過程里出來的,接着向上找跳轉會看到有一個jne plantsvszombies.431599
此處如果將其改為jmp
的話即可實現自動收集陽光,也就是說如果jne跳轉實現則執行收集陽光,否則繼續執行陽光下落的過場動畫。
注意:如果我們在關鍵跳jne plantsvszombies.4313FD
處下斷點時,會發現當陽光出現后程序會被無限的斷下,這說明是有一個定時器線程在不斷的執行判斷代碼,每次都會判斷你是否點擊了陽光,所以X64dbg才會被一直斷下。
知道了修改流程,那我們就通過編程的方式來實現修改程序的硬編碼,首先我們可以通過以下代碼完成字節集的讀取。
#include <stdio.h>
#include <Windows.h>
byte *ReadByteSet(DWORD Pid, DWORD Base, DWORD Size)
{
HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, 0, Pid);
byte *buf = new byte[Size];
ReadProcessMemory(handle, (LPVOID)Base, buf, Size, NULL);
return buf;
}
int main()
{
byte *Buff = new byte[10];
Buff = ReadByteSet(2232, 0x00401000, 10);
for (int i = 0; i < 10; i++)
printf("%02X ", Buff[i]);
return 0;
}
既然有讀取內存字節集,那么就有寫入字節集,如下代碼就是一種字節集寫入的實現方式。
#include <stdio.h>
#include <Windows.h>
BOOL WriteByteSet(DWORD Pid, DWORD Base, unsigned char *ShellCode, DWORD Size)
{
BYTE *Buff = new BYTE[Size];
memset(Buff, *ShellCode, Size);
HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, 0, Pid);
BOOL Ret = WriteProcessMemory(handle, (LPVOID)Base, Buff, Size, NULL);
if (Ret)
return TRUE;
else
return FALSE;
}
int main()
{
unsigned char shell[] = { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 };
BOOL temp = WriteByteSet(3772, 0x00401010, shell, 8);
return 0;
}
想要實現陽光自動收集,只需要將0x0043158F
機器碼0x75 0x08
修改為0xEB 0x08
即可實現效果。
unsigned char shell[] = { 0xEB };
BOOL temp = WriteByteSet(9744, 0x0043158F, shell, 1);