汇编语言(assembly language)

一.概述

语言分类
1.机器语言:
  • 由0和1组成的机器指令,如:0101 0001 1101 0110
2.汇编语言
  • 使用符号代替难编写和很难阅读机器语言,也成为符号语言。如:mov,ax,bx,call
3.高级语言
  • C/C++/OC/Java/Swift,更让人易读和编写的语言(更接近人类的自然语言)
    编程语言编译时的区别:
    C++/OC/Swift... ------编译器编译------>汇编代码------->机器码
    JS/Python... ------脚本引擎执行------>中间代码------->机器码
    Java... ------编译器编译------>字节码----JVM--->机器码

汇编语言与机器语言一一对应, 每一条机器指令都有与之对应的汇编指令
汇编语言可以通过编译得到机器语言,机器语言可以通过反汇编得到汇编语言
高级语言可以通过编译得到汇编语言/机器语言,但汇编语言/机器语言几乎不可能还原成高级语言

特点
  • 可直接访问、控制各种硬件设备。比如存储器、CPU等,能最大限度地发挥硬件的功能
  • 能够不受编译器的限制,对生成的二进制代码进行完全的控制
  • 目标代码简短,占用内存少,执行速度快
  • 汇编指令是机器指令的助记符,同机器指令一一对应。每种CPU都有自己的机器指令集/汇编指令集,所以汇编语言不具备可移植性
  • 汇编语言知识点过多,开发者需要对CPU等硬件结构有所了解,不宜于编写、调试、维护
  • 不区分大小写,比如mov和MOV是一样的
用途
  • 编写驱动程序、操作系统(比如Linux内核的某些关键部分)
  • 对性能要求极高的程序或者代码片段,可与高级语言混合使用(内嵌汇编)
  • 软件安全
  • 病毒分析和防治
  • 逆向\加壳\脱壳\破解\外挂\免杀\加密解密\漏洞\黑客
  • 理解整个计算机系统的最佳起点和最有效途径
  • 为编写高效代码打下基础
  • 弄清楚代码的本质
  • 函数的本质是什么?
    sizeof
    ++a + ++a + ++a 底层如何执行的?
  • 编译器到底帮我们干了什么?
  • DEBUG模式和RELEASE模式有什么关键的地方被我们忽略
种类

目前讨论比较多的汇编语言

  • 8086汇编(8086处理器是16bit的CPU)
  • Win32汇编
  • Win64汇编
  • ARM汇编(嵌入式、Mac、iOS)
    我们iPhone里面的用到的是ARM汇编,但是不同的设备也有差异。因为CPU的架构不同,5s以后的手机都是ARM64汇编

二.ARM64汇编

1.寄存器

通用寄存器
64bit x0-x28
32bit w0-w28

(lldb) register read x0   //读取x0地址
      x0 = 0x0000000000000003
(lldb) register read   //读取所有寄存器地址
(lldb) register write x0 0x0000000000000123  //写入寄存器x0地址
(lldb) register read x0 
      x0 = 0x0000000000000123
(lldb) register read w0
      w0 = 0x00000123

零寄存器
用来清空内存数据

  • wzr(32bit),存储#0x0000,
  • xzr(64bit) ,存储#0x00000000,

程序计数器

  • pc 记录当前执行的指令的内存的位置


    WX20190920-102457.png
  • lr 记录函数返回的内存地址的位置,也是x30寄存器
    可以用bl指令验证,调用bl 跳转函数段后,lr保存下一行代码内存地址(也就是函数返回地址)


    WX20190920-105830.png

堆栈指针

  • sp
  • fp 也就是x29寄存器
2.指令
  • ret 函数返回
  • mov a b 把b赋给a
    测试:
    1.创建一个armtest头文件和汇编的实现.s文件
