2017-12-26 12 views
1

Ich versuche, etwas Speicher in Linux mit sys_brk syscall zuzuweisen. Hier ist, was ich versucht:Warum ändern x86-64 Linux Systemaufrufe RCX und was bedeutet der Wert?

BYTES_TO_ALLOCATE equ 0x08 

section .text 
    global _start 

_start: 
    mov rax, 12 
    mov rdi, BYTES_TO_ALLOCATE 
    syscall 

    mov rax, 60 
    syscall 

Die Sache ist laut Linux-Aufrufkonvention I der Rückgabewert erwartet in rax Register (Zeiger auf den reservierten Speicher) zu sein. Ich lief dies in gdb und nach der Herstellung sys_brk syscall bemerkte ich die folgenden Registerinhalte

Vor syscall

rax   0xc  12 
rbx   0x0  0 
rcx   0x0  0 
rdx   0x0  0 
rsi   0x0  0 
rdi   0x8  8 

Nach syscall

rax   0x401000 4198400 
rbx   0x0  0 
rcx   0x40008c 4194444 ; <---- What does this value mean? 
rdx   0x0  0 
rsi   0x0  0 
rdi   0x8  8 

ich nicht ganz den Wert im rcx Register verstehen in dieser Fall. Welchen als Zeiger auf den Anfang von 8 Bytes verwende ich mit sys_brk?

+3

RCX und R11 werden von der Anweisung [SYSCALL] (http://www.felixcloutier.com/x86/SYSCALL.html) selbst geplagt. Von der Befehlssatzreferenz: _nach dem Speichern der Adresse der Anweisung, die SYSCALL in RCX folgt) _. _RFLAGS_ wird in _R11_ gespeichert –

+0

@MichaelPetch Sehr interessant. Es bedeutet, um sagen zu können, "cl" registrieren zu müssen, muss ich es zuerst löschen, richtig? Ich meine zum Beispiel "xor cl, cl" und dann "mov cl, 7". –

+0

Sie können sich nach dem SYSCALL nicht auf den Wert von _RCX_ oder _R11_ verlassen. Sie müssen also entweder eines der anderen Register anstelle von _RCX_ und _R11_ (und RAX) verwenden oder Sie müssen den Wert speichern (zum Beispiel Stapel) und ihn danach wiederherstellen. _RCX_ und _R11_ werden von Ihnen nicht gesetzt, Sie können sie einfach nicht verwenden und erwarten, dass sie vor und nach dem SYSCALL gleich sind. –

Antwort

2

Der Systemaufruf-Rückgabewert ist wie immer rax. Siehe What are the calling conventions for UNIX & Linux system calls on i386 and x86-64.

Beachten Sie, dass sys_brk eine etwas andere Schnittstelle hat als die brk/sbrk POSIX-Funktionen; siehe C library/kernel differences section of the Linux brk(2) man page. Spezifisch, Linux sys_brksetzt das Programm brechen; Arg und Rückgabewert sind beide Zeiger. Siehe Assembly x86 brk() call use. Diese Antwort braucht Upvotes, weil es der einzig gute in dieser Frage ist.


Der andere interessante Teil Ihrer Frage ist:

ich in diesem Fall

Sie sehen, die Mechanik, wie der Wert in den RCX-Registern nicht ganz verstehen Die Anweisungen syscall/sysret ermöglichen es dem Kernel, die Ausführung des Benutzerbereichs fortzusetzen, aber immer noch schnell zu sein.

syscall lädt oder speichert nicht, es ändert nur Register. Anstatt spezielle Register zu verwenden, um eine Rückkehradresse zu speichern, verwendet es einfach reguläre Ganzzahlregister.

Es ist kein Zufall, dass RCX=RIP und R11=RFLAGS nach dem Kernel wieder in Ihren User-Space-Code. Der einzige Weg für diese nicht ist der Fall ist, wenn ein ptrace Systemaufruf die gespeicherte rcx oder r11 Wert des Prozesses geändert, während es im Kernel war. (ptrace ist der Systemaufruf, den gdb verwendet). In diesem Fall würde Linux iret anstelle von sysret verwenden, um zum Benutzerbereich zurückzukehren, weil der langsamere allgemeine Fall iret das tun kann. (Vgl. What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code? für einige Durchgänge von Linux-Systemaufruf-Einstiegspunkten. Meistens die Einstiegspunkte von 32-Bit-Prozessen, jedoch nicht von syscall in einem 64-Bit-Prozess.

)

Statt eine Absenderadresse auf den Kernel-Stack des Drückens (wie int 0x80 der Fall ist), syscall:

  • Sätze RCX = RIP, R11 = rflags (so ist es unmöglich, den Kernel Sieh dir auch die Originalwerte dieser Regs an, bevor du syscall ausgeführt hast).
  • Masken mit einer vorkonfigurierten Maske aus einem Config-Register (IA32_FMASK MSR). Dadurch kann der Kernel Interrupts (IF) deaktivieren, bis er fertig ist swapgs und rsp setzen, um auf den Kernel-Stack zu zeigen. Selbst mit cli als erster Anweisung am Einstiegspunkt würde ein Fenster der Schwachstelle entstehen. Sie erhalten auch cld kostenlos durch Maskierung aus DF so rep movs/stos gehen nach oben, auch wenn User-Space std verwendet hatte.

    Fun Tatsache: AMDs erste vorgeschlagene syscall/swapgs Design nicht RFLAGS Maske, sondern they changed it after feedback from kernel developers on the amd64 mailing list (in ~ 2000, ein paar Jahre vor dem ersten Silizium).

  • springt zum konfigurierten syscall Einstiegspunkt (Einstellung CS: RIP = IA32_LSTAR). Der alte CS Wert ist nirgendwo gespeichert, denke ich.

  • Es tut nichts anderes, der Kernel muss swapgs verwenden, um Zugriff auf einen Info-Block zu bekommen, wo er den Kernel-Stack-Zeiger gespeichert hat, weil rsp immer noch seinen Wert aus User-Space hat.

So ist die Gestaltung von syscall erfordert eine ABI-System-Call, das Register clobbers, und das ist, warum die Werte sind, was sie sind.

Verwandte Themen