硬件上机第五次实验

2017-11-26  本文已影响0人  Karen_Duane

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指令是将两个操作数做与来设置零标志位和负数标志(状态码),常用方法(也是本题的方法)为<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;
}

知识要点分析


拓展题

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 查看地址的命令

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)

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函数的运行逻辑

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

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

上一篇下一篇

猜你喜欢

热点阅读