2013-03-23 163 views
23

我有以下工作NASM代碼:Linux Shellcode「Hello,World!」

global _start 

section .text 

_start: 
    mov eax, 0x4 
    mov ebx, 0x1 
    mov ecx, message 
    mov edx, 0xF 
    int 0x80 

    mov eax, 0x1 
    mov ebx, 0x0 
    int 0x80 

section .data 
    message: db "Hello, World!", 0dh, 0ah 

它打印 「!您好,世界\ N」 到屏幕上。我也有以下的C包裝包含以前NASM對象代碼:

char code[] = 
"\xb8\x04\x00\x00\x00" 
"\xbb\x01\x00\x00\x00" 
"\xb9\x00\x00\x00\x00" 
"\xba\x0f\x00\x00\x00" 
"\xcd\x80\xb8\x01\x00" 
"\x00\x00\xbb\x00\x00" 
"\x00\x00\xcd\x80"; 

int main(void) 
{ 
    (*(void(*)())code)(); 
} 

然而,當我運行的代碼,它似乎像彙編代碼不被執行,但是在程序退出的罰款。有任何想法嗎?

感謝

回答

63

當你注入這個shellcode的,你不知道什麼是message

mov ecx, message 
在注射過程中

,它可以是任何東西,但它不會是"Hello world!\r\n",因爲它是在數據部分,而您僅轉儲文本部分。你可以看到你的shellcode沒有"Hello world!\r\n"

"\xb8\x04\x00\x00\x00" 
"\xbb\x01\x00\x00\x00" 
"\xb9\x00\x00\x00\x00" 
"\xba\x0f\x00\x00\x00" 
"\xcd\x80\xb8\x01\x00" 
"\x00\x00\xbb\x00\x00" 
"\x00\x00\xcd\x80"; 

這是shellcode的發展普遍存在的問題,解決它的方法是這樣的:

global _start 

section .text 

_start: 
    jmp MESSAGE  ; 1) lets jump to MESSAGE 

GOBACK: 
    mov eax, 0x4 
    mov ebx, 0x1 
    pop ecx   ; 3) we are poping into `ecx`, now we have the 
        ; address of "Hello, World!\r\n" 
    mov edx, 0xF 
    int 0x80 

    mov eax, 0x1 
    mov ebx, 0x0 
    int 0x80 

MESSAGE: 
    call GOBACK  ; 2) we are going back, since we used `call`, that means 
         ; the return address, which is in this case the address 
         ; of "Hello, World!\r\n", is pushed into the stack. 
    db "Hello, World!", 0dh, 0ah 

section .data 

現在轉儲文字部分:

$ nasm -f elf shellcode.asm 
$ ld shellcode.o -o shellcode 
$ ./shellcode 
Hello, World! 
$ objdump -d shellcode 

shellcode:  file format elf32-i386 


Disassembly of section .text: 

08048060 <_start>: 
8048060: e9 1e 00 00 00 jmp 8048083 <MESSAGE> 

08048065 <GOBACK>: 
8048065: b8 04 00 00 00 mov $0x4,%eax 
804806a: bb 01 00 00 00 mov $0x1,%ebx 
804806f: 59    pop %ecx 
8048070: ba 0f 00 00 00 mov $0xf,%edx 
8048075: cd 80   int $0x80 
8048077: b8 01 00 00 00 mov $0x1,%eax 
804807c: bb 00 00 00 00 mov $0x0,%ebx 
8048081: cd 80   int $0x80 

08048083 <MESSAGE>: 
8048083: e8 dd ff ff ff call 8048065 <GOBACK> 
8048088: 48    dec %eax     <-+ 
8048089: 65    gs        | 
804808a: 6c    insb (%dx),%es:(%edi)   | 
804808b: 6c    insb (%dx),%es:(%edi)   | 
804808c: 6f    outsl %ds:(%esi),(%dx)   | 
804808d: 2c 20   sub $0x20,%al     | 
804808f: 57    push %edi      | 
8048090: 6f    outsl %ds:(%esi),(%dx)   | 
8048091: 72 6c   jb  80480ff <MESSAGE+0x7c> | 
8048093: 64    fs        | 
8048094: 21    .byte 0x21      | 
8048095: 0d    .byte 0xd      | 
8048096: 0a    .byte 0xa      <-+ 

$ 

標誌着我的線是我們的"Hello, World!\r\n"字符串:

$ printf "\x48\x65\x6c\x6c\x6f\x2c\x20\x57\x6f\x72\x6c\x64\x21\x0d\x0a" 
Hello, World! 

$ 

因此,我們的C包裝將是:

