2014-11-09 4 views
1

Я пытаюсь написать свой первый «Hello world» shellcode на моем 64-битном Ubuntu, но он не работает.Linux 64-bit 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 в моей программе 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, но после запуска у меня есть «Ошибка сегментации (ядро сбрасывается) ».

Я понятия не имею, что я делаю неправильно. Ассемблерный код, похоже, работает, потому что когда я запускаю ./hello, он печатает «Hello world».

+2

'.data' раздел (где код заканчивается) не является исполняемым по умолчанию. Кроме того, вы должны убедиться, что ваш код не зависит от положения. – Jester

+0

На x86_64 вы уверены, что это действительно: 'length: equ $ -message' –

ответ

0

Надежда не слишком поздно, чтобы ответить на этот вопрос :)

Вы должны использовать этот код, чтобы скомпилировать код C (Чтобы отключить защиту стека и сделать его исполняемым):

gcc -fno-stack-protector -z execstack -o hello hello.c 

Удачи

+0

Код не в стеке, а в сегменте данных, поэтому ваш ответ не применяется. –

+0

@HristoIliev Я много раз писал и исполнял свой шелл-код. Это из-за безопасности стека, не связанного с сегментом данных. Пожалуйста, сначала проверьте свою идею, а затем оставьте комментарий. –

+0

Нет, это не из-за защиты стека. Это происходит из-за неисполняемого раздела '.data', в котором находится символ' code'. '-z execstack' отмечает сегмент' GNU_STACK' как исполняемый файл, который имеет побочный эффект и делает исполняемый файл секцией данных. Код отлично работает без '-fno-stack-protector'. Кроме того, создание 'code []' 'const' переносит его из раздела' .data' в раздел '.rodata', и последний все равно находится в исполняемом сегменте, поэтому' -z execstack' не требуется. –

2

Это хорошая практика, чтобы удалить 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

Это почти правильно, но не совсем. Это должен быть 'const char code []' (тогда вам не нужен '-fno-stack-protector -zexecstack'); и 'add rdx, 14' должно быть' add rdx, 12', чтобы не писать два за конец строки. – zwol

+0

Поскольку код не находится в стеке, вам не нужен '-fno-stack-protector', а только' -z execstack'. Обратите внимание, что наличие исполняемого раздела данных в результате '-z execstack' является ** специфичным для Linux образом **, подробно описанным здесь (http://blog.ioactive.com/2013/11/-short -tale-о-executablestack-in.html). Он не работает на FreeBSD, то есть '-z execstack' оставляет' .data' неисполнение в этой ОС. –

0

Там в два слоя проблемы здесь.

1) char data[] = ... будет помещен в сегмент данных, который не является исполняемым. Вам нужно const char data[] = ..., чтобы он был помещен в текстовый сегмент. (Технически, это будет в только для чтения данных сегмента, который может быть отмечен неисполняемым, но в данный момент никто не делает, что, насколько я знаю.)

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. Проблема в том, что вы только ставили код в data, а не на строку.Вам нужно поместить строку сразу после кода и использовать относительную адресацию для ПК, чтобы получить ее. Ответ nighter показывает, как это делается (при дополнительном ограничении, которое может отсутствовать \x00 байтов в data, что было бы необходимо, если бы вы фактически использовали типичный переполнение буфера здесь вместо того, чтобы просто работать с машинным языком).

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 

Compile/выход

$ 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!!! 
+0

Проблема заключается в следующем: (a) неисполняемый раздел '.data'; (b) абсолютная адресация в 'mov rsi, message'. 'equ $ -message' работает и на x64. –

Смежные вопросы