实战IDA脚本编程--用idc实现JumpNotFunction
用IDA反汇编可执行文件时,经常遇到未命名代码片段。对于少量这样的代码片段可以用IDA的Search->not function来定位(快捷键通常为Alt-U),再用Edit->Functoins->Create function...(快捷键为P)创建新函数。如果出现大量这样的代码片段,反复用Alt-U,P操作就很繁琐。于是就想到利用用idc脚本来实现这个功能。查阅了IDA的Help(https://www.hex-rays.com/products/ida/support/idadoc/index.shtml),Search->not function这个功能的名称为JumpNotFunction,但在Help->Index of IDC functions中找不到相对应的函数(快捷键P有对应的MakeFunction)。
用Google搜索 "How to implement JumpNotFunction using idc script in IDA environment" (如何在IDA环境下用idc脚本实现JumpNotFunction),找不到答案。于是决定自己动手解决问题。经过一番学习,找到了主要的可以使用的相关函数:FindCode、GetFunctionName、FindFuncEnd、isCode、GetFlags,摘录如下。
1.
// ea - address to start from
// flag is combination of the following bits:
#define SEARCH_DOWN 0x01 // search forward
#define SEARCH_NEXT 0x02 // search next occurence
#define SEARCH_CASE 0x04 // search case-sensitive
// (only for bin&txt search)
#define SEARCH_REGEX 0x08 // enable regular expressions
#define SEARCH_NOBRK 0x10 // don't test ctrl-break
#define SEARCH_NOSHOW 0x20 // don't display the search progress
// return BADADDR - not found
long FindCode(long ea,long flag);
2.
// ea - any address belonging to the function
// returns: null string - function doesn't exist otherwise returns function name
string GetFunctionName(long ea);
3.
// ea - starting address of a new function
// returns: if a function already exists, then return its end address.
// if a function end cannot be determined, then return BADADDR
// otherwise return the end address of the new function
long FindFuncEnd(long ea);
4.
#define MS_CLS 0x00000600L // Mask for typing
#define FF_CODE 0x00000600L // Code ?
#define FF_DATA 0x00000400L // Data ?
#define FF_TAIL 0x00000200L // Tail ?
#define FF_UNK 0x00000000L // Unknown ?
#define isCode(F) ((F & MS_CLS) == FF_CODE) // is code byte?
5.
// ea - linear address
// returns: 32-bit value of internal flags. See start of IDC.IDC file for explanations.
long GetFlags(long ea); // get internal flags for ea
以上函数有关参数的简要说明如下(其中的BADADDR为无效地址):
1. FindCode参数:
输入:
ea=有效起始地址
flag=SEARCH_DOWN=向下(地址增加方向)搜索
(SEARCH_NEXT=搜索下一个;
SEARCH_CASE=搜索时区分大小写,仅对二进制和文本有效;
SEARCH_REGEX=允许正则表达式;
SEARCH_NOBRK=不检测Ctrl-Break按键;
SEARCH_NOSHOW=不显示搜索进度)
输出:
返回代码片段起始地址(如果没找到代码片段,则返回BADADDR)。
2. GetFunctionName参数:
输入:
ea=属于某个函数代码范围内的任何有效地址。
输出:
返回函数名字符串,如果函数不存在,则返回空字符串。
3. FindFuncEnd参数说明:
输入:
ea=某个新建函数的起始有效地址。
输出:
如果函数已经存在,则返回其结束地址;如果函数的结束地址不确定,则返回BADADDR;否则返回新建函数的结束地址。
4. isCode参数说明(isCode实际上为宏定义):
输入:
F=相对与某个有效地址的标志。
输出:
逻辑值:1(True)=是代码;0(False)=非代码。
5. GetFlags参数说明:
输入:
ea=线性有效地址。
输出:
对应于ea的标志。
有了这些资料就可以动手构建JumpNotFunction了。首先要获取一个有效地址,通常使用ScreenEA获取当前屏幕光标所在地址,然后判断该地址是否为代码,关键在于判断代码是否属于某个函数。也可以用ScreenEA保存当前地址,再用MinEA获取代码段起始地址并对整个代码段进行扫描,然后恢复当前地址。
我们可以用File->IDC Command...(快捷键Shift-F2)把以下代码粘贴到弹出文本窗口测试一下:
auto ea,name; // 声明自动类型变量
ea = ScreenEA(); // 获取当前地址
ea = FindCode(ea,SEARCH_DOWN); // 找代码片段
name = GetFunctionName(ea); // 获取函数名
Message("ea=%lx name=%s\n",ea,name); // 显示信息
先找到某个已经定义函数(sub_40xxxx为某个函数的起始地址),运行上述代码,可以到到以下信息:
ea=40xxxx name=sub_40xxxx
再用Edit->Functions->Delete function删除该函数后运行上述代码,可以到到以下信息:
ea=40xxxx name=
这说明上述代码有效。
以下是该函数以及利用该函数解决问题的完整代码:
#include <idc.idc>
// 输入:
// ea=起始有效地址
// 输出:
// 返回未命名代码片段有效地址;如果没找到,则返回BADADDR。
static JumpNotFunction(ea)
{
auto name;
do {
if (!isCode(GetFlags(ea)) || Byte(ea) == 0x90 || Byte(ea) == 0xCC) // 过滤无效代码
ea = FindCode(ea,SEARCH_DOWN);
name = GetFunctionName(ea);
if (name == "" && ea != BADADDR) // 如果找到未定义函数,就跳转到该有效地址
{
Jump(ea);
}
else
ea = FindFuncEnd(ea); // 如果是一定义函数,继续查找
} while(name != "" && ea != BADADDR);
return ea;
}
// 利用JumpNotFunction解决问题
static main(void)
{
auto ea0,ea,ea_end,fok;
ea0 = ScreenEA(); // 记住当前光标位置
ea=MinEA(); // 从头开始
ea_end = SegEnd(ea); // 防止地址越界
Message("ea0=%lx\n",ea);
do{
ea = JumpNotFunction(ea); // 调用刚建立的函数
if (ea != BADADDR)
fok = MakeFunction(ea,BADADDR);
if (!fok) {
ea = FindFuncEnd(ea);
Jump(ea);
}
} while (ea < ea_end && ea != BADADDR);
Jump(ea0); // 恢复光标位置
}
将以上内容另存为JumpNotFunction.idc文件并复制到idc子文件夹下,就可以用File->IDC file...打开运行了。
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界