一:前言
雖然靜態分析有Radare2,Hopper這種新星之秀,動態調試有Ollydbg,Windbg這種老牌霸主,但是IDA Pro仍然是大部分二進制安全工程師最喜愛的工具,除了價格過於昂貴,基本無懈可擊。在筆者眼里,它有下面幾個特點是別的工具無法比擬的
1:反編譯插件,說它是當今世界最好的反編譯器也不為過,這個革命性的插件,極大的降低逆向工程的門檻,也極大的提高了逆向工程師的工作效率。
2:IDA的編程接口,單純的任何工具無法滿足安全工程師的所有使用需求,但是完善的SDK包給了這個工具無限可能,特別在自動批量化處理的方面,如虎添翼。
3:以數據庫的形式保存,方便對文件進行任何操作並保存
並不是其它的功能就不優秀了,相反,它的其它功能也很強大,比如FLART功能,等等。這系列文章主要以IDA IDC,SDK編程,IDAPython變成的具體案例為主,插敘IDA的各種奇淫巧技。
二:准備工作
先回答一個問題:
1:為什么用IDAPython,而不是用 IDC或者IDA SDK編程?
IDC可以快速解決一些簡單的問題,但是對於復雜的問題,就有點力不從心了。IDA SDK包文檔過少,而且在調試相關的API,BUG比較多,使用比較難受,相比於起來,IDAPython可以調用IDC和 IDA SDK包的所有函數,而且文檔資料豐富。
當然,之前你需要懂Python,逆向工程,能熟練使用IDA Pro,懂得調試的一些常規知識。再加上一個IDA Pro6.8帶IdaPython即可。
三:編寫
第一步:先來看一下插件的框架
class myIdaPlugin(plugin_t): flags=0 wanted_name="my ida plugin" wanted_hotkey="Alt+c" comment="my ida plugin" help="Something helpful" def init(self): return PLUGIN_KEEP def term(self): pass def run(self,arg): pass def PLUGIN_ENTRY(): return myIdaPlugin()
其中,flags規定了Ida在不同情況下怎么處理插件,一般為0。
wanted_name為插件名稱,comments為插件注釋,help為幫助字符串,wanted_hotkey為快捷鍵,沒有則賦為空值。
其中最重要的是那三個函數了,init()函數用於加載你的插件,term()函數用於卸載時的清理活動(釋放內存,結束處理,保存狀態等等)
第二步:看一下調試框架
from idaapi import * class MyDbgHook(DBG_Hooks): """ Own debug hook class that implementd the callback functions """ def dbg_process_start(self, pid, tid, ea, name, base, size): print("MyDbgHook : Process started, pid=%d tid=%d name=%s" % (pid, tid, name)) def dbg_process_exit(self, pid, tid, ea, code): print("MyDbgHook : Process exited pid=%d tid=%d ea=0x%x code=%d" % (pid, tid, ea, code)) def dbg_library_unload(self, pid, tid, ea, info): print("MyDbgHook : Library unloaded: pid=%d tid=%d ea=0x%x info=%s" % (pid, tid, ea, info)) return 0 def dbg_process_attach(self, pid, tid, ea, name, base, size): print("MyDbgHook : Process attach pid=%d tid=%d ea=0x%x name=%s base=%x size=%x" % (pid, tid, ea, name, base, size)) def dbg_process_detach(self, pid, tid, ea): print("MyDbgHook : Process detached, pid=%d tid=%d ea=0x%x" % (pid, tid, ea)) return 0 def dbg_library_load(self, pid, tid, ea, name, base, size): print "MyDbgHook : Library loaded: pid=%d tid=%d name=%s base=%x" % (pid, tid, name, base) def dbg_bpt(self, tid, ea): print "MyDbgHook : Break point at %s[0x%x] pid=%d" % (GetFunctionName(ea), ea, tid) idaapi.continue_process() return 0 def dbg_suspend_process(self): print "MyDbgHook : Process suspended" def dbg_exception(self, pid, tid, ea, exc_code, exc_can_cont, exc_ea, exc_info): print("MyDbgHook : Exception: pid=%d tid=%d ea=0x%x exc_code=0x%x can_continue=%d exc_ea=0x%x exc_info=%s" % ( pid, tid, ea, exc_code & idaapi.BADADDR, exc_can_cont, exc_ea, exc_info)) return 0 def dbg_trace(self, tid, ea): print("MyDbgHook : Trace tid=%d ea=0x%x" % (tid, ea)) return 0 def dbg_step_into(self): print("MyDbgHook : Step into") self.dbg_step_over() def dbg_run_to(self, pid, tid=0, ea=0): print "MyDbgHook : Runto: tid=%d" % tid idaapi.continue_process() def dbg_step_over(self): print("MyDbgHook : 0x%x %s" % (eip, GetDisasm(eip))) debughook = MyDbgHook() debughook.hook()
這里是調試框架,代碼看起來很長,其實,只要在插件框架的init函數進行初始化,即可。然后在調試過程中,會因為各種事件而觸發各種函數,從而觸發自己需要的操作,實現自動化脫殼或者anti-debug等功能。
第三步:研究實現x64和x32位antii-anti-debug功能
一般anti-anti-debug功能從兩方面着手 ,一方面patch內存,一方面是hook函數。
Patch內存:這需要獲取FS(x86)/GS(x64)指向的地址,這里提供三種方法,第一種直接使用IDApython提供的API接口
fsBase = regval_t() get_reg_val("fs",fsBase) return internal_get_sreg_base(idaapi.get_current_thread(),int(fsBase.ival) )
但這種在64位上無效,估計是IDA自身Bug,已經提交給 hex-ray公司了。
第二種是利用appcall函數來調用windows api得到,這種過於復雜。
第三種是通過注入dll,來直接用asm匯編進行編程,這里可以使用IDA的APPCALL機制來實現LoadLibrary操作,代碼如下:
def LoadLibrary(self,dll_name): return Appcall.proto("kernel32_LoadLibraryA","int __stdcall LoadLibrary(const char * fn);")(dll_name)
之后就可以直接patch_long(addr,byte)了
Hook函數:如上文,最簡單的方法是采用dll注入,采用APPCALL來加載並調用函數,如下
def callfunc(self,funcname): if self.bits == "x86": return Appcall.proto("stealthx86_"+funcname,"bool _stdcall "+funcname+"();")(); else: return Appcall.proto("stealthx64_"+funcname,"bool _stdcall "+funcname+"();")();
關於需要注入的dll,由於不在本文 內容中,請自行探究。
關於一些調試過程中自動化處理的一些,留待下一篇繼續講解。