zoukankan      html  css  js  c++  java
  • 20145212 《信息安全系统设计基础》第5周学习总结

    20145212 《信息安全系统设计基础》第5周学习总结

    教材学习内容总结

    1.X86 寻址方式的变化:

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

    • 2.8086的分段模式;

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

    2.机器编程的两种抽象:

    • 1.指令集体系结构(Instruction set architecture,ISA)——定义指令格式以及每条指令执行之后对状态的影响。大多数ISA将程序行为描述成按顺序执行的;

    • 2.虚拟地址

    3.一些处理器状态

    • 1.PC,即程序计数器,用来指示将要执行的下一条指令在存储器中的地址;

    • 2.整数寄存器,存储数据;条件码寄存器,保存逻辑指令状态信息;等

    4.gcc -S xxx.c 可以得到C语言编译器产生的汇编代码,但不会做其他工作;使用“-c”命令,GCC就会编译并汇编该代码,得到二进制文件XXX.o。由此可见,机器执行的实际上是对一系列指令进行编码的字节序列。

    5.函数中通用的汇编语句:

    pushl %ebp //将该寄存器内容全部压入程序栈
    movl %esp,%ebp
    ……
    addl %eax,accum
    popl %ebp
    

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

    7.反汇编器:
    根据目标代码产生一种类似于汇编代码的格式。在linux中,可以通过objdump -d xxx.o 实现

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

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

    9.Linux和的汇编格式为ATT格式,而Windows的为Intel格式。二者在语法上有区别——后者省略了指示大小的后缀、寄存器前的%等。

    10.intel用术语“字”来表示16位数据类型。
    int和long int都是4字节即双字长度;char是单字节;指针存储为4字节的双字。另外,在汇编代码中,b表示字节;w表示字,l表示双字

    11.一个IA32中央处理单元(CPU)包含一组8个存储32位值的寄存器。其中,esi edi可以用来操纵数组,esp ebp用来操纵栈帧。另外四个寄存器为通用(在32位和16位机器上都可以用)寄存器。然而,当单独使用其低四位的时候,一旦结果多于8位,仍然会发生溢出。

    12.理解操作数的三种类型:立即数(不超过32位的数值)、寄存器(用Ea表示任意寄存器a,R[Ea]表示它的值)、存储器(会根据计算出来的地址访问某个内存,用M[addr]表示)

    13.寻址方式的通用公式:

    • 有效地址可以表示为Imm+R[Eb]+R[Ei]*s。Imm为立即数偏移;Eb为基址寄存器;Ei为变址寄存器;s为比例因子。如:
      1.Ea——操作数值:R[Ea]
      2.(Ea)——操作数值:M(R[Ea])
      3.Imm(Ea)——操作数值:M(Imm+R[Ea])

    14.mov语句表示将值从源操作数“移给”目的操作数(前者在前,后者在后),相当于赋值。在IA32中规定不能从内存地址直接MOV到另一个内存地址,要用寄存器进行中转。其中,MOVS是符号扩展;MOVZ是零扩展。
    1.MOV:将源操作数的值复制到目的操作数中;
    2.MOVS:将一个较小的源数据复制到一个较大的数据位置,高位用位扩展;
    3.MOVZ:将一个较小的源数据复制到一个较大的数据位置,高位用零扩展。

    15.push,pop语句:
    1.push:把数据压入栈中;
    2.pop:删除数据。
    3.栈遵循“后进先出”的原则,且栈顶向下增长;在栈指针%esp中保存着栈顶元素的指针。
    16.leal,加载有效地址;将数据从存储器读到寄存器

    1.NEG,取负
    2.SUB S,D,将D-S的结果送至D
    3.移位操作 SAL,SHL,SAR,SHR的移位量可以是立即数或%cl中的数

    17.除了leal外,其他逻辑操作都会设置条件码。另外,有些操作只设置条件码而不将结果送至操作数——CMP:比较指令,与SUB类似;TEST指令,与AND类似(当两数相等的时候,会将条件码都设置成0)

    18.SET类指令根据t=a-b的结果所设置的条件码来将一个字节(目的操作数)设置为0或者1

    19.跳转指令:

    • 1.无条件跳转——jmp.<标号> 跳转到标号所指示的语句处;jmp *<操作数指示符> 【注意:如果形如%eax,即以%eax中的值作为跳转目标;而形如(%eax)则是以其中的值作为地址,读出跳转目标】
    • 2.有条件跳转——类似于SET类指令,是根据条件码或者其组合来跳转

    20.do-while语句等价的goto语句

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

    21.while语句等价的goto语句

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

    22.for循环的流程:程序首先对初试表达式init-expr求值,然后进入循环;在循环中它先对测试条件test-expr求值,如果为假则退出循环否则执行循环体;最后对更新表达式求值。

    23.switch语句根据一个整数索引值进行多重分支;通过使用跳转表使其更加高效。跳转表是一个数组,表项i是一个代码段地址(C语言用&表示一个指向数据值的指针;而&&表示一个指向代码位置的指针)

    24.IA32利用程序栈来支持过程调用(包括将数据和控制)。为单个过程分配的那部分栈做栈帧。最底端(地址最大)%ebp为帧指针;最顶端(地址最小)%esp为栈指针。当程序执行时,栈指针可以移动。

    25.转移控制

    • 1.call指令:后接被调用过程的起始的指令地址。效果是将返回地址入栈,并跳转到被调用过程的起始处。
    • 2.ret指令:从栈中弹出地址,并跳转到这个位置。

    26.编译器根据一组很简单的惯例产生管理栈结构的代码。参数在栈上传递给函数,可以从栈中相对于%ebp的正偏移量来访问它们。可以用push指令或者是从栈指针中减去偏移量来分配在栈上的空间。

    注意:
    1.当带选项-S和-O1运行gcc时,会产生xxx.s文件,其中带有'.'开头的行是指导汇编器和链接器的命令。
    2.gcc -S产生的汇编代码中可以把以'.'开头的语句删除再使用也没关系。
    3.了解Linux和windows的汇编格式的区别,Intel代码省略了指示大小的后缀,即'l';Intel代码省略了寄存器名字前面的'%'符号,用的是esp,而不是%esp。

    代码编写练习题总结

    1.练习题3.3 指出错误

    movb $0xf,(%bl) ---目的操作数只能是一个寄存器或者一个存储器地址。(%bl)表示一个值
    movw (%eax),4(%esp)---目的操作数与源操作数不能都是存储器
    movb %si, 8(%ebp)---指令后缀与寄存器地址不匹配
    注:寻址要符合以下任意一条:

    1.立即寻址方式 MOV AH, 80H(直接给寄存器赋值)
    2. 寄存器寻址方式(源或者目的有一个是寄存器)
    2.1) 源操作数是寄存器寻址方式
    如:ADD VARD, EAX  ADD VARW, AX   MOV VARB, BH等。
    其中:VARD、VARW和VARB是双字,字和字节类型的内存变量。
    2.2) 目的操作数是寄存器寻址方式
    如:ADD BH, 78h    ADD AX, 1234h   MOV EBX, 12345678H等。
    2.3) 源和目的操作数都是寄存器寻址方式
    如:MOV EAX, EBX   MOV AX, BX     MOV DH, BL等。
    3. 直接寻址方式(地址值在括号里)
    MOV BX, [1234H] (默认使用DS)
    MOV ES:[1000H], AX
    4. 寄存器间接寻址方式(寄存器在括号里)
    MOV BX,[DI]
    操作数的有效地址用SI、DI、BX和BP等四个寄存器之一来指定,称这种寻址方式为寄存器间接寻址方式。
    若有效地址用SI、DI和BX来指定,则其缺省的段寄存器为DS;
    若有效地址用BP来指定,则其缺省的段寄存器为SS(即:堆栈段)。
    5. 寄存器相对寻址方式(一个寄存器和一个立即数在括号里,并且还要计算)
    MOV BX, [SI+100H]
    6. 基址加变址寻址方式(两个寄存器在括号里,并且还要计算)
    MOV BX, [BX+SI]
    7. 相对基址加变址寻址方式(两个寄存器在括号里和一个立即数在括号里,并且还要计算)
    MOV AX, [BX+SI+200H]
    

    2.练习题3.14 考虑以下C语言代码:

    int test(data_t a)
    {
        return a TEST 0;
    }
    

    根据以下每条指令序列,确定哪种数据类型和比较TEST会使编译器产生这样的代码?

    A. testl %eax,%eax setne %al

    [既然是l,表明是32位,且data_t可以是int,unsigned和指针;而ne表示比较类型是 !=,对有无符号的数字都成立;对于unsigned,比较还可以是>]

    B. testb %al,%al setg %al

    [既然是b,表明是16位,且比较是 == ;则data_t应该是short或者unsigned short]

    3.练习题3.16 已知下列C语言代码:

    void cond(int a,int *p)
    {
        if(p&&a>0)
            *p +=a;
    }
    

    按照与汇编代码等价的C语言goto版本,写一个与之等价的C语言代码。 答:

    void goto_cond(int a,int *p)
    {
        if(p == 0)
            goto done;
        if(a<=0)
            goto done;
        *p +=a;
        done:
            return;
    }
    

    4.根据P148图3-20的汇编代码,填写补充C源代码

    int switcher(int a,int b,int c)
    {
        int answer;
        switch(a)
        {
            case 5 :
                c = b ^ 15;
            case 0 :
                answer = c+112;
            case 2 :
            case 7 :
                answer = c+6;
                break;
            case 4 :
                answer = a;
                break;
            default :
                answer = b;
        }
        return answer;
    }
    

    5.练习题3.30

    call next
    next:
    popl %eax
    

    这段代码是IA32中将程序计数器中的值放到整数计数器中的唯一办法

    6.实验楼代码练习

    • 使用VIM编写代码
    • 查看汇编文件
    • ⑤修改汇编代码并另存

    本周代码托管截图

    补充

    反汇编
    寄存器使用惯例
    程序寄存器组是唯一能被所有过程共享的资源。虽然在给定时刻只能有一个过程是活动的,但是我们必须保证当一个过程(调用者)调用另一个过程(被调用者)时,被调用者不会覆盖某个调用者稍后会使用的寄存器的值。根据惯例,寄存器%eax、%edx和%ecx被划分为调用者保存寄存器。当过程P调用Q时,Q可以覆盖这些寄存器,而不会破任何P所需要的数据。另一方面,寄存器%ebx、%esi和%edi被划分为被调用者保存寄存器。这意味着Q必须在覆盖这些寄存器之前,先把它们保存到栈中,并在返回前恢复它们。

    • %esp:寄存器存放当前线程的栈顶指针
    • %ebp:寄存器存放当前线程的栈底指针
    • %eip:寄存器存放下一个CPU指令存放的内存地址,当CPU执行完当前的指令后,从EIP寄存器中读取下一条指令的内存地址,然后继续执行。

    实验楼的代码反汇编分析

    g:
         pushl   %ebp            //保存现场,将父函数的栈底寄存器存入当前程序栈中
         movl    %esp, %ebp      //构建当前函数堆栈
         movl    8(%ebp), %eax   //从父函数堆栈中取得参数,存入ax寄存器
         addl    $3, %eax        //完成+3操作
         popl    %ebp            //恢复原父函数堆栈
         ret                     //g函数所占用的栈帧“消失”
    f:
         pushl   %ebp            //保存现场,将父函数的栈底寄存器存入当前程序栈中
         movl    %esp, %ebp      //构建当前函数堆栈
         pushl   8(%ebp)         //压栈内存空间
         call    g               //调用g
         addl    $4,%esp         //数据压入堆栈,栈顶指针esp减少4字节
         leave                   //清理局部变量空间
         ret                     //返回,f函数所占用的栈帧“消失”
    main:
         pushl   %ebp            //保存现场,将父函数的栈底寄存器存入当前程序栈中
         movl    %esp, %ebp      //构建当前函数堆栈
         pushl   $8              //压栈
         call    f               //调用f
         addl    $4,%esp         //数据压入堆栈,栈顶指针esp减少4字节
         addl    $1, %eax        //完成+1操作`
         leave                   //清理局部变量空间
         ret                     //返回,main函数所占用的栈帧“消失”
    

    GDB调试,在main函数设置断点

    在这一步遇到了问题,因为之前-m32命令出现错误,我就没有使用-m32指令,得到的汇编代码是64位的
    解决方式:参考本博客,两种方法都可以设置:
    在linux64机上编译32位的mongodb

    • 查看寄存器的值

    • 用gdb调试汇编代码
      disassembler $pc
      display/i $pc
      x/i $pc


    • 结合上图列表分析

    代码(以main函数部分为例) 代码涵义 变化
    pushl %ebp 保存现场,将父函数的栈底寄存器存入当前程序栈中 主函数栈基址0Xffffd058
    movl %esp, %ebp 构建当前函数堆栈
    pushl $8 压栈 将参数压入栈
    call f 调用f 将返回地址入栈,并跳到目标地址
    addl $4,%esp 数据压入堆栈 栈顶指针esp减少4字节
    addl $1, %eax 完成+1操作 根据表达式return mid(9)+1,最后需加1再返回
    leave 清理局部变量空间,main函数所占用的栈帧“消失” 将保存的%ebp的值交由%esp,然后弹出保存的%ebp
    ret 这是取出EIP,返回到调用处

    推荐一个关于栈帧的博客:

    学习进度条

    代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
    目标 5000行 30篇 400小时
    第一周 0/0 1/2 10/10
    第二周 300/300 1/3 20/30
    第三周 300/600 2/5 16/46
    第五周 300/900 1/6 15/61

    参考资料

  • 相关阅读:
    C/C++多文件之间的变量定义
    PKU POJ 2186 Popular Cows 强连通分量
    重载函数
    ZOJ 2763 Prison Break
    201357 训练赛总结
    hdu 4467 Graph 构造
    201356 训练赛总结
    201353 NEERC 2012, Eastern subregional contest
    2013512 CF 183 总结
    一道动态规划
  • 原文地址:https://www.cnblogs.com/alovera/p/5954743.html
Copyright © 2011-2022 走看看