匯編常用指令
- sub 拉伸椝瘢空間
sub sp, sp, #0x20
sp 是棧頂铺然,由于棧是由高地址到低地址裕偿,
一次性拉伸32個(gè)字節(jié)就需要將棧頂?shù)刂窚p去0x20(棧是16字節(jié)對(duì)齊的)
- str (store register) 將數(shù)據(jù)從寄存器中讀出來(lái),存到內(nèi)存中.
- stp 是str的延伸赡鲜,可以同時(shí)存儲(chǔ)兩個(gè)
str x0,[sp,#0x10]
將x0的值存在以sp+0x10為地址的存儲(chǔ)器中(棧空間內(nèi)存)
stp x29,x30,[sp,#0x20]
sp往上加32(20 = 2 * 16)個(gè)字節(jié),存放x29 和 x30
ldr(load register)讀取指令
將數(shù)據(jù)從內(nèi)存中讀取出來(lái), 存到寄存器中氮帐,LDR:通常都是作加載指令的隘截,但是它也可以作偽指令ldp是ldr的衍生, 可以同時(shí)讀兩個(gè)寄存器
LDR r0,[r1] //將R1中的值存到r0中
LDR r1,[r2,#16] //將(r2+16)地址中的內(nèi)容存到r1中
LDR r1,[r2],#4 //將r2地址中的內(nèi)容存到r1中,同時(shí)r2=r2+4
sub sp, sp, #0x20 ; 拉伸椩祝空間32(20 = 2*16)個(gè)字節(jié)
stp x0 , x1, [sp, #0x10] ; sp往上加16(10 = 1 * 16)個(gè)字節(jié),存放x0 和 x1
ldp x1 , x0, [sp, #0x10] ; 將sp偏移16個(gè)字節(jié)的值取出來(lái),放入x1 和 x0
- adrp 通過(guò)基地址 + 偏移 獲得一個(gè)字符串(全局變量)
原理 adrp操作步驟adrp x0, 1
1. 將1的值,左移12位 1 0000 0000 0000 == 0x1000
2.將PC寄存器的低12位清零 0x1045228b0 ==> 0x104522000
3.將將1 和 2 的結(jié)果相加 給 X0 寄存器!! ==>0x104523000
4.add x1,x0,#0xfd8 即將第三布得出的結(jié)果加上0xfd8 ==>0x104523fd8 就可以得出全局變量或者常量所在的地址
注:地址為pc寄存器左邊的地址,adrp是找出要獲取參數(shù)的地址范圍婶芭,然后下個(gè)pc寄存器執(zhí)行的代碼會(huì)定位到準(zhǔn)確的物理地址东臀。
- cbz :【cbz 寄存器,地址 】指令的意思是 如果寄存器的值==0犀农,則跳轉(zhuǎn)到地址惰赋,如果不等于0,則繼續(xù)執(zhí)行代碼
- subs : subs其實(shí)就是sub指令呵哨,但是這個(gè)指令操作結(jié)束后赁濒,會(huì)影響到標(biāo)志寄存器
cmp(Compare)比較指令
???CMP 把一個(gè)寄存器的內(nèi)容和另一個(gè)寄存器的內(nèi)容或立即數(shù)進(jìn)行比較。但不存儲(chǔ)結(jié)果孟害,只是正確的更改標(biāo)志拒炎。
???一般CMP做完判斷后會(huì)進(jìn)行跳轉(zhuǎn),后面通常會(huì)跟上b指令挨务!
- bl 標(biāo)號(hào):跳轉(zhuǎn)到標(biāo)號(hào)處執(zhí)行
- b.gt 標(biāo)號(hào):比較結(jié)果是大于(greater than)击你,執(zhí)行標(biāo)號(hào),否則不跳轉(zhuǎn)
- b.ge 標(biāo)號(hào):比較結(jié)果是大于等于(greater than or equal to)谎柄,執(zhí)行標(biāo)號(hào)丁侄,否則不跳轉(zhuǎn)
- b.eq 標(biāo)號(hào):比較結(jié)果是等于,執(zhí)行標(biāo)號(hào)朝巫,否則不跳轉(zhuǎn)
- b.hi 標(biāo)號(hào):比較結(jié)果是無(wú)符號(hào)大于鸿摇,執(zhí)行標(biāo)號(hào),否則不跳轉(zhuǎn)
- b.le 標(biāo)號(hào):判斷上面cmp的值是小于等于 執(zhí)行標(biāo)號(hào),否則直接往下走
- b.lt 判斷上面cmp的值是 小于 執(zhí)行后面的地址中的方法 否則直接往下走
CPY 把一個(gè)寄存器的值拷貝(COPY)到另一個(gè)寄存器中
EOR 近位異或
LSL 邏輯左移(Logic Shift Left)
LSR 邏輯右移(Logic Shift Right)
MOV 寄存器加載數(shù)據(jù)竟痰,既能用于寄存器間的傳輸卢肃,也能用于加載立即數(shù)
MUL 乘法(Multiplication)
MVN 加載一個(gè)數(shù)的 NOT值(取到邏輯反的值)
NEG 取二進(jìn)制補(bǔ)碼
ORR 按位或
ROR 循環(huán)右移
SBC 帶借位的減法
SUB 減法(Subtraction)
TST 測(cè)試(Test,執(zhí)行按位與操作庐镐,并且根據(jù)結(jié)果更新Z)
REV 在一個(gè)32位寄存器中反轉(zhuǎn)(Reverse)字節(jié)序
REVH 把一個(gè)32位寄存器分成兩個(gè)(Half)16位數(shù),在每個(gè)16位數(shù)中反轉(zhuǎn)字節(jié)序
REVSH 把一個(gè)32位寄存器的低16位半字進(jìn)行字節(jié)反轉(zhuǎn)变逃,然后帶符號(hào)擴(kuò)展到32位
SXTB 帶符號(hào)(Signed)擴(kuò)展一個(gè)字節(jié)(Byte)到 32位
SXTH 帶符號(hào)(Signed)擴(kuò)展一個(gè)半字(Half)到 32位
UXTB 無(wú)符號(hào)(Unsigned)擴(kuò)展一個(gè)字節(jié)(Byte)到 32位
UXTH 無(wú)符號(hào)(Unsigned)擴(kuò)展一個(gè)半字(Half)到 32位
Switch
1必逆、假設(shè)switch語(yǔ)句的分支比較少的時(shí)候(例如3,少于4的時(shí)候沒(méi)有意義)沒(méi)有必要使用此結(jié)構(gòu)揽乱,相當(dāng)于if名眉。
2、各個(gè)分支常量的差值較大的時(shí)候凰棉,編譯器會(huì)在效率還是內(nèi)存進(jìn)行取舍损拢,這個(gè)時(shí)候編譯器還是會(huì)編譯成類似于if,else的結(jié)構(gòu)撒犀。
3福压、在分支比較多的時(shí)候:在編譯的時(shí)候會(huì)生成一個(gè)表(跳轉(zhuǎn)表每個(gè)地址四個(gè)字節(jié))掏秩。
void __switch_1__(){
int value = 5;
switch (value) {
case 0:
printf("1");
break;
case 1:
printf("2");
break;
case 2:
printf("3");
break;
default:
printf("else");
break;
}
}
匯編代碼如下:
01-匯編-IF-SWITCH`__switch_1__:
0x100c96644 <+0>: sub sp, sp, #0x30 ; =0x30
0x100c96648 <+4>: stp x29, x30, [sp, #0x20]
0x100c9664c <+8>: add x29, sp, #0x20 ; =0x20
-> 0x100c96650 <+12>: mov w8, #0x5 ; w8 = 5
0x100c96654 <+16>: stur w8, [x29, #-0x4]
0x100c96658 <+20>: ldur w8, [x29, #-0x4] ; value = w8
0x100c9665c <+24>: mov x9, x8
0x100c96660 <+28>: stur w9, [x29, #-0x8]
0x100c96664 <+32>: cbz w8, 0x100c96694 ; cbz 指令的意思是 如果w8==0,則跳轉(zhuǎn)到 0x100c96694荆姆,如果不等于0蒙幻,則繼續(xù)執(zhí)行代碼
0x100c96668 <+36>: b 0x100c9666c ; <+40> at main.m:26
0x100c9666c <+40>: ldur w8, [x29, #-0x8]
0x100c96670 <+44>: subs w9, w8, #0x1 ; w9=w8-1
0x100c96674 <+48>: stur w9, [x29, #-0xc]
0x100c96678 <+52>: b.eq 0x100c966a8 ; 如果w9==0,則跳轉(zhuǎn)到 0x100c966a8
0x100c9667c <+56>: b 0x100c96680 ; <+60> at main.m:26
0x100c96680 <+60>: ldur w8, [x29, #-0x8]
0x100c96684 <+64>: subs w9, w8, #0x2 ; w9=w8-2
0x100c96688 <+68>: str w9, [sp, #0x10]
0x100c9668c <+72>: b.eq 0x100c966bc ; 如果w9==0,則跳轉(zhuǎn)到 0x100c966bc
0x100c96690 <+76>: b 0x100c966d0 ; 如果都不滿足即default,則跳轉(zhuǎn)到 0x100c966d0
0x100c96694 <+80>: adrp x0, 1
0x100c96698 <+84>: add x0, x0, #0xf29 ; =0xf29
0x100c9669c <+88>: bl 0x100c96c04 ; printf("1");
0x100c966a0 <+92>: str w0, [sp, #0xc]
0x100c966a4 <+96>: b 0x100c966e0 ; <+156> at main.m:40
0x100c966a8 <+100>: adrp x0, 1
0x100c966ac <+104>: add x0, x0, #0xf2b ; =0xf2b
0x100c966b0 <+108>: bl 0x100c96c04 ; printf("2");
0x100c966b4 <+112>: str w0, [sp, #0x8]
0x100c966b8 <+116>: b 0x100c966e0 ; <+156> at main.m:40
0x100c966bc <+120>: adrp x0, 1
0x100c966c0 <+124>: add x0, x0, #0xf2d ; =0xf2d
0x100c966c4 <+128>: bl 0x100c96c04 ; printf("3");
0x100c966c8 <+132>: str w0, [sp, #0x4]
0x100c966cc <+136>: b 0x100c966e0 ; <+156> at main.m:40
0x100c966d0 <+140>: adrp x0, 1
0x100c966d4 <+144>: add x0, x0, #0xf24 ; =0xf24
0x100c966d8 <+148>: bl 0x100c96c04 ; printf("else");
0x100c966dc <+152>: str w0, [sp]
0x100c966e0 <+156>: ldp x29, x30, [sp, #0x20]
0x100c966e4 <+160>: add sp, sp, #0x30 ; =0x30
0x100c966e8 <+164>: ret
由上可見(jiàn)胆筒,當(dāng)case為3個(gè)時(shí)邮破,匯編執(zhí)行的代碼為if else語(yǔ)句 而當(dāng)增加到4個(gè)case時(shí),匯編代碼發(fā)生了相關(guān)變化
0x100be66f8 <+48>: adrp x8, 0
0x100be66fc <+52>: add x8, x8, #0x790 ; x8 = 0x0000000100be6790
0x100be6700 <+56>: ldur x9, [x29, #-0x10] ; x9 就是前面存進(jìn)來(lái)的值 也就是3
0x100be6704 <+60>: ldrsw x10, [x8, x9, lsl #2] ; 取出[0x0000000100be6790 + 03 << 2],給x10
0x100be6708 <+64>: add x8, x10, x8 ; 計(jì)算出要調(diào)轉(zhuǎn)的位置
0x100be670c <+68>: br x8 ; 跳轉(zhuǎn)
ldrsw ldrsw x10, [x8, x9, lsl #2] lsl就是將后面的值左移 []里面的意思是將x9+ 左移后的值 賦值給x8仆救,然后通過(guò)[]取出x8寄存器所對(duì)應(yīng)的值賦值給x10
br (brew register) 意思與b ’地址‘ 一樣抒和,就是跳轉(zhuǎn)后面的地址
??這時(shí)分析匯編代碼就可得知:當(dāng)超過(guò)3個(gè)case并且case值里面盡可能的連續(xù)時(shí),編譯器會(huì)通過(guò)表來(lái)存儲(chǔ)相關(guān)的偏移值(由于本身編譯的時(shí)候會(huì)增加ASLR一個(gè)隨機(jī)值彤蔽,故而通過(guò)pc寄存器與偏移值之間的加減就可以直接得出相關(guān)值存在的地址)摧莽,通過(guò)br指令跳轉(zhuǎn)對(duì)應(yīng)的地址執(zhí)行下面的相關(guān)代碼。