1.CE修改游戲特例說明
模擬器游戲不能直接修改游戲的程序代碼(即不能直接使用代碼注入的手段修改code段代碼)赃蛛,因?yàn)橛螒虿⒎鞘褂闷脚_(tái)語言所寫疙筹,只有模擬器是使用平臺(tái)語言寫的沛硅,即殼是匯編寫的福青,殼用來翻譯跨平臺(tái)程序摄狱,因此不能直接修改跨平臺(tái)程序脓诡。
2.精確搜索
精確搜索時(shí)需要根據(jù)具體的數(shù)值范圍(數(shù)據(jù)類型)搜索數(shù)據(jù):
例如:
- 搜索時(shí)間,可能使用的浮點(diǎn)數(shù)存儲(chǔ)時(shí)間
- 搜索字符串時(shí)媒役,可能使用字符數(shù)組存儲(chǔ)字符串
3.模糊搜索
當(dāng)模糊搜索的值再次搜索過濾多次還是很多時(shí)祝谚,可以先精確搜索到能搜索到的地址,然后根據(jù)精確地址找模糊地址(因?yàn)槎际亲兞繑?shù)據(jù)刊愚,都會(huì)存儲(chǔ)在data中踊跟,所以他們的地址應(yīng)該很近)
大部分游戲顯示的數(shù)值都經(jīng)過了加密(*2+1),因此需要模糊搜索來查找鸥诽,以植物大戰(zhàn)僵尸的金幣為例:搜索值應(yīng)為游戲中金幣數(shù)/10(植物大戰(zhàn)僵尸的金幣最小單位為10)商玫,才可以搜到結(jié)果。
4.指針與多級(jí)指針
基址:
- 基址, 即是程序代碼被編譯成二進(jìn)制碼時(shí)就已經(jīng)規(guī)定好的一個(gè)絕對(duì)地址值.一般反匯編后為: mov 寄存器, [xxxxxx] // 基址[xxxxxx]=模塊地址+偏移
- 基址是一串16進(jìn)制數(shù)字代表的地址值, 除非程序被破壞,或者被重新編譯生成,否則是不會(huì)改變的牡借。詳細(xì)可參考PE文件結(jié)構(gòu)拳昌。
- 基址地址一般在程序模塊中(>00401000)(<7FXXXXXX系統(tǒng)領(lǐng)空)(綠色基地址)
指針:
- 查找動(dòng)態(tài)地址指針時(shí)使用
是什么改寫
搜索 - 查找指針地址時(shí)使用
是什么訪問
搜索
CE中基地址找到動(dòng)態(tài)地址:
-
[基地址(綠色模塊地址)]+偏移
取基地址值加上偏移值后的值作為地址(一級(jí)指針) -
[[基地址(綠色模塊地址)]+偏移]+偏移
取一級(jí)指針的值+偏移值作為地址(二級(jí)指針)
32位游戲一般多級(jí)指針?biāo)阉鞯?級(jí)即可,64位一般最多則為8級(jí)或12級(jí)钠龙,但是一般我們超過4級(jí)指針后可以通過人造指針來實(shí)現(xiàn)炬藤。
5.代碼注入—AOB注入
AOB注入是最常用的注入腳本,選中需要注入的代碼行碴里,
彈出自動(dòng)匯編窗口中選擇AOB注入
生成代碼如圖:
AOB腳本支持ENABLE與DISABLE沈矿,因此可以加入到CT列表中,一鍵開啟或關(guān)閉腳本功能咬腋。
注入點(diǎn)
有時(shí)直接對(duì)當(dāng)前代碼行注入可能會(huì)造成程序報(bào)錯(cuò)羹膳,選擇當(dāng)前代碼行的上一行注入可能會(huì)避免程序報(bào)錯(cuò)問題。
6.匯編知識(shí)
寄存器
可以理解為一個(gè)臨時(shí)變量根竿,每個(gè)寄存器有他們慣例的用途陵像,不過這僅僅是慣例,你想用他們做其他事也可以(除了 esp
棧頂指針以外)寇壳。
通常我們使 eax
, ebx
,ecx
, edx
做普通的操作醒颖,雖然這些寄存器也有一些特殊的用途,但是用途較少所以一般的操作用這些寄存器就可以壳炎。
esi
和edi
:看他們的名字泞歉,叫做“源索引寄存器”和“目標(biāo)索引寄存器” (source index
、destination index
)匿辩,因?yàn)樵诤芏嘧址僮髦噶钣玫氖枞眨谄渌募拇嫫鞯臅r(shí)候不用這幾個(gè),當(dāng)然如果你能掌控得好的話 esi 和 edi 其實(shí)也可以隨便使用撒汉。
esp
:esp
是不可以亂動(dòng)的沟优,esp
指向堆棧頂部,push
和 pop
指令會(huì)影響 esp
睬辐,由于寄存器的數(shù)量太少了挠阁,我們編寫程序時(shí)需要的變量有很多宾肺,所以使用內(nèi)存來輔助我們,函數(shù)的局部變量就會(huì)保存在棧中侵俗,調(diào)用函數(shù)的參數(shù)也會(huì)保存在棧中锨用。
比如說,現(xiàn)在我們的寄存器都用完了隘谣,我需要騰出一個(gè)寄存器來做其他事情增拥。那么就把寄存器的變量放到內(nèi)存(棧)中(push),然后就可以對(duì)這個(gè)寄存器為所欲為了,然后用完之后,再把棧中的值提取出來(pop)本涕,放回寄存器。
push eax
; 做一堆有關(guān)eax的事
; 比如:
; mov eax, [ebp+04]
; add eax, [edx]
; mov [ebp+04], eax
pop eax
常見指令
基礎(chǔ)指令集 —— 8086指令集
8086匯編指令總結(jié)
sse指令集
movss
表示 Move Scalar Single猾封,移動(dòng)標(biāo)量單精度浮點(diǎn)值。
xorps
表示 XOR Packed Single噪珊,壓縮單精度浮點(diǎn)值邏輯位異或晌缘。
cvtps2pd
表示 Convert Packed Single to Packed Double,壓縮單精度浮點(diǎn)值轉(zhuǎn)換成壓縮雙精度浮點(diǎn)值痢站。
addsd
表示 Add Scalar Double磷箕,標(biāo)量單精度浮點(diǎn)值加法。
mulsd
表示 Multiply Scalar Double阵难,標(biāo)量單精度浮點(diǎn)值乘法搀捷。
這些可以直接進(jìn)行浮點(diǎn)運(yùn)算的指令、可以直接操作XMM寄存器的指令多望,都屬于 SSE 指令集。
每條匯編指令的名字起得都是有意義的氢烘,好好學(xué)習(xí)英語可以幫助我們更好地理解他們怀偷。
看到一條 SSE 指令,要把他拆成兩部分播玖,“操作”和“數(shù)據(jù)類型”椎工,
第一部分:操作。mov蜀踏、xor维蒙、add、mul 這些指令果覆,x86 最基礎(chǔ)的指令集中也有颅痊。
第二部分:數(shù)據(jù)類型。ss局待、sd斑响、ps菱属、pd 這一部分又要拆分成兩部分來看。第二位是 s 表示是 single 單精度浮點(diǎn)型舰罚,一個(gè)數(shù)據(jù)占 32 位纽门,第二位是 d 表示 double 雙精度浮點(diǎn)型,一個(gè)數(shù)據(jù)占 64 位营罢。第一位是 s 表示只操作 XMM 寄存器的第一個(gè)數(shù)據(jù)(ss 就是 32 位赏陵,sd 就是 64 位),第一位是 p 表示同時(shí)操作全部 128 位數(shù)據(jù)(ps 就是 4 個(gè) 32 位饲漾,pd 就是 2 個(gè) 64 位)蝙搔。
xor
后面的兩個(gè)操作數(shù)相同的話就是用來清零的,比如 xor eax,eax 就是令 eax 為 0能颁,這是最簡(jiǎn)單最快捷的寄存器清零方法杂瘸。對(duì)于 XMM 寄存器同樣也是清零。
cvt
指令就是浮點(diǎn)數(shù)精度的轉(zhuǎn)換伙菊,主要看 s 和 d 的位置败玉,s2d 就是 single to double 單精度浮點(diǎn)數(shù)到雙精度浮點(diǎn)數(shù),從只占 32 位變成占 64 位镜硕,反之 d2s就是雙精度到單精度的轉(zhuǎn)換运翼。
常見的函數(shù)開頭
push ebp ; 保存上一個(gè) ebp
mov ebp, esp ; 把 esp 給 ebp
sub esp, 08 ; 分配8字節(jié)棧空間用于局部變量
; 函數(shù)主體內(nèi)容...
; 在這里可以使用 ebp 來定位與函數(shù)有關(guān)的變量
; [ebp-08] 代表第 2 個(gè)局部變量
; [ebp-04] 代表第 1 個(gè)局部變量
; [ebp] 剛才 push 進(jìn)來那個(gè) ebp
; [ebp+04] 函數(shù)返回之后的要執(zhí)行的指令所在位置
; [ebp+08] 代表函數(shù)的第 1 一個(gè)輸入?yún)?shù)
; [ebp+0C] 代表函數(shù)的第 2 個(gè)輸入?yún)?shù)
add esp, 08
pop ebp ; 復(fù)原 ebp
ret 08 ; 返回函數(shù)調(diào)用位置兴枯,并且把棧指針 +8血淌,把調(diào)用的參數(shù)從棧中移除
范例
以植物大戰(zhàn)僵尸為例:
陽光地址是一個(gè)二級(jí)地址。查看反匯編代碼是這樣子的:
mov [eax*2 + edi + 00005578],esi
其中财剖,edi
是指針悠夯,eax*2 + 00005578
是偏移值,esi
是陽光數(shù)量躺坟,這句代碼的作用是把陽光的值放在陽光的內(nèi)存地址中沦补。
7.共用代碼段的利用與處理
公共代碼段是代碼復(fù)用的體現(xiàn),好的代碼復(fù)用性很高咪橙,也就是說一個(gè)函數(shù)很可能供多個(gè)調(diào)用者調(diào)用夕膀。
針對(duì)公共代碼處理:
以火炬之光為例:
當(dāng)匯編nop
了掉血代碼時(shí),發(fā)現(xiàn)不僅人物血不掉美侦,而且怪物血也不掉了(因?yàn)榈粞瘮?shù)是人物與怪物扣血的共用函數(shù))产舞。
使用代碼注入的方式時(shí),要利用cmp
和 je
jne
jle
等條件轉(zhuǎn)移指令判斷是否是己方還是敵方菠剩,達(dá)到己方不掉血易猫。
以CE:STEP9為例:
STEP9中扣除血量的代碼就是公用代碼
先使用浮點(diǎn)搜索找到一個(gè)血量地址后,使用什么改寫
搜索到代碼段地址具壮,右鍵找出指令訪問的地址
依次點(diǎn)擊攻擊擦囊,4個(gè)單位血量分別降低违霞,可以看到4個(gè)單位的血量地址依次顯示出來。
全選地址瞬场,右鍵打開選中地址的分析數(shù)據(jù)
买鸽,彈窗默認(rèn)確認(rèn)即可,顯示如圖所示贯被。
代碼注入(模版使用全部注入)如下:
此時(shí)點(diǎn)擊攻擊眼五,友軍不再會(huì)掉血。
利用公共代碼找相關(guān)地址:
當(dāng)我們找到物品欄的某個(gè)格子物品數(shù)量時(shí)彤灶,可以通過掃描誰改寫了值找到改寫代碼處看幼,再右鍵選擇“找出指令訪問的地址”打開掃描窗口,然后游戲中移動(dòng)物品到其他格子幌陕,就能找到這個(gè)代碼訪問的其他格子地址诵姜。原理即是利用公共代碼操作多個(gè)地址。
8.特征代碼的應(yīng)用 (針對(duì)程序段地址會(huì)變動(dòng))
1. 被迫使用特征碼:
以flash游戲國(guó)王的戰(zhàn)爭(zhēng)為例:
由于使用的flash啟動(dòng)的游戲搏熄,游戲的內(nèi)存地址每次都會(huì)不同(類似于模擬器游戲)棚唆,因此只能根據(jù)特征碼(指令的字節(jié)數(shù)組)找到修改的地址,再做修改:
CEAA提供一個(gè)函數(shù)可以查詢特征碼的地址:
aobscan //CEAA函數(shù) 搜索特征碼心例,并將地址賦給變量
例:
aobscan(Money,特征碼) 把特征碼的地址給了money
money+修改地址的相對(duì)偏移量 即要修改代碼的地址
2. 主動(dòng)使用特征碼: 實(shí)現(xiàn)多版本兼容修改器
以植物大戰(zhàn)僵尸為例:有兩個(gè)版本(無盡版宵凌、年度版)進(jìn)程名相同,代碼實(shí)現(xiàn)相同(特征碼一樣)止后,使用特征碼便可以實(shí)現(xiàn)通用的CT腳本瞎惫。
9.人造指針 (針對(duì)指針級(jí)別過多、找不到基址的情況)
以植物大戰(zhàn)僵尸為例:
原理:陽光的地址每次重開游戲都會(huì)變化译株,有一個(gè)固定的程序段記錄陽光的地址
- 方法一【固定地址】:
(1) 右鍵陽光的地址是誰訪問了地址(讀取地址的值時(shí)觸發(fā),陽關(guān)地址是變動(dòng)的瓜喇,只有訪問斷點(diǎn)可以找到是誰訪問了它),找到固定的程序段
(2) 找到固定程序段-代碼注入
(3) (a)找一處可讀可寫內(nèi)存模塊的空閑地址(固定地址)保存陽光地址
(b)當(dāng)可讀可寫內(nèi)存地址沒有空閑時(shí),我們也可以使用只讀內(nèi)存的空閑地址歉糜,但是需要使用一條CE指令[fullaccess(只讀內(nèi)存地址,修改為可讀的字節(jié)長(zhǎng)度)修改內(nèi)存地址保護(hù)為可讀寫乘寒,這個(gè)指令不一定每次成功]
(4) 建立指針指向空閑地址 - 方法二【申請(qǐng)地址】:
(1) 右鍵陽光的地址是誰訪問了地址(讀取地址的值時(shí)觸發(fā),陽關(guān)地址是變動(dòng)的,只有訪問斷點(diǎn)可以找到是誰訪問了它),找到固定的程序段
(2) 找到固定程序段-代碼注入
(3) 使用alloc
(標(biāo)識(shí)名,地址長(zhǎng)度)動(dòng)態(tài)申請(qǐng)內(nèi)存,將陽光地址保存到申請(qǐng)的內(nèi)存中
(4) 由于申請(qǐng)的內(nèi)存地址目前只能在當(dāng)前腳本中使用现恼,我們需要將其保存到一個(gè)全局變量中,使得我們可以在其它腳本中也能訪問
CEAA提供了一個(gè)函數(shù)命令[regiestersymbol
(標(biāo)識(shí)名)黍檩,表示將參數(shù)標(biāo)識(shí)加入到全局表]
(5) 在[disable]中添加釋放申請(qǐng)內(nèi)存的指令dealloc
(標(biāo)識(shí)名,地值長(zhǎng)度)
(6) 前面將申請(qǐng)的內(nèi)存標(biāo)識(shí)加入了全局表叉袍,那么在關(guān)閉腳本時(shí)須將標(biāo)識(shí)從全局表中剔除
[unregiestersymbol
(標(biāo)識(shí)名),將參數(shù)標(biāo)識(shí)從全局表中剔除]
(7) CE手動(dòng)添加地址刽酱,地址為標(biāo)識(shí)名
10.線程注入的應(yīng)用
以植物大戰(zhàn)僵尸修改陽光為例:
腳本代碼:
[ENABLE]
alloc(newmem,256)
lable(exit)
Createthread(newmem)
newmem:
mov eax,[755e0c] //陽光基址
mov eax,[eax+868]
add [eax+5578],1F4
cmp [eax+5578],2709 //陽光上限是9990
jle exit
mov [eax+5578],2706
exit:
ret
[DISABLE]
線程注入效果:當(dāng)啟動(dòng)一次腳本陽光數(shù)量就+500喳逛,相當(dāng)于游戲修改器中點(diǎn)一次加一次屬性的操作。
11.Windows函數(shù)應(yīng)用
以火炬之光為例:
模擬自動(dòng)回藍(lán):判斷藍(lán)的值小于15時(shí)棵里,自動(dòng)摁2鍵吃藥
[ENABLE]
alloc(newmem.1024)
lable(returnhere)
lable(originalcode)
label(exit)
newmem:
fld dword ptr [ecx+000003B8]
cmp [ecx+000003B8],40A00000
ja originalcode
push 0
push 0
push 0
push 32 //2鍵的鍵碼
call keybd_event
push 0
push 0
push 0
push 32 //2鍵的鍵碼
call keybd_event
originalcode:
exit:
jmp returnhere
Torchlight.exe+85FE0:
jmp newmem
nop
......
12.CE的斷點(diǎn)選擇與跟蹤
以皇家守衛(wèi)軍怪物秒殺為例:
在找到怪物血量減少的地方后應(yīng)該會(huì)有判斷怪物是否該死亡的跳轉(zhuǎn)润文,修改跳轉(zhuǎn)完成秒殺功能
以植物大戰(zhàn)僵尸自動(dòng)收集陽光為例:
收集陽光的時(shí)候陽光會(huì)增加姐呐,下內(nèi)存寫入斷點(diǎn),找到陽光增加的語句典蝌,返回到上一層曙砂,在陽光增加call的上面會(huì)有點(diǎn)擊陽光的call,修改跳轉(zhuǎn)完成自動(dòng)收集陽光的功能
【三種斷點(diǎn)的原理】
(1)int 3斷點(diǎn)骏掀,即cc斷點(diǎn)鸠澈,這是一種基于軟中斷機(jī)制斷點(diǎn),3為中斷號(hào)截驮。OD中笑陈,當(dāng)你在代碼區(qū)某行按F2即可實(shí)現(xiàn),其機(jī)理是把所在代碼的第一個(gè)字節(jié)保存到一張表上葵袭,然后將其修改為CC涵妥,當(dāng)程序運(yùn)行到此代碼時(shí),就會(huì)產(chǎn)生中斷坡锡,從而轉(zhuǎn)至中斷服務(wù)程序蓬网。當(dāng)你去除斷點(diǎn)時(shí),OD會(huì)從表里讀取出當(dāng)前斷點(diǎn)原來的字節(jié)內(nèi)容娜氏。
(2)內(nèi)存斷點(diǎn)拳缠,假如你用int 3斷點(diǎn)對(duì)數(shù)據(jù)區(qū)下斷,OD會(huì)提示你斷點(diǎn)可能不會(huì)實(shí)現(xiàn)贸弥,其實(shí)也是必然窟坐,程序不可能執(zhí)行數(shù)據(jù)區(qū),然而我們卻可以當(dāng)數(shù)據(jù)被讀取或?qū)懭霑r(shí)進(jìn)行下斷绵疲,這種原理主要基于內(nèi)存屬性哲鸳,當(dāng)下讀寫斷點(diǎn)是,OD會(huì)修改斷點(diǎn)處讀寫屬性盔憨,如果程序?qū)Υ藬?shù)據(jù)讀寫的話徙菠,會(huì)產(chǎn)生讀寫異常,OD捕捉此異常并分析郁岩,其可以知道運(yùn)行到何處婿奔,對(duì)代碼段也可以下此斷點(diǎn),機(jī)理相似问慎。
(3)硬件斷點(diǎn)萍摊,這是由硬件實(shí)現(xiàn)(這里是CPU實(shí)現(xiàn)),其由CPU調(diào)試器實(shí)現(xiàn)如叼,斷點(diǎn)長(zhǎng)度有限冰木,其只用兩位記錄斷點(diǎn)長(zhǎng)度,所以只支持4個(gè)硬件斷點(diǎn),調(diào)試寄存器中有3位表示斷點(diǎn)狀態(tài)及屬性踊沸,000 保留 001 執(zhí)行斷點(diǎn) 010 訪問斷點(diǎn) 011 寫入斷點(diǎn) 100 保留 101 臨時(shí)斷點(diǎn) 110 保留 111 保留