美文网首页
硬件上机第五次实验

硬件上机第五次实验

作者: Karen_Duane | 来源:发表于2017-11-26 17:20 被阅读0次

    LAB5


    3.56

    题目描述

    consider the following assembly code
    x at %ebp+8, n at %ebp+12

      movl 8(%ebp), %esi
      movl 12(%ebp), %ebx
      movl $1431655765, %edi
      movl $-2147483648, %edx
    .L2:
      movl %edx, %eax
      andl %esi, %eax
      xorl %eax, %edi
      movl %ebx, %ecx
      shrl %cl, %edx
      testl %edx, %edx
      jne .L2
      movl %edi, %eax
    

    the preceding code was generated by compiling C code that had the following overall from:

    int loop(int x,int n){
        int result = 0x55555555;
        int mask;
        for(mask = -0x80000000;mask!=0;mask = mask>>n){
            result ^= (mask&x);
        }
        return result;
    }
    

    思考问题

    1. Which registers hold program values x,n,result, and mask?

      %esi : x %ebx : n
      %edi : result %edx : mask

    2. What are the initial values of result and mask?

      result = 1431655765
      mask = -2147483648

    3. What are the test condition for mask?

      test condition == ~ZF & SF when the loop continue

    4. How does mask get updated?

      shrl %cl, %edx

    5. How does result get updated?

      xorl %eax, %edi

    6. Fill in all missing parts of the C code?

      as show in the C code

    知识要点分析

    • testl命令的应用

    testl指令是将两个操作数做与来设置零标志位和负数标志(状态码),常用方法(也是本题的方法)为<code>testl %eax, %eax</code>
    这种方法用来检查%eax是正数、负数还是零。
    另外,该命令不改变%%eax的值,而是设置状态码

    • 数据寄存器

    数据寄存器主要用来保存操作数和运算结果等信息。
    32位CPU有4个32位的通用寄存器,%eax、%ebx、%ecx、%edx,对于低16位的存取,不会影响高16位数据的存取,这几个低16位的寄存器分别命名为%ax、%bx、%cx、%dx,而4个16位寄存器又可分割为8个独立的8位寄存器(AX:AH-AL、BX:BH-BL、CX:CH-CL、DX:DH-DL),每个寄存器都有自己的名称,可独立存取。
    八位寄存器的功能

    • %ax、%al 累加器(Accumulator) high using rate
    • %bx 基地址寄存器(Base Register) pointer to memory
    • %cx 计数寄存器(Count Register) control the times of the loop
    • %cl declare the bits of shifting 在位运算中声明移位的位数,%cl取%ecx的低十六位

    3.56(2)

    题目描述

    _Change the assembly code in 3.56 with the following code, then fill in all the missing parts of the C code

    x at %ebp+8, n at %ebp+12

      movl 8(%ebp), %esi
      movl 12(%ebp), %ebx
      movl $-1, %edi
      movl $1, %edx
    .L2:
      movl %edx, %eax
      andl %esi, %eax
      xorl %eax, %edi
      movl %ebx, %ecx
      sall %cl, %edx
      testl %edx, %edx
      jne .L2
      movl %edi, %eax
    

    the complete C code are:

    int loop(int x,int n){
        int result = -1;
        int mask;
        for(mask = 1;mask!=0;mask = mask<<n){
            result ^= (mask&x);
        }
        return result;
    

    3.59

    题目描述

    this problem will give you a chance to reverse engineer a switch statement from machine code. In the following procedure, the body of the switch statement has been removed, please use the assembly code and gdb fill the missing C code.
    all the info of disassemble code and gdb(x) has been omited.

    题目分析

    1. (gdb)x/6w 0x80485d0 就是switch的跳转表
    2. 根据题目反汇编代码按0-5的顺序查找对应的跳转位置即可

    C code

    int switch_prob(int x,int n){
        int result = x;
        switch(n){
          case 32:
          case 34:
             result = result<<2;
             break;
          case 33:
             result = result+12;
             break;
          case 35:
             result = result>>2;
             break;
          case 36:
             result = result*3;
             result = result*result;
             result = result + 12;
             break;
          case 37:
             result = result*result;
             result = result+12;
             break;
          default:
             result+=12;
        }
        return result;
    }
    

    知识要点分析

    • 跳转表
      gcc编译器中,如果switch语句中case的种数相对较多且值的变化范围较小,那么在汇编中将使用跳转表
      我们可以将跳转表理解为指针数组,数组的索引值与case值相关,指针指向要跳转的代码位置。

    拓展题

    problem one

    题目

    下面的代码片段常常出现在库函数的编译版本中:

       call next
    next:
       popl %eax
    

    A.寄存器%eax被设置成了什么值?

    %eax被设置为了popl指令的地址
    执行call next之前,PC(程序计数器) 为popl指令的地址(PC始终未下条指令地址);
    执行call next之后,%esp被设置为popl指令的地址(call指令的顺序下一条,即调用者返回的地址)
    popl指令使得%eax的值设置为M[R[%esp]],也就是将popl指令的地址放入了%eax中

    B.解释为什么这个调用没有与之匹配的ret指令

    这不是一个真正地过程调用,因为控制是按照与指令相同的顺序进行的,而返回值是从栈中弹出的,所以没有ret指令。

    C.这段代码完成了什么功能?

    将程序计数器的值放入整数寄存器

    D.call指令的伪汇编代码表示

    pushl CP
    jmp <address>

    preblem two

    题目描述

    运行程序a,给出输出结果

    运行结果

    Hello World!
    401353 PointTo?

    problem three & four

    题目描述

    反汇编main函数的汇编代码,观察main函数

    Dump of assembler code for function main:

       0x000005a0 <+0>: lea    0x4(%esp),%ecx
       0x000005a4 <+4>: and    $0xfffffff0,%esp
       0x000005a7 <+7>: pushl  -0x4(%ecx)
       0x000005aa <+10>:    push   %ebp
       0x000005ab <+11>:    mov    %esp,%ebp
       0x000005ad <+13>:    push   %ebx
       0x000005ae <+14>:    push   %ecx
       0x000005af <+15>:    call   0x5ff <x86.get_pc_thunk.ax>
       0x000005b4 <+20>:    add    $0x1a4c,%eax
       0x000005b9 <+25>:    sub    $0x8,%esp
       0x000005bc <+28>:    call   0x5c7 &lt;main+39>
       0x000005c1 <+33>:    bound  %ebp,0x6e(%ecx)
       0x000005c4 <+36>:    outsl  %ds:(%si),(%dx)
       0x000005c6 <+38>:    add    %ch,%al
       0x000005c8 <+40>:    sbb    (%eax),%al
       0x000005ca <+42>:    add    %al,(%eax)
       0x000005cc <+44>:    dec    %eax
       0x000005cd <+45>:    gs insb (%dx),%es:(%edi)
       0x000005cf <+47>:    insb   (%dx),%es:(%edi)
       0x000005d0 <+48>:    outsl  %ds:(%esi),(%dx)
       0x000005d1 <+49>:    and    %dl,0x6f(%edi)
       0x000005d4 <+52>:    jb     0x642 <libc_csu_init+50>
       0x000005d6 <+54>:    and    %ecx,%fs:(%edx)
       0x000005d9 <+57>:    and    $0x6f502058,%eax
       0x000005de <+62>:    imul   $0xa3f6f54,0x74(%esi),%ebp
       0x000005e5 <+69>:    add    %cl,-0x1ec173d(%ecx)
       0x000005eb <+75>:    (bad)  
       0x000005ec <+76>:    incl   0xb810c4(%ebx)
       0x000005f2 <+82>:    add    %al,(%eax)
       0x000005f4 <+84>:    add    %cl,0x5b59f865(%ebp)
       0x000005fa <+90>:    pop    %ebp
       0x000005fb <+91>:    lea    -0x4(%ecx),%esp
       0x000005fe <+94>:    ret
    
    1. 找到输出的地址指向的是什么
    2. 找到printf中的格式串的地址
    3. 解释为什么没有printf却有字符串打印出来了
    4. 请用合理的语言/图示来表示main函数的实际逻辑

    题目分析

    1. 使用gdb查看gdb运行结果中的地址

    (由于gdb禁用ASLR,所以可以这么做)

    Starting program: /home/vermouthdky/code_test/LAB4/a  
    Hello World!      
    565555C1 PointTo?  
    
    (gdb) x/s 0x565555C1
    0x565555c1 &lt;main+33>:    "bingo"
    

    发现运行结果中的地址指向一个字符串“bingo”

    2. 使用(gdb) x/ns 查看地址call 0x565555c7 <main+39>的字符串

    (gdb) x/94s main    
    

    打印部分输出结果

    0x565555c1 <main+33>: "bingo"
    0x565555c7 <main+39>: "\350\032"
    0x565555ca <main+42>: ""
    0x565555cb <main+43>: ""
    0x565555cc <main+44>: "Hello World!\n%X PointTo?\n"
    0x565555e6 <main+70>: "\211\303\350\023\376\377\377\203\304\020\270"

    得到printf格式串的地址 0x565555cc <main+44>

    3.使用(gdb)x/i 查看地址的命令

    • 查看格式字符串地址<main+44>上的一条指令call 0x5c7 <main+39>中的地址(gdb) x/4i main+39,得到输出:

    0x565555c7 <main+39>: call 0x565555e6 <main+70>
    0x565555cc <main+44>: dec %eax
    0x565555cd <main+45>: gs insb (%dx),%es:(%edi)
    0x565555cf <main+47>: insb (%dx),%es:(%edi)

    • 查看<main+70>的指令,得到输出:

    0x565555e6 <main+70>: mov %eax,%ebx
    0x565555e8 <main+72>: call 0x56555400 <printf@plt>
    0x565555ed <main+77>: add $0x10,%esp
    0x565555f0 <main+80>: mov $0x0,%eax

    由此我们知道,main函数在<main+70>处调用了<printf@plt>
    所以main函数实际上是调用了printf函数的,这就解释了为什么会有字符串打印出来

    4.说明main函数的运行逻辑

    • 下面是main的call指令与字符串的地址分布
    address content
    main ~ main+27 initialize
    main+28 goto main + 39
    main+33 ~ main+38 "bingo"(there is a '\0')
    main+44 ~ main+69 "Hello World!\n%X PointTo?\n"
    main+72 call <printf@plt>

    需要注意的是main+28处的call指令实际上起到了jmp的作用

    • main函数的运行逻辑

    main函数的运行逻辑实际上就是顺序的,只不过由于字符串的储存使得在顺序执行的时候用到了call跳转地址的情况。

    push "bingo"
    push "Hello World!\n%X PointTo?\n"
    call printf
    ret

    相关文章

      网友评论

          本文标题:硬件上机第五次实验

          本文链接:https://www.haomeiwen.com/subject/wtvcbxtx.html