//在程序启动的时候调用函数test
int main(int argc, char * argv[]) {
    @autoreleasepool {
        test();
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
![WX20190917-165220.png](https://upload-images.jianshu.io/upload_images/5734145-05ac6f41ad4d0aa4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

//armtest.h:
#ifndef armtest_h
#define armtest_h
void test();
#endif /* armtest_h */

//armtest.s:
.text   //声明代码段
.global _test  //指定test函数为全局函数

_test:
mov x0,#0x9   //16进制9赋给x0寄存器
mov x1,x0       //x0赋给x1寄存器
ret

2.断点测试


WX20190917-165930.png
  • add a,b,c 把b,c相加赋值给a
  • sub a,b,c 把b,c相减赋值给a
    一般寄存器x0-x7存放函数参数,更多的存放在堆栈中,x0存放函数的返回值
//1.定义函数:
int addM(int a,int b);
int subM(int a,int b);

//2.调用函数
NSLog(@"%d",addM(2,5));
NSLog(@"%d",subM(8,3));

//3.函数的汇编实现
.text
.global _addM,_subM
_addM:
add x0,x0,x1  //x0,x1表示存放函数参数,x0f存放函数返回值
ret

_subM:
sub x0,x0,x1
ret
  • cmp a b 比较a,b(a-b的值寄存器)的结果放到cpsr寄存器中的标志位
  • b 跳转到指定程序段[可以结合条件和cmp指令进行跳转eq(相等) ne(不等) gt(大于) ge(大于等于) lt(小于) le(小于等于)]
//register read 查看所有寄存器的值
_cmpM:
mov x0,#0x8
mov x1,#0x2
cmp x0,x1           //x0>x1 cpsr =0x80000000  1000...0000

mov x0,#0x1
mov x1,#0x1
cmp x0,x1           //x0=x1 cpsr = 0x20000000  0010...0000

mov x0,#0x1
mov x1,#0x6
cmp x0,x1           //x0<x1 cpsr = 0x60000000  0110...0000
ret
_bM:
mov x0,#0x7
mov x1,#0x7
cmp x0,x1
beq _bres    //eq ne gt ge lt le
mov x0,#0x0  //如果结果相等就跳转到_bres方法,否则继续向下执行
ret

_bres:
mov x0,#0x1
ret
  • bl 带返回的跳转指令
_blM:
mov x1,#0x1
mov x2,#0x2
//b code 直接执行code段代码,不执行mov x3,#0x3
//bl code 直接执行code段代码,然后再接着执行mov x3,#0x3,类似函数调用
bl code
mov x3,#0x3
ret

code:
add x0,x1,x2
ret

使用b,bl指令可以简单处理if-else和函数调用。
查看if-else语句的汇编代码

        int a=3;
        int b=5;
        if(a>b)
        {
            printf("a>b");
        }else
        {
            NSLog(@"a<=b");
        }
WX20190919-150249.png

cmp w8,w9 (3,5)
b.le
w8小于等于w9 b.le指令跳到NSLog代码段
否则直接向下执行,遇到NSLog代码段直接b指令跳过

查看mytest()函数调用的汇编代码

void mytest(void)
{
    NSLog(@"test");
}

int main(int argc, char * argv[]) {
    @autoreleasepool {
        int a=1;
        int b=2;
        if(a<b)
        {
            mytest();
        }
        printf("continue");
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

WX20190919-151814.png

bl指令跳转到mytest()代码块,然后再执行printf代码块

从内存中读取数据的指令

  • ldr w0,[x1,#0x5] 偏移位是正,[x1,#0x5]表示x1+#0x5赋值给w0;[x1,#0x5]!表示x1+#0x5赋值x1,再赋值给w0
  • ldur w0,[x1,#-0x3] 偏移位是负
  • ldp w0,w1,[x1,#0x7]使用两个寄存器按低到高位存储数据
_ldrM:
ldr w0,[x1]  //把x1的地址(前32bit)赋给w0寄存器
ret
WX20190919-163728.png

随便打断点运行一段程序都可以看到这些指令的存在


WX20190919-165638.png

从内存中写入数据的指令(用法基本同读取指令)

  • str
  • stur
  • stp
int a=8;
strM();

strM:

_strM:
str w0,[x1] //把w0寄存器的值赋给x1的内存地址
ret
WX20190919-172217.png

随便打断点运行一段程序都可以看到这些指令的存在


WX20190919-172650.png
3.堆栈

函数的分类:

  • 叶子函数
    函数内部不调用其他函数
  • 非叶子函数
    函数内部还要调用其他函数

叶子函数的堆栈调用:

void leafF()
{
    int a=3;
    int b=9;
}

int main(int argc, char * argv[]) {
    @autoreleasepool {
        leafF();
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

leafF对应的汇编:

ARMTest`leafF:
->  0x100ce66b0 <+0>:  sub    sp, sp, #0x10             ; =0x10     //sp堆栈指针sp=sp-16 
    0x100ce66b4 <+4>:  orr    w8, wzr, #0x3     //3赋值给w8
    0x100ce66b8 <+8>:  str    w8, [sp, #0xc]     //sp+12写入w8
    0x100ce66bc <+12>: mov    w8, #0x9         //9赋值给w8
    0x100ce66c0 <+16>: str    w8, [sp, #0x8]    //sp+8写入w8
    0x100ce66c4 <+20>: add    sp, sp, #0x10             ; =0x10  //sp堆栈指针sp=sp+16 
    0x100ce66c8 <+24>: ret    
WX20191011-155954.png

说明:
上图就是模拟一段函数调用时的栈空间;
sub sp,sp #0x10 就是把起始的sp位置-16,目的是分配函数调用空间。
str w8, [sp, #0xc]和str w8, [sp, #0x8] 就是把sp+12和sp+8的内存分配给a,b(4个字节);
add sp,sp #0x10 就是把sp的位置+16,如果不+16,那么会导致内存被浪费,这里是局部变量,所以+16之后让sp回到起点,3,9分配的空间被忽略。

非叶子函数的堆栈调用:

void leafF()
{
    int a=3;
    int b=9;
}

void NOleafF()
{
    int a=4;
    int b=5;
    leafF();
}

int main(int argc, char * argv[]) {
    @autoreleasepool {
        NOleafF();
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

NOleafF对应的汇编:

ARMTest`NOleafF:
    0x10466e6a0 <+0>:  sub    sp, sp, #0x20             ; =0x20   //sp堆栈指针sp=sp-32
    0x10466e6a4 <+4>:  stp    x29, x30, [sp, #0x10]                  //x29(fp),x30(lr)寄存器顺序写入sp+16的地址
    0x10466e6a8 <+8>:  add    x29, sp, #0x10            ; =0x10   //fp指针指向当前的fp区域
->  0x10466e6ac <+12>: orr    w8, wzr, #0x4                            
    0x10466e6b0 <+16>: stur   w8, [x29, #-0x4]                         //4写入x29-4
    0x10466e6b4 <+20>: mov    w8, #0x5                
    0x10466e6b8 <+24>: str    w8, [sp, #0x8]                             //5写入sp+8
    0x10466e6bc <+28>: bl     0x10466e684               ; leafF at main.m:13   //跳转到内部函数(leafF)执行段(叶子函数执行)
    0x10466e6c0 <+32>: ldp    x29, x30, [sp, #0x10]                                      //从sp+16顺序读取出赋值给x29,x30
    0x10466e6c4 <+36>: add    sp, sp, #0x20             ; =0x20                        //sp堆栈指针sp=sp+32
    0x10466e6c8 <+40>: ret    

WX20191012-155930.png

说明:
stp x29, x30, [sp, #0x10] 函数开始时用sp-32这段内存空间写入该函数fp(堆栈信息),lr(函数返回)的地址
非叶子函数会跳转到其他函数体执行代码,很可能修改x29,x30寄存器中的值,所以函数跳转回来之后,需要执行
ldp x29, x30, [sp, #0x10] ,为了恢复当前函数x29,x30寄存器为之前存储fp,lr的值,达到堆栈平衡。
所以,也可以称sp寄存器为栈顶指针,fp寄存器为栈底指针。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,588评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,456评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,146评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,387评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,481评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,510评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,522评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,296评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,745评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,039评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,202评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,901评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,538评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,165评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,415评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,081评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,085评论 2 352

推荐阅读更多精彩内容