我在hello.asm
中有以下简单的汇编代码:
section .text:
global _start
_start:
jmp short ender
starter:
; clean registers
xor rax, rax
xor rbx, rbx
xor rcx, rcx
xor rdx, rdx
; writes 'hello\n' to stdout
mov al, 0x04 ; 4 = syscall number of sys_write
mov bl, 0x01 ; 1 = file descriptor of stdout
pop rcx ; points to 'hello\n'
mov dl, 0x06 ; writes 6 characters
int 0x80
; exits with status 5
xor rax, rax
mov al, 0x01 ; 1 = syscall number of sys_exit
xor rbx, rbx
mov bl, 0x05 ; 5 = exit status number
int 0x80
ender:
call starter
db 'hello',0x0a
受an article on shellcoding的启发,汇编代码将hello\n
压入堆栈,将其写入标准输出,然后以代码5
退出。
我可以编译并成功运行它:
$ nasm -f elf64 hello.asm
$ ld hello.o -o hello
$ ./hello
hello
使用objdump -D hello
和一个简短的python脚本,我可以提取相关的shellcode字符串并使用以下简单的C代码对其进行测试:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
char* shellcode = "\xeb\x21\x48\x31\xc0\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\xb0\x04\xb3\x01\x59\xb2\x06\xcd\x80\x48\x31\xc0\xb0\x01\x48\x31\xdb\xb3\x05\xcd\x80\xe8\xda\xff\xff\xff\x68\x65\x6c\x6c\x6f\x0a";
void main() {
(*(void(*)()) shellcode)();
}
./test-shellcode
的预期输出为hello\n
,退出状态为5,就像运行编译的程序集时一样。但是,该程序根本不输出任何内容,但仍以5状态退出!
在使用GDB对其进行调试之后,我观察到该程序按预期执行了shellcode。特别是,应该将hello\n
写到stdout的这一部分没有错误地执行,并且rcx
的内容是hello\n
字符串的地址:
0x5555555546d6: mov $0x4,%al
0x5555555546d8: mov $0x1,%bl
0x5555555546da: pop %rcx
0x5555555546db: mov $0x6,%dl
0x5555555546dd: int $0x80
但是,尽管sys_write
显然被正确调用并且没有错误地执行了,但 没有 被写入标准输出。