倚天屠龙(一):妙用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,由于不在本文 内容中,请自行探究。

  关于一些调试过程中自动化处理的一些,留待下一篇继续讲解。

 

 

    

posted @ 2017-09-15 16:50  0xJDchen  阅读(4321)  评论(1编辑  收藏