在对嵌入式设备进行逆向分析的过程中,偶尔会遇到一些比较小众或专业性较强的处理器,使用ida默认不支持的指令集。这时为了愉快的使用ida pro逆向就需要编写对应的处理器模块。出于学习目的,找一个非常简单的指令集来探路,CHIP-8再合适不过了。
CHIP-8简介
CHIP-8是一种解释型编程语言,出现在上世纪七十年代中期,被用来开发电子游戏以及图形化计算器等。CHIP-8的代码运行在CHIP-8虚拟机上。
- 多数时候其解释器需要占据内存空间的前512个字节,所以程序通常加载在0x200处,且不会用到前512字节地址。
- CHIP-8使用16个8bit的数据寄存器,命名为V0-VF。其中VF寄存器在执行某些指令时作为状态寄存器。有一个16位的地址寄存器I。
- CHIP-8有两个定时器,延时寄存器(Delay timer)与声音寄存器(Sound timer)。
- 唯一使用栈的地方是call的时候存储返回地址
其他信息可以在wikipedia上了解:https://en.wikipedia.org/wiki/CHIP-8
初探ida处理器模块(IDA Pro 7.0)
ida处理器模块的样例可以在sdk中modules目录下找到,一打开目录映入眼帘的就是许多的c++写的处理器模块。在module/script下有三个python的处理器模块,包含一个proctemplate.py
模板。这里选择使用python编写,在模板文件中可以看到主要代码为PROCESSOR_ENTRY方法返回一个继承自processor_t的sample_proccesst,类中有许多的`notify开头的回调方法,每个都有少量对其应该负责的工作的注释。 通过注释可以知道其中
notify_emu`notify_out_operand
notify_out_insn
notify_ana
几个方法是强制性的。
IDA指令解析流程
分析(Ana)
在notify_ana(self, insn)
中,需要做的是读取数据并解析成指令。根据opcode找到对应的指令,读取并设置好指令的参数与属性。其中insn为一个insn_t类型的变量,即为要设置的指令对象。调用insn.get_next_byte()系列方法可以获取到当前的数据,也就是下一步要解析的指令或操作数。设置insn的itype属性为指令的itype,根据操作数数量设置insn.Op1
及insn.Op2
的系列属性如type(操作数类型):o_imm(立即数)/o_reg(寄存器)/o_mem(内存)……、dtype(操作数大小):dt_byte/dt_word/dt_dword和value(操作数的值)。
模拟(Emu)
体现在notify_emu(self)
中,用于设置数据与代码之间的关系,例如当前指令执行完毕后如果顺序执行下一条指令就要通过add_cref(insn.ea, insn.ea + insn.size, fl_F)
来告诉ida这条指令的下一条在哪。其中第一个参数为出发点,多数时候为当前指令地址,第二个参数为将跳到的地址,第三个参数有可能为 fl_F(顺序),fl_JN/fl_JF(跳转)以及fl_CN/fl_CF(调用)。以及使用add_dref(op.addr, op.offb, dr_O)
类似指令来添加数据的引用信息。
如果有对栈数据的分析也在模拟过程中处理,可参考ebc.py中emu回调所调用的trace_sp
方法。再笔者的理解中,除了个别情况,大多数修改ida数据库中数据的操作都在模拟部分进行。
输出(output)
体现在notify_out_operand(self, ctx, op)
与notify_out_insn(self, ctx)
两个方法。notify_out_operand用来输出参数,notify_out_insn用来输出指令。注释中特意指出在这两个方法中不应该有任何操作数据库,修改标志位等操作。笔者认为与output操作执行次数较多有关。
看一看script.dll
在ida的procs目录下可以看到script.dll
与script64.dll
两个文件,其与我们使用python编写处理器模块密切相关。用ida打开script.dll,字符串中可以看到许多在proctemplate.py
模板中出现过的熟悉的方法名。
初始化过程中sub_130021F0=>sub_13005B30=>sub_13006100会寻找PROCESSOR_ENTRY方法并尝试接收其返回的对象
成功后在sub_13003F70中检查前文提到的几个强制性的回调函数是否存在
随后会在sub_13004C90中解析几个处理器属性,可以注意到assembler
与instruc
,也是一会必须要编写好的属性。
最重要的地方是13003b51h处的call,hook_to_notification_point(3i64, sub_130041E0)
。查看ida sdk include/idp.hpp可以看到对hook_to_notification_point说明和函数原型:
hook_type_t枚举体的定义:
第一个参数为3,即HT_IDB,从注释看出make_code, make_data等事件会触发回调。回调函数的原型如下
可以看出sub_130041E0的第二个参数即为notification_code,此处为processor_t::event_t。其定义可以在ida文档中找到,也可通过qword_1300E000处得知。
函数会根据不同的notification_code调用不同的回调函数。
编写最简单的ida处理器模块
定义处理器属性
修改sample_processor_t为chip8_processor_t。首先需要设置一些处理器模块的基本信息。每个处理器模块都需要有id,name。这里直接在模板基础上更改就好,挑一个没人用的id,psnames与plnames为名字缩写与全称。cnbits与dnbits设为8即可,assembler暂时无需更改。flag指定了一些特性,根据需要设置。PTRSZ为指针大小,由于chip8地址空间有0x1000个字节,这里设置为2即16bits。
定义寄存器
为了代码可读性,学习ebc.py做法,将定义寄存器的代码单独编写为一个方法并在chip8_processor_t的init方法中调用。设置好chip8所用的寄存器。由于ida的实现问题,虽然用不到段寄存器,但仍需要设置好假的段寄存器。
设置指令集
在刚才分析script.dll时可以看到ida需要一个instruc属性存放指令信息,同时我们在解析指令时可以查表更加方便。还需要设置chip8_processor_t的itypexx与之对应。这里同样编写一个在init中调用的方法,初始化指令表。这里指令对应的助记表示是根据github上CHIP-8项目的指令集说明。
由于指令无法按顺序解析,定义itable时每条指令设置mask与opcode,通过操作数与掩码结合来判断指令,要注意用这种方法定义指令集需要将更精确的匹配项放在前面。idef的d属性为对应指令的解析方法,这里根据操作数类型等编写了几个方法解析并设置操作数信息,方便后面分析过程分析指令。
不同指令的文字表达相同,所以不能像ebc.py中将指令名称直接与`”itype“拼接作为属性名称,而是将指令的操作数与
“itype_”拼接后作为属性名称。
最后还需要设置指令集表项的起始索引与结束索引
self.instruc_start`self.instruc_end
,ret指令的itype信息self.icode_return
。
实现回调函数
实现分析与输出
首先要实现notify_ana(self, insn)
,这里因为实现了单独的解析函数并设置好了指令表,直接获取两字节数据与掩码and后查表即可,如果遇到不认识的操作数,return 0
告诉ida识别出现了问题。识别正常则返回解析指令及操作数的大小。
decode系列用于解析操作数的函数大同小异。
然后实现notify_out_operand(self, ctx, op)
与notify_out_insn(self, ctx)
两个方法,一个负责输出操作数,一个负责输出指令。
在notify_out_insn中调用ctx.out_mnemonic来输出instruc中指令的name。之后调用ctx.out_one_operand(i)输出操作数,此方法会调用到下面的notify_out_operand(self, ctx, op)
回调来输出操作数信息。其中op参数会携带指令操作数信息,在分析时可以设置specval属性来携带额外信息给输出方法。
到这里已经可以使用这个处理器模块了,将其放置在ida目录的procs下。加载文件时选择处理器类型CHIP-8。在想要反汇编的地方c来make_code,成功。但是会发现按一下c只识别一个,指令之间没有联系,更别提跳转了。如下图
这就是因为没有实现notify_emu方法,指令解析完后没有互相的引用导致的。下面来实现模拟部分。
实现模拟
|
|
JP与CALL指令根据操作数来设置引用地址。所有JP、CALL以及RET指令在定义itable的时候feature都设置了CF_STOP标志位,其不引用相邻指令,除此之外指令对相邻指令添加引用。
简单实现emu之后再用ida载入,设置加载地址为0x200(见CHIP8简介)。直接按p定义函数,效果如下图(载入的rom文件:BLITZ)
尾声
实现了一个非常简单的指令集的处理器模块,关于trace_sp,switch等均未涉及到,算是对idp有一个简单的了解和实践,完整代码:chip8.py。珠玉在前,下面给出一些过程中参考的优秀的文章,感谢大佬们的指点。