AlphaRom是日本游戲常用的保護(hù)方式,它的新版本比以前加強(qiáng)了不少。最近脫一個(gè)galgame的殼,略有心得,記錄如下:
首先是反調(diào)試強(qiáng)度增加,在xp中用普通ollydbg也無(wú)法運(yùn)行,可能是StrongOD和HideOD沒(méi)調(diào)對(duì)選項(xiàng)吧。還好用nooby的ollydbg能正常運(yùn)行。
其次是IAT加密,它把一大部分API的代碼復(fù)制進(jìn)自己分配的內(nèi)存中去,并加上大量垃圾指令。如果事先在API的任何位置下了int 3斷點(diǎn)的話,復(fù)制過(guò)程中就直接出錯(cuò)導(dǎo)致程序跳出。而如果在API下硬件斷點(diǎn),則經(jīng)常無(wú)法斷在需要的地方,因?yàn)槌绦虮倔w執(zhí)行API大部分都是去殼復(fù)制完的代碼里執(zhí)行。
以下是脫殼過(guò)程:
1. 運(yùn)行AlphaRom激活工具alsignup_act_100723_110729.exe并運(yùn)行游戲, 這樣游戲就被激活,可以正常運(yùn)行了。
2. PEID顯示連接器版本6.0,所以找GetVersion,把它的最后一個(gè)指令retn改成EB FE。然后運(yùn)行一陣暫?匆谎,如果停在這個(gè)指令上就手動(dòng)讓函數(shù)返回。繼續(xù)運(yùn)行直到某次停在另外一個(gè)地址的EB FE上,這就是被殼復(fù)制過(guò)去的GetVersion,返回后上面的push ebp就是OEP了。
3. 找到OEP后以后只要在OEP上下硬件斷點(diǎn)就可以停在OEP了。停在OEP上后可以dump,只需要頭兩個(gè)section就夠了,后面5個(gè)都是殼的section。
4. 在dump下來(lái)的文件中搜索call [address]和jmp [address],也就是FF15和FF25開(kāi)頭的call和jmp,address限制在程序本體范圍內(nèi)。這可以寫幾句C完成。
5. 整理上面搜到的address,就是iat的thunks的地址。在importrec里手動(dòng)填寫iat的起始地址和大小,Get Imports后Save tree保存為iat.txt。注意這里如果叫importrec自動(dòng)搜索iat的話搜不全。
6. 發(fā)現(xiàn)有一小部分iat是正確API地址,其他大多都是殼分配的地址,還有幾個(gè)地址本身處于殼區(qū)段。
7. 首先解決在殼分配的地址里復(fù)制的API。先嘗試hook GetVersion,讓它用E9跳到一個(gè)空白處再跳回來(lái)。重新運(yùn)行程序,驚喜地發(fā)現(xiàn)GetVersion對(duì)應(yīng)的iat地址上直接變成了舊版AlphaRom里的jmp GetVersion。得出結(jié)論:API開(kāi)頭若是E9大跳,殼就不會(huì)復(fù)制代碼了。通過(guò)對(duì)GetVersion開(kāi)頭下內(nèi)存訪問(wèn)斷點(diǎn)分析,找到了四個(gè)相關(guān)的跳轉(zhuǎn)位置,寫一個(gè)ODBGScript,負(fù)責(zé)在這四個(gè)跳轉(zhuǎn)處改變eip,也就是讓殼以為所有API的開(kāi)頭都是E9大跳轉(zhuǎn)。不能直接在代碼上patch,否則會(huì)出錯(cuò)跳出。完事后復(fù)制代碼部分的iat就全得到了。ODBGScript如下:
lc
dbh
mov OEP, 43ab20
mov CHECK_1, 9692db
mov JUMP_1, 9695cd
mov CHECK_2, 965781
mov JUMP_2, 965868
mov CHECK_3, 967828
mov JUMP_3, 968443
mov CHECK_4, 968524
mov JUMP_4, 968540
bphws OEP, "x"
loop:
bphws CHECK_1, "x"
run
cmp eip, CHECK_1
jne end
mov eip, JUMP_1
bphwc CHECK_1
bphws CHECK_2, "x"
run
cmp eip, CHECK_2
jne end
mov eip, JUMP_2
bphwc CHECK_2
bphws CHECK_3, "x"
run
cmp eip, CHECK_3
jne end
mov eip, JUMP_3
bphwc CHECK_3
bphws CHECK_4, "x"
run
cmp eip, CHECK_4
jne end
mov eip, JUMP_4
bphwc CHECK_4
jmp loop
end:
cmp eip, OEP
jne final
log "Finished"
final:
8. 注意,此時(shí)得到的API中有ntdll里的,要把這些換成對(duì)應(yīng)的kernel32里的API。在dump下來(lái)的文件中,iat的thunks的地址后面一點(diǎn)就有全部的API名字與dll名字,把API名字整理出來(lái),找出現(xiàn)在iat.txt中不存在的,然后就可以輕易找出ntdll函數(shù)與kernel32函數(shù)的對(duì)應(yīng)。這種對(duì)應(yīng)比如像ntdll.RtlFreeHeap其實(shí)是kernel32.HeapFree。
9. 然后還剩5個(gè)殼區(qū)段中的iat,這些都是復(fù)制的API。這個(gè)并沒(méi)有被前面的腳本簡(jiǎn)化,應(yīng)該是殼在不同的地方進(jìn)行的復(fù)制。通過(guò)與剩下的名字對(duì)比,并查看調(diào)用它們的地方,很容易根據(jù)參數(shù)個(gè)數(shù)和種類猜出來(lái)。
10. 最后用importrec來(lái)Load tree,加載修改過(guò)的iat.txt,F(xiàn)ix dump,脫殼就完成了。