Skip to content

Latest commit

 

History

History
322 lines (268 loc) · 13.5 KB

汇编语言学习笔记.md

File metadata and controls

322 lines (268 loc) · 13.5 KB

汇编语言学习笔记

前端时间阅读了王爽老师的汇编语言,之后一直没有抽出时间进行整理,最近终于得空,进行一个小的梳理以及回顾

基础知识

CPU数据的读写主要分三类

  1. 存储单元的地址(地址信息)
    • 通过地址总线
  2. 器件的选择,读或写的命令(控制信息)
    • 通过控制总线
  3. 读或写的数据(数据信息)
    • 通过数据总线

寄存器

Register Conventional use Low 32-bits Low 16-bits Low 8-bits
rax 返回值 eax ax al
rdi 第一个参数 edi di dil
rsi 第二个参数 esi si sil
rdx 第三个参数 esx dx dl
rcx 第四个参数 ecx cx cl
r8 第五个参数 r8d r8w r8b
r9 第六个参数 r9d r9w r9b
r10 内部调用暂时寄存器 r10d r10w r10b
r11 内部调用暂时寄存器 r11d r11w r11b
rsp 栈指针 esp sp spl
rbx 临时变量 ebx bx bl
rbp 临时变量 ebp bp bpl
r12 临时变量 r12d r12w r12b
r13 临时变量 r13d r13w r13b
r14 临时变量 r14d r14w r14b
r15 临时变量 r15d r15w r15b
rip 调用寄存器
eflags 标志位寄存器

RAX

对rax做个简单的探究

- (NSInteger)returnInt {
    return 100;
}

断点在函数中,在lldb中输入finish

image

可见rax中存储的是100(返回值)

rdi rsi rdx rcx r8 r9

rdi rsi rdx rcx r8 r9分别负责存储函数的第一、二、三、四、五、六个参数

- (void)argumentWithOne:(int)one two:(int)two three:(int)three four:(int)four five:(int)five six:(int)six {
    NSLog(@"test");
}

断点之后在lldb打印

image

可看到rdx rcx r8 r9分别存储了1,2,3,4

理论上rdi rsi分别存储第1、2个参数,所以看看rdi rsi分别存储了什么

image

可看到rdi里面存储的是ViewController的一个对象指针,这可以理解了,OC方法消息传递,第一个参数是对象,第二个参数是SEL

image

rip

rip为指令寄存器,存储下一条要执行的指令的地址

在行数首行断点

image

打印rip的值

image

寻找argumentWithOne:two:three:four:five:six:的内存地址

image

可看到range = [0x000000010c76ae80-0x000000010c76aec3),可看到0x000000010c76ae80为rip的值

rsp rbp

  • RSP

    • 指向当前栈的顶端(由于栈向下增长,push RSP会递减,pop会递增)
  • rbp

    • 一直指向栈的顶端

超过6个参数

前面有提到,x86共有6个寄存器用来存储参数,但是当参数超时6个的时候怎么处理呢?这边便需要用到一种常见的数据结构--

编写函数

- (void)argumentWithOne:(int)one two:(int)two three:(int)three four:(int)four five:(int)five six:(int)six seven:(int)seven eight:(int)eight nine:(int)nine ten:(int)ten {
    NSLog(@"test");
}

在调用处加断点

[self argumentWithOne:1 two:2 three:3 four:4 five:5 six:6 seven:7 eight:8 nine:9 ten:10];

打开Debug->Debug Workflow->Always Show Disassembly,可看到如下

image

可看到前6个参数正常存储于寄存器,之后放入栈中

可看到入栈用的不是push操作,同时在前面可以看到sub rsp, 0x60这一条指令,这是一个优化,提前给参数创造出一片空间,用来存储参数,防止多个push操作影响性能,同时12个参数(10个入参+id+SEL)正好为12*8 = 0x60

函数调用栈

连接时序图 (2)

可看到函数调用基本步骤

  1. 主函数将被调函数需要的参数压入寄存器或栈中
  2. 调用call,跳到被调函数的地址,同时将主函数的下一条指令入栈
  3. 被调函数设置rbp为栈顶
  4. 将0-5参数以及临时变量入栈
  5. 完成将返回值(如果有)存入rax
  6. 通过ret返回主函数

