逆向一个最简单的函数,返回预定常亮应该够简单了。

C/C++代码:

int f()
{
	return 123;
};

0x01 x86

开启优化功能后,GCC编译器的汇编代码如下:

f:
		mov  eax,123
		ret

这个函数只是两条指令构成:第一条指令把数值123存放到eax寄存器中;再根据函数调用约定,后面一条指令会把eax的值当做返回值传递给调用者主函数,调用者(Caller)函数会从eax寄存器中取值把它当做返回结果。

0x02 ARM

ARM模式的情况:

f PROC
				MOV    r0,#0x7b;123
				BX     lr
				ENDP

ARM程序使用r0寄存器传递函数的返回值,所以指令把数值123赋值给r0。
ARM程序使用LR寄存器存储函数结束后的返回地址(Return Address);x86程序使用栈结构存放返回地址。可见,BX LR指令的作用是跳转到返回地址,返回到调用者函数,然后继续执行调用体caller的后续指令。

x86和ARM指令集中的mov是复制的意思,而不是移动。

逆向生成javaClientGenerator service 逆向生成函数_赋值

0x03 MIPS

在mips指令中,寄存器有两种命名方式,一种是以数字命名($0-逆向生成javaClientGenerator service 逆向生成函数_安全漏洞_02V0-$A0),在GCC生成的汇编指令中,寄存器都采用数字方式命名。

j	$31
li  $2,123

IDA会显示寄存器的伪名称

jr	$ra
li	$v0,0x7B

根据伪名称和寄存器数字编号可以知道,存储函数返回值的寄存器都是$逆向生成javaClientGenerator service 逆向生成函数_赋值_03V0),此处LI指令的意思是Load Immediate。

J和JR指令都是跳转指令,他们把执行流程递交给调用者函数,转到¥31寄存器中的地址上,这个寄存器相当于ARM平台上的LR寄存器。

为什么赋值指令LI和跳转指令J/JR的位置会反过来,这属于精简指令集的特性之一,转移指令延迟槽现象。简单来说就是,不管跳转发生与否,位于跳转指令后面的一条指令总是被先于跳转指令执行。

总之,转移指令后面的这条赋值指令是先于转移指令执行的。

逆向生成javaClientGenerator service 逆向生成函数_转移指令_04