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

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

        

  • 相关阅读:
    React父组件调用子组件
    ES6数组操作
    ant design mobile入坑记
    vue
    图片上传七牛
    CSS
    CSS矩形、三角形等
    使用POST下载文件
    http https协议
    前端网络必备知识
  • 原文地址:https://www.cnblogs.com/0xJDchen/p/7527236.html
Copyright © 2020-2023  润新知