首页
社区
课程
招聘
[求助]关于反汇编引擎的问题!
2008-12-1 15:38 11103

[求助]关于反汇编引擎的问题!

2008-12-1 15:38
11103
最近在做对程序自动跟踪调试的工作。在用的反汇编引擎:libdasm1.5
但是在处理CALL语句时出现问题:
例:
00401000   .  E8 0B000000   call    00401010
00401005   . /E9 16000000   jmp     00401020
在网上查了下,说是0x401010 = 0x00401005 + 0x0B,
但是用libdasm反汇编出的语句是
call 0x15
怎么会这样呢?
另:
如何通过CALL XXXX判断XXXX是用户自己定义的函数,还是系统调用呢?
初步想是通过代码段地址范围来判断,但是动态载入的DLL呢,各位有什么其他好办法吗?
谢谢!
问题太简单,可能也有点偏,请各位提点思路,不胜感激!

阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开 发者可享99元/年,续费同价!

收藏
点赞7
打赏
分享
最新回复 (10)
雪    币: 202
活跃值: (58)
能力值: ( LV9,RANK:370 )
在线值:
发帖
回帖
粉丝
egogg 9 2008-12-1 17:10
2
0
反汇编成call 0x0B还是可以理解的,因为E8和E9的参数都是16/32为的相对偏移地址,符号扩展后加到EIP(也就是下条指令的地址)上去就是新的EIP,也就是翻译出来给我们看的地址。
call 0x15没有道理啊!你确定的确是被翻译成了call 0x15?又没有什么运行的例子什么的?发出来一起研究研究?

Call xxx的对xxx的判断,你可以试试这个方法(菜鸟愚见,望高手毋笑)?
1、首先判断该地址是否在pe文件的.txt/.code代码映射后的空间范围类,是则表示跳转后的地址应该是程序自身。
2、如果不是1,读取pe文件的所有import的函数的地址,逐个比较。
3、如果还不是,读取实例所有模块,比较地址。
雪    币: 220
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
westboy 2008-12-1 20:01
3
0
十分感谢egogg。经你提醒,我试验了下所用的反汇编引擎,以下和OD做对比:
OD:
00401000   .  E8 0B000000   call    00401010
00401017  |.  E8 D4B50000   call    0040C5F0
00401025   .  E8 27090000   call    00401951
用libdasm的结果:
call 0x15
call 0xb5de
call 0x931
反汇编的结果与相对偏移都相差A,应该是这里的问题吧。
对反汇编引擎实在不了解,刚搜到《打造自己的反汇编引擎》系列,感谢精彩分享。
不知道怎么传附件,所用libdasm反汇编引擎地址如下:
http://www.nologin.org/main.pl?action=codeView&codeId=49
搞不懂当初为什么会用这个引擎,害人啊
不知道除了OD的dasm外,还有什么常用的反汇编引擎呢?

关于CALL操作数的问题,感谢你的三点建议,只是第二点是不是会非常耗时呢?每个CALL都要去遍历import表?我想只要操作数地址在程序CODE段或用户自己加载的DLL段,那就应该是用户函数了,但是如何区分系统的DLL还是用户自己加载的DLL呢?

再次感谢egogg的及时答复和精彩建议!
雪    币: 202
活跃值: (58)
能力值: ( LV9,RANK:370 )
在线值:
发帖
回帖
粉丝
egogg 9 2008-12-1 21:06
4
0
od的反汇编引擎就挺好的啊,你当初为什么不就用od的呢?其他的反汇编引擎看雪老大的书上都列了,具体哪些我现在手头上没有书。他没有列的我知道的还有nasm的反汇编引擎,pvdasm。

至于判断跳转指令的问题,做指令分析的话,这些遍历可能是不可避免的,具体实现我也不知道,ida的文档中应该有相应的解决方案。还有就是,不一定每一个call都需要比较,只需要比较far调用,像E8这样的短跳转指令,正常应用都应该是程序自己代码的程序调用,远指令调用(指令以9A、FF开头)才需要比较地址范围。这部分我也就使曾经想过也不知道对不对,而且由于水平有限还没有试过,你可以试试,看看效果。

我大致检查了一下,libdasm1.5中并没有任何错误(关于call near这部分),应该是你在使用的过程中有不小心的地方,函数入口有一个offset,这里offset的值被初始化成了0xA,如果你不用这个值话,应该把这个值赋0(这时你得到的就是call 0x0B)或者变成当前指令的偏移地址(线性地址,这时你得到的就是 call 00401010)。你可以试一下。
雪    币: 723
活跃值: (81)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
mik 4 2008-12-1 21:52
5
0
显示格式不同,

显示格式是: call $+offset        //这个$ 指本条指令边界

call 0x15              // call 00401010 对于$ 来说是偏移 0x15
call 0xb5de          // call 0040c5f0    对于$ 来说是偏移 0xb5de
call 0x931            // call 00401951  对于 $ 来说是偏移 0x931

