【发布时间】:2019-04-15 23:51:08
【问题描述】:
我正在学习编写 shellcode 并尝试读取文件(在本例中为 /flag/level1.flag)。此文件包含一个字符串。
通过在线查看教程,我想出了以下shellcode。它打开文件,逐字节读取(将每个字节压入堆栈),然后写入 stdout 并提供指向堆栈顶部的指针。
section .text
global _start
_start:
jmp ender
starter:
pop ebx ; ebx -> ["/flag/level1.flag"]
xor eax, eax
mov al, 0x5 ; open()
int 0x80
mov esi, eax ; [file handle to flag]
jmp read
exit:
xor eax, eax
mov al, 0x1 ; exit()
xor ebx, ebx ; return code: 0
int 0x80
read:
xor eax, eax
mov al, 0x3 ; read()
mov ebx, esi ; file handle to flag
mov ecx, esp ; read into stack
mov dl, 0x1 ; read 1 byte
int 0x80
xor ebx, ebx
cmp eax, ebx
je exit ; if read() returns 0x0, exit
xor eax, eax
mov al, 0x4 ; write()
mov bl, 0x1 ; stdout
int 0x80
inc esp
jmp read ; loop
ender:
call starter
string: db "/flag/level1.flag"
这是我编译和测试它的方法:
nasm -f elf -o test.o test.asm
ld -m elf_i386 -o test test.o
当我运行./test 时,我得到了预期的结果。现在,如果我从二进制文件中提取 shellcode 并在精简的 C 运行程序中对其进行测试:
char code[] = \
"\xeb\x30\x5b\x31\xc0\xb0\x05\xcd\x80\x89\xc6\xeb\x08\x31\xc0\xb0\x01\x31\xdb\xcd\x80\x31\xc0\xb0\x03\x89\xf3\x89\xe1\xb2\x01\xcd\x80\x31\xdb\x39\xd8\x74\xe6\x31\xc0\xb0\x04\xb3\x01\xcd\x80\x44\xeb\xe3\xe8\xcb\xff\xff\xff\x2f\x66\x6c\x61\x67\x2f\x6c\x65\x76\x65\x6c\x31\x2e\x66\x6c\x61\x67";
int main(int argc, char **argv){
int (*exeshell)();
exeshell = (int (*)()) code;
(int)(*exeshell)();
}
编译如下:
gcc -m32 -fno-stack-protector -z execstack -o shellcode shellcode.c
然后运行它,我看到我正确读取了文件,但随后继续将垃圾打印到终端(我必须 Ctrl+C)。
我猜这与read() 没有遇到\x00 有关,因此继续从堆栈打印数据,直到找到空标记。那是对的吗?如果是这样,为什么编译后的二进制文件可以工作?
【问题讨论】:
-
您是否尝试过在
strace ./a.out下运行您的程序,用于 asm 版本与 shellcode 版本?或者在 GDB 下运行它们以确保 shellcode 正确反汇编?实际上,您可以使用objdump -DrwC -Mintel检查以反汇编可执行文件的所有部分。
标签: linux assembly x86 nasm shellcode