将汇编代码逆向工程为 C

Reverse engineering assembly code to C

有没有大神帮我拆解下面的shell代码(在评论区),并解释一下最后一行代码的作用?

# include <stdlib .h>
# include <stdio .h>
# include <string .h>
const char code [] =
  "\x31\xc0"       /* Line  1: xorl  %eax, %eax */
  "\x50"           /* Line  2: pushl %eax */
  "\x68""// sh"    /* Line  3: pushl [=10=]x68732f2f */
  "\x68""/bin"     /* Line  4: pushl [=10=]x6e69622f */
  "\x89\xe3"       /* Line  5: movl  %esp, %ebx */
  "\x50"           /* Line  6: pushl %eax */
  "\x53"           /* Line  7: pushl %ebx */
  "\x89\xe1"       /* Line  8: movl  %esp, %ecx */
  "\x99"           /* Line  9: cdq */
  "\xb0\x0b"       /* Line 10: movb  [=10=]x0b, %al */
  "\xcd\x80"       /* Line 11: int   [=10=]x80 */;

  int main (int argc , char ** argv ) {
     char buf [ sizeof ( code )];
     strcpy (buf , code );
     (( void (*)( )) buf )( ); /*I don't understand what this line is for*/
  }

我阅读了一些关于 gdb 的内容,但我不知道如何在这种情况下使用它。

P.S : 我在 x86-32 Ubuntu Linux 机器上。

您已经有了内联代码的反汇编,它在评论中。所以那部分有点混乱。

最终语句将buf转换为函数指针,并调用代码。这是漏洞利用的实际用途。它也是(据我所知)未定义的行为,并且不会在防止执行随机内存区域的环境中工作。

C 可以做到这一点,但据我所知,没有反向编译器可以解决这个问题。

此代码进行系统调用。这就是 int [=11=]x80 的意思。 前面的行说它是系统调用 11。 其余指令为其设置参数。

xorl  %eax, %eax     ; clear the 32-bit A register to 0
pushl %eax           ; push 32-bit 0 on the stack

pushl [=10=]x68732f2f    ; push two 32-bit constants on the stack
pushl [=10=]x6e69622f

movl  %esp, %ebx     ; copy the stack pointer into the 32-bit B register
pushl %eax           ; push another 0
pushl %ebx           ; push the stack pointer
movl  %esp, %ecx     ; copy the stack pointer into the 32-bit C register
cdq                  ; A already has 0. This also sets D to zero, 32-bit

movb  [=10=]x0b, %al     ; move a constant 11 into the lower 8 bits of the A register
int   [=10=]x80          ; and do the system call

如果此代码是为某些邪恶目的而设计的,我不会感到惊讶。 它是一种在程序中引入一些未知代码,并让计算机执行它的方法。

程序集所做的是构建一个参数列表以传递给操作系统调用以执行二进制文件 (/bin/sh)。

这两行将 /bin/sh 压入堆栈。

pushl [=10=]x68732f2f
pushl [=10=]x6e69622f

这些行根据 calling convention.

将指向 /bin/sh 的指针放置在参数寄存器中
movl  %esp, %ebx
pushl %eax
pushl %ebx
movl  %esp, %ecx
cdq

此行将系统调用号 11 作为中断的参数放入寄存器中。

movb  [=12=]x0b, %al

最后一行调用操作系统。

int   [=13=]x80

C 代码中的这一行只是将缓冲区转换为函数指针,然后调用该函数。

(( void (*)( )) buf )( );

请注意,这曾经是利用软件的一种常见方法,但是可以使用类似的方法JIT编译代码。

我首先想到第 3 行和第 4 行正在设置 /bin/sh,但后来又想到它可能是 sh /bin/X 对于某些我不太明白的 X。事实上 // sh 部分看起来几乎像一个评论??

我的直觉是第 11 行的系统调用是 exec 系统调用,前面几行正在为 exec 调用设置 args。

我敢猜测您要小心 运行 此代码 ;)

您可能想研究 Hex-Rays 反汇编程序,它可以(某种程度上)将机器 code/assembler(两者等价)转换为 C。

代码很可能正在调用 sys_execve() 内核函数

const char code [] =
  "\x31\xc0"       /* Line  1: xorl  %eax, %eax */
  "\x50"           /* Line  2: pushl %eax */
  "\x68""// sh"    /* Line  3: pushl [=10=]x68732f2f */
  "\x68""/bin"     /* Line  4: pushl [=10=]x6e69622f */
  "\x89\xe3"       /* Line  5: movl  %esp, %ebx */
  "\x50"           /* Line  6: pushl %eax */
  "\x53"           /* Line  7: pushl %ebx */
  "\x89\xe1"       /* Line  8: movl  %esp, %ecx */
  "\x99"           /* Line  9: cdq */
  "\xb0\x0b"       /* Line 10: movb  [=10=]x0b, %al */
  "\xcd\x80"       /* Line 11: int   [=10=]x80 */;
  1. 第 1 行将 eax 寄存器设置为零,将用作 空字符串终止符
  2. 第 2、3 和 4 行使用堆栈作为临时缓冲区来存储以空字符结尾的 ASCII 字符串“/bin/sh”;由于堆栈按递减顺序增长,因此字符被颠倒,这就是为什么先压入终止符的原因。
  3. 第 5 行正在加载 into ebx 堆栈指针的当前值,此时指向我们在前面步骤中形成的字符串的第一个字符的地址。 Ebx 是 sys_execve() 期望带有文件的字符串为 运行.
  4. 的寄存器
  5. 第 6 行和第 7 行再次使用堆栈作为 用于保存 空终止 ASCII 字符串
  6. 的临时缓冲区
  7. 第 8 行正在将此空字符串的地址加载到 ecx,其中 sys_execve() 需要命令行参数
  8. 第 9 行,不知道它的用途
  9. 第 10 行,将十进制值 11 加载到 AL 寄存器,指定 sys_execve() 函数是我们希望调用的函数
  10. 第 11 行触发中断 0x80 - 它的处理程序是实际为我们执行 sys_execve() 调用的代码