char code[] = 

    "\xe9\x1e\x00\x00\x00" //   jmp 8048083 <MESSAGE> 
    "\xb8\x04\x00\x00\x00" //   mov $0x4,%eax 
    "\xbb\x01\x00\x00\x00" //   mov $0x1,%ebx 
    "\x59"     //   pop %ecx 
    "\xba\x0f\x00\x00\x00" //   mov $0xf,%edx 
    "\xcd\x80"    //   int $0x80 
    "\xb8\x01\x00\x00\x00" //   mov $0x1,%eax 
    "\xbb\x00\x00\x00\x00" //   mov $0x0,%ebx 
    "\xcd\x80"    //   int $0x80 
    "\xe8\xdd\xff\xff\xff" //   call 8048065 <GOBACK> 
    "Hello wolrd!\r\n";  // OR  "\x48\x65\x6c\x6c\x6f\x2c\x20\x57" 
          //   "\x6f\x72\x6c\x64\x21\x0d\x0a" 


int main(int argc, char **argv) 
{ 
    (*(void(*)())code)(); 

    return 0; 
} 

讓我們測試一下:

$ gcc test.c -o test 
$ ./test 
Hello wolrd! 
$ 

它的工作原理。

+2

不知道爲什麼你沒有得到任何upvotes,但這是一個很好的答案。謝謝您的幫助。 – 2013-05-03 13:48:31

+0

空字節應該被移除以執行shellcode tho – REALFREE 2013-10-14 04:14:25

+2

@REALFREE如果您使用函數需要空終止字符串(如字符串函數爲「strcpy」),則空字節將會成爲問題,它將不會讀取整個shellcode字符串。否則就沒關係。 – 2013-10-15 13:38:21

18

由於提到BSH,您的shellcode不包含消息字節。跳到MESSAGE標籤,只是定義msg字節之前調用GOBACK程序是一個很好的舉措作爲味精的地址將是對堆棧返回地址的頂部可能被彈出到ecx,其中味精的地址存儲。

但無論你和BSH的代碼有輕微的限制。 它包含NULL bytes (\x00)當由所述函數指針解除引用這將被認爲是字符串的結尾。

周圍有這樣一個聰明的辦法。您存儲到eax, ebx and edx的值是足夠小,分別訪問al, bl and dl直接寫入一氣呵成各寄存器的下半字節。 高半字節可能包含垃圾值,因此可以進行着色。

b8 04 00 00 00 ------ mov $0x4,%eax 


變得

b0 04   ------ mov $0x4,%al 
31 c0   ------ xor %eax,%eax 


不同於現有的指令集,所述新指令集不包含任何空字節。

所以,最後的方案是這樣的:

global _start 

section .text 

_start: 
jmp message 

proc: 
    xor eax, eax 
    mov al, 0x04 
    xor ebx, ebx 
    mov bl, 0x01 
    pop ecx 
    xor edx, edx 
    mov dl, 0x16 
    int 0x80 

    xor eax, eax 
    mov al, 0x01 
    xor ebx, ebx 
    mov bl, 0x01 ; return 1 
    int 0x80 

message: 
    call proc 
    msg db " y0u sp34k 1337 ? " 

section .data 

組裝和鏈接:

$ for i in `objdump -d hello | tr '\t' ' ' | tr ' ' '\n' | egrep '^[0-9a-f]{2}$' ` ; do echo -n "\\x$i" ; done 

輸出:

$ nasm -f elf hello.asm -o hello.o 
$ ld -s -m elf_i386 hello.o -o hello 
$ ./hello 
y0u sp34k 1337 ? $ 

現在從打招呼二進制提取的shellcode

\xeb\x19\x31\xc0\xb0\x04\x31\xdb\xb3\x01\x59\x31\xd2\xb2\x12\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xb3\x01\xcd\x80\xe8\xe2\xff\xff\xff\x20\x79\x30\x75\x20\x73\x70\x33\x34\x6b\x20\x31\x33\x33\x37\x20\x3f\x20 

現在我們可以讓我們的驅動程序啓動shellcode。

#include <stdio.h> 

char shellcode[] = "\xeb\x19\x31\xc0\xb0\x04\x31\xdb" 
        "\xb3\x01\x59\x31\xd2\xb2\x12\xcd" 
        "\x80\x31\xc0\xb0\x01\x31\xdb\xb3" 
        "\x01\xcd\x80\xe8\xe2\xff\xff\xff" 
        "\x20\x79\x30\x75\x20\x73\x70\x33" 
        "\x34\x6b\x20\x31\x33\x33\x37\x20" 
        "\x3f\x20"; 


int main(int argc, char **argv) { 
    (*(void(*)())shellcode)(); 
    return 0; 
} 

有在現代編譯器等NX protection這防止在數據段或堆棧代碼執行某些安全功能。所以我們應該明確地指定編譯器來禁用它們。

$ gcc -g -Wall -fno-stack-protector -z execstack launcher.c -o launcher 

現在可以調用launcher來啓動shellcode。

$ ./launcher 
y0u sp34k 1337 ? $ 

對於更復雜的shellcode,會有另一個障礙。現代Linux內核有ASLRAddress Space Layout Randomization 您可能需要在注入shellcode之前禁用它,尤其是在通過緩衝區溢出時。

[email protected]:~# echo 0 > /proc/sys/kernel/randomize_va_space