arm64汇编基础
iOS汇编
- 真机:
arm64
汇编 - 模拟器:
x86
汇编
xcrun --sdk iphoneos clang -S -arch arm64 main.c -o main.s
1. 寄存器
lldb
查看当前arm64
(iOS
真机)的所有寄存器: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
的时候可以使用低位32bit
的w0 ~ 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位,对应不同的功能状态:文章图片
cpsr 1.3 程序计数器寄存器
pc
-
pc
寄存器- 记录CPU当前指令是哪一条指令
- 存储着当前CPU正在执行的指令的地址
lr
-
lr
寄存器(也是x30
寄存器)- 是链接寄存器(
link register
) - 存储着当前函数的返回地址,即函数下一条指令的地址
- 是链接寄存器(
sp
fp
-
sp
(Stack Pointer
) -
fp
(Frame Pointer
),也就是x29
寄存器
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
指令后边可以跟的条件域:-
EQ
:equal
,等于 -
NE
:not equal
,不等于 -
GT
:great than
,大于 -
GE
:great equal
,大于等于 -
LT
:less than
,小于 -
LE
:less equal
,小于等于
cpsr
的2进制标志位:文章图片
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
相关的)
-
ldr
、ldur
-
ldp
(p
是pair
(一对)的简称)
-
-
store
,往内存中写入数据(带s
相关的)
-
str
、stur
stp
- 零寄存器,里面存储的值是0
-
wzr
(32bit
,Word Zero Register
) -
xzr
(64bit
)
-
-
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
xzr
0寄存器
寄存器里面存储的值是0,是专门用来存储0的寄存器-
wzr
是32bit
-
xzr
是64bit
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)
之前的跳转指令
b
和bl
的区别就是:-
b
指令仅仅只是跳转 - 而
bl
可以跳转回函数的返回地址,以便继续执行下面的指令,我们来看一下其本质
bl
和ret
指令配合使用的本质bl
跳转指令执行的操作:- 将下一条指令的地址存储到
lr
(x30
)寄存器中,lr
寄存器存储着下一条指令的地址 - 跳转到标记处开始执行代码
ret
返回指令执行的操作:- 将
lr
(x30
)指令的地址赋值给pc
指令,此时lr
寄存器正好存储着下一条指令的地址,所以pc
指令现在是下一条指令的地址 - 继续执行
pc
指令,即使函数返回的地址
bl
和ret
指令配合才能达到函数返回的目的5. 堆栈平衡
5.1 函数类型
- 叶子函数:函数里面没有再调用其他函数了
- 非叶子函数:函数里面有调用其他函数
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.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
程序一样,通过修改汇编代码达到破解的目的。推荐阅读
- Python基础|Python基础 - 练习1
- Java|Java基础——数组
- Java基础-高级特性-枚举实现状态机
- 营养基础学20180331(课间随笔)??
- iOS面试题--基础
- HTML基础--基本概念--跟着李南江学编程
- typeScript入门基础介绍
- c++基础概念笔记
- 集体释放
- 自我修养--基础知识