• 20135308-信息安全系统设计基础第五周学习总结


    第3章 程序的机器级表示

    一、X86 寻址方式

    1 DOS时代的平坦模式,不区分用户空间和内核空间,很不安全

    2 8086的分段模式

    3 IA32的带保护模式的平坦模式

    二、程序编码

    编译如下代码:

    unix> gcc -01 -o p p1.c p2.c

    • 01 表示告诉编译器使用第一级优化。通常,提高优化级别会使最终程序运行的更快,但编译时间可能会变长,用调试工具对代码进行调试会更困难。
    • 从得到的程序性能方面考虑,第二级优化-02被认为是是较好的选择。

    二、机器级代码

    1、两种抽象

    (1)ISA

    ISA(Instruction set architecture)指令体系结构:机器级程序的格式和行为,定义了处理器状态、指令的格式、每条指令对状态的影响。

    (2)机器级程序使用的存储器地址是虚拟地址

    提供的存储器模型看上去是一个非常大的字节数组,实际实现是将多个硬件存储器和操作系统软件组合起来。

    2、汇编代码特点

    它用可读性更好的文本格式来表示。

    3、状态可见的几种处理器

    • 程序计数器 (PC,用%eip表示)
    • 整数寄存器 (包含8个命名的位置,存储32位的值)
    • 条件码寄存器 (实现if和while语句)
    • 浮点寄存器 (存放浮点数)

    机器代码简单地将存储器看成一个很大的、按字节寻址的数组。 汇编代码也不区分有符号或无符号整数,不区分各种类型的指针,甚至不区分指针和整数。

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

    三、获得汇编代码

    (1)gcc -S xxx.c -o xxx.s 获得汇编代码

    eg:unix> gcc -01 -S code.c
    

    (2)objdump -d xxx 反汇编;

    eg:unix> objdump -d code.o
    

    注意: 64位机器上想要得到32代码:gcc -m32 -S xxx.c

    MAC OS中没有objdump, 有个基本等价的命令otool

    Ubuntu中 gcc -S code.c (不带-O1) 产生的代码更接近教材中代码(删除"."开头的语句)

    四、查看二进制格式文件

    二进制文件可以用od 命令查看,也可以用gdb的x命令查看。 有些输出内容过多,我们可以使用 more或less命令结合管道查看,也可以使用输出重定向来查看

            od code.o | more
    
            od code.o > code.txt
    

    五、关于格式的注解

    以“.”开头的行都是指导汇编器和链接器的命令,我们通常可以忽略这些行。

    gcc -S 产生的汇编中可以把 以”.“开始的语句都删除了再阅读。

    六、Linux和Windows的汇编格式

    ATT格式的汇编代码

    是GCC、OBJDUMP及其他我们使用的一些工具的默认格式。


    Intel格式的汇编代码

    包括Microsoft工具等编程工具。

    1、C语言基本数据类型对应的IA32表示。


    数据传送指令有三个变种:

    movb(传送字节)

    movw(传送字)

    movl(传送双字)

    后缀'l'用来表示双字

    注:汇编代码也使用后缀'l'来表示4字节整数和8字节双精度浮点数。

    七、访问信息

    一个IA32中央处理单元(CPU)包含一组8个存储32位值的寄存器。这些寄存器用来存储整数数据和指针。

    esi edi可以用来操纵数组,esp ebp用来操纵栈帧。


    1、操作数指示符

    操作数:指示出执行一个操作中要引用的源数据值,以及放置结果的目标位置。

    (1)操作数的三种类型

    • 立即数
    • 寄存器
    • 存储器

    (2)多种不同的寻址方式


    2、数据传送指令


    (1)MOV类指令

    定义:将数据从一个位置复制到另一个位置,将源操作数的值复制到目的操作数。

    (2)PUSH&POP指令

    PUSH:将数据压入程序栈中

    POP:从程序栈中弹出数据

    注意:

    1.后进先出:即弹出的值永远是最近被压入,仍然在栈内的值

    2.栈指针指向栈顶元素

    3.栈向下即低地址方向增长

    4.栈顶元素的地址是所有栈中元素地址中最低的。

    3、数据传送示例

    (1)指针示例

    eg:int x = *xp;
    

    读出参数xp,放在寄存器%edx里,然后将x读到%eax中,实现了C程序中的操作 x=*xp,然后用寄存器%eax从该函数返回一个值x。

    八、算术和逻辑操作

    1、操作分类

    • 加载有效地址
    • 一元操作

      只有一个操作数,既是源又是目的。它可以是一个寄存器,或者存储器位置。

    • 二元操作

      有两个操作数,其中第二个操作数,又是源,又是目的。

    • 移位

      SAL 算术左移

      SHL 逻辑左移

      SAR 算术右移(补符号位)

      SHR 逻辑右移(补0)

    先给出位移量,再给出要位移的位数,可进行算术和逻辑右移。

    2、加载有效地址

    加载有效地址(load effective address)指令leal是movl指令的变形。

    将有效地址写入到目的操作数。


    3、特殊算术操作

    下图描述的指令支持产生两个32位数字的全64位乘积以及整数除法。 


    (1)乘法

    乘积截断:产生一个32位乘积

    乘积不截断:

    • 无符号数乘法(mull)
    • 补码乘法(imull)

    (2)除法

    有符号除法指令:idivl

    将寄存器%edx,%eax中的64位数作为被除数

    将商存储在寄存器%eax中,余数存储在%edx中。

    九、控制

    1、条件码

    条件码寄存器:他们描述了最近的算术或逻辑操作的属性。

    常用的条件码:

    CF:进位标志
    ZF:零标志
    SF:符号标志
    OF:溢出标志
    

    常见指令:

    LEAL:不改变条件码寄存器
    XOR:进位标志和溢出标志会设置成0
    INC:设置溢出和零标志
    DEC:设置溢出和零标志
    CMP:根据操作数之差设置条件码
    SUB:设置条件码,更新寄存器
    TEST:改变目的寄存器的值
    

    2、访问条件码

    常用使用方法:

    (1)根据条件码的某个组合,将一个字节设置成0或1

    (2)可以条件跳转到某个其他部分

    (3)可以有条件的传送数据

    (1)SET指令

    通过set与不同的条件码的组合,达到不同的跳转条件。


    3、跳转指令及其编码

    JUMP指令,导致执行切换到程序中一个全新的位置。

    注意:jump分为直接跳转和间接跳转

    • 直接跳转:后面跟标号作为跳转目标
    • 间接跳转:*后面跟一个操作数指示符


    十、翻译条件分支

    将条件表达式和语句从c语言翻译成机器语言,最常用的方式就是结合有条件和无条件跳转。

    使用goto通常认为是一种不太好的风格。

    十一、循环

    1、do-while循环

    汇编中,根据do-while形式产生循环代码

    do-while语句的通用形式:

    do
    body-statement
    while(test-expr);
    

    可翻译成如下:

    loop:
    body-statement
    t = test-expr;
    if(t)
        goto loop;

    2、while循环

    while语句的通用形式:

    while (test-expr)
    body-statement
    

    GCC采用的方法,是使用条件分支,需要时省略循环体的第一次执行::

    if(!test-expr)
        goto done;
    
    do
        body-statement
        while(test-expr);
    done:
    

    接下来,这个代码可直接翻译成goto代码:

        t = test-expr;
         if(!t)
        goto done:
    loop:
        body-statement
        t = test-expr;
        if(t)
        goto loop;
    done:
    

    3、for循环

    for循环的通用形式:

    for(init-expr;test-expr;update-expr)
    body-statement
    

    汇编结构:

        init-expr
        t=test-expr;
        if(!t)
        goto done;
    loop:
        body-statement
        update-expr;
        t=test-expr;
        if(t)
            goto loop;
        done:
    

    4、switch语句

    根据一个整数索引值进行多重分支,执行switch语句的关键步骤是通过跳转表来访问代码位置,使结构变得更加高效。

    十二、过程

    过程调用

    • 进入时为过程的局部变量分配空间

    • 将数据(以过程参数和返回值的形式)和控制从代码的一部分传递到另一部分。

    • 退出时释放这些空间。

    1、栈帧结构

    • IA32程序用程序栈来支持过程调用。

    • 机器用栈来传递过程参数、存储返回信息、保存寄存器,以及本地存储。

    2、转译控制

    (1)call指令

    • 目标是指明被调用过程起始的指令地址

    • 效果是将返回地址入栈,并跳转到被调用过程的起始处。

    (2)ret指令

    • 从栈中弹出地址,并跳转到这个位置。

    • 函数返回值存在%eax中

    3、寄存器使用惯例

    注意:保证当一个过程调用另一个过程时,被调用者不会覆盖某个调用者稍后会用的寄存器的值。

    十三、应用:使用GDB调试器

    关于栈帧的gdb命令:

    1、backtrace/bt

    打印当前的函数调用栈的所有信息。
    
    n是一个正整数,表示只打印栈顶上n层的栈信息。
    
    -n表一个负整数,表示只打印栈底下n层的栈信息。
    

    2、frame

    n为栈中的层编号,是一个从0开始的整数
    
    比如:frame 0,表示栈顶,frame 1,表示栈的第二层。
    
    该指令是移动到n指定的栈帧中去,并打印选中的栈的信息。
    
    如果没有n,则打印当前帧的信息。
    

    3、up

    表示向栈顶移动n层
    
    可以不打n,表示向上移动一层。
    

    4、down

    表示向栈底移动n层
    
    可以不打n,表示向下移动一层。
  • 相关阅读:
    Nginx入门
    Spring基础知识汇总 Java开发必看
    java ArrayList倒序
    1
    js event 的target 和currentTarget
    java 文件的写入和读取
    DOMContentLoaded与load的区别
    JS中先有Object还是先有Function?
    JSON.stringify的三个参数
    Java的泛型约束和限制
  • 原文地址:https://www.cnblogs.com/bonjourvivi/p/4869684.html
Copyright © 2020-2023  润新知