我們編寫的匯編程序還是不夠底層,CPU都是對機器碼進行操作的,所以還需要用匯編器將匯編代碼轉換成機器碼才能被CPU處理。下面舉幾個例子來說說分析ARM機器碼的方法。
對編譯連接之后得到的ELF進行反匯編:arm-linux-objdump
查看得到的反匯編代碼。這里如果想同時看到匯編代碼和機器碼,在編譯的時候需要加上-g調試選項。
1、mov r1,#0xff
1110 00 1 1101 0 0000 0001 000011111111
前者是匯編代碼,后者是其對應的機器碼。用第一個例子具體講一下分析的方法。首先必須具備的一個手冊就是ARM Architecture Reference Manual。這是學習ARM處理器絕對權威的一個參考資料。先看看每種ARM指令的一般格式
31--28是條件段,取值表如下所示
這里的例子中mov后面沒有跟條件,所以是AL,Always (unconditional) ,對應的機器碼為1110
27--26為保留位,恆為00
25位:Distinguishes between the immediate and register forms of <shifter_operand>.
標志shifter_operand段存放的是立即數還是寄存器。若為寄存器則置零,若為立即數則置一。
24--21為opcode,標明指令的類型,下面是opcode的取值表
這個例子中mov對應的為1101
20位:Signifies that the instruction updates the condition codes.
表明該指令是否會影響程序狀態字寄存器。是則置一,否則置零
19--16位:Specifies the first source operand register.
標明第一個源操作數寄存器,見每個指令的格式,有的有Rd,有的沒有。
由MOV指令的一般格式可以看出,他是沒有使用Rd的,所以這幾位填全0,其他使用到Rn的,這幾位填通用寄存器標號的二進制值。如r2,則為0010
15--12位:Specifies the destination register.
標明目的寄存器,填充方法同Rn
11--0位:Specifies the second source operand.
標明第二個源操作數,若為立即數則填該立即數的二進制值,若為通用寄存器則填通用寄存器標號的二進制值。
第一條指令的機器碼到這里就分析完了,下面具其他幾個不同的情況來驗證上面的說法,分析方法還是一樣的,這里就不一一分析了。
2、 movne r2,r1
0001 00 0 1101 0 0000 0010 000000000001
3、 cmp r1,r2
1110 00 0 1010 1 0001 0000 000000000010
4、 add r0,r0,r1
1110 00 0 0100 0 0000 0000 000000000001
5、 bic r0,r1,#0b101
1110 00 1 1110 0 0001 0000 000000000101
from: https://www.cnblogs.com/51qianrushi/p/4614491.html
bl指令機器碼
ARM64:
B:0x17向前跳轉,0x14向后跳轉
BL:0x97向前跳轉 0x94向后跳轉
偏移地址計算過程:
(目標地址 - 指令地址)/ 4 = 偏移
// 減8,指令流水造成。
// 除4,因為指令定長,存儲指令個數差,而不是地址差。
完整指令:
.text:000000000008CC84 8D B3 FF 97 BL je_arena_malloc_hard
.text:0000000000079AB8 je_arena_malloc_hard
計算偏移:
(79AB8-8CC84) / 4 = FFFFFFFFFFFFB38D
FFB38D | 0x97000000 = 97FFB38D
from: https://blog.csdn.net/lwanttowin/article/details/78385440