Linux 64位shellcode

时间:2014-11-09 00:35:47

标签: assembly nasm shellcode

我正在尝试在我的Ubuntu 64位上编写我的第一个“Hello world”shellcode,但它不起作用。

我有文件 hello.asm

; 64-bit "Hello World!" in Linux NASM

global _start            ; global entry point export for ld

section .text
_start:

    ; sys_write(stdout, message, length)

    mov    rax, 1        ; sys_write
    mov    rdi, 1        ; stdout
    mov    rsi, message    ; message address
    mov    rdx, length    ; message string length
    syscall

    ; sys_exit(return_code)

    mov    rax, 60        ; sys_exit
    mov    rdi, 0        ; return 0 (success)
    syscall

section .data
    message: db 'Hello, world!',0x0A    ; message and newline
    length:    equ    $-message        ; NASM definition pseudo-instruction

我使用了这个命令:

nasm -felf64 hello.asm -o hello.o
ld -o hello hello.o
objdump -d hello

我将来自objdump的shellcode放入我的C程序中:

char code[] = "\xb8\x01\x00\x00\x00\xbf\x01\x00\x00\x00\x48\xbe\xd8\x00\x60\x00\x00\x00\x00\x00\xba\x0e\x00\x00\x00\x0f\x05\xb8\x3c\x00\x00\x00\xbf\x00\x00\x00\x00\x0f\x05";

int main(int argc, char **argv)
{
    int (*func)();
    func = (int (*)()) code;
    (int)(*func)();
    return 0;
}

并在gcc中编译它,但运行后我有“Segmentation fault(core dumped)”。

我不知道我做错了什么。汇编程序代码似乎有效,因为当我运行 ./ hello 时,它会输出“Hello world”。

4 个答案:

答案 0 :(得分:3)

如果要将其插入缓冲区,最好删除nullbytes,但主要问题可能是数据段中有字符串?

我像这样重写了它并让它发挥作用。

    global _start
    _start:
    jmp short string

    code:
    pop rsi
    xor rax, rax
    mov al, 1
    mov rdi, rax
    mov rdx, rdi
    add rdx, 14
    syscall

    xor rax, rax
    add rax, 60
    xor rdi, rdi
    syscall

    string:
    call code
    db  'Hello, world!',0x0A

$ nasm -felf64 hello.asm -o hello.o
$ ld -s -o hello hello.o
$ for i in $(objdump -d hello |grep "^ " |cut -f2); do echo -n '\x'$i; done; echo
\xeb\x1e\x5e\x48\x31\xc0\xb0\x01\x48\x89\xc7\x48\x89\xfa\x48\x83\xc2\x0e\x0f\x05\x48\x31\xc0\x48\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xdd\xff\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64\x21\x0a

char code[] = "\xeb\x1e\x5e\x48\x31\xc0\xb0\x01\x48\x89\xc7\x4\x89\xfa  \x48\x83\xc2\x0e\x0f\x05\x48\x31\xc0\x48\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xdd\xff\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64\x21\x0a";

int main(int argc, char **argv)
{
    int (*func)();
    func = (int (*)()) code;
    (int)(*func)();
     return 0;
}

$ gcc -fno-stack-protector -z execstack -o code code.c    
$./code
Hello, world!

答案 1 :(得分:1)

您可以简单地使用nasm -felf64进行编译并与ld相关联。不需要gcc。我相信您偶然发现的问题是代码中的x86样式equ $-message计算。对于x86_64,这是无效的。这是一个简单的例子:

section .data
    string1 db  0xa, "  Hello StackOverflow!!!", 0xa, 0xa, 0

section .text
    global _start

    _start:
        ; calculate the length of string
        mov     rdi, string1        ; string1 to destination index
        xor     rcx, rcx            ; zero rcx
        not     rcx                 ; set rcx = -1
        xor     al,al               ; zero the al register (initialize to NUL)
        cld                         ; clear the direction flag
        repnz   scasb               ; get the string length (dec rcx through NUL)
        not     rcx                 ; rev all bits of negative results in absolute value
        dec     rcx                 ; -1 to skip the null-terminator, ecx contains length
        mov     rdx, rcx            ; put length in rdx
        ; write string to stdout
        mov     rsi, string1        ; string1 to source index
        mov     rax, 1              ; set write to command
        mov     rdi,rax             ; set destination index to rax (stdout)
        syscall                     ; call kernel

        ; exit
        xor     rdi,rdi             ; zero rdi (rdi hold return value)
        mov     rax, 0x3c           ; set syscall number to 60 (0x3c hex)
        syscall                     ; call kernel

<强>编译/输出

$ nasm -felf64 -o hello-stack_64.o hello-stack_64.asm
$ ld -o hello-stack_64 hello-stack_64.o
$ ./hello-stack_64

  Hello StackOverflow!!!

答案 2 :(得分:0)

希望回答这个问题还为时不晚:)

您必须使用此代码编译C代码(禁用堆栈保护并使其可执行):

gcc -fno-stack-protector -z execstack -o hello hello.c
祝你好运

答案 3 :(得分:0)

这里存在两层问题。

1)char data[] = ...将被放入数据段,这是不可执行的。您需要const char data[] = ...,以便将其放入文本段中。 (从技术上讲,它将位于只读数据段中,可能被标记为不可执行,但据我所知目前没有人这样做。)< / p>

2)这是我在反汇编data

的内容时得到的结果
   0:   b8 01 00 00 00          mov    eax, 0x1
   5:   bf 01 00 00 00          mov    edi, 0x1
   a:   48 be d8 00 60 00 00    movabs rsi, 0x6000d8
  11:   00 00 00 
  14:   ba 0e 00 00 00          mov    edx, 0xe
  19:   0f 05                   syscall 
  1b:   b8 3c 00 00 00          mov    eax, 0x3c
  20:   bf 00 00 00 00          mov    edi, 0x0
  25:   0f 05                   syscall 

这是我尝试运行时得到的结果:

$ strace ./a.out
execve("./a.out", ["./a.out"], [/* 38 vars */]) = 0
write(1, 0x6000d8, 14)                  = -1 EFAULT (Bad address)
_exit(0)                                = ?
+++ exited with 0 +++

你可以看到 问题是您只将代码放入0x6000d8,而不是字符串。您需要在代码之后立即放置字符串,并使用PC相对寻址来获取它。 nighter的答案显示了这样做(在data下可能没有出现\x00字节的额外约束下,如果你实际上在这里利用了典型的缓冲区溢出,这将是必要的而不只是用机器语言搞笑。)