arm64汇编基础

iOS汇编

  • 真机:arm64汇编
  • 模拟器:x86汇编
将c语言的代码转化成汇编:
xcrun --sdk iphoneos clang -S -arch arm64 main.c -o main.s

1. 寄存器
lldb查看当前arm64iOS真机)的所有寄存器:
register read

输出:
General Purpose Registers: x0 = 0x1100000000000022 x1 = 0x00000002815a1040 x2 = 0x00000002824bdb20 x3 = 0x00000002815a1040 x4 = 0x0000000280ab1c80 x5 = 0x0000000000000001 x6 = 0x0000000281ba03c0 x7 = 0x00000000000004b0 x8 = 0x0000200000000000 x9 = 0x000141a1d420434d (0x00000001d420434d) (void *)0x01d42045c8000000 x10 = 0x0000000000000000 x11 = 0x0000000128853ff8 x12 = 0x00000000000000bf x13 = 0x0000000000000000 x14 = 0x0000000000000001 x15 = 0x0000000000000000 x16 = 0x000000018b560130libobjc.A.dylib`objc_release x17 = 0x00000001c3bfeac0 x18 = 0x0000000000000000 x19 = 0x0000000127d0d9e0 x20 = 0x00000002824bdb60 x21 = 0x00000002815a1040 x22 = 0x00000001c3bfeac0 x23 = 0x0000000127d02200 x24 = 0x00000002824bdb20 x25 = 0x0000000127d02200 x26 = 0x00000002815a1040 x27 = 0x000000018fbecb7cUIKitCore`__UILogCategoryNewNode.llvm.6688940174485051596 x28 = 0x00000001d420bd08UIKitCore`UIApp fp = 0x000000016dc614a0 lr = 0x00000001021a200cARM64`-[ViewController touchesBegan:withEvent:] + 64 at ViewController.m:23:5 sp = 0x000000016dc61470 pc = 0x00000001021a200cARM64`-[ViewController touchesBegan:withEvent:] + 64 at ViewController.m:23:5 cpsr = 0x00000000

1.1 通用寄存器x0 ~ x28
  • 64bit: x0 ~ x28
  • 32bit: w0 ~ w28(属于x0 ~ x28的低位32bit
当将64bit拆分成32bit的时候可以使用低位32bitw0 ~ w28来操作
其中:
  • x0 ~ x7寄存器通常用来存放函数的参数,更多的参数使用堆栈来传递
  • x0寄存器通常用来存放函数的返回值
读取某一个寄存器存储的值:
register read x0

给某一个通用寄存器写入值:
register write x0 0x1100000000000022 register write x1 5

1.2 程序状态寄存器cpsr spsr
  • cpsr(current program status register)寄存器:
  • spsr(saved program status register)寄存器:程序异常状态下使用
cpsr寄存器有32位,对应不同的功能状态:
arm64汇编基础
文章图片
cpsr 1.3 程序计数器寄存器pc
  • pc寄存器
    • 记录CPU当前指令是哪一条指令
    • 存储着当前CPU正在执行的指令的地址
1.4 链接状态寄存器lr
  • lr寄存器(也是x30寄存器)
    • 是链接寄存器(link register
    • 存储着当前函数的返回地址,即函数下一条指令的地址
1.5 堆栈指针寄存器sp fp
  • spStack Pointer
  • fpFrame Pointer),也就是x29寄存器
2. 指令
lldb指令:si是单步运行汇编指令,可以使用si指令来进入函数内部汇编
汇编实现test函数:
.text ; 指定代码在text段 .global _test ; test函数对外可见; test函数实现 _test: ... ... ret ; 返回

2.1 mov指令 汇编实现:
mov x0, #0x8 ; 将0x8赋值给通用寄存器x0 mov x1, x0 ; 将通用寄存器x0的值赋值给通用寄存器x1

【arm64汇编基础】查看运行时汇编:
ARM64`test: 0x102fe2024 <+0>: movx0, #0x8 0x102fe2028 <+4>: movx1, x0 ->0x102fe202c <+8>: ret