编码是一样的,只是显示不同而已
雪    币: 220
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
westboy 2008-12-2 10:30
6
0
非常感谢egogg和mik的答复,问题解决了。确实是offset的问题,只要传入当前指令的偏移地址就可以得到正确的反汇编指令,是我大脑短路了,应该仔细看看sample
由于水量太菜,当时用OD的dasm的时候总是由于版本问题,一堆错误,就偷懒换了。

再次感谢两位!
雪    币: 220
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
westboy 2008-12-2 16:49
7
0
再弱问一下,那我是不是可以通过call指令的near或far来区分后面的操作数呢?
看了egogg推荐的coder table,发现CALL指令对应的代码只有:E8,FF,9A(这个还没见过)
那我如果要区分CALL XXXX中XXXX的范围,
例如:
if(E8)
{
     if(min<XXXX<max)
         blablabla;
}
else if(FF)
{
      if(min<DS:[XXXX]<max)
          blablabla
}
else
{
     (9A????)
}
想法太菜,各位见笑!
雪    币: 202
活跃值: (58)
能力值: ( LV9,RANK:370 )
在线值:
发帖
回帖
粉丝
egogg 9 2008-12-2 20:41
8
0
我认为,根据libdasm的特点,不用比较near far什么的也行,只要是该跳转指令(INSTRUCTION_TYPE_CALL 或者JMP),取串中的地址比较就行了。
雪    币: 220
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
westboy 2008-12-3 19:48
9
0
near调用时,CALL的操作数确实就是跳转地址,但是当是far时,或者需要取DS:[XXXX],或者类似:CALL EDI,再取EDI的值,有点麻烦。
您说的libdasm的特点我不太明白指的是什么?
雪    币: 202
活跃值: (58)
能力值: ( LV9,RANK:370 )
在线值:
发帖
回帖
粉丝
egogg 9 2008-12-3 21:00
10
0
[QUOTE=westboy;544923]near调用时,CALL的操作数确实就是跳转地址,但是当是far时,或者需要取DS:[XXXX],或者类似:CALL EDI,再取EDI的值,有点麻烦。
您说的libdasm的特点我不太明白指的是什么?[/QUOTE]

我是看到:
typedef struct _INSTRUCTION {
	int length;		// Instruction length
	enum Instruction type;	// Instruction type
	enum Mode mode;		// Addressing mode
	BYTE opcode;		// Actual opcode
	BYTE modrm;		// MODRM byte
	BYTE sib;		// SIB byte
	int modrm_offset;	// MODRM byte offset
	int extindex;		// Extension table index
	int fpuindex;		// FPU table index
	int dispbytes;		// Displacement bytes (0 = no displacement)
	int immbytes;		// Immediate bytes (0 = no immediate)
	int sectionbytes;	// Section prefix bytes (0 = no section prefix)
	OPERAND op1;		// First operand (if any)
	OPERAND op2;		// Second operand (if any)
	OPERAND op3;		// Additional operand (if any)
	PINST ptr;		// Pointer to instruction table
	int flags;		// Instruction flags
	short eflags_affected;	// Process eflags affected
	short eflags_used;      // Processor eflags used by this instruction
	int iop_written;	// mask of affected implied registers (written)
	int iop_read;		// mask of affected implied registers (read)
} INSTRUCTION, *PINSTRUCTION;

这个结构所说的。不需要判断near,还是far调用。

enum Instruction type;        // Instruction type

可以根据这个来判断是不是INSTRUCTION_TYPE_CALL/JMP,如果是则进行判断。由于跳转指令一般只有一个操作数,所以再根据op1来判断:
typedef struct _OPERAND {
	enum Operand type;	// Operand type (register, memory, etc)
	int reg;		// Register (if any)
	int basereg;		// Base register (if any)
	int indexreg;		// Index register (if any)
	int scale;		// Scale (if any)
	int dispbytes;		// Displacement bytes (0 = no displacement)
	int dispoffset;		// Displacement value offset
	int immbytes;		// Immediate bytes (0 = no immediate)
	int immoffset;		// Immediate value offset
	int sectionbytes;	// Section prefix bytes (0 = no section prefix)
	WORD section;		// Section prefix value
	DWORD displacement;	// Displacement value
	DWORD immediate;	// Immediate value
	int flags;		// Operand flags
} OPERAND, *POPERAND;


根据

enum Operand type;
这个成员判断操作数的类型,直接根据操作数结构体中的各个成员来从相应的地方取值判断。

我想说的就是,可以根据这些以有的信息来判断,而不是解析最后生成的字符串。解析字符串也挺好的,但是还是感觉这种方法相对更合适一点。

总之,实践的人(比如说你自己)才有最发言权,我只是说说我的想法而已,能实现功能的方法才是好方法。
雪    币: 220
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
westboy 2008-12-3 21:52
11
0
呵呵,非常感谢egogg。
明白一些您的意思,利用引擎已得到信息确实可以减少很多工作。
感谢您的精彩建议。
游客
登录 | 注册 方可回帖
返回