倚天屠龍(一):妙用IDA Pro--利用IDAPython編寫調試插件


一:前言

  雖然靜態分析有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,由於不在本文 內容中,請自行探究。

  關於一些調試過程中自動化處理的一些,留待下一篇繼續講解。

 

 

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM