一. 测试的C语句及编译后的x86汇编代码
int a; int b; int main(void) { int c; if (c) a = 4; else b = 5; return 0; }
1 .text:0000000000400457 push rbp 2 .text:0000000000400458 mov rbp, rsp 3 .text:000000000040045B sub rsp, 10h 4 .text:000000000040045F mov [rbp-10], rsp 5 .text:0000000000400463 lea rax, [rbp-4] 6 .text:0000000000400467 movsxd rax, dword ptr [rax] 7 .text:000000000040046A cmp eax, 0 8 .text:000000000040046D jz short loc_400483 9 .text:000000000040046F lea rax, a 10 .text:0000000000400476 push rax 11 .text:0000000000400477 mov rax, 4 12 .text:000000000040047E pop rdi 13 .text:000000000040047F mov [rdi], eax 14 .text:0000000000400481 jmp short loc_400495 15 .text:0000000000400483 ; --------------------------------------------------------------- 16 .text:0000000000400483 17 .text:0000000000400483 loc_400483: ; CODE XREF: main+16↑j 18 .text:0000000000400483 lea rax, b 19 .text:000000000040048A push rax 20 .text:000000000040048B mov rax, 5 21 .text:0000000000400492 pop rdi 22 .text:0000000000400493 mov [rdi], eax 23 .text:0000000000400495 24 .text:0000000000400495 loc_400495: ; CODE XREF: main+2A↑j 25 .text:0000000000400495 mov rax, 0 26 .text:000000000040049C jmp short loc_4004A5 27 .text:000000000040049E ; --------------------------------------------------------------- 28 .text:000000000040049E mov rax, 0 29 .text:00000000004004A5 30 .text:00000000004004A5 loc_4004A5: ; CODE XREF: main+45↑j 31 .text:00000000004004A5 mov rsp, rbp 32 .text:00000000004004A8 pop rbp 33 .text:00000000004004A9 retn
二. x86汇编到虚拟机汇编
第4句"mov [rbp-10], rsp",内存地址 [rbp-10]在第2句"mov rbp, rsp"赋值,所以第4句转化为
"mov [rsp-10], rsp",再来判断rsp的值,rsp在第3句"sub rsp, 10h",所以第4句转化为"mov [rsp-10], rsp-10",
此时可以定义变量v1,v1地址为[rsp-10],生成虚拟机汇编语句v1 = &v1。
第8句"jz short loc_400483"也是我们重点关注的语句,因为这是一条条件跳转,程序的执行流可能因为
这条语句不再是顺序执行。第8句的跳转判断cpu flag寄存器标志位z位是否为0,所以搜寻最近一条影响标志位
的语句,这条语句就是第7句"cmp eax, 0",向上搜寻修改eax的语句,第6句"movsxd rax, dword ptr [rax]",
这是一条读内存操作,继续向上寻找谁修改了rax,找到第5句"lea rax, [rbp-4]",则生成变量v2,v2地址为
[rbp-4],并生成if语句,if语句条件判断为v2。
继续向下分析汇编语句,接下来的汇编语句是if语句的then部分,遇到第13句"mov [rdi], eax",这是一条
写内存操作,分析内存地址和值。内存地址在rdi中,第12句"pop rdi",继续搜索最近的压栈操作,遇到第10句
"push rax",继续分析rax的值,第9句"lea rax, a",则rdi为a的地址。再来追寻eax,第11句"mov rax, 4",
则生成虚拟机汇编语句a = 4。第14句jmp生成一条goto标签,then部分直到汇编语言地址小于地址loc_400483
的最大值为止。
再来分析第8句"jz short loc_400483",跳转到loc_400483,接下来的语句是if的else部分,分析看到第22
句"mov [rdi], eax",这是一条写内存操作,分析内存地址和值。内存地址在rdi中,第21句"pop rdi",
继续搜索最近的压栈操作,第19句"push rax",继续分析rax的值,第18句"lea rax, b",则rdi为b的地址。再来
追寻eax,第20句"mov rax, 5",则生成虚拟机汇编语句b = 5。此时判断汇编指令的地址是否大于第14句生成的
goto标签,如果大于或等于则结束else部分。
三. 虚拟机汇编到c语言
1. 虚拟机汇编语言
code段如下
|OP_GET_LOCAL|
|1|
|OP_JUMP_IF_FALSE|
|0xXX|
|0xXX|
|OP_POP|
|OP_CONSTANT|
|0|
|OP_SET_GLOBAL|
|2|
|OP_JUMP|
|0xYY|
|0xYY|
0xXXXX:|OP_POP|
|OP_CONSTANT|
|1|
|OP_SET_GLOBAL|
|3|
0xYYYY:|其他指令|
常量段为:
|4|
|5|
|a|
|b|
Local段为:
|v1|
|v2|
上面的语句执行过程为,OP_GET_LOCAL 1将变量v2 push到栈中,OP_JUMP_IF_FALSE判断栈顶v2
是否为真,如果为真则不跳转,执行then分支,反之跳转到0xXXXX处运行else分支。then分支中先将栈顶v2
弹出,然后将常量4压入栈,然后将4写入变量a。然后跳转到0xYYYY处运行后面的语句。else分支中先将
栈顶v2弹出,然后将常量5压入栈,然后将5写入变量b。
2. 反编译为c语言
遇到OP_JUMP_IF_FALSE,生成if语句,if的条件为栈顶元素,追踪操作栈顶的操作,为
OP_GET_LOCAL,则if语句的条件为v2。
顺序分析到OP_SET_GLOBAL 2,此条语句写公共变量a,值为OP_CONSTANT 0压入的4,生成语句
a = 4。OP_JUMP 生成一条标签,if语句的then部分结束,因为此时汇编地址已经到达0xXXXX了。
再来分析从0xXXXX开始处的指令,OP_SET_GLOBAL 3,此条语句写公共变量b,值为
OP_CONSTANT 1压入的5,生成语句b = 5。此时结束else分支,因为汇编地址已经到达OP_JUMP
生成的标签。