linux asm x86生产段错误

问题描述:

我正在学习一些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

我不是很熟悉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

如果编译并在64位内核下运行,此代码可能会崩溃。 64位返回地址不适合32位ecx寄存器,您将不得不弹出rcx。此外,此代码使用32位API,可能在64位内核下不可用。您应该使用64位API,如我的博客文章中所述:x86-64 assembly on Linux

适合我。这是我做了(注意,我是一个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