我正在学习汇编,所以我正在研究shellcode。我写了一个简单的"你好世界"程序在nasm中,但在运行时崩溃。
; write.asm
[SECTION .text]
global _start
_start:
jmp short message;
write: ; takes two arguments pushed onto stack -> text, textlen
pop edx ; pop length into edx
pop ecx ; pop ptr to text into ecx
pushad
mov al, 4
mov bl, 1
int 80h ; syscall
popad
ret
exit: ; push exit_code onto stack
mov al, 1
pop ebx ; pop exit_code into ebx
int 80h
main:
pop eax ; pop ptr to message into eax
push 7 ; length of string
push eax ; push ptr to message
call write
xor ebx, ebx; zero out ebx
push ebx
call exit
message:
call main
db 'Hello!', 10
我用以下代码编译它:
nasm -f elf write.asm
ld -m elf_i386 -o write write.o
得到这个:
Segmentation fault (core dumped)
我尝试用gdb调试它,但它没有帮助。
答案 0 :(得分:3)
我想向您展示如何使用gdb找到您的问题:
>> gdb write
(gdb) b _start
。您可以让程序运行直到段错误,但如果堆栈搞砸了,则可能性很高,您将看不到任何内容。(gdb) display/x {unsigned int[5]}$sp
(gdb) display/i $pc
(gdb) run
调试器命中断点,您将看到系统设置的堆栈:
1: /x {unsigned int[5]}$sp = {0x1, 0xffffd284, 0x0, 0xffffd29d, 0xffffd2b2}
一切都好到目前为止,程序以一个参数(顶部为0x1
)启动 - 程序的路径(您可以通过(gdb) print (char[10])*(0xffffd284)
看到它)。
(gdb) si
,现在我们跳转到message
符号。 使用(gdb) disas
可以看到更多代码:
(gdb) disas
Dump of assembler code for function message:
=> 0x08048083 <+0>: call 0x8048072 <main>
0x08048088 <+5>: dec %eax
0x08048089 <+6>: gs
0x0804808a <+7>: insb (%dx),%es:(%edi)
0x0804808b <+8>: insb (%dx),%es:(%edi)
0x0804808c <+9>: outsl %ds:(%esi),(%dx)
0x0804808d <+10>: and %ecx,(%edx)
End of assembler dump.
如您所见,您的“Hello”字符串被解释为从0x08048088
开始的操作
(gdb) si
,进入函数main
。 现在看一下堆栈:
1: /x {unsigned int[5]}$sp = {0x8048088, 0x1, 0xffffd283, 0x0, 0xffffd29c}
call
- 指令推送堆栈上的返回地址 - 0x08048088
- 字符串的地址。好的伎俩,让我们希望main
永远不会回来......
write
(gdb) si 3
的电话,然后进入(gdb) si
。 让我们看看堆栈,正如预期的那样,call
将返回地址添加到堆栈中:
1: /x {unsigned int[5]}$sp = {0x804807b, 0x8048088, 0x7, 0x1, 0xffffd283}
write
:(gdb) si 7
。看看堆栈:
1: /x {unsigned int[5]}$sp = {0x7, 0x1, 0xffffd283, 0x0, 0xffffd29c}
下一个操作 - ret
将从堆栈弹出0x7
并尝试在地址0x7
处执行,这会导致段错误(如可疑的rcd)。
所以你的问题是你的函数破坏了堆栈。通常情况下,为呼叫设置堆栈的功能也负责在之后进行清理。