IDA 教程-脚本化的调试器

来源:互联网 发布:unity3d格式文件打开 编辑:程序博客网 时间:2024/05/16 05:11

从 2003 年开始,IDA PRO 开始支持调试器功能,这很好的弥补了静态分析的不足。但
在很多情况下,它的功能仍然弱于动态分析。IDA PRO 的调试器目前支持 32 位和 64 位下
MS Windows 可执行程序,它支持在 Windows,Linux,Mac OS x 下进行本地和远程调试。然
而,因为调试器的 API 需要熟悉我们的 SDK 和使用基于事件的模型,这对我们有的用户来
说有些困难和并不容易操作。
·  由于 API 使用了基于事件的模型,这使得它很难用我们常用的方法来设计一个顺序
执行(线性模型)的程序。使用者被强制设计一个事件句柄,来实现一个有限状态
机(插件的核心逻辑)。很多时候这是个强大的方式,但可能对某些简单的任务来
说又过于复杂了。
·  因为 API 只能在插件层使用,一个简单的调试器操作需要写一个插件,这比写一个
简单的 IDC 脚本需要花费更多的时间和精力。
IDA 5.2 针对这两个问题做了改进。以往的基于事件的模型依然可用,同时也可通过使
用 get_debugger_event()这个函数来支持简单的线性模型。这个函数暂停插件(或脚本)
的执行直到一个新的调试器时间发生。使用者可以指定他是只对进程挂起事件感兴趣或是对
所有事件。也可以进行定时设置,如果没有其它事件发生,超时后程序可以继续执行。
 
新功能允许我们抛弃以往的事件模型(除了在那些事件逻辑优先线性逻辑的那些情况),
通过编写 IDC 脚本来控制调试器。例如,启动调试器,运行到指定位置,输出一些数据和
两次单步运行,如下所示意:

AppBpt(some_address); StartDebugger("","","");         // start debugger with default params GetDebuggerEvent(WFNE_SUSP, -1); // ... and wait for bpt Message ("Stopped at %a, event code is %x\n", GetEventEA(), GetEventId()); StepInto();                      // request a single step GetDebuggerEvent(WFNE_SUSP, -1); // ... and wait for app to execute StepInto();                       // request a single step GetDebuggerEvent(WFNE_SUSP, -1); // ... and wait for app to execute

在 IDA 5.1 中这需要使用一个事件句柄来处理一个小的有限状态机自动执行,一共需要
超过 200 行的代码。请注意,在上面的例子中,为了清晰,错误处理代码被忽略。在实际
生活中,你应该需要检查一些不希望的情况例如单步 StepInto()之后的发生异常的情况。
 
为了演示使用新的方式来编写脚本是如何简单,我们重写了 UUNP 解压器插件的核心
功能。原程序需要大概 600 行代码,并有一个复杂的逻辑。新的脚本只需要 100 行的代码
(其中几乎有一半是注释和空行)。更重要的是,脚本易懂并可根据你的需要随意修改。它
展示了 IDA5.2 的新调试器功能的使用。

#include <idc.idc>  //-------------------------------------------------------------------------- static main() {   auto ea, bptea, tea1, tea2, code, minea, maxea;   auto r_esp, r_eip, caller, funcname;    // Calculate the target IP range. It is the first segment.   // As soon as the EIP register points to this range, we assume that   // the unpacker has finished its work.   tea1 = FirstSeg();   tea2 = SegEnd(tea1);    // Calculate the current module boundaries. Any calls to GetProcAddress   // outside of these boundaries will be ignored.   minea = MinEA();   maxea = MaxEA();    // Launch the debugger and run until the entry point   if ( !RunTo(BeginEA()) )     return Failed(-1);    // Wait for the process to stop at the entry point   code = GetDebuggerEvent(WFNE_SUSP, -1);   if ( code <= 0 )     return Failed(code);    // Set a breakpoint at GetProcAddress   bptea = LocByName("kernel32_GetProcAddress");   if ( bptea == BADADDR )     return Warning("Could not locate GetProcAddress");   AddBpt(bptea);    while ( 1 )   {     // resume the execution and wait until the unpacker calls GetProcAddress     code = GetDebuggerEvent(WFNE_SUSP|WFNE_CONT, -1);     if ( code <= 0 )       return Failed(code);      // check the caller, it must be from our module      r_esp = GetRegValue("ESP");     caller = Dword(r_esp);


 

if ( caller < minea || caller >= maxea )       continue;      // if the function name passed to GetProcAddress is not in the ignore-list,     // then switch to the trace mode     funcname = GetString(Dword(r_esp+8), -1, ASCSTR_C);     // ignore some api calls because they might be used by the unpacker     if ( funcname == "VirtualAlloc" )       continue;     if ( funcname == "VirtualFree" )       continue;      // A call to GetProcAddress() probably means that the program has been     // unpacked in the memory and now is setting up its import table     break;   }    // trace the program in the single step mode until we jump to   // the area with the original entry point.   DelBpt(bptea);   EnableTracing(TRACE_STEP, 1);   for ( code = GetDebuggerEvent(WFNE_ANY|WFNE_CONT, -1); // resume         code > 0;         code = GetDebuggerEvent(WFNE_ANY, -1) )   {     r_eip = GetEventEa();     if ( r_eip >= tea1 && r_eip < tea2 )       break;   }   if ( code <= 0 )     return Failed(code);    // as soon as the current ip belongs OEP area, suspend the execution and   // inform the user   PauseProcess();   code = GetDebuggerEvent(WFNE_SUSP, -1);   if ( code <= 0 )     return Failed(code);    EnableTracing(TRACE_STEP, 0);    // Clean up the disassembly so it looks nicer   MakeUnknown(tea1, tea2-tea1, DOUNK_EXPAND|DOUNK_DELNAMES);   MakeCode(r_eip);
AutoMark2(tea1, tea2, AU_USED);   AutoMark2(tea1, tea2, AU_FINAL);   TakeMemorySnapshot(1);   MakeName(r_eip, "real_start");   Warning("Successfully traced to the completion of the unpacker code\n"           "Please rebuild the import table using renimp.idc\n"           "before stopping the debugger"); } //-------------------------------------------------------------------------- // Print an failure message static Failed(code) {   Warning("Failed to unpack the file, sorry (code %d)", code);   return 0; }