2016-10-09 12 views
0

Durch einige Manipulation habe ich es auf ein Problem mit der ret OP eingegrenzt. Ich weiß call schiebt die Rücksprungadresse zum Stapel; Ist es illegal, es zu knacken und zurückzuschieben?Warum bekomme ich einen segfault, wenn ich "ret"? (FASM)

format ELF64 executable 3 

entry start 

segment readable executable 

start: 
    pop rcx   ; argc 
    mov [argc],cl  ; int -> ASCII 
    add [argc],'0' 
    push 1 argc 1 
    call sys_write 

    mov rdi,0 
    mov rax,60 
    syscall 

sys_write: ; (fd,*buf,count) 
    pop r11 
    pop rdx rsi rdi 
    mov rax,1 
    syscall 
    push r11 
    ret 

segment readable writable 

argc rb 1 

Ausgang ist:

$ ./prog 
1Segmentation fault 
$ _ 
+1

SYSCALL zerstört r11. Sie hätten fast jedes andere Register auswählen können. Aber es ist viel einfacher, Funktionsargumente in Registern zu übergeben (wie es die ABI-Standard-Aufrufkonvention tut) oder den Stapel mit 'mov ..., [rsp + 8]' zu lesen. Die 'pop rdx rsi rdi'-Syntax von FASM ist nur syntaktischer Zucker, und sie besteht immer noch aus 3 Anweisungen. Dies ist nicht effizient. –

+0

Wenn Sie einen Debugger verwendet hätten, hätten Sie die Registerwerte überprüfen können. –

+0

Sie müssen mich verarschen. Nun, 'r12' hat funktioniert. Nur mein Glück. Vielen Dank. – qrpnxz

Antwort

1

Die syscall Anweisung clobbers den Inhalt mit dem R11RFLAGS Register, das bedeutet, dass ein syscall nach der Herstellung der Wert man in R11 gespeichert überschrieben wird.

Eine Lösung könnte sein, einfach ein passendes Register auszuwählen, das nicht von einem Systemaufruf geändert wird. Gemäß Intels Betriebsanleitung (http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-manual-325462.pdf, Seite 4-668, Band 2B) syscall werden RCX, RIP, R11 und als Teil des Betriebs überschrieben, aber das Betriebssystem ist natürlich in der Lage, RIP und RFLAGS wiederherzustellen.

Dies lässt viele andere Optionen. Eine gute Wahl wäre ein Register, bei dem ein Funktionsaufruf in der standardmäßigen Benutzer-Space-Aufrufkonvention klobben darf, aber Linux-Systemaufrufe bleiben unverändert. R8 passt die Rechnung. Da Sie nicht den Standard x86-64 System V function-calling convention, RBX oder RBP verwenden, wäre sogar eine bessere Wahl (kleinere Maschinencode-Größe, da sie kein REX-Präfix benötigen).

+0

Die 'syscall'-Anweisung scheint' RCX' zu verwenden, um die Rücksendeadresse vom syscall zu speichern, daher scheint sie auch nicht verfügbar zu sein. – SevenStarConstellation

+0

/facepalm, ja natürlich. mein Fehler. Ich glaube nicht, dass es für einen 3-Operanden-Systemaufruf irgendwelche freien Call-Clob-Low-8-Register gibt. (RBX und RBP sind Anruf-konserviert). Die beste Option ist natürlich, die Absenderadresse nicht zu löschen. –

+0

Warte, R8 wird nicht in der [x86-64 SystemV ABI] (http://stackoverflow.com/documentation/x86/3261/calling-conventions/11197/64-bit-system-v#t.) Gespeichert = 201610092111176889144). Und nein, Sie müssen RSP oder RFLAGS nie speichern/wiederherstellen. Das Speichern von FLAGS ist das, wofür SYSCALL R11 verwendet (so dass es maskiert werden kann, in den Kernel einzutreten, wenn Interrupts deaktiviert sind oder nicht, was auch immer das Betriebssystem konfiguriert). Die ursprünglichen Werte von RCX und R11 werden zerstört, aber alles andere geht irgendwo hin und wird vor oder durch die [SYSRET-Anweisung] des Kernels wiederhergestellt (http://www.felixcloutier.com/x86/SYSRET.html). –

Verwandte Themen