前言:想必大家都知道了维苔,新浪閱讀整個(gè)業(yè)務(wù)線砍掉的事情,是的茫负,朕的大清亡了蕉鸳,不過賠償很到位,也有了幸福感忍法。從北京回到鄭州休息好一陣子,思考了一下未來的工作方向榕吼,業(yè)余時(shí)間總結(jié)一下所學(xué)知識(shí)吧饿序。
以下就是學(xué)習(xí)過程中的筆記!
-
函數(shù)的本質(zhì)
寄存器:64位:x0-x30,XZR(零寄存器)
32位:W0-W30,WZR(零寄存器)
PC寄存器:用來確定CPU即將執(zhí)行的地址羹蚣;
改變PC寄存器:使用bl跳轉(zhuǎn)值指令棧:是一種具有特殊的訪問方式的存儲(chǔ)空間(后進(jìn)先出)
sp寄存器在任一時(shí)刻都會(huì)保存棧頂?shù)牡刂罚?br> 只要調(diào)用函數(shù)就會(huì)開辟椩剑空間;系統(tǒng)通過操作SP寄存器顽素,SP往高地址走時(shí)都屬于棧區(qū)域咽弦,SP往低地址走時(shí)這塊區(qū)域是沒有用的。對(duì)內(nèi)存的操作就是讀和寫胁出,
ldr 把內(nèi)存數(shù)據(jù)讀到寄存器型型,str把寄存器寫到內(nèi)存;
函數(shù)調(diào)用完畢全蝶,要做棧平衡闹蒜。
fp寄存器也稱為x29寄存器屬于通用寄存器,在某一時(shí)刻我們可以利用它保存棧底地址抑淫。
-
bl和ret指令
bl標(biāo)號(hào):- 將下一條指令的地址放到lr(x30)寄存器绷落;
- 轉(zhuǎn)到標(biāo)號(hào)處執(zhí)行。
ret指令:
- 默認(rèn)使用x30中的值始苇,x30放的是回去的路砌烁。
解讀一下:
stp x29, x30, [sp, #-0x10]!
- 將x29,x30放到sp減去0x10的位置并且sp減去0x10
什么條件才能這樣寫催式?
- 椇恚空間只需要保護(hù)29和30,沒有額外的椥钛酰空間函似;也就是函數(shù)里面只是一個(gè)簡單的調(diào)用,沒有局部變量喉童。
ldr x30, [sp], #0x10
- [sp]表示:把它當(dāng)作內(nèi)存地址撇寞,取出其中的值
函數(shù)的參數(shù)與返回值:
什么時(shí)候節(jié)約內(nèi)存:寫入到棧區(qū)顿天,寫入到內(nèi)存時(shí)。
寄存器不存在節(jié)約內(nèi)存蔑担。
寄存器里面有個(gè)運(yùn)算器
- 參數(shù) x0-x7牌废,w0-w7
補(bǔ)充:
返回值 x0
補(bǔ)充:
sp棧頂
x29棧底
orr = mov
為什么參數(shù)最好8個(gè)?
- 通過寄存器的訪問是最高效的
還原高級(jí)代碼
函數(shù)調(diào)用棧
eg:main函數(shù)參數(shù)入棧:保護(hù)現(xiàn)場啤握;
當(dāng)函數(shù)參數(shù)多的時(shí)候x0-x7放到棧區(qū)域進(jìn)行保護(hù)
先放參數(shù)鸟缕、再放局部變量
棧里面存在野指針?
eg1:用4個(gè)字節(jié)去訪問8字節(jié)的地址排抬;
eg2:一個(gè)指針指向垃圾地址懂从。
棧里面一般不存放真實(shí)的結(jié)構(gòu)體數(shù)據(jù)(不過在函數(shù)內(nèi)部也可能有純粹的結(jié)構(gòu)體,一般我們使用的第三方框架都是使用malloc開辟內(nèi)存區(qū)域蹲蒲,再去創(chuàng)建結(jié)構(gòu)體放到里面)番甩,一般存放簡單的引用型數(shù)據(jù)(指針)。什么時(shí)候29届搁、30需要保護(hù)缘薛?
有嵌套調(diào)用的時(shí)候。什么時(shí)候用到更多的棧卡睦?
當(dāng)有參數(shù)和局部變量的時(shí)候宴胧。
-
狀態(tài)寄存器(又稱:標(biāo)記寄存器):當(dāng)前執(zhí)行方法的狀態(tài)
32位寄存器的最高4位- N:記錄相關(guān)的指令執(zhí)行結(jié)束后,結(jié)果是否位負(fù)表锻,如果為負(fù)
- N=1
- Z:計(jì)算結(jié)果如果為0恕齐,Z=1
- C:無符號(hào)加法進(jìn)位,C=1;無符號(hào)減法有借位C=0;
- V:有符號(hào)溢出標(biāo)記浩嫌,有溢出V=1;
正+正=負(fù) v=1
負(fù)-負(fù)=正 v=1
if指令可以改變標(biāo)志寄存器
add sub orr 會(huì)影響標(biāo)志寄存器-
內(nèi)存:
- 代碼區(qū):存放代碼檐迟、可讀可寫
- 棧區(qū):放參數(shù)、局部變量码耐、臨時(shí)數(shù)據(jù)
- 堆區(qū):動(dòng)態(tài)申請
- 常量區(qū):只讀
帶#就是立即數(shù)(數(shù)字)
解讀:adrp x0, 1
- 1追迟、將pc寄存器的值低12位清0
- 2、將1左移12位
- 3骚腥、將整兩個(gè)值相加敦间,賦值給x0寄存器
方法編號(hào):是個(gè)字符串常量存放在MackO文件里面
虛擬內(nèi)存和物理內(nèi)存
虛擬內(nèi)存:
物理內(nèi)存:
adrp x0, #0x1
adrp里面要么是全局變量要么是局部變量要么就是一個(gè)對(duì)象的屬性。
-
全局變量和常量
- 一般內(nèi)存中獲取全局變量和常量時(shí)束铭,出現(xiàn)adrp和add兩條指令獲得一個(gè)地址的情況廓块。
- ADRP(Address Page)
-
adrp x0, 1
- 將1的值,左移12位契沫!16進(jìn)制就是0x1000
- 將當(dāng)前PC寄存器低12位清0
- 將以上兩個(gè)結(jié)果相加带猴,存放到x0寄存器
-
通過ADD指令,獲取這頁內(nèi)存中的偏移懈万。- image.png
-
image.png
使用hopper還原高級(jí)代碼:
-
- 一句一句還原成高級(jí)代碼拴清。
先看main函數(shù)在bl調(diào)用func時(shí)有木有把參數(shù)傳進(jìn)去靶病,有說明有參數(shù)。再看func函數(shù)體內(nèi)有木有對(duì)x0做操作口予,一般情況下如果有說明有返回值(可能會(huì)在棧里面)但是不能確定返回值類型娄周,可以確定方法編號(hào),從中確定返回值類型沪停。 - 由下到上簡化
-
CMP比較指令:把一個(gè)寄存器的內(nèi)容和另一個(gè)寄存器的內(nèi)容或立即數(shù)進(jìn)行比較煤辨,但不存儲(chǔ)結(jié)果,只是正確的更改標(biāo)志寄存器木张,底層做的是減法運(yùn)算众辨。
- B.LE標(biāo)號(hào): 標(biāo)記結(jié)果是小于等于,執(zhí)行標(biāo)號(hào)舷礼,否則不跳轉(zhuǎn)
- B.LT標(biāo)號(hào): 標(biāo)記結(jié)果是小于泻轰,執(zhí)行標(biāo)號(hào),否則不跳轉(zhuǎn)
- B.GE標(biāo)號(hào): 標(biāo)記結(jié)果是大于等于且轨,執(zhí)行標(biāo)號(hào),否則不跳轉(zhuǎn)
- B.GT標(biāo)號(hào): 標(biāo)記結(jié)果是大于虚婿,執(zhí)行標(biāo)號(hào)旋奢,否則不跳轉(zhuǎn)
- B.EQ標(biāo)號(hào): 標(biāo)記結(jié)果是等于,執(zhí)行標(biāo)號(hào)然痊,否則不跳轉(zhuǎn)
- B.NQ標(biāo)號(hào): 標(biāo)記結(jié)果是不等于至朗,執(zhí)行標(biāo)號(hào),否則不跳轉(zhuǎn)
- B.HI標(biāo)號(hào): 標(biāo)記結(jié)果是無符號(hào)大于剧浸,執(zhí)行標(biāo)號(hào)锹引,否則不跳轉(zhuǎn)
- 稍后補(bǔ)充....
adrp x30, #0x100008000 add x30, x30, #0xcf0 還原成高級(jí)代碼為:int *x30 = &g;
-
循環(huán)&選擇
- cmp和subs區(qū)別?cmp不會(huì)保存結(jié)果唆香,subs會(huì)保存結(jié)果嫌变。
- do while判斷條件在后面,滿足條件往前跳
- while和for很像躬它,判斷條件在前面腾啥,滿足條件往前跳
-
switch
- 分之少于3個(gè)沒有意義和if else很像
- 各個(gè)分支常量差值比較大時(shí),編譯器會(huì)在內(nèi)存和效率之間取舍
- 分支較多時(shí)冯吓,編譯器會(huì)生成一個(gè)表倘待,進(jìn)行跳轉(zhuǎn)
-
代碼優(yōu)化
- 非本文件的函數(shù)或代碼編譯器不會(huì)優(yōu)化
-
指針
- 指針可以做簡單的運(yùn)算。如:自增/自減
-
block反匯編:最主要的是找到invoke(實(shí)現(xiàn)地址)组贺、signature(類型)凸舵,進(jìn)而重寫block,hook別人的block失尖。
- 先看一下block源碼
#define BLOCK_DESCRIPTOR_1 1 struct Block_descriptor_1 { uintptr_t reserved; uintptr_t size; }; #define BLOCK_DESCRIPTOR_2 1 struct Block_descriptor_2 { // requires BLOCK_HAS_COPY_DISPOSE BlockCopyFunction copy; BlockDisposeFunction dispose; }; struct Block_descriptor_3 { // requires BLOCK_HAS_SIGNATURE const char *signature; const char *layout; // contents depend on BLOCK_HAS_EXTENDED_LAYOUT }; struct Block_layout { void *isa; volatile int32_t flags; // contains ref count int32_t reserved; BlockInvokeFunction invoke;//實(shí)現(xiàn)地址! struct Block_descriptor_1 *descriptor; // imported variables };
- 尋找invoke和signature
實(shí)例一: 高級(jí)代碼如下:
int main(int argc, char * argv[]) { void (^block)(void) = ^(){ NSLog(@"nslog"); }; block(); return 0; }
匯編代碼如下:
0x1000ba2a0 <+12>: adrp x8, 2 0x1000ba2a4 <+16>: add x8, x8, #0x70 ; =0x70 0x1000ba2a8 <+20>: stur wzr, [x29, #-0x4] 0x1000ba2ac <+24>: stur w0, [x29, #-0x8] 0x1000ba2b0 <+28>: str x1, [sp, #0x10] -> 0x1000ba2b4 <+32>: mov x0, x8 0x1000ba2b8 <+36>: bl 0x1000ba5fc ; symbol stub for: objc_retainBlock 0x1000ba2bc <+40>: str x0, [sp, #0x8] 0x1000ba2c0 <+44>: ldr x8, [sp, #0x8] 0x1000ba2c4 <+48>: mov x0, x8 0x1000ba2c8 <+52>: ldr x8, [x8, #0x10] 0x1000ba2cc <+56>: blr x8
(lldb) x 0x1000bc070 0x1000bc070: 18 86 f9 9f 01 00 00 00 00 00 00 50 00 00 00 00 ...........P.... 0x1000bc080: f8 a2 0b 00 01 00 00 00 50 c0 0b 00 01 00 00 00 ........P.......
(lldb) dis -s 0x01000ba2f8 反匯編`__main_block_invoke: 0x1000ba2f8 <+0>: sub sp, sp, #0x20 ; =0x20 0x1000ba2fc <+4>: stp x29, x30, [sp, #0x10] 0x1000ba300 <+8>: add x29, sp, #0x10 ; =0x10 0x1000ba304 <+12>: str x0, [sp, #0x8] 0x1000ba308 <+16>: str x0, [sp] 0x1000ba30c <+20>: adrp x0, 2 0x1000ba310 <+24>: add x0, x0, #0xb0 ; =0xb0 0x1000ba314 <+28>: bl 0x1000ba5a8 ; symbol stub for: NSLog
實(shí)例二: 高級(jí)代碼如下:
int main(int argc, char * argv[]) { int a = 10; void (^block)(void) = ^(){ NSLog(@"nslog--%d", a); }; block(); return 0; }
匯編代碼如下:
0x10004a24c <+12>: adrp x8, 2 0x10004a250 <+16>: add x8, x8, #0x58 ; =0x58 0x100082254 <+20>: adrp x9, 0 0x100082258 <+24>: add x9, x9, #0x2e0 ; =0x2e0
(lldb) x 0x100084058 0x100084058: 00 00 00 00 00 00 00 00 24 00 00 00 00 00 00 00 ........$....... 0x100084068: 8c 3f 08 00 01 00 00 00 fb 33 08 00 01 00 00 00 .?.......3......
(lldb) x 0x1000822e0 0x10004a2e0: ff c3 00 d1 fd 7b 02 a9 fd 83 00 91 a0 83 1f f8 .....{.......... 0x10004a2f0: e8 03 00 aa e8 0b 00 f9 09 20 40 b9 e0 03 09 aa ......... @.....
(lldb) p (char *)0x0100083f8c (char *) $1 = 0x0000000100083f8c "v8@?0"
dis -s 0x1000822e0 反匯編`__main_block_invoke: 0x1000822e0 <+0>: sub sp, sp, #0x30 ; =0x30 0x1000822e4 <+4>: stp x29, x30, [sp, #0x20] 0x1000822e8 <+8>: add x29, sp, #0x20 ; =0x20 0x1000822ec <+12>: stur x0, [x29, #-0x8] 0x1000822f0 <+16>: mov x8, x0 0x1000822f4 <+20>: str x8, [sp, #0x10] 0x1000822f8 <+24>: ldr w9, [x0, #0x20] 0x1000822fc <+28>: mov x0, x9