簡單來看,hook就是修改目標函數內容導致其執行流改變,跑去執行其他函數,而unhook就是把目標函數內容修改回來,讓其執行流恢復正常。
hook可好可壞,全看改變流程後執行的函數是做什麼事情。
inline hooking就是修改函數A,把A開頭的內容改成跳到B之類的指令,導致A被呼叫時,會先去執行B,然後B執行完功能後會unhook,把A開頭的內容恢復原先的樣子,再把流程給A執行其原本的功能,像B這樣的函數可稱作detour function。
跳轉指令如下。
PUSH B_addr |
或
MOV eax, B_addr |
inline hooking關鍵在於B的參數與calling convention要和A一致,要讓B把A的參數當成自己的參數的樣子。
這個也很好理解,因為當實際執行到A的instruction時,等於已經進入A函數體,而在這之前參數早就已經擺好了,進入函數體A後,會執行hook的跳轉指令跳到B去執行,如果B的參數與calling convention與A不一致,可能會搞爆stack上擺放的參數,例如B清理stack時會多清或少清(假設calling convention規定由callee清理參數),導致之後程式會執行錯誤。
以下會hook MessageBoxA為例子,步驟大致是
- 先取得要hook的MessageBoxA地址
- 保存MessageBoxA內前幾個bytes,因為hook要修改這幾個bytes,而後面unhook時需要恢復
- 取得hook function的地址
- 把MessageBoxA內前幾個bytes修改成
PUSH hook_func_addr
和RET
兩條指令 - hook function執行完功能後(還沒return),unhook恢復原先API前幾個bytes
- return到MessageBoxA執行原先該執行的作業
在尚未hook前,原來MessageBoxA的前六個byte如下圖所示
hook後,MessageBoxA的前六個byte,被修改成push HookedMessageBox的地址和ret兩個指令,當執行MessageBoxA時,就會執行這兩個指令,從而改變flow,先去執行HookedMessageBox
可以看到PUSH的值就是HookedMessageBox的地址
當執行到HookedMessageBox中unhook的地方,unhook前,MessageBoxA的內容還是PUSH & RET
unhook後,MessageBoxA又恢復成原來的樣子了,之後呼叫MessageBoxA都是正常流程了
執行
1 |
|
如果想在HookedMessageBox中正常呼叫MessageBoxA的話,就一定要先在HookedMessageBox中unhook,如果沒有先unhook就直接呼叫MessageBoxA,會導致無限遞迴。
MessageBoxA(因為PUSH & RET) -> HookedMessageBox -> MessageBoxA -> HookedMessageBox ->…..
例如我把line 14拿掉,就會這樣= =。
參考資料
https://www.ired.team/offensive-security/code-injection-process-injection/how-to-hook-windows-api-using-c++
https://labs.nettitude.com/blog/windows-inline-function-hooking/
https://guidedhacking.com/threads/how-to-hook-functions-code-detouring-guide.14185/