OD插件编写以及与IDC脚本的配合使用(含OD插件源码及IDC脚本)
天易love 2010-11-29
用OD调试程序时,有时对其中某一段关键代码比较感兴趣,而你想节省时间也不想为了看那一小段伪代码而用IDA载入那个大家伙,这个时候怎么办呢?由于OD下我们还不能一下子看到伪代码,所以有了这个想法:
1、在OD中选中你感兴趣的代码导出机器码到文件中;
2、在IDA中用IDC脚本打开机器码文件并patch到某个空白处;
3、按F5看伪代码。
通过实验已经实现,下面具体说一下:
一、关于导出机器码到文件中的OD插件的实现;
由于本人孤陋寡闻不知道有没有这种插件,所以只好用vc 6.0自己编程实现。网上翻出来一个模板,照猫画虎,竟然也编译成功了。下面奉献自己的一点点经验,告诉大家我是如何从一无所知到写出自己需要的插件的全过程,希望对大家有益。
我起初的想法是:通过选中一段代码,得到代码的首尾地址,这样就可以把该段内存中的机器码读取并保存到一个指定的文件中。但是这个地址怎么得到呢?读取内存要用什么函数呢?对于写插件我完全是个门外汗,所以我必须先逆向一个类似插件找点感觉。发现了一个插件Asm2Clipboard,它是把汇编代码以某种格式复制到粘贴板,看来可以先拿它开刀。
先静态调试直接看它的Pluginaction伪代码,如下:
int __cdecl ODBG_Pluginaction(int a1, int a2, int a3)
{
int result; // eax@1
result = a1;
if ( a1 )
{
if ( a1 == 31 ) //CPU Disassembler窗口调用的?
{
result = a2;
if ( a2 )
{
if ( a2 == 1 ) //菜单1?
{
sub_100011F0(a3); //关键函数 入口参数是item,可能item中有一些重要信息
result = sub_100019D0();
}
}
Else //菜单2?
{
sub_100011F0(a3);
result = sub_10001520();
}
}
}
查看OllyDbg Plugin API v1.10中有关的ODBG_Pluginaction的解释,才得出上面的注释。
Optional callback function. If present, OllyDbg calls it each time the user selected menu item added to menu by ODBG_Pluginmenu. 简单点就是OD会调用这个函数,当你点你自己的插件菜单时。
void ODBG_Pluginaction(int origin,int action,void *item);
Parameters:
origin - code of window that calls ODBG_Pluginaction. OllyDbg supports following codes:
Code Cast item to Who calls ODBG_Pluginmenu
PM_MAIN item is always NULL Main window
PM_DUMP (t_dump *) Any Dump window
PM_MODULES (t_module *) Modules window
PM_MEMORY (t_memory *) Memory window
PM_THREADS (t_thread *) Threads window
PM_BREAKPOINTS (t_bpoint *) Breakpoints window
PM_REFERENCES (t_ref *) References window
PM_RTRACE (int *) Run trace window
PM_WATCHES (1-based index) Watches window
PM_WINDOWS (t_window *) Windows window
PM_DISASM (t_dump *) CPU Disassembler
PM_CPUDUMP (t_dump *) CPU Dump
PM_CPUSTACK (t_dump *) CPU Stack
PM_CPUREGS (t_reg *) CPU Registers
//第一个参数是个代码,Asm2Clipboard显然是用的31,那么这个31是哪个呢?在头文件Plugin.h中搜到了#define PM_DISASM 31 这一条,显然首先判断是否是CPU Disassembler这个反汇编窗口调用了Pluginaction函数,其次判断是点了哪个菜单。那么到底该用哪个函数呢?进入sub_100011F0(a3)看看,发现OLLYDBG_101(&v16, v1, 16, 3);它的入口参数就是得到的内存数据buffer,选中汇编代码区域的首地址,将要读取的代码字节长度,读取模式,而返回的机器码就在buffer中了。这个OLLYDBG_101到底是什么函数呢?用lordpe看了下OD原来是ulong Readmemory(void *buf,ulong addr,ulong size,int mode);这样使用的函数就搞定了;接下来就是选中区域的首尾地址该上哪找呢?通过动态调试原来Pluginaction(int a1, int a2, int a3)的第3个参数item是一个t_dump 结构指针。
typedef struct t_dump { // Current status of dump window
t_table table; // Treat dump window as custom table
int dimmed; // Draw in lowcolor if nonzero
ulong threadid; // Use decoding and registers if not 0
int dumptype; // Current dump type, DU_xxx count size
SPECFUNC *specdump; // Decoder of DU_SPEC dump types
int menutype; // Standard menus, MT_xxx
int itemwidth; // Length of displayed item, characters
int showstackframes; // Show stack frames in address dump
int showstacklocals; // Show names of locals in stack
int showsource; // Show source as comment in disassembler
char filename[MAXPATH]; // Name of displayed or backup file
ulong base; // Start of memory block or file
ulong size; // Size of memory block or file
ulong addr; // Address of first displayed byte
ulong lastaddr; // Address of last displayed byte 1
ulong sel0; // Address of first selected byte
ulong sel1; // Last selected byte (not included!)
ulong startsel; // Start of last selection
int captured; // Mouse is captured by dump
ulong reladdr; // Addresses relative to this
char relname[SHORTLEN]; // Symbol for relative zero address base
char *filecopy; // Copy of the file or NULL
char *backup; // Old backup of memory/file or NULL
int runtraceoffset; // Offset back in run trace
ulong reserved[8]; // Reserved for the future extentions
} t_dump;
其中sel0 、sel1成员就是我想要的东西。
ulong sel0; // Address of first selected byte
ulong sel1; // Last selected byte (not included!)
action - identifier of menu item (0..63), as set by ODBG_Pluginmenu;
//第二个参数,判断是点击了哪个菜单
item - pointer either to selected element of sorted data displayed in window or, in case of dump windows, pointer to dump descriptor, or NULL. You may need this element to carry out requested action.//得到选择区域的首尾地址
关键代码如下:
extc int _export cdecl ODBG_Pluginmenu(int origin,char data[4096],void *item)
{
t_dump *pd;
if (origin==PM_DISASM) // Popup menu in Disassembler
{
pd = (t_dump *)item;
if (NULL == pd || 0 == pd->size) return 0; // Window empty, don't add
if (pd->sel1 > pd->sel0) sprintf(data,"0 &Export to C:\\code.txt");//右键弹出菜单文 字
return 1;
}
return 0;
}
用vc 6.0编写了一个body.exe 小程序,里面做了个地址0x401020开头的函数,填充了几百字节的nop指令,用来作为patch的地方,用IDA打开body.idb,G到0x401020处,先在OD中选中一段汇编代码,右键在弹出菜单中选择“export to C:\code.txt”,而后在IDA中执行脚本,在0x401020处按一下P键,如果不出意外的话,再F5一下就可以看到相应的伪C代码了。由于F5是有一定要求的,能否有用和你选择的代码也有一定的关系。脚本代码如下:
#define base_addr 0x401020
#define binaryfile "C:\\code.txt"
static main() {
auto i,pos, st, st2, f, st_new, addr,b_new;
for ( i=0; i < 0x200; i=i 1 )
{
PatchByte(base_addr i,0x90);
}
f=fopen(binaryfile,"r");
if (f!=-1)
{
addr=base_addr;
while ((st=readstr(f))!=-1)
{
//处理1行
while (strlen(st)>0)
{
while (strstr(st," ")==0 & strlen(st)>0)
{ // 滤去开头的空格
st2=substr(st,1,-1);
st=st2;
}
st_new=substr(st,0,2);
b_new=xtol(st_new);
st2=substr(st,2,-1); st=st2;
PatchByte(addr,b_new);
MakeUnkn(addr,1);
addr=addr 1;
}
}//while
} //if
fclose(f);
PatchByte(addr,0xc3);
Jump(base_addr);
MakeCode(base_addr);
}