目录
概述
该工具可以对程序进行反汇编,然后打印出运行每个汇编时各个寄存器的值(类似于gdb单步调试,然后每一步都打印寄存器的值)。同时可以实时地打印各个分支跳转指令实际跳转的方向。
一、下载源码
这个不是dynamorio的官方仓库,是我自己fork出来的,加了自己的代码
git clone https://gitee.com/xiaolicangdao/instr_trace.git dynamorio
二、安装dynamorio
为了方便,我在WSL进行的环境配置
1、安装依赖
sudo apt-get install cmake g++ g++-multilib doxygen git zlib1g-dev
cmake版本有一定要求,如果apt安装的版本不够新,可以自己从源码安装
2、编译
$ cd dynamorio
$ mkdir build
$ cd build
# 以防有人对cmake不熟悉,提示一下注意命令后面有两个点
$ cmake ..
$ make -j
3、测试安装是否成功
$ ./bin64/drrun echo hello world
参考截图
三、安装instr_trace工具
1、文件说明
mytool文件下为instr_trace工具的所有内容,run.sh为参考运行脚本
2、编译
$ cd mytool/build
$ bash compile.sh
3、运行
前提:切换当前目录到dynamorio的主目录
参数解释:bash run.sh 程序 trace文件
$ bash run.sh ~/test/test ./test.asm
$ head ./test.asm # 查看文件的前10行
参考截图:
四、生成的文件格式说明
我利用dynamorio工具打印出了每条指令的源操作数、源操作数的值和目标操作数(注意没有打印目标操作数的值,因为脚本的功能不够完善),且注释的格式是我自己根据实际需要写的,并不是说非要使用这个格式。
下面挑几个例子介绍一下
(1)mov指令(寄存器->寄存器)
mov rbp, rsp
; dst: reg: rbp(8 byte)
; src: rsp 0x00007ffff85e7280
; 0.000000
mov rbp, rsp
:反汇编的指令; dst: reg: rbp(8 byte)
:dst
表示它是目标操作数,reg
表示目标操作数是寄存器类型,rbp
为具体的寄存器名称,(8byte)
表示这个指令操作的数据宽度为8byte,即64bit; src: rsp 0x00007ffff85e7280
:src
表示它是源操作数,rsp
为具体的寄存器名称,0x00007ffff85e7280
为该寄存器的值(这个值显然是一个地址); 0.000000
:是源操作数的浮点数格式,即将源操作数的值用浮点数来解释,在此处是没有意义的,因为rsp寄存器里放的并不是浮点数。
(2)mov指令(内存->寄存器)
mov rax, qword ptr [0x00491eb0]
; dst: reg: rax(8 byte)
; src: mem: 0x491eb0, read_sz=0x8, val=0x00000002 0x00000001
; 0.000000
mov rax, qword ptr [0x00491eb0]
:反汇编的指令dst: reg: rax(8 byte)
:略; src: mem: 0x491eb0, read_sz=0x8, val=0x00000002 0x00000001
:src
表示它是源操作数,mem
表示源操作数是内存类型的,0x491eb0
为内存地址(单位为byte),read_sz=0x8
表示该操作的数据宽度为8byte,val=0x00000002 0x00000001
为该内存指向的内存的值,val宽度与前面对应,为8byte。
注意:val的值我是按高地址到地址打印的,即0x00000002为高地址(0x491eb0+0x4),0x00000001为低地址(0x491eb0),这个可以使用gdb验证。; 0.000000
:在此处没有意义的,因为源操作数不是浮点数。
(3)mov指令(寄存器->内存)
mov qword ptr [rbp-0x28], rax
; dst: mem: 0x7ffff85e7258(8 byte)
; src: rax 0x0000000400000003
; 0.000000
mov qword ptr [rbp-0x28], rax
:反汇编的指令; dst: mem: 0x7ffff85e7258(8 byte)
:dst
表示它是目标操作数,mem
表示目标操作数是内存类型,0x7ffff85e7258
为具体的内存地址,(8byte)
表示数据宽度为8byte; src: rax 0x0000000400000003
:src
表示它是源操作数,rax
表示源操作数的寄存器为rax,0x0000000400000003
为该寄存器的值,val宽度与前面对应,为8byte。
注意:rax 的值我也是按高地址到地址打印的,即0x00000004为rax寄存器的高地址部分,0x00000003为rax寄存器的低地址部分(即eax寄存器),这个也可以使用gdb验证。; 0.000000
:在此处没有意义的,因为源操作数不是浮点数。
(4)lea指令
lea rdi, [rbp-0x30]
; dst: reg: rdi(8 byte)
; src: mem: 0x7ffff85e7250, read_sz=0x0, val=0x00
; 0 double:
; 0 float:
lea rdi, [rbp-0x30]
:反汇编的指令,lea指令相当于c语言中的&(取地址操作); dst: reg: rdi(8 byte)
:目标寄存器; src: mem: 0x7ffff85e7250, read_sz=0x0, val=0x00
:src
表示它是源操作数,mem
表示源操作数为地址,然后注意read_sz=0,即它没有从内存中取数据,它只是在计算地址,即rdi=[rbp-0x30]的地址,而不是[rbp-0x30]的数据。0 double:和; 0 float:
:在此处没有意义的,因为源操作数不是浮点数。
(5)jmp指令
jmp <INVALID>
; src:
无条件跳转指令,没有源操作数和目标操作数
关于跳转指令,可以参考跳转指令手册
(6) cmp指令
cmp 0x7ffff85e71ec(mem) 0x8
; src: mem: 0x7ffff85e71ec, read_sz=0x4, val=0x00000004
; 0.000000
; src: (int)0x8
cmp 0x7ffff85e71ec(mem) 0x8 ]
:反汇编的指令,cmp指令是比较两个操作数,然后设置标志位寄存器,此处是比较内存0x7ffff85e71ec处的数据与立即数0x8; src: mem: 0x7ffff85e71ec, read_sz=0x4, val=0x00000004
:打印了地址0x7ffff85e71ec处的值