• 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

    参考资料

  • 相关阅读:
    win10下查看进程,杀死进程
    Pycharm,debug调试时怎样带参数
    struts2,登录功能模块实现
    struts2处理.do后缀的请求
    struts2 修改action的后缀
    j2ee中如何拦截jsp页面?
    4个好用的JS联动选择插件
    css position:absolute 如何居中对齐
    使用jquery插件报错:TypeError:$.browser is undefined的解决方法
    phpcms v9后台多表查询分页代码
  • 原文地址:https://www.cnblogs.com/alovera/p/5954743.html
Copyright © 2020-2023  润新知