2010-12-15 157 views
0

J'apprends un peu d'assembleur pour linux et j'ai ce programme d'exemple qui est juste supposé appeler l'écriture syscall et imprimer 'Hello, World!' Sur l'écran, cependant il produit un segfault. J'apprends dans mon temps libre, pas de devoirs, je ne vais plus à l'école!linux asm x86 produisant segfault

Quelqu'un peut-il voir quelque chose de mal avec ce code?

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

Je dois dire que c'est une méthode assez inhabituelle pour charger l'adresse de votre chaîne dans 'ecx'. –

+0

Est-ce qu'il produit la sortie et ALORS segfault, ou ne produit pas de sortie avant la segfaulting? –

+0

@Greg: d'accord, même si c'est l'une des méthodes les plus courantes de l'écriture de shellcode indépendant de la position ... –

Répondre

1

Je ne suis pas très familier avec l'ensemble de linux, mais voici une supposition:

Lorsque vous appelez API, vous devez utiliser une convention d'appel spécifique. Une des choses qui sont spécifiées par une convention est la liste des registres qui sont préservés dans l'appel de l'API. Dans votre cas, l'erreur utilise dec bl au lieu de xor ebx, ebx. Parce que bl est utilisé comme paramètre d'entrée, il est assez improbable qu'il sera conservé. De même pour mov al, 1, il est plus sûr d'écrire mov eax, 1

Et je suis d'accord avec @Greg Hewgil, votre façon d'obtenir l'adresse de chaîne est assez inhabituel. La façon courante d'écrire la position code indépendant avec des chaînes est la suivante:

... 
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

votre façon «commune» n'est pas particulièrement différente que l'OP. Vous faites toujours un 'jmp' juste avant les données et faites un' call' tout de suite en faisant finalement un 'pop' pour obtenir l'adresse des données dans un registre. La seule différence est la façon dont vous utilisez le registre après tout cela. –

+0

@Evan Teran: la différence est que la chaîne apparaît là où elle est utilisée, et la longueur est calculée automatiquement. – ruslik

+0

Je pourrais difficilement appeler une boucle qui cherche ''\ 0' '" automatique ". –

0

Ce code est susceptible de se bloquer si compilé et exécuté sous un noyau 64 bits. L'adresse de retour de 64 bits ne rentrera pas dans le registre ecx 32 bits, vous devrez alors utiliser la commande rcx. En outre, ce code utilise l'API 32 bits, qui peut ne pas être disponible sous un noyau 64 bits. Vous devriez utiliser l'API 64 bits à la place, comme décrit dans mon blog: x86-64 assembly on Linux.

2

Fonctionne pour moi. Voici ce que je l'ai fait (note, je suis sur une machine 64 bits, donc j'ai un drapeau supplémentaire pour créer un binaire 32 bits):

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!' 

commandes:

qui a produit un avertissement, mais ce n'est pas grave.

/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 

Je couru:

$ ./test 

et en effet sortie "Hello, World!" (sans saut de ligne). Pas de segfault.

+0

On dirait que c'était le problème. – ruslik