接下来进行验证:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self argumentWithOne:1 two:2 three:3 four:4 five:5 six:6];
}

- (int)argumentWithOne:(int)one two:(int)two three:(int)three four:(int)four five:(int)five six:(int)six {
    [self argumentWithOne:7 two:8 three:9 four:10 five:11 six:12 seven:13 eight:14 nine:15 ten:16];
    NSLog(@"test");
    return 10;
}

- (int)argumentWithOne:(int)one two:(int)two three:(int)three four:(int)four five:(int)five six:(int)six seven:(int)seven eight:(int)eight nine:(int)nine ten:(int)ten {
    int a = 1; int b = 2;
    a = 3; b = 9;
    NSLog(@"%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", one, two, three, four, five, six, seven, eight, nine, ten, a, b);
    return 10;
}

在两个函数中间分别断点,可见

AssamblyStudy`-[ViewController argumentWithOne:two:three:four:five:six:]:
    0x10316cc70 <+0>:   push   rbp
    0x10316cc71 <+1>:   mov    rbp, rsp
    0x10316cc74 <+4>:   sub    rsp, 0x70
    0x10316cc78 <+8>:   mov    eax, dword ptr [rbp + 0x18]
    0x10316cc7b <+11>:  mov    r10d, dword ptr [rbp + 0x10]
    0x10316cc7f <+15>:  mov    qword ptr [rbp - 0x8], rdi
    0x10316cc83 <+19>:  mov    qword ptr [rbp - 0x10], rsi
    0x10316cc87 <+23>:  mov    dword ptr [rbp - 0x14], edx
    0x10316cc8a <+26>:  mov    dword ptr [rbp - 0x18], ecx
    0x10316cc8d <+29>:  mov    dword ptr [rbp - 0x1c], r8d
    0x10316cc91 <+33>:  mov    dword ptr [rbp - 0x20], r9d
->  0x10316cc95 <+37>:  mov    rsi, qword ptr [rbp - 0x8]
    0x10316cc99 <+41>:  mov    rdi, qword ptr [rip + 0x7740] ; "argumentWithOne:two:three:four:five:six:seven:eight:nine:ten:"
    0x10316cca0 <+48>:  mov    qword ptr [rbp - 0x28], rdi
    0x10316cca4 <+52>:  mov    rdi, rsi
    0x10316cca7 <+55>:  mov    rsi, qword ptr [rbp - 0x28]
    0x10316ccab <+59>:  mov    edx, 0x7
    0x10316ccb0 <+64>:  mov    ecx, 0x8
    0x10316ccb5 <+69>:  mov    r8d, 0x9
    0x10316ccbb <+75>:  mov    r9d, 0xa
    0x10316ccc1 <+81>:  mov    dword ptr [rsp], 0xb
    0x10316ccc8 <+88>:  mov    dword ptr [rsp + 0x8], 0xc
    0x10316ccd0 <+96>:  mov    dword ptr [rsp + 0x10], 0xd
    0x10316ccd8 <+104>: mov    dword ptr [rsp + 0x18], 0xe
    0x10316cce0 <+112>: mov    dword ptr [rsp + 0x20], 0xf
    0x10316cce8 <+120>: mov    dword ptr [rsp + 0x28], 0x10
    0x10316ccf0 <+128>: mov    dword ptr [rbp - 0x2c], eax
    0x10316ccf3 <+131>: mov    dword ptr [rbp - 0x30], r10d
    0x10316ccf7 <+135>: call   qword ptr [rip + 0x2303]  ; (void *)0x00007fff20175280: objc_msgSend
    0x10316ccfd <+141>: lea    rsi, [rip + 0x2314]       ; @"test"
    0x10316cd04 <+148>: mov    rdi, rsi
    0x10316cd07 <+151>: mov    dword ptr [rbp - 0x34], eax
    0x10316cd0a <+154>: mov    al, 0x0
    0x10316cd0c <+156>: call   0x10316d354               ; symbol stub for: NSLog
    0x10316cd11 <+161>: mov    eax, 0xa
    0x10316cd16 <+166>: add    rsp, 0x70
    0x10316cd1a <+170>: pop    rbp
    0x10316cd1b <+171>: ret    

可以看到在主函数中给rsp开辟了0x70(0x10316cc74 <+4>: sub rsp, 0x70)的空间,即为12个参数+2个临时变量的空间

AssamblyStudy`-[ViewController argumentWithOne:two:three:four:five:six:seven:eight:nine:ten:]:
    0x10316cd20 <+0>:   push   rbp
    0x10316cd21 <+1>:   mov    rbp, rsp
    0x10316cd24 <+4>:   push   r15
    0x10316cd26 <+6>:   push   r14
    0x10316cd28 <+8>:   push   r13
    0x10316cd2a <+10>:  push   r12
    0x10316cd2c <+12>:  push   rbx
    0x10316cd2d <+13>:  sub    rsp, 0x98
    0x10316cd34 <+20>:  mov    eax, dword ptr [rbp + 0x38]
    0x10316cd37 <+23>:  mov    r10d, dword ptr [rbp + 0x30]
    0x10316cd3b <+27>:  mov    r11d, dword ptr [rbp + 0x28]
    0x10316cd3f <+31>:  mov    ebx, dword ptr [rbp + 0x20]
    0x10316cd42 <+34>:  mov    r14d, dword ptr [rbp + 0x18]
    0x10316cd46 <+38>:  mov    r15d, dword ptr [rbp + 0x10]
    0x10316cd4a <+42>:  lea    r12, [rip + 0x22e7]       ; @"%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d"
    0x10316cd51 <+49>:  mov    qword ptr [rbp - 0x30], rdi
    0x10316cd55 <+53>:  mov    qword ptr [rbp - 0x38], rsi
    0x10316cd59 <+57>:  mov    dword ptr [rbp - 0x3c], edx
    0x10316cd5c <+60>:  mov    dword ptr [rbp - 0x40], ecx
    0x10316cd5f <+63>:  mov    dword ptr [rbp - 0x44], r8d
    0x10316cd63 <+67>:  mov    dword ptr [rbp - 0x48], r9d
    0x10316cd67 <+71>:  mov    dword ptr [rbp - 0x4c], 0x1
    0x10316cd6e <+78>:  mov    dword ptr [rbp - 0x50], 0x2
    0x10316cd75 <+85>:  mov    dword ptr [rbp - 0x4c], 0x3
    0x10316cd7c <+92>:  mov    dword ptr [rbp - 0x50], 0x9