lldb调试:
(lldb) si (lldb) register read x0 x0 = 0x0000000104806038 (lldb) si (lldb) register read x0 x0 = 0x0000000000000008 (lldb) register read x1 x1 = 0x0000000104806000 (lldb) si (lldb) register read x1 x1 = 0x0000000000000008 (lldb)

2.2 add sub指令 汇编实现:
mov x0, #0x2 mov x1, #0x1 add x2, x0, x1 ; x0 + x1 赋值给 x2 sub x3, x0, x1 ; x0 - x1 赋值给 x3

查看运行时汇编:
ARM64`test: 0x1002ae01c <+0>:movx0, #0x2 0x1002ae020 <+4>:movx1, #0x1 0x1002ae024 <+8>:addx2, x0, x1 0x1002ae028 <+12>: subx3, x0, x1 ->0x1002ae02c <+16>: ret

lldb调试:
(lldb) si (lldb) si (lldb) si (lldb) si (lldb) si (lldb) register read x2 x2 = 0x0000000000000003 (lldb) register read x3 x3 = 0x0000000000000001 (lldb)

用汇编实现一个加法add函数和减法sub函数:
.text ; 存放在text段 .global _add, _sub ; 函数对外可见; x0通常用来存放函数返回值,x0 ~ x7通常用来存放函数参数 ; add函数 _add: add x0, x0, x1 ; x0 + x1 赋值给 x0 ret; sub函数 _sub: sub x0, x0, x1 ; x0 - x1 赋值给 x0 ret

2.3 cmp指令 将两个寄存器相减,将比较结果放到状态寄存器cpsr里面,减法的结果会影响到状态寄存器cpsr的2进制标志位
比较3和1的大小:
汇编实现:
mov x0, #0x3 mov x1, #0x1 cmp x0, x1 ; 做比较,相当于将 x0 - x1 的结果值给 cpsr

查看运行时汇编:
ARM64`test: 0x10092a020 <+0>:movx0, #0x3 0x10092a024 <+4>:movx1, #0x1 0x10092a028 <+8>:cmpx0, x1 ->0x10092a02c <+12>: ret

lldb调试:
(lldb) si (lldb) si (lldb) si (lldb) si (lldb) register read cpsr // 读取cpsr的值 cpsr = 0x20000000 // 对应cpsr寄存器的2进制的29位是1 (lldb)

比较1和1的大小:
汇编实现:
mov x0, #0x1 mov x1, #0x1 cmp x0, x1 ; 做比较,相当于 x0 - x1 的结果值给 cpsr

查看运行时汇编:
ARM64`test: 0x102656020 <+0>:movx0, #0x1 0x102656024 <+4>:movx1, #0x1 0x102656028 <+8>:cmpx0, x1 ->

lldb调试:
(lldb) si (lldb) si (lldb) si (lldb) si (lldb) register read cpsr cpsr = 0x60000000 // 对应cpsr寄存器的2进制的30位是1 (lldb)

比较1和3的大小:
汇编实现:
mov x0, #0x1 mov x1, #0x3 cmp x0, x1 ; 做比较,相当于将 x0 - x1 的结果值给 cpsr

查看运行时汇编:
ARM64`test: 0x1008be020 <+0>:movx0, #0x1 0x1008be024 <+4>:movx1, #0x3 0x1008be028 <+8>:cmpx0, x1 ->0x1008be02c <+12>: ret

lldb调试:
(lldb) si (lldb) si (lldb) si (lldb) si (lldb) register read cpsr cpsr = 0x80000000 // 对应cpsr寄存器的2进制的31位是1 (lldb)

2.4 b指令 跳转指令
汇编实现:
b mycode ; 跳转到下面的 mycode mov x0, #0x5 ; 这条指令不会执行 mycode: mov x1, #0x6

查看运行时汇编:
ARM64`test: ->0x1004f2020 <+0>: b0x1004f2028; mycode 0x1004f2024 <+4>: movx0, #0x5

跳转到mycode
ARM64`mycode: 0x1004f2028 <+0>: movx1, #0x6 ->0x1004f202c <+4>: ret

