编辑: 865397499 | 2019-07-06 |
00 mov eax,dword ptr fs:[ebp] 这里的
26 和64 就是指令前缀中的段改写前缀了,其实不难理解 重复前缀说明: 要重点说明一下重复前缀的特点:只有针对特定的指令才会生效,假如说将 rep 强行写在一些 其他的指令前面,是不生效的. 前缀大全: 重复前缀(rep/repne): 总线加锁前缀(LOCK):控制处理器总线 F0h repne、repnz F2h rep、repz、repe F3h 段寄存器前缀(segment): CS 2Eh SS 36h DS 3Eh ES 26h FS 64h GS 65h 修改操作数长度前缀(operand-size) 66h 修改地址长度前缀(address-size) 67h 修改操作数长度前缀: 这个很简单了,只是让长度操作数在
32 位和
16 位之间转换.和前文讲的一样了,Intel 为了节省位置,工作在
32 位模式的时候,默认就是用
32 位寄存器(eax,ebx 等),如果要用
16 位寄存器(ax,bx 等),就加个改长度前缀(66)就好了.(64 位的 REX 前缀先不讨论了) 修改地址长度前缀: 这个也很简单了,直接举例:
00401000 8B03 mov eax, dword ptr [ebx]
00401002 67:8B03 mov eax, dword ptr [bp+di] 只是用了
16 位寄存器来寻址而已,现在很多地方可能不支持这么写,OD 好像就不支 持 剩下的 lock 前缀就是暂时锁定一下总线而已, 比较少用到, 有兴趣的话可以去看 Intel 手册, 里面有讲解 ,我们只需要对应各种前缀进行解析,反汇编才是我们的初衷嘛(^__^)! 对前缀进行解析: 反汇编的时候,最先解析前缀. 我们要做的事如下:
1、获得前缀对应的汇编代码 2(处理特殊情况)、如果连续出现同一种前缀,就应该将重复中第一个前缀单独作为一 条指令 举一个例子:
00401364 F3: prefix rep:
00401365 2E:67:66:F2:A4 repne movs byte ptr es:[di],byte ptr cs:[si] 由于连在一起的前缀中,第一个前缀 F3 与最后一个前缀 F2 属于同一组,所以将 F3 单独开 来 解析步骤 第一步(把前缀的字符串都保存在一个数组里): const char * RepPref[] = { lock , rep , repe , repz , repne , repnz };
const char * SegPref[] = { cs , ss , ds , es , fs , gs };
例如读取到前缀的值是 2Eh,就保存起对应的数组索引,如果没读取到前缀值,就保存 为-1 第二步(判断是否有连续相同前缀) 这一步有一点特殊了, 由于指令前缀可以是任意顺序出现, 所以连续的重复前缀可能有 多种顺序组合. 我们按照 CPU 的解析思路来写,也就是说,我们可以加一个判断:如果连续解析前缀的 时候,出现一个同组的前缀,就返回到前缀解析开始的地址,去找到同组的第一个前缀,并 把它单独输出. 解析伪代码: 主要解析的思路伪代码如下: char * Disasm(当前地址, 指令信息地址, 返回字符串地址) { 初始化 循环(如果当前字节为前缀) 如果为 F0h 如果已标记//标记说明现在是退回来第二次扫描,需要单独输出前缀 单独输出前缀(prefix lock) 如果为第一次同类前缀 保存对应数组下标 如果为第二次同类前缀 当前地址=循环前地址 标记 ...... } 指令信息封装成一个结构体(或类)就OK.这里直接枚举判断了,代码可能不够简洁, 但扫描解析的方式是正确的,大家有自己的思路可以指教. #include #include const char * RepPref[] = { lock , rep , repe , repz , repne , repnz };
const char * SegPref[] = { cs , ss , ds , es , fs , gs };
typedef struct pinstr//指令信息结构体 { charRepPre;
charSegPre;
charOperPre;
charAddrPre;
}Instr, *PInstr;
char * DisAsm(char *, PInstr, char *);