• 20145213《信息安全系统设计》第五周学习总结下篇


    20145213《信息安全系统设计》第五周学习总结下篇

    教材学习内容总结

    1. 寻址方式历史
      (1)DOS时代的平坦模式
      (2)8086的分段模式
      (3)IA32的带保护模式的平坦模式
    2. 程序编码
      (1)编译
      gcc -01 -o p p1.c
      -01:表示使用第一级优化。通常提高优化级别会使最终程序运行得更快,但是编译时间可能会变长,用调试工具对代码进行调试会更困难。(实际中,第二级优化-02被认为是较好的选择)
      gcc命令调用一系列程序将源代码转换成可执行代码:
      • C预处理器 扩展源代码,插入#include命令指定的文件,扩展#define声明指定的宏。(.i)
      • 编译器 产生两个源代码的汇编代码。(.s)
      • 汇编器 将汇编代码转化成二进制目标代码(.o)
      • 连接器 将两个目标代码与实现库函数的代码合并,并产生最终的可执行代码文件。
        (2)两种抽象
        计算机系统使用了多种不同形式的抽象,利用更简单的抽象模型来隐藏实现的细节。对于机器级编程来说,两种抽象尤为重要:
        机器级程序的格式和行为,定义为 ISA :指令集体系结构。
    处理器的硬件并发的执行许多指令,但是可以采取措施保证整体行为与ISA指定的顺序整形完全一致。
    
    解决的问题:
        指令的编码方式(即如何编码)
        操作数和操作结构的存放位置
        数据的类型和大小
        支持哪些操作
        下一条指令的地址
    

    机器级程序使用的存储器地址是虚拟地址,提供的存储器模型看上去是一个非常大的字节数组。

    (3)IA32机器代码
    一些通常对C语言程序员隐藏的机器代码在IA32中是可见的:

    程序计数器(在IA32中,通常称为“PC”,用%eip表示)
    指示将要执行的下一条指令在存储器中的地址。
    
    整数寄存器
    包含8个命名的位置,分别存储32位的数值,这些寄存器可以存储地址(对应C语言的指针)或整数数据,有的寄存器被用来记录某些重要的程序状态,其他的寄存器用来保存临时数据,例如过程的局部变量和函数的返回值。
    
    条码寄存器
    保存着最近执行的算术或逻辑指令的状态信息,他们用来实现控制或数据流中的条件变化。
    
    浮点寄存器
    一组浮点寄存器存放浮点数据
    

    一条机器指令只执行一个非常基本的操作

    获得汇编代码:

    gcc -S xxx.c -o xxx.s
    

    反汇编:

    objdump -d xxx 
    

    汇编代码(函数前两条和后两条汇编代码,所有函数都有,建立函数调用栈帧):

    前两条:
        pushl %ebp          将寄存器%ebp的内容压入程序栈
        movl %esp,%ebp      得到新栈低,将当前栈顶赋予栈低
    后两条:
        popl %ebp            过程调用结束,恢复旧栈低
        ret                 子程序的返回指令
    注意:
    
    64位机器上想要得到32代码:gcc -m32 -S xxx.c
    MAC OS中没有objdump, 有基本等价的命令otool
    Ubuntu中 gcc -S code.c (不带-O1) 产生的代码更接近教材中代码(删除"."开头的语句)
    

    (5)机器代码和它的反汇编表示的一些特性
    IA32指令长度从1到15个字节不等
    设计指令格式的方式是,从某个给定位置开始,可以将字节唯一的解码成机器指令
    反汇编器只是基于机器代码文件中的字节序列来确定汇编代码,不需要访问程序的源代码或汇编代码
    反汇编器使用的指令命名规则与GCC生成的汇编代码使用的有些差别
    3. 数据格式
    (1)Intel(由16位体系扩展为32位)
    字(word) 16位
    双字(double words) 32位
    四字(quad words) 64位
    (2)C语言数据类型在IA32中的大小
    IA32不支持64位整数运算
    大多数GCC生成的汇编代码指令都有一个字符后缀,表明操作数的大小。
    4. 访问信息
    (1)IA32的整数寄存器
    一个IA32的中央处理器单元包含一组8个存储32位数值的寄存器。所有八个寄存器都可以作为16位(字)或32位(双字)来访问。

    %esi,%edi可以用来操纵数组
    %esp,%ebp用来操纵栈帧。```
    可以独立访问前四个寄存器的两个低位字节(后向兼容)。
    32位的%eax,16位的%ax,8位的%ah,%al都是独立的
    (2)操作数指示符
    操作数三种类型
    立即数,即常数值
    寄存器,表示某个寄存器的内容
    存储器,根据计算出来的地址(有效地址)访问某个存储器位置。
    有效地址的计算方式
    

    Imm(Eb,Ei,s) = Imm + R[Eb] + R[Ei]*s```
    (3)数据传送指令

    符号                      意义
    r8          任意一个8位通用寄存器AH/AL/BH/BL/CH/CL/DH/DL
    r16         任意一个16通用寄存器AX/BX/CX/DX/SI/DI/BP/SP
    reg         代表r8或r16
    seg         段寄存器CS/DS/ES/SS
    m8          一个8位存储器操作数单元(包括所有主存寻址方式)
    m16         一个16位存储器操作数单元(包括所有主存寻址方式)
    mem         代表m8或m16
    i8          一个8位立即数
    i16         一个16位立即数
    imm         代表i8或i16
    dest        目的操作数
    src         源操作数
    mov指令(指令的第一个是源操作数,第二个是目的操作数)
    
    
    
        MOV reg/mem,imm     立即数送寄存器或是存储器
        MOV reg/mem/seg,reg 寄存器送寄存器(包括段寄存器)或贮存
        MOV reg/seg,mem     主存送寄存器(包括段寄存器)
        MOV reg/mem,seg     段寄存器送主存或寄存器
    

    IA32的限制:两个操作数都不能指向存储器。

    不能从内存地址直接MOV到另一个内存地址,要用寄存器中转一下。

    push与pop

    先进后出:push将数据压入栈中,pop弹出,弹出的永远是最近被压入的。用数组实现栈,进行操作的一端为栈顶。
    栈向下增长,栈顶元素的地址是所有栈中元素地址中最低的。栈指针%esp保存栈顶元素的地址。
    
        进栈指令PUSH:
             PUSH reg/mem/seg       SP←SP-2,SS←reg/mem/seg
        - 进栈指令先使堆栈指令SP减2,然后把一个字操作数存入堆栈顶部。
        - 堆栈操作的对象只能是字操作数。
        - 进栈时,底字节存放于低地址,高字节存放在高地址,SP相应向低地址移动两个字节单元。
    
        出栈指令POP:
            POP reg/seg/mem         reg/seg/mem←SS:[SP],SP←SP+2
        - 出栈指令把栈顶的一个字传送至指定的目的操作数,然后堆栈指针SP加2。
        - 目的操作数应为字操作数。
        - 字从栈顶弹出时,低地址字节送低字节,高地址字节送高字节。
    

    (4)数据传送与C语言
    C语言中“指针”其实就是地址。间接引用指针就是将该指针放在一个寄存器中,然后在存储器引用中使用这个寄存器。
    局部变量通常是保存在寄存器中,而不是存储器中。寄存器访问比存储器访问要快得多。
    5. 算术和逻辑操作
    (1)加载有效地址指令
    是movl指令的变形

    指令形式:从存储器读取到寄存器
    实际上是将有效地址写入目的操作数。
    leal指令通常用来执行简单的算术操作
    (2)一元操作和二元操作
    一元操作
    
    • INC 加1
    • DEC 减1
    • NEG 取负
    • NOT 取补
    只有一个操作数,既是源又是目的,可以是一个寄存器,或者存储器位置。
    二元操作
    
    • ADD 加
    • SUB 减
    • IMUL 乘
    • XOR 异或
    • OR 或
    • AND 与
    第一个操作数可以是立即数、寄存器或者存储器位置
    第二个操作数既是源也是又是目的。可以是寄存器或者存储器位置,但是不能同时是存储器位置。
    注意操作的顺序:
     第二个操作数 操作符 第一个操作数
    (3)移位操作
    先给出移位量,第二项给出要移位的数值。
    
    • SAL 左移
    • SHL 左移(等同于SAL)
    • SAR 算术右移
    • SHR 逻辑右移
    源操作数(移位量):立即数或者放在单字节寄存器元素%cl中。
    目的操作数:一个寄存器或是一个存储器位置。
    (4)特殊操作
    乘法
    乘积截断
    
    imull   双操作数
    - 从两个32位操作数产生一个32位的乘积。
    
    乘积不截断
    
    mull    无符号数乘法
    imull   有符号数乘法
    - 要求一个参数必须在寄存器%eax中,另一个作为指令的源操作数给出。
    - 乘积的高32位在%edx中,低32位在%eax中。
    
    除法
    有符号除法
    
    idivl 操作数
    - 将DX:AX中的64位数作为被除数,操作数中为除数
    - 结果:商在AX中,余数在DX中。
    
    无符号除法
    
    divl指令
    - 通常会事先设定寄存器%edx为0.
    
    6. 控制
    (1)状态寄存器(条件码寄存器)
    常用条件码:
    

    CF:进位标志。最近操作使最高位产生进位。用于检查无符号操作数的溢出。
    ZF:零标志。最近操作结果为0。
    SF:符号标志。最近操作得到的结果为负数。
    OF:溢出标志。最近操作导致一个补码溢出。
    leal 不改变条件码寄存器

    比较和测试指令:不修改任何寄存器的值,只设置条件码
    (2)访问条件码
    常用的使用方法:
    根据条件码的某个组合,将一个字节设置为0或1。
        SET指令:执行比较指令,根据计算t=a-b的结果设置条件码
    可以条件跳转到程序的某个其他部分
    switch语句可以根据一个整数索引值进行多重分支。处理具有多种可能结果的测试时,这种语句特别有用。
    优点:提高了代码的可读性;使用跳转表这个数据结构使用实现更加高效。
    跳转表:是一个数组,表项i是一个代码段的地址,这个代码段实现当switch索引值等于i时程序应该执行的动作。程序代码用于索引值来执行一个跳转表内的数组引用,确定跳转指令的目标。和使用一组很长的if-else相比,使用跳转表的优点是执行switch语句的时间与switch的case数量无关。GCC根据switch语句中case的数量和case中值的稀少程序来翻译开关语句。当case数据比较多(例如4个以上),并且值的范围跨度比较小时,就会使用跳转表。
    执行switch语句的关键步骤是通过跳转表来访问代码位置。
    7. 过程
    (1)概述
    一个过程调用包括将数据和控制从代码的一部分传递到另一部分,需要在进入时为过程的局部变量分配空间,并在退出时释放这些空间。
    数据传递、局部变量的分配和释放通过操纵程序栈来实现。
    (2)栈帧结构
    栈帧: 为单个过程分配的那部分栈
    最顶端的栈帧以两个指针界定:
    
    • 寄存器%ebp为帧指针
    • 寄存器%esp为栈指针
      程序执行时,栈指针可以移动,大多数信息的访问都是相对于帧指针的。
      栈向低地址方向增长,栈指针%esp指向栈顶元素:
    - 栈指针值适当减小可以分配没有指定初始值的数据的空间
    - 类似的,可以通过增加栈指针来释放空间
    (3)转移控制
    call指令
    目标:指明被调用过程起始的指令地址。
    效果:将返回地址入栈,并跳转到被调用过程的起始处。
    
    ret指令
    从栈中弹出地址,并跳转到这个位置.
    使用这个指令时,栈指针要指向前面call指令存储返回地址的位置。
    
    leave
    这个指令可以使栈做好返回的准备
    等价于:
    
    movl %ebp,%esp
    popl %ebp
    
    (4)寄存器使用惯例
    程序寄存器组是唯一能被所有过程共享的资源。
    为什么必须遵守惯例:必须保证一个过程(调用者)在调用另一个过程(被调用者)时,被调用者不会覆盖某个调用者寄存器中的值。
    惯例:
    
    • %eax,%edx,%ecx 划分为调用者保存寄存器
    • %ebx,%esi,%edi 划分为被调用者保存寄存器
    • %ebp,%esp 保持寄存器
    • %eax 保存函数返回值
    (5)递归过程
    当过程被调用时分配局部存储,返回时释放存储。
    # 代码截图
    ![](http://images2015.cnblogs.com/blog/898082/201610/898082-20161016220743077-1553258057.png)
    ## 学习进度条
     |            | 代码行数(新增/累积)| 博客量(新增/累积)|学习时间(新增/累积)|重要成长|
     | --------   | :----------------:|:----------------:|:---------------: |:-----:|
     |  目标      | 5000行           |   30篇           |   400小时           |       |
     |  第一周    | 0/0              |   1/1            |   10/10            |       | 
     |  第二周    | 0/0              |   1/2            |   10/20          |       |
     |  第三周    | 0/0              |   1/3            |   10/30          |       |
     |  第四周    | 0/0              |   1/4            |   10/40          |       |
     |  第五周    | 100/100              |   3/7            |   10/40          |       |
     
    ## 参考资料
    [Linux基础入门](https://www.shiyanlou.com/courses/1)
    [别出心裁的Linux命令学习法](http://www.cnblogs.com/rocedu/p/4902411.html)
    [深入理解计算机系统](https://www.shiyanlou.com/courses/413)
    [学长博客](http://www.cnblogs.com/hyq20135317/p/4868782.html)
  • 相关阅读:
    5道趣味Python热身题【新手必学】
    操作系统特征
    二叉树的中序遍历
    英语一图画作文模板
    函数
    双阶乘与华里士公式
    因式分解
    【】连通图——详细解释
    【】this指针——c++中的特殊指针
    咱们程序员好用的云笔记
  • 原文地址:https://www.cnblogs.com/qiwei/p/5967709.html
Copyright © 2020-2023  润新知