lldb调试:
(lldb) si (lldb) si (lldb) si (lldb) register read x1 x1 = 0x0000000000000006 (lldb

2.5 带条件跳转b指令 一般和cmp指令配合使用
b指令后边可以跟的条件域:
  1. EQ: equal,等于
  2. NE: not equal,不等于
  3. GT: great than,大于
  4. GE: great equal,大于等于
  5. LT: less than,小于
  6. LE: less equal,小于等于
条件指令对应的状态寄存器cpsr的2进制标志位:
arm64汇编基础
文章图片
condtions 汇编实现:
mov x0, #0x1 mov x1, #0x3 cmp x0, x1 ; 将x0 - x1的结果存放到cpsr寄存器 beq mycode ; eq会从读取cpsr寄存器的2进制标志位Z,相当于只有在x0 == x1的时候才会跳转到mycode ret mov x0, #0x5 mycode: mov x1, #0x6

查看运行时汇编:
ARM64`test: 0x104dc6010 <+0>:movx0, #0x1 0x104dc6014 <+4>:movx1, #0x3 0x104dc6018 <+8>:cmpx0, x1 0x104dc601c <+12>: b.eq0x104dc6028; mycode ->0x104dc6020 <+16>: ret 0x104dc6024 <+20>: movx0, #0x5

lldb调试:
(lldb) si (lldb) si (lldb) si (lldb) si (lldb) register read cpsr cpsr = 0x80000000 // 对应cpsr寄存器的2进制Z位(30位)为0 (lldb) si (lldb)

2.6 bl指令 带返回的跳转指令
汇编实现:
; 内部私有函数,配合bl指令 privatecode: mov x0, #0x1 mov x2, #0x2 add x2, x0, x1 retbl privatecode ; 只有使用bl指令,调用privatecode之后才能返回到下一行指令,使用b指令返回不到下一行指令 mov x3, #0x2 mov x4, #0x1

查看运行时汇编:
bl指令跳转到privatecode:
ARM64`test: ->0x1043c2020 <+0>:bl0x1043c2010; privatecode 0x1043c2024 <+4>:movx3, #0x2 0x1043c2028 <+8>:movx4, #0x1 0x1043c202c <+12>: ret

来到privatecode:
ARM64`privatecode: ->0x1043c2010 <+0>:movx0, #0x1 0x1043c2014 <+4>:movx2, #0x2 0x1043c2018 <+8>:addx2, x0, x1 0x1043c201c <+12>: ret

privatecode执行ret之后再回到之前的函数执行下一行:
ARM64`test: 0x1043c2020 <+0>:bl0x1043c2010; privatecode ->0x1043c2024 <+4>:movx3, #0x2 0x1043c2028 <+8>:movx4, #0x1 0x1043c202c <+12>: ret

3. 内存相关指令
  • load、从内存中读取数据(带l相关的)
    • ldrldur
    • ldpppair(一对)的简称)
  • store,往内存中写入数据(带s相关的)
    • strstur
    • stp
    • 零寄存器,里面存储的值是0
      • wzr32bitWord Zero Register
      • xzr64bit
3.1 ldr指令 外部调用:
int a = 8; test();

汇编实现:
ldr x0, [x1] ; 寻址:x1的地址,取8字节(x0对应8字节),赋值给x0

