[分享] Cheat Engine 各個模組原碼分析以及運作原理

幫轉:John 大大以前在 TW-Share 寫過有關的遊戲作弊文章

【標 題】:Cheat Engine 各個模組原碼分析以及運作原理
【作 者】:john0312
【時 間】:2006-12-17 10:45 PM
【連 結】:http://bbs.twshare.net/viewthread.php?tid=122985

我中文不好,若有詞不達意,請各位見諒.
小弟在此獻醜,希望這裡的大大們不嫌棄.

1. 簡介
“工欲善其事,必先利其器", 當我們要搞外掛, 就要對我們的工具 — Cheat Engine 瞭如指掌. Cheat Engine
是由荷蘭人 Dark Byte (它的暱稱) 寫的. 至今, Dark Byte 已將五年的心血花在 Cheat Engine 上,
這程式的規模自然不小, 因此我今天將為大家解釋 Cheat Engine 的各個模組, 以及他內部的運作.

2. 必要的知識,以及用詞的定義
一. 從 CPU 說起
我們目前用的 x86 CPU ( x86 就是一般 Intel 和 AMD 的 32位元 CPU ) 有4個權限層次 ( 英文裡叫做
Ring ) — Ring 0, Ring 1, Ring 2, Ring 3.
Ring 0 是甚麼指令都可以執行,甚麼記憶體都可存取.
Ring 3 是最受限制的層次, 也不可以存取非Ring3以外的記憶體

我們用的 Windows 作業系統只用到Ring3以及Ring0, 分別是 User-mode 以及 Kernel-mode.
一般程式執行於 Ring3 ( User-mode ). 則作業系統核心以及驅動程式執行於 Ring0 ( Kernel-Mode ).

二. 虛擬記憶體

我們的 CPU 有一個功能,那就是將任何一塊實體記憶體貼在虛擬記憶體上,當存取那塊虛擬記憶體時,就像存取那塊實體記憶體一樣.
正確一點來說,作業系統可以規定哪一塊實體記憶體對應虛擬記憶體的哪一個位置,例如:作業系統說0x00001000到0x00002000的實體記憶體貼到0x00401000的記憶體,那程式存取0x004018FF,就等於存取實體記憶體的0x000018FF,若程式存取0x00403E66,那也會存取對應的實體記憶體.
另外一點,虛擬記憶體的觀念是由CPU和作業系統管理的,一般的程式是不知道的,程式中的地址也是虛擬記憶體的地址(你們的代碼也是虛擬記憶體的地址)
在Windows作業系統裡,虛擬記憶體地址0x00000000~0x7FFFFFFF是Usermode記憶體
0x80000000~0xFFFFFFFF是KernelMode記憶體.
在每個程序間,KernelMode的記憶體都是相同的,但UserMode記憶體卻對應著不同的實體記憶體.

3. 外掛運行的原理
外掛主要分兩種: 1. 記憶體修改 ( 包括: Debug Register ) 2. 人工智慧練功 ( 如: 達人, MSBot.. etc )
Cheat Engine 屬於記憶體修改, 我們用的地址, 是遊戲程式用來決定一些事情的指令 ( 例如: 無敵地址是決定你有沒有被打中 )
由於大家都是執行於UserMode. 而UserMode的程式又不能存取實體記憶體,所以需要透過一些系統的API來達成修改遊戲程式的記憶體.

4. 模組間的關係,以及Cheat Engine整體運作原理

Windows 本身有提共一些外掛用得到的API,例如: OpenProcess(), ReadProcessMemory(),
WriteProcessMemory()… 等.
由於一些可惡(又可愛 XD)的防外掛軟件,用Hooking技術,導致這些API不能使用. 因此Dark Byte寫了一些代替的API.
Cheat Engine 內的選項允許你選擇使用Windows的API(透過kernel32.dll)或是用Cheat
Engine本身的API(部分跟Windows相同)
CheatEngine的API都是由dbk32.sys處理. CheatEngine.exe 本身不呼叫 dbk32.sys,
但是他載入dbk32.dll,而dbk32.dll呼叫dbk32.sys
CheatEngine還有許多模組,例如: cehook.dll, stealth.dll, dxhook.dll… 等,
但他們不常用到,因此這次就不提了.

