最近研究沙箱優(yōu)化,發(fā)現(xiàn)需要學(xué)習(xí)一下 hook API 的方法焦蘑,于是去啃了cuckoo的源碼盯拱,發(fā)現(xiàn)基本原理是一種叫做inline hook的hook方法,于是去專門查了一下inline hook例嘱,看到了luoyesiqiu的文章狡逢,讓我明白了很多之前不懂的地方。(文章鏈接https://www.cnblogs.com/luoyesiqiu/p/12306336.html)
于是我就把例子代碼copy回來拼卵,稍微改動(dòng)一下奢浑,想運(yùn)行一下看看情況。?
StartHook和UnHook都沒動(dòng)腋腮,就改了一下調(diào)用和回調(diào)函數(shù):
先不在意其他細(xì)節(jié)雀彼,總體邏輯就是hook GetSystemInfo,然后調(diào)用GetSystem即寡,再解hook
回調(diào)函數(shù)就是在GetSystemInfo 被調(diào)用的時(shí)候輸出“Im hook;惭啤!聪富!”
編譯莺丑,運(yùn)行
問題出現(xiàn)了,程序確實(shí)跳到回調(diào)了墩蔓,可是沒跳回來梢莽。
反復(fù)翻看大哥的文章原文,發(fā)現(xiàn)大哥的例子其實(shí)只是單純的講述了Inline hook的原理奸披,而不是用inline 來hook API的昏名,因此例子代碼僅在hook地址是一個(gè)5字節(jié)call 的時(shí)候,才能順利跳到回調(diào)函數(shù)再跳回來阵面。
如果hook一個(gè)系統(tǒng)函數(shù)葡粒,函數(shù)體頭部幾乎不可能是一個(gè)call指令,所以還要多考慮一些事情膜钓。
于是我腦袋里設(shè)想了一下大概的思路嗽交,就是替換5字節(jié)之后,要把原有的第一條指令進(jìn)行備份颂斜,并記錄下第二條指令的起始位置夫壁,在hook 跳轉(zhuǎn)到回調(diào)函數(shù)后,執(zhí)行完附加的工作之后沃疮,還要執(zhí)行一下備份第一條指令的原有操作盒让,然后再jmp回函數(shù)體的第二條指令繼續(xù)執(zhí)行梅肤,這樣才能保證程序正常執(zhí)行。
于是邑茄,我明白了調(diào)用了反編譯器的lde函數(shù)在計(jì)算什么姨蝴,它應(yīng)該是想得到一條完整指令的長度。
我也明白了一件事肺缕,想輕松HookAPI 不是一件輕松的事左医,所以我打算站在巨人的肩膀上,使用cuckoo的代碼(畢竟后面優(yōu)化沙箱也會(huì)用到)
為便于理解同木,我打算先以自己的方式完成這個(gè)hook:
先把所有和hook相關(guān)的部分刪掉調(diào)試一下浮梢,觀察GetSystemInfo的函數(shù)體,記住這個(gè)地址彤路。
然后秕硝,再編譯一個(gè)有hook的版本,再觀察這個(gè)位置的變化洲尊,checkCPU入口(0x00d26dd0)下斷點(diǎn)远豺,發(fā)現(xiàn)跟我想的不一樣,改的不是GetSystemInfo的函數(shù)體坞嘀,而是一個(gè)6字節(jié)的jmp憋飞,想想也是,這里是kernel32.dll的一個(gè)函數(shù)列表姆吭,為什么不是直接走函數(shù)體榛做,我也不理解,可能是出于安全考慮内狸。
這樣的話事情似乎變得簡(jiǎn)單了检眯,和大哥的例子就很像了。
可以看到 我們覆蓋的位置(0x775d9f80)5個(gè)字節(jié)昆淡,就是一條jmp(FF 25)指令的前五個(gè)字節(jié)锰瘸,跳轉(zhuǎn)地址00186477,這個(gè)地址應(yīng)該是不變的昂灵。
執(zhí)行過starthook函數(shù)避凝,看一下,我們的確成功替換了5個(gè)字節(jié)眨补,代碼執(zhí)行到這的時(shí)候會(huì)跳轉(zhuǎn)到我們的hook函數(shù)上:
但我們做的不夠好管削,第一,我們落下一個(gè)0x77沒有替換撑螺,讓他變成了一個(gè)ja 指令含思。第二,我們沒有讓程序回到00186477(0x77641800)
我們先不管第一個(gè)問題,先讓hook函數(shù)做完想做的事后含潘,繼續(xù)去執(zhí)行GetSystemInfo函數(shù)饲做。
編譯運(yùn)行,程序又又又崩了遏弱,是沒跳回去嗎盆均?調(diào)試!
和前面的步驟漱逸,執(zhí)行到GetSystemInfo跟進(jìn)去泪姨,跳進(jìn)hook函數(shù),可以看到虹脯,做完想做的事之后,會(huì)call getsysteminfo(那為什么崩了呢奏候?)
call進(jìn)來之后循集,這崩了……
這里搞錯(cuò)了,這里原來的指令應(yīng)該是 jmp [0x77641800] 的地址 0x7648f370蔗草。
再修改一次代碼:
編譯執(zhí)行:
bingoV渫!咒精!