->  0x10316cd83 <+99>:  mov    esi, dword ptr [rbp - 0x3c]
    0x10316cd86 <+102>: mov    edx, dword ptr [rbp - 0x40]
    0x10316cd89 <+105>: mov    ecx, dword ptr [rbp - 0x44]
    0x10316cd8c <+108>: mov    r8d, dword ptr [rbp - 0x48]
    0x10316cd90 <+112>: mov    r9d, dword ptr [rbp + 0x10]
    0x10316cd94 <+116>: mov    r13d, dword ptr [rbp + 0x18]
    0x10316cd98 <+120>: mov    edi, dword ptr [rbp + 0x20]
    0x10316cd9b <+123>: mov    dword ptr [rbp - 0x54], eax
    0x10316cd9e <+126>: mov    eax, dword ptr [rbp + 0x28]
    0x10316cda1 <+129>: mov    dword ptr [rbp - 0x58], eax
    0x10316cda4 <+132>: mov    eax, dword ptr [rbp + 0x30]
    0x10316cda7 <+135>: mov    dword ptr [rbp - 0x5c], eax
    0x10316cdaa <+138>: mov    eax, dword ptr [rbp + 0x38]
    0x10316cdad <+141>: mov    dword ptr [rbp - 0x60], eax
    0x10316cdb0 <+144>: mov    eax, dword ptr [rbp - 0x4c]
    0x10316cdb3 <+147>: mov    dword ptr [rbp - 0x64], eax
    0x10316cdb6 <+150>: mov    eax, dword ptr [rbp - 0x50]
    0x10316cdb9 <+153>: mov    dword ptr [rbp - 0x68], edi
    0x10316cdbc <+156>: mov    rdi, r12
    0x10316cdbf <+159>: mov    dword ptr [rsp], r13d
    0x10316cdc3 <+163>: mov    r13d, dword ptr [rbp - 0x68]
    0x10316cdc7 <+167>: mov    dword ptr [rsp + 0x8], r13d
    0x10316cdcc <+172>: mov    r13d, dword ptr [rbp - 0x58]
    0x10316cdd0 <+176>: mov    dword ptr [rsp + 0x10], r13d
    0x10316cdd5 <+181>: mov    r13d, dword ptr [rbp - 0x5c]
    0x10316cdd9 <+185>: mov    dword ptr [rsp + 0x18], r13d
    0x10316cdde <+190>: mov    r13d, dword ptr [rbp - 0x60]
    0x10316cde2 <+194>: mov    dword ptr [rsp + 0x20], r13d
    0x10316cde7 <+199>: mov    r13d, dword ptr [rbp - 0x64]
    0x10316cdeb <+203>: mov    dword ptr [rsp + 0x28], r13d
    0x10316cdf0 <+208>: mov    dword ptr [rsp + 0x30], eax
    0x10316cdf4 <+212>: mov    al, 0x0
    0x10316cdf6 <+214>: mov    dword ptr [rbp - 0x6c], r10d
    0x10316cdfa <+218>: mov    dword ptr [rbp - 0x70], r11d
    0x10316cdfe <+222>: mov    dword ptr [rbp - 0x74], ebx
    0x10316ce01 <+225>: mov    dword ptr [rbp - 0x78], r14d
    0x10316ce05 <+229>: mov    dword ptr [rbp - 0x7c], r15d
    0x10316ce09 <+233>: call   0x10316d354               ; symbol stub for: NSLog
    0x10316ce0e <+238>: mov    eax, 0xa
    0x10316ce13 <+243>: add    rsp, 0x98
    0x10316ce1a <+250>: pop    rbx
    0x10316ce1b <+251>: pop    r12
    0x10316ce1d <+253>: pop    r13
    0x10316ce1f <+255>: pop    r14
    0x10316ce21 <+257>: pop    r15
    0x10316ce23 <+259>: pop    rbp
    0x10316ce24 <+260>: ret    