查看运行时汇编:
ARM64-Memory`test: 0x100122054 <+0>: ldrx0, [x1] ->0x100122058 <+4>: ret

lldb调试:
(lldb) p &a (int *) $0 = 0x000000016fce3854 (lldb) si (lldb) register read x1 x1 = 0x0000000101002000 (lldb) register write x1 0x000000016fce3854 // 将a的内存地址写入x1寄存器// 查看a所在的内存地址0x16fce3854,也是x1寄存器的地址,之后会读取8字节赋值给x0 (lldb) x 0x000000016fce3854 0x16fce3854: 08 00 00 00 00 00 00 00 00 00 00 00 b8 38 ce 6f.............8.o 0x16fce3864: 01 00 00 00 01 00 00 00 00 00 00 00 90 38 ce 6f.............8.o (lldb) // x0之前的地址 (lldb) register read x0 x0 = 0x0000000101002038// 通过ldr指令,会从x1内存地址中读取8个字节存入x0寄存器,正好对应上面的8字节 (lldb) si (lldb) register read x0 x0 = 0x0000000000000008

汇编实现:
ldr x2, [x1, #0x3] ; 寻址:x1的地址 + 0x3,读取8字节x2(x2对应8字节)赋值给x2

3.2 ldur指令 汇编实现:
; ldur指令用于负数 ldur x2, [x1, #-0x3]! ; 寻址:x1的地址 - 0x3,并且改变x1的地址(x1 - 0x3),然后取8字节赋值给x2(x2对应8字节)

查看运行时汇编:
ARM64-Memory`test`: 0x10428e054 <+0>: ldrx2, [x1, #0x3]! ->0x10428e058 <+4>: ret

lldb调试:
(lldb) p &a (int *) $0 = 0x000000016bb77854 (lldb) si (lldb) register write x1 0x000000016bb77854 (lldb) si ; 执行ldr指令 (lldb) register read x1; x1的内存是+0x3以后的内存 x1 = 0x000000016bb77857 (lldb) register read x2 ; 寻址:x1 + 0x3,读取前8字节(x2)赋值给x2 x2 = 0x0000000000000000; 可以看到前8字节正好是x2的地址 (lldb) x 0x000000016bb77857 0x16bb77857: 00 00 00 00 00 00 00 00 00 b8 78 b7 6b 01 00 00..........x.k... 0x16bb77867: 00 01 00 00 00 00 00 00 00 90 78 b7 6b 01 00 00..........x.k... (lldb)

3.3 ldp指令 汇编实现:
ldp w0, w1, [x2, #0x10] ; 寻址:x2的内存地址 + 0x10,分别各取4字节分别放赋值给一对寄存器w0和w1(w0和w1对应4字节)

查看运行时汇编:
ARM64-Memory`test: 0x100552054 <+0>: ldpw0, w1, [x2, #0x10] ->0x100552058 <+4>: ret

lldb调试:
(lldb) p &a (int *) $0 = 0x000000016f8b3854 (lldb) si (lldb) register write x2 0x000000016f8b3854 (lldb) x 0x000000016f8b3854+0x10 0x16f8b3864: 01 00 00 00 01 00 00 00 00 00 00 00 90 38 8b 6f.............8.o 0x16f8b3874: 01 00 00 00 60 03 62 8b 01 00 00 00 60 03 62 8b....`.b.....`.b. (lldb) si (lldb) register read w0 w0 = 0x00000001 // 取了前面4字节 (lldb) register read w1 w1 = 0x00000001// 继续取4字节 (lldb)

3.4 str stur stp指令 对应上面的ldr ldur ldp指令
ldr ldur ldp指令是从寄存器读取内存地址
str stur stp指令是往寄存器写入内存地址
汇编实现:
str w0, [x1] ; 寻址:x1的地址,取4字节w0的值,写入8字节到x1的内存(x1的地址不变,存储的内容会被覆盖)

查看运行时汇编:
ARM64-Memory`test: 0x10269a054 <+0>: strw0, [x1] ->0x10269a058 <+4>: ret

lldb调试:
lldb) p &a (int *) $0 = 0x000000016d76b854 (lldb) si (lldb) register write w0 5 // w0的值为5 (lldb) register write x1 0x000000016d76b854 // x1的内存地址 = a的内存地址 (lldb) si // 取4字节w0的值 5 写入8字节到x1(a的内存),x1的内存地址(a的内存地址)不变,存储的内容变为5 (lldb) c Process 25740 resuming (lldb) po a // 得到a的值也变成了5 5(lldb)

同样也可以进行地址+ / -操作:
str w0, [x1, #0x4] ; 取4字节寄存器w0的值写入8字节的[x1寄存器的内存地址 + 0x4]之后的地址

3.5 wzr xzr0寄存器 寄存器里面存储的值是0,是专门用来存储0的寄存器
  • wzr32bit
  • xzr64bit
3.6 pc lr寄存器 直接查看运行时汇编:
0x10278e274 <+52>:adrpx0, 7 0x10278e278 <+56>:addx0, x0, #0x388; =0x388 0x10278e27c <+60>:ldrx0, [x0] ->0x10278e280 <+64>:bl0x10278e5c8; symbol stub for: objc_opt_class

可以发现pc寄存器存储着当前执行指令的内存地址0x10278e280
register read General Purpose Registers: x0 = 0x0000000102795418(void *)0x00000001027953f0: AppDelegate x1 = 0x0000000103007003 x2 = 0x4c45524f545541a1 x3 = 0x000000016d677980 x4 = 0x0000000000000000 x5 = 0x0000000000000000 x6 = 0x0000000000000000 x7 = 0x0000000000000000 x8 = 0x0000000000000008 x9 = 0x00000000a1a1a1a1 x10 = 0x0000000000000d21 x11 = 0x00000001d6ec0537 x12 = 0x00000001d6ec0537 x13 = 0x0000000000000008 x14 = 0x0000000000000028 x15 = 0x00000000000000d0 x16 = 0x000000018b561428libobjc.A.dylib`objc_autoreleasePoolPush x17 = 0x0000000102795490_dyld_private x18 = 0x0000000000000000 x19 = 0x0000000000000000 x20 = 0x0000000000000000 x21 = 0x0000000000000000 x22 = 0x0000000000000000 x23 = 0x0000000000000000 x24 = 0x0000000000000000 x25 = 0x0000000000000000 x26 = 0x0000000000000000 x27 = 0x0000000000000000 x28 = 0x000000016d6778a8 fp = 0x000000016d677870 lr = 0x000000010278e274ARM64-Memory`main + 52 at main.m:19:9 sp = 0x000000016d677840 pc = 0x000000010278e280ARM64-Memory`main + 64 at main.m:21:50 cpsr = 0x80000000

lr链接寄存器也是x30寄存器:
(lldb) register read x30 lr = 0x000000010278e274ARM64-Memory`main + 52 at main.m:19:9

lr寄存器存储着当前函数的返回地址,即函数下一条指令的地址:
bl跳转指令即将进入函数test
->0x1044ca270 <+48>:bl0x1044ca054; test 0x1044ca274 <+52>:adrpx0, 7 0x1044ca278 <+56>:addx0, x0, #0x388; =0x388 0x1044ca27c <+60>:ldrx0, [x0]

进入test函数:
ARM64-Memory`test: ->0x1044ca054 <+0>: ldrx2, [x1, #0x3]! 0x1044ca058 <+4>: ret

此时查看lr寄存器地址,正好是上面的地址0x00000001044ca274,即test函数的下一条指令内存地址:
(lldb) si (lldb) register read lr lr = 0x00000001044ca274ARM64-Memory`main + 52 at main.m:19:9 (lldb) si (lldb) si (lldb)

之前的跳转指令bbl的区别就是:
  • b指令仅仅只是跳转
  • bl可以跳转回函数的返回地址,以便继续执行下面的指令,我们来看一下其本质
4. blret指令配合使用的本质
bl跳转指令执行的操作:
  1. 将下一条指令的地址存储到lrx30)寄存器中,lr寄存器存储着下一条指令的地址
  2. 跳转到标记处开始执行代码
ret返回指令执行的操作:
  1. lrx30)指令的地址赋值给pc指令,此时lr寄存器正好存储着下一条指令的地址,所以pc指令现在是下一条指令的地址
  2. 继续执行pc指令,即使函数返回的地址
所以blret指令配合才能达到函数返回的目的
5. 堆栈平衡
5.1 函数类型
  • 叶子函数:函数里面没有再调用其他函数了
  • 非叶子函数:函数里面有调用其他函数
5.2 叶子函数的栈寄存器sp平衡 实现一个c语言函数CTest.c
void haha(void) { int a = 2; int b = 3; }

查看运行时汇编:
ARM64-Memory`haha: ; 切换sp寄存器的指向:sp地址 - 0x10,申请栈空间,存放变量a和b ->0x104ebe218 <+0>:subsp, sp, #0x10; =0x10 ; 将变量a的值2写入栈空间 0x104ebe21c <+4>:orrw8, wzr, #0x2 0x104ebe220 <+8>:strw8, [sp, #0xc] ; 将变量b的值3写入栈空间 0x104ebe224 <+12>: orrw8, wzr, #0x3 0x104ebe228 <+16>: strw8, [sp, #0x8]; 恢复sp寄存器的指向:sp地址 + 0x10,指向原来的地址 0x104ebe22c <+20>: addsp, sp, #0x10; =0x10 ; 返回 0x104ebe230 <+24>: ret

调用函数的时候sp寄存器存放某一个地址0x000000016af47840
(lldb) si (lldb) register read sp ; sp起始地址 sp = 0x000000016af47840 (lldb) si (lldb) register read sp ; sp - 0x10,切换sp指向,用来存放a和b sp = 0x000000016af47830 (lldb) si (lldb) si ; w8的值是2,str指令将w8的值2写入a的栈内存:sp + 0xc (lldb) p &a (int *) $0 = 0x000000016af4783c (lldb) si (lldb) si ; w8的值是3,str指令将w8的值3写入b的栈内存:sp + 0x8 (lldb) p &b (int *) $1 = 0x000000016af47838 (lldb) si (lldb) register read sp sp = 0x000000016af47840 (lldb)

5.3 非叶子函数的栈寄存器sp平衡 实现一个c语言函数CTest.c
void haha(void) { int a = 2; int b = 3; }void hehe() { int a = 4; int b = 5; haha(); }

查看运行时汇编:
ARM64-Memory`hehe: ; 切换sp指针:sp指针 - 0x20,申请栈空间 ->0x100306208 <+0>:subsp, sp, #0x20; =0x20 ; 先在当前函数栈空间记录之前的sp和lr寄存器的值: ; stp指令,寻址:[sp + #0x10],取8字节将原来的x29寄存器(fp寄存器)的值写入当前函数栈内存 ; 再取8字节将原来x30寄存器(lr)寄存器的值写入当前栈内存地址 ; lr寄存器的值是函数的返回地址 0x10030620c <+4>:stpx29, x30, [sp, #0x10]; 改变fp的指向:[sp + 0x10] ,此时fp指向当前栈内存地址,指向栈底 ; 此时,lr和sp都有存储地址 0x100306210 <+8>:addx29, sp, #0x10; =0x10 ; 开始使用存储函数局部变量的值: ; 存储a的值4,内存地址:fp - 0x4 0x100306214 <+12>: orrw8, wzr, #0x4 0x100306218 <+16>: sturw8, [x29, #-0x4]; 存储b的值5,内存地址:sp + 0x8 0x10030621c <+20>: movw8, #0x5 0x100306220 <+24>: strw8, [sp, #0x8]; 跳转到haha函数 0x100306224 <+28>: bl0x1003061ec; haha at CTest.c:11; 恢复fp,lr ,因为开始存储到了当前函数栈 ; 从[sp + #0x10]读取8字节的内容赋值给fp, ; 再读取8字节的内容赋值给lr 0x100306228 <+32>: ldpx29, x30, [sp, #0x10]; 恢复sp寄存器指向:sp指针 + 0x20 0x10030622c <+36>: addsp, sp, #0x20; =0x20 ; 返回 0x100306230 <+40>: ret

lldb调试:
lldb) si ; 改变sp指向:sp - 0x20,申请当前函数栈空间,使其指向栈顶 (lldb) register read sp sp = 0x000000016d2e7840 (lldb) si (lldb) register read sp sp = 0x000000016d2e7820; 这是fp和lr最开始的地址 (lldb) register read fp fp = 0x000000016d2e7870 (lldb) register read lr lr = 0x0000000102b1e260ARM64-Memory`main + 44 at main.m:26:13; 先将fp和lr的地址在当前栈空间存储,起始地址:sp + 0x10,分别取8字节存储 (lldb) si (lldb) x 0x000000016d2e7830 0x16d2e7830: 70 78 2e 6d 01 00 00 00 60 e2 b1 02 01 00 00 00px.m....`....... 0x16d2e7840: 00 00 00 00 00 00 00 00 38 80 80 03 01 00 00 00........8.......; 改变fp指向,使其指向栈底 (lldb) si (lldb) register read fp fp = 0x000000016d2e7830; 存储临时变量a的值4,地址:fp - 0x4 (lldb) si (lldb) si (lldb) p &a (int *) $0 = 0x000000016d2e782c ; 存储临时变量b的值5,地址:sp + 0x8 (lldb) si (lldb) si (lldb) p &b (int *) $1 = 0x000000016d2e7828; bl跳转到haha函数 (lldb) si (lldb) si (lldb) si (lldb) si (lldb) si (lldb) si (lldb) si (lldb) si; 从当前函数栈读取之前存储的fp和lr,起始地址:sp + 0x10,分别取8字节,恢复其指向 (lldb) si (lldb) register read fp fp = 0x000000016d2e7870 (lldb) register read lr lr = 0x0000000102b1e260ARM64-Memory`main + 44 at main.m:26:13 (lldb) si (lldb) register read sp sp = 0x000000016d2e7840 (lldb)

  • sp也叫栈顶指针
  • fp也叫栈底指针
6. 简单应用
6.1 lldb打印寄存器对应的函数 根据runtime方法调用的本质objc_msgSend(调用者,方法选择器,参数1,参2数,......),使用lldb可以打印函数的参数:
// 打印方法调用者 po $x0// 打印方法名,以字符串的形式打印内存 x/s $x1// 打印参数 po $x2... ...

6.2 破解Mac命令行程序 通过修改汇编代码来破解程序:
使用Hopper来分析汇编代码:
; while循环 loc_100000ed6: ; cmp指令比较 0000000100000ed6cmpdword [rbp+var_14], 0x1e240; CODE XREF=_main+89 ; 跳转到loc_100000f0e执行 0000000100000eddjeloc_100000f0e; while循环的代码,可以删除这段汇编代码达到破解的目的 0000000100000ee3leardi, qword [aXe8xafxb7xe8xb]; argument "format" for method imp___stubs__printf, "\\xE8\\xAF\\xB7\\xE8\\xBE\\x93\\xE5\\x85\\xA5\\xE5\\xAF\\x86\\xE7\\xA0\\x81:" 0000000100000eeamoval, 0x0 0000000100000eeccallimp___stubs__printf; printf 0000000100000ef1leardi, qword [aD]; argument "format" for method imp___stubs__scanf, "%d" 0000000100000ef8learsi, qword [rbp+var_14] 0000000100000efcmovdword [rbp+var_24], eax 0000000100000effmoval, 0x0 0000000100000f01callimp___stubs__scanf; scanf 0000000100000f06movdword [rbp+var_28], eax 0000000100000f09jmploc_100000ed6 ; 结尾处有返回到了loc_100000ed6loc_100000f0e: 0000000100000f0eleardi, qword [aXe5xafx86xe7xa]; argument "format" for method imp___stubs__printf, "\\xE5\\xAF\\x86\\xE7\\xA0\\x81\\xE6\\xAD\\xA3\\xE7\\xA1\\xAE\\xEF\\xBC\\x8C\\xE6\\xAC\\xA2\\xE8\\xBF\\x8E\\xE4\\xBD\\xBF\\xE7\\x94\\xA8!\\n", CODE XREF=_main+45 0000000100000f15moval, 0x0 0000000100000f17callimp___stubs__printf; printf 0000000100000f1cmovrdi, qword [rbp+var_20]; argument "pool" for method imp___stubs__objc_autoreleasePoolPop 0000000100000f20movdword [rbp+var_2C], eax 0000000100000f23callimp___stubs__objc_autoreleasePoolPop; objc_autoreleasePoolPop 0000000100000f28xoreax, eax 0000000100000f2aaddrsp, 0x30 0000000100000f2epoprbp 0000000100000f2fret ; endp

6.3 破解iOS程序 原理同破解Mac程序一样,通过修改汇编代码达到破解的目的。

    推荐阅读