5. CheatEngine.exe 分析
CheatEngine.exe主要是由Pascal(Delphi)語言寫的, 是原碼中的CheatEngine.dpr編譯成的.
CheatEngine.dpr 使用(Include/use)原碼中主目錄下面大多的檔案. CheatEngine.dpr
的檔案主要有兩種,一種主要與你溝通,這種檔案每一個 .pas 檔和 .dfm 檔組成一個視窗 ( 如:
MemoryBrowserFormUnit.pas 以及 MemoryBrowserFormUnit.dfm 組成我們用的 Memory
View ) 另一種是與下面一層的API溝通,如Debuger.pas, Debuger2.pas,
NewKernelHandler.pas… 等
NewKernelHandler.pas直得我們得關注,他與dbk32.dll溝通. 不像一般程式載入DLL,
newkernelhandler.pas用LoadLibrary()及GetProcAddress(), 手動載入DLL.
….
DarkByteKernel:= LoadLibrary(dbkdll);
if DarkByteKernel=0 then exit;
KernelVirtualAllocEx:=GetProcAddress(darkbytekernel,’VAE’);
KernelOpenProcess:=GetProcAddress(darkbytekernel,’OP’);
KernelReadProcessMemory:=GetProcAddresS(darkbytekernel,’RPM’);
KernelWriteProcessMemory:=GetProcAddress(darkbytekernel,’WPM’);
….
NewKernelHandler.pas 同時也會跟 Windows 本身的 API 講話.
….
WindowsKernel:=LoadLibrary(‘Kernel32.dll’);
if WindowsKernel=0 then Raise Exception.create(‘Something is really
messed up on your computer! You don"t seems to have a kernel!!!!’);
ReadProcessMemory:=GetProcAddress(WindowsKernel,’ReadProcessMemory’);
WriteProcessMemory:=GetProcAddress(WindowsKernel,’WriteProcessMemory’);
OpenProcess:=GetProcAddress(WindowsKernel,’OpenProcess’);
VirtualQueryEx:=GetProcAddress(WindowsKernel,’VirtualQueryEx’);
….

6. dbk32.dll 的分析
dbk32.dll 可以說是今天說到的幾個最小的模組, 是原碼在dbk32目錄下. 少的只有兩個檔案 — dbk32.dpr and
dbk32functions.pas.
dbk32.dll 與 dbk32.sys 溝通.
在我分析dbk32.dll的原碼之前,我要先說明KernelMode的驅動程式和Usermode的程式是如何溝通的. Kernelmode
與 Usermode 之間有 n 種溝通方式,
但最常用的是DeviceIOControl,UserMode程式一般先對KernelMode驅動程式提出要求(並提共一個要求代碼(CTL_CODE),這代碼是兩邊都懂的),並準備一個緩衝區(Buffer).
KernelMode驅動程式收到要求,就從緩衝區讀出資料,做該做的動作,再將結果寫回緩衝區.
我們來看看dbk32functions.pas中是如何做的:
….
const IOCTL_CE_READMEMORY = (IOCTL_UNKNOWN_BASE shl 16) or
($0800 shl 2) or (METHOD_BUFFERED ) or (FILE_RW_ACCESS shl 14);
const IOCTL_CE_WRITEMEMORY = (IOCTL_UNKNOWN_BASE shl 16) or
($0801 shl 2) or (METHOD_BUFFERED ) or (FILE_RW_ACCESS shl 14);
const IOCTL_CE_OPENPROCESS = (IOCTL_UNKNOWN_BASE shl 16) or
($0802 shl 2) or (METHOD_BUFFERED ) or (FILE_RW_ACCESS shl 14);
….
以上是KernelMode驅動程式以及Usermode的dbk32.dll約定好的要求代碼,我在以GetIDTCurrentThread做一個例子
….
function GetIDTCurrentThread:dword;
var cc,br: dword;
idtdescriptor: packed record
wLimit:word;
vector: dword;
end;
begin
if hdeviceINVALID_HANDLE_VALUE then
begin
cc:=IOCTL_CE_GETIDT;
deviceiocontrol(hdevice,cc,nil,0,@idtdescriptor,6,br,nil);
result:=idtdescriptor.vector;
end else result:=0;
end;
….
他先檢查dbk32.sys是否已連接上
要求代碼存在cc中,之後傳到DeviceIOControl Function上. idtdescriptor 則是緩衝區

7. dbk32.sys 分析
由於dbk32.sys執行於KernelMode,幾乎所有外掛的動作皆是由dbk32.sys執行的. 因此,我個人認為dbk32.sys是最精華的一部分.
dbk32.sys 在原碼的DBKKernel目錄下, 程式的進入點在DBKDrv.c.
DBKDrv.c 主要跟 dbk32.dll 溝通. 一些要求當場在DBKDrv.c處理,一些則交給其他檔案處理.
DBKDrv.c 的 MSJDispatchIoctl 處理dbk32.dll的DeviceIOControl要求
由於dbk32.sys中的原碼比較難,所以我就不多說.

8. 附錄: 推薦閱讀
I.
II.
III.
IV.

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 變更 )

Twitter picture

You are commenting using your Twitter account. Log Out / 變更 )

Facebook照片

You are commenting using your Facebook account. Log Out / 變更 )

Google+ photo

You are commenting using your Google+ account. Log Out / 變更 )

連結到 %s


%d 位部落客按了讚: