汇编语言基本指令与phase(1)解题过程

Posted by SGQ on March 22, 2020

一、汇编指令介绍

1.数据传送指令(movl)

enter description here

其中: Imm表示操作数,Reg表示寄存器,Mem表示存储器, (%eax)表示%eax寄存器的内存地址。

注意:不能两个操作数都为内存地址;

总结:

  • movl %eax, %ebx 意思是把32位的EAX寄存器值传送给32为的EBX寄存器值。
  • 其中%eax可理解成c语言的一个变量temp,(%eax)这种形式的可理解成一个指针变量*p。
  • 若遇到8(%ebp) ,可理解地址[ebp+8],%ebp表示起始地址,8可理解为偏移量。
  • 指令格式多采用小写, 寄存器名的前面加”%”前缀,立即数前加”$”前缀。
  • 很多指令带后缀(如上面代码中的 movl,addl中的l就是后缀),用来表示指令访问的操作数的宽度。对于32位的机器,后缀”b”表示8位,”w”表示16位,”l”表示32位。

2.cmp指令

  • cmpl b, a
  • 相当于计算a-b,但并不保存结果
  • 主要是为了设置标志寄存器的标志位
  • 比如如果a==b,则ZF被设置

3.jmp:无条件跳转

4.jz/jnz:如果a==b/a!=b则跳转

  • je 或jz 若相等则跳转(机器码74 或0F84)

5.call指令与ret指令

  • call指令:保存现场(返回地址,标志寄存等 )并跳转。
  • ret指令:得到返回地址并返回。类似于return 。

6.sub指令

  • 比如: sub $0x10,%esp
    表示 为esp分配了16个字节空间. 可理解为c语言中的 int a ;进行初始化。

7.test指令

  • 将两个操作数进行逻辑与运算,并根据运算结果设置相关的标志位。
  • Test的一个非常普遍的用法是用来测试一方寄存器是否为空:

    test ecx, ecx jz somewhere 如果ecx为零,设置ZF零标志为1,Jz跳转

二、 题目分析

     08048b90 <phase_1>: 
        sub  $0x1c,%esp  //为phase_1函数的栈分配28字节的空间

        mov    $0x804a204, 0x4(%esp)//将地址0x804a204中存的东西入栈。
		
        mov    0x20(%esp), %eax   //把%ebp+0x20位置存放的东西赋给%eax。
        mov    %eax,     (%esp) //把%eax存到%esp指向的位置,即栈顶
     	call   80490da   <strings_not_equal>  //调用这个函数
        test   %eax,     %eax        
        je     8048bb0   <phase_1+0x20>  //test 与je一起用,进行判断是否%eax为0,如果为0,就执行下一个函数。
      	call    80491e5   <explode_bomb> //执行explode_bomb函数
        add    $0x1c,     %esp   //返回栈顶
        ret        
		
  • ` mov 0x20(%esp), %eax `
    mov %eax, (%esp) 最开始不太明白为什么要这样赋值,0x20(%esp)中的应该是存放的用户输入,为什么先赋给%eax,再把%eax存放到%esp指向的位置。 后来我知道了,可见上面的注意: 不能两个操作数都为内存地址; * call 80490da <strings_not_equal> 根据函数名可知,这应该是判断两个字符串是否相等的函数,如果不相等返回1,如果相等返回0;具体函数代码还没有仔细分析,明天接着看!
  • test %eax, %eax
    je 8048bb0 <phase_1+0x20> 这两个语句往往搭配一起使用,如果%eax为0,设置ZF零标志为1,Je跳转,就是跳转到下一个程序的地址,即执行下一个phase程序。
  • call 80491e5 <explode_bomb> 上面跳转条件不成立时,即%eax不为0,就执行explode_bomb,根据名字可知这是一个引爆程序。

总结

解决此题的关键是找出0x804a204存放的字符串,然后输入一个与其相同的,即可进入下一个phase程序。

三、gdb调试器的使用

  • 输入gdb bomb 即可加载bomb程序

  • enter description here

  • enter description here

可以使用examine命令(简写是x)来查看内存地址中的值。x命令的语法如下所示:

x/<n/f/u> <addr> n、f、u是可选的参数。

  • n是一个正整数,表示需要显示的内存单元的个数,也就是说从当前地址向后显示几个内存单元的内容

  • f 表示显示的格式,参见下面。
  • u 表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4个bytes。u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字 节,g表示八字节。当我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来

x系列
x/xw addr 显示某个地址处开始的16进制内容,如果有符号表会加载符号表
x/x $esp 查看esp寄存器中的值
x/s addr 查看addr处的字符串
x/b addr 查看addr处的字符
x/i addr 查看addr处的反汇编结果

四、遇到问题:

我发现 ubuntu命令行下 输入./无法打开bomb可执行文件,但是我ls查看明明存在bomb程序,可执行文件存在但是不能执行,一般都是报错No such file or directory觉得很奇怪,怎么也发现不了问题所在,最后我找到了原因:
在64位系统上,出问题的可执行文件是32位的。通过file命令可以看到这个可执行程序的位数,通过ldd可以查看这个可执行文件需要的动态链接库,比如:

# ldd xxx
 linux-gate.so.1 (0xf7f44000)
 libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7d4b000)
 /lib/ld-linux.so.2 (0xf7f46000)

如果这个文件却是存在,但是不能执行,而文件又没有损坏,那很可能就是因为依赖的动态库不存在,一般是这个:

 /lib/ld-linux.so.2

有这个库才能在64位的系统上执行32位的程序,所以根据你的系统,把这个库安装上即可。命令就是:

sudo dpkg --add-architecture i386
sudo apt install libc6:i386


五、参考链接:

嵌入式Linux

C语言中的函数调用,栈的使用

汇编语言基本指令

Ubuntu GDB调试

Ubuntu可执行文件存在但是无法执行

gdb的调试与使用

GDB下查看内存命令(x命令)