2010-12-15 43 views
0

我正在學習一些linux的彙編程序,我有這個示例程序只是應該調用write syscall並在屏幕上打印'Hello,World!',但它會產生段錯誤。我在空閒時間學習,而不是作業,我不再去學校!linux asm x86生產段錯誤

任何人都可以看到這個代碼有什麼問題嗎?

xor eax,eax 
xor ebx,ebx 
xor ecx,ecx 
xor edx,edx 
jmp short string 
code: 
pop  ecx 
mov  bl,1 
mov  dl,13 
mov  al,4 
int  0x80 
dec  bl 
mov  al,1 
int  0x80 
string: 
call code 
db  'Hello, World!' 
+3

我必須說,這是一種非常不尋常的將字符串地址加載到'ecx'中的方法。 – 2010-12-15 22:28:55

+0

它是否會產生輸出,然後是段錯誤,還是在分段之前不產生輸出? – 2010-12-15 23:29:57

+0

@Greg:同意了,儘管它是編寫位置獨立shellcode最常用的方法之一...... – 2010-12-15 23:52:16

回答

1

我不是很熟悉Linux組件,但這裏有一個猜測:

當調用的API,你必須使用一個特定調用約定。由約定指定的其中一項是通過API調用保留的寄存器列表。在你的情況下,錯誤是使用dec bl而不是xor ebx, ebx。因爲bl被用作輸入參數,所以它不太可能被保留。對於mov al, 1也是一樣,寫得更安全mov eax, 1

而且我同意@Greg Hewgil,你獲得字符串地址的方式很不尋常。用字符串寫位置無關代碼的常用方法是:

... 
call my_print 
db 'hello world!', 0 
... 

my_print: 
pop ecx 
xor edx, edx 
lp: 
cmp byte [ecx + edx], 0 
inc edx 
jne lp 
lea eax, [ecx + edx] 
push eax // return address 
dec edx 
mov eax, 4 
int 0x80 
ret 
+0

您的「常用」方式與OP沒有特別的不同。你仍然在做數據前的'jmp',然後做一個'call',最後做一個'pop'來獲取數據的地址到一個寄存器中。唯一的區別就是在完成之後你如何使用寄存器。 – 2010-12-16 00:03:34

+0

@Evan Teran:區別在於字符串出現在使用它的地方,並且長度是自動計算的。 – ruslik 2010-12-16 00:05:45

+0

我幾乎無法調用一個搜索「'\'自動」的循環。 – 2010-12-16 07:04:04

0

如果編譯並在64位內核下運行,此代碼可能會崩潰。 64位返回地址不適合32位ecx寄存器,您將不得不彈出rcx。此外,此代碼使用32位API,可能在64位內核下不可用。您應該使用64位API,如我的博客文章中所述:x86-64 assembly on Linux

2

適合我。這是我做了(注意,我是一個64位的機器上,所以我有一個額外的標誌來創建一個32位二進制):

TEST.ASM

_start: 
xor eax,eax 
xor ebx,ebx 
xor ecx,ecx 
xor edx,edx 
jmp short string 
code: 
pop  ecx 
mov  bl,1 
mov  dl,13 
mov  al,4 
int  0x80 
dec  bl 
mov  al,1 
int  0x80 
string: 
call code 
db  'Hello, World!' 

命令:

$ nasm -felf test.asm -o test.o 
$ gcc -m32 -nostdlib -nostdinc test.o -o test 

哪些產生了警告,但沒關係。

/usr/lib/gcc/x86_64-pc-linux-gnu/4.5.1/../../../../x86_64-pc-linux-gnu/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000008048080 

然後我跑:

$ ./test 

,它確實輸出"Hello, World!"(不換行)。沒有段錯誤。

+0

看起來就是這個問題。 – ruslik 2010-12-16 00:13:17