可见

0x10316cd51 <+49>:  mov    qword ptr [rbp - 0x30], rdi
0x10316cd55 <+53>:  mov    qword ptr [rbp - 0x38], rsi
0x10316cd59 <+57>:  mov    dword ptr [rbp - 0x3c], edx
0x10316cd5c <+60>:  mov    dword ptr [rbp - 0x40], ecx
0x10316cd5f <+63>:  mov    dword ptr [rbp - 0x44], r8d
0x10316cd63 <+67>:  mov    dword ptr [rbp - 0x48], r9d
0x10316cd67 <+71>:  mov    dword ptr [rbp - 0x4c], 0x1
0x10316cd6e <+78>:  mov    dword ptr [rbp - 0x50], 0x2

这个函数一共12个参数,前6个在寄存器中,则[rbp+0x10]-[rbp+0x38]存放第7-12个参数(0xb-0x10),现在进行验证

(lldb) x/gx '$rbp + 0x10'
0x7ffeeca90ff0: 0x000000000000000b
(lldb) x/gx '$rbp + 0x18'
0x7ffeeca90ff8: 0x000000000000000c
(lldb) x/gx '$rbp + 0x20'
0x7ffeeca91000: 0x000000000000000d
(lldb) x/gx '$rbp + 0x28'
0x7ffeeca91008: 0x000000000000000e
(lldb) x/gx '$rbp + 0x30'
0x7ffeeca91010: 0x000000000000000f
(lldb) x/gx '$rbp + 0x38'
0x7ffeeca91018: 0x0000000000000010

同时,[rbp+0x8]应该是函数的返回地址

(lldb) x/gx '$rbp + 0x8'
0x7ffeeca90fe8: 0x000000010316ccfd

可见这个地址即为-[ViewController argumentWithOne:two:three:four:five:six:]调用call的下一个地址

0x10316ccf7 <+135>: call   qword ptr [rip + 0x2303]  ; (void *)0x00007fff20175280: objc_msgSend
0x10316ccfd <+141>: lea    rsi, [rip + 0x2314]       ; @"test"

验证成功~

注:以上都在Mac的x86_64架构进行