基本的nasm shellcode崩溃

时间:2016-07-29 01:29:25

标签: linux assembly nasm system-calls ld

我正在学习汇编,所以我正在研究shellcode。我写了一个简单的"你好世界"程序在nasm中,但在运行时崩溃。

; write.asm
[SECTION .text]

global _start

_start:
    jmp short message;

write:          ; takes two arguments pushed onto stack -> text, textlen
    pop edx     ; pop length into edx
    pop ecx     ; pop ptr to text into ecx
    pushad
    mov al, 4
    mov bl, 1
    int 80h    ; syscall
    popad
    ret

exit:           ; push exit_code onto stack
    mov al, 1
    pop ebx     ; pop exit_code into ebx
    int 80h

main:
    pop eax     ; pop ptr to message into eax
    push 7      ; length of string
    push eax    ; push ptr to message
    call write
    xor ebx, ebx; zero out ebx
    push ebx
    call exit
message:
    call main
    db 'Hello!', 10

我用以下代码编译它:

nasm -f elf write.asm
ld -m elf_i386 -o write write.o

得到这个:

Segmentation fault (core dumped)

我尝试用gdb调试它,但它没有帮助。

1 个答案:

答案 0 :(得分:3)

我想向您展示如何使用gdb找到您的问题:

  1. 启动gdb并加载您的程序:>> gdb write
  2. 直接在开始时设置断点:(gdb) b _start。您可以让程序运行直到段错误,但如果堆栈搞砸了,则可能性很高,您将看不到任何内容。
  3. 设置显示以在每个步骤后自动在堆栈上显示5个最高值:(gdb) display/x {unsigned int[5]}$sp
  4. 要查看下一个执行的行:(gdb) display/i $pc
  5. 现在运行:(gdb) run
  6. 调试器命中断点,您将看到系统设置的堆栈:

    1: /x {unsigned int[5]}$sp = {0x1, 0xffffd284, 0x0, 0xffffd29d, 0xffffd2b2}
    

    一切都好到目前为止,程序以一个参数(顶部为0x1)启动 - 程序的路径(您可以通过(gdb) print (char[10])*(0xffffd284)看到它)。

    1. 迈出一步(gdb) si,现在我们跳转到message符号。
    2. 使用(gdb) disas可以看到更多代码:

      (gdb) disas
      Dump of assembler code for function message:
      => 0x08048083 <+0>: call   0x8048072 <main>
         0x08048088 <+5>: dec    %eax
         0x08048089 <+6>: gs
         0x0804808a <+7>: insb   (%dx),%es:(%edi)
         0x0804808b <+8>: insb   (%dx),%es:(%edi)
         0x0804808c <+9>: outsl  %ds:(%esi),(%dx)
         0x0804808d <+10>:    and    %ecx,(%edx)
      End of assembler dump.
      

      如您所见,您的“Hello”字符串被解释为从0x08048088开始的操作

      1. 再做一步:(gdb) si,进入函数main
      2. 现在看一下堆栈:

         1: /x {unsigned int[5]}$sp = {0x8048088, 0x1, 0xffffd283, 0x0, 0xffffd29c}
        

        call - 指令推送堆栈上的返回地址 - 0x08048088 - 字符串的地址。好的伎俩,让我们希望main永远不会回来......

        1. 让我们快速拨打write (gdb) si 3的电话,然后进入(gdb) si
        2. 让我们看看堆栈,正如预期的那样,call将返回地址添加到堆栈中:

          1: /x {unsigned int[5]}$sp = {0x804807b, 0x8048088, 0x7, 0x1, 0xffffd283}
          
          1. 您的程序期望指向字符串的指针和长度位于顶部,但事实并非如此。
          2. 让我们快速返回函数write(gdb) si 7
          3. 看看堆栈:

            1: /x {unsigned int[5]}$sp = {0x7, 0x1, 0xffffd283, 0x0, 0xffffd29c}
            

            下一个操作 - ret将从堆栈弹出0x7并尝试在地址0x7处执行,这会导致段错误(如可疑的rcd)。

            所以你的问题是你的函数破坏了堆栈。通常情况下,为呼叫设置堆栈的功能也负责在之后进行清理。