相信大多数人都阅读过神书《IDA Pro权威指南》,但是这本书是基于比较老版的IDA,里面讲的许多概念和代码都无法在最新的IDA上使用。本文打算以IDA7.5为基础讲讲其中编写IDA处理器模块的变化,如果你打算编写IDA模块,这篇文章希望对你有所帮助。如有错误,欢迎指正。
IDA7.6、IDA SDK7.6、Visual studio
模块:IDA二次开发的功能之一,分为加载器、处理器和插件。
加载器:用于分析文件结构,处理文件数据,为后面处理器作准备。常见的有PE文件加载器和ELF文件加载器。
处理器:提供二进制文件的反汇编功能,包含函数识别、分支处理、代码着色等任何跟处理器相关的功能
插件:开发者可以使用插件定制任何功能,著名的有hex-rays开发的F5插件。
脚本:IDA二次开发的功能之一,作为插件的补充,可以使用IDC或python开发。插件能够调用大部分的SDK API并且拥有大量的封装函数,使其非常容易开发。
IDA从7.5开始支持多idb加载和处理,如果你需要使用IDA的idb功能,你可能需要使用set_module_data、clr_module_data、get_module_data这三个函数与你的模块进行绑定。
IDA模块支持事件处理机制,处理器模块导出的LPH中的u_ana、u_emu等回调函数在新版IDA SDK中已被删除,取而代之的是一个notify的回调函数,在该函数中可以处理IDA消息(例如ev_ana_insn和ev_emu_insn)。
IDA从7.4开始把所有的脚本函数统一规范化了,如果你使用python进行IDA脚本开发,需要注意这点,否则你可能发现你的脚本在7.3上能运行而在7.4上无法运行。IDA官网提供了函数的映射关系Porting from IDAPython 6.x-7.3, to 7.4
处理器模块导出LPH相比旧版sdk已经少了很多字段,但该结构体仍然有26个字段需要初始化,这是一个非常繁琐的工作。幸运的是,大多数时候,某些字段不会用到,我们只需要初始化其中几个关键的字段即可。
新版的sdk推荐以下形式创建一个处理器模块,注意SET_MODULE_DATA需要使用一个名称为data_id作为全局变量并且该名称不能改变。
新版sdk的plugin_t为了兼容旧版插件无太大变化,但其中一些成员变量已经不再使用。
新版sdk中plugin_t的init函数一般来说很简单,只需要返回一个plugmod_t类的实例指针即可。以下是一个init函数的参考。
旧版sdk中plugin_t的init、term和run函数分别对应plugmod_t类的构造函数、析构函数和纯虚函数run。
在编写插件时,需要编写一个类继承plugmod_t类并实现run函数,然后将该类的实例指针在init中返回。以下为继承类的参考。
/
/
[
*
]表示必须设置的字段
struct processor_t
{
int32 version;
/
/
[
*
]必须为IDP_INTERFACE_VERSION
int32
id
;
/
/
[
*
]自定义的处理器模块必要大于
0x8000
uint32 flag;
/
/
以PR_开头的flag组合,根据需要添加,可以设置为
0
uint32 flag2;
/
/
以PR2_开头的flag组合,根据需要添加,可以设置为
0
int32 cnbits;
/
/
[
*
]代码段中
1
个字节有多少个bit可用,设置为
8
即可
int32 dnbits;
/
/
[
*
]非代码段中
1
个字节有多少个bit可用,设置为
8
即可
const char
*
const
*
psnames;
/
/
[
*
]处理器模块的短名称,最多
9
个字符,数组以NULL指针结尾
const char
*
const
*
plnames;
/
/
[
*
]处理器模块的长名称,长度不限,数组以NULL指针结尾
const asm_t
*
const
*
assemblers ;
/
/
汇编清单,可以设置为{ NULL },数组以NULL指针结尾
hook_cb_t
*
_notify;
/
/
[
*
]事件通知的回调函数,在该函数中处理IDA发出的各种事件,包括ev_ana_insn、ev_emu_insn、ev_out_insn、ev_out_operand,上述四个事件分别对应旧版SDK的
4
个回调函数
const char
*
const
*
reg_names;
/
/
[
*
]寄存器名称数组
int32 regs_num;
/
/
[
*
]寄存器名称数组的元素个数
int32 reg_first_sreg;
/
/
第一个段寄存器的序号,可以设置为rVcs
int32 reg_last_sreg;
/
/
最后一个段寄存器的序号,可以设置为rVds
int32 segreg_size;
/
/
段寄存器大小,单位为字节,可以设置为
0
int32 reg_code_sreg;
/
/
代码段寄存器,可以设置为rVcs
int32 reg_data_sreg;
/
/
数据段寄存器,可以设置为rVds
const bytes_t
*
codestart;
/
/
代码
/
函数开始的特征码,可以设置为NULL
const bytes_t
*
retcodes;
/
/
代码
/
函数结束的特征码,可以设置为NULL
int32 instruc_start;
/
/
[
*
]指令枚举的第一个数
int32 instruc_end;
/
/
[
*
]指令枚举的最后一个数,枚举最后一个数设置为XXX_LAST,
const instruc_t
*
instruc;
/
/
[
*
]定义指令的汇编输出和操作数属性,要与指令枚举一一对应
size_t tbyte_size;
/
/
长浮点(
long
double)类型的大小,单位为字节,可以设置为
0
char real_width[
4
];
/
/
浮点数中小数点后位数,用于浮点数显示时保留多少位,可以设置为{
0
}
int32 icode_return;
/
/
返回指令的指令码(指令枚举),可以设置为非指令枚举中的元素,一般设置为XXX_NULL(指令枚举的第一个元素)
void
*
unused_slot;
/
/
预留,设置为NULL
};
/
/
[
*
]表示必须设置的字段
struct processor_t
{
int32 version;
/
/
[
*
]必须为IDP_INTERFACE_VERSION
int32
id
;
/
/
[
*
]自定义的处理器模块必要大于
0x8000
uint32 flag;
/
/
以PR_开头的flag组合,根据需要添加,可以设置为
0
uint32 flag2;
/
/
以PR2_开头的flag组合,根据需要添加,可以设置为
0
int32 cnbits;
/
/
[
*
]代码段中
1
个字节有多少个bit可用,设置为
8
即可
int32 dnbits;
/
/
[
*
]非代码段中
1
个字节有多少个bit可用,设置为
8
即可
const char
*
const
*
psnames;
/
/
[
*
]处理器模块的短名称,最多
9
个字符,数组以NULL指针结尾
const char
*
const
*
plnames;
/
/
[
*
]处理器模块的长名称,长度不限,数组以NULL指针结尾
const asm_t
*
const
*
assemblers ;
/
/
汇编清单,可以设置为{ NULL },数组以NULL指针结尾
hook_cb_t
*
_notify;
/
/
[
*
]事件通知的回调函数,在该函数中处理IDA发出的各种事件,包括ev_ana_insn、ev_emu_insn、ev_out_insn、ev_out_operand,上述四个事件分别对应旧版SDK的
4
个回调函数
const char
*
const
*
reg_names;
/
/
[
*
]寄存器名称数组
int32 regs_num;
/
/
[
*
]寄存器名称数组的元素个数
int32 reg_first_sreg;
/
/
第一个段寄存器的序号,可以设置为rVcs
int32 reg_last_sreg;
/
/
最后一个段寄存器的序号,可以设置为rVds
int32 segreg_size;
/
/
段寄存器大小,单位为字节,可以设置为
0
int32 reg_code_sreg;
/
/
代码段寄存器,可以设置为rVcs
int32 reg_data_sreg;
/
/
数据段寄存器,可以设置为rVds
const bytes_t
*
codestart;
/
/
代码
/
函数开始的特征码,可以设置为NULL
const bytes_t
*
retcodes;
/
/
代码
/
函数结束的特征码,可以设置为NULL
int32 instruc_start;
/
/
[
*
]指令枚举的第一个数
int32 instruc_end;
/
/
[
*
]指令枚举的最后一个数,枚举最后一个数设置为XXX_LAST,
const instruc_t
*
instruc;
/
/
[
*
]定义指令的汇编输出和操作数属性,要与指令枚举一一对应
size_t tbyte_size;
/
/
长浮点(
long
double)类型的大小,单位为字节,可以设置为
0
char real_width[
4
];
/
/
浮点数中小数点后位数,用于浮点数显示时保留多少位,可以设置为{
0
}
int32 icode_return;
/
/
返回指令的指令码(指令枚举),可以设置为非指令枚举中的元素,一般设置为XXX_NULL(指令枚举的第一个元素)
void
*
unused_slot;
/
/
预留,设置为NULL
};
int
data_id;
/
/
for
SET_MODULE_DATA
static ssize_t idaapi notify(void
*
,
int
msgid, va_list)
{
if
( msgid
=
=
processor_t::ev_get_procmod )
return
size_t(SET_MODULE_DATA(tms6_t));
return
0
;
}
int
data_id;
/
/
for
SET_MODULE_DATA
static ssize_t idaapi notify(void
*
,
int
msgid, va_list)
{
if
( msgid
=
=
processor_t::ev_get_procmod )
return
size_t(SET_MODULE_DATA(tms6_t));
return
0
;
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)