

 Polymorphie in Cryptern
 by SnakeByte [ SnakeByte@kryptocrew.de ]


 
 Hier werde ich versuchen mal etwas ber polymorphe Crypter zu schreiben,
 wie sie funktionieren, was man verbessern kann und Mglichkeiten sie zu 
 umgehen um einen Unpacker zu schreiben, da ich denke das ein aktueller
 Crypter Polymorphe routinen enthalten sollte. ( Wenn man nicht erkennt ob
 es mit dem Crypter gepackt ist, kann man es auch nicht entpacken )

 Was ist Polymorphie eigentlich ? Polymorph bedeutet vielgestaltig,
 die Datei kann also mehr als nur ein Aussehen annehmen. Bei einer
 guten Poly-Engine sieht sieht jede Datei anders aus, es gibt keine
 Gemeinsamkeiten mehr. Normalerweise sieht eine gecryptete Datei 
 folgendermassen aus :

 Decrypter fr die Datei
 Starten der Datei
 --- Start der verschlsselten Datei
 Datei
 --- Ende der verschlsselten Datei

 Hierbei kann man den Decrypter relativ schnell erkennen, indem man die
 ersten paar ( 20 - 30 ) Byte nach dem Entrypoint ( EIP ) auswertet.
 Damit kann relativ schnell ein Unpacker erzeugt werden.
 Doch mitlerweile verwenden Programme wie telock polymorphie. Dann sieht
 das ganze in etwa so aus :

 --- Start des verschlsselten Decryptor
 Decrypter fr die Datei
 Starten der Datei
 --- Ende des verschlsselten Decryptor
 --- Start der verschlsselten Datei
 Datei
 --- Ende der verschlsselten Datei

 Hierbei ist der Entpacker fr die Datei auch verschlsselt, mit einem
 einfacheren Algorithmus, der weniger Bytes beansprucht. Dieser ist dann
 nur noch um die 30 Bytes gross und es ist daher viel einfacher ihn polymorph zu 
 generiern als den eigentlichen Decryptor.  Was das ganze nun polymorph macht, ist
 das fr die Verschlsselung des Decryptors jedesmal ein anderer Schlssel
 und ein anderes Verfahren angewendet wird. Ausserdem wird die Entschlsselungs-
 routine fr den Decryptor jedesmal neu erzeugt.

 lea esi, decryptor     ; Offset des Decrypters laden
 mov eax, key           ; Schlssel laden
 mov ecx, size          ; Grsse laden ( in double words ! )

 my_Loop:
  xor dword ptr [esi], eax  ; entschlsseln =)
  add esi, 4h               ; nchstes dword ermitteln
 loop my_Loop               ; und weiter gehts
 
 decryptor:  


 Das ganze knnte auch anders aufgebaut werden, so das sich keine Bytefolge
 gleicht :

 push key
 pop ebx
 xor ecx, ecx
 add ecx, si
 mov ecx, size
 sub edi, edi
 add edi, offset decryptor

 my_Loop:
  mov eax, dword ptr [edi]
  xor eax, ebx
  mov dword ptr [edi], eax
  sub edi, -4h
 dec ecx
 jnz my_Loop
 
 decryptor:  


 Man kann also sowohl die einzelnen Register austauschen, als auch 
 andere Instruktionen verwenden ( floating point, mmx Instruktionen auch )
 So gibt es zum Beispiel massig mglichkeiten um einen Register zu leeren :

  mov eax, 0 oder sub eax, eax oder xor eax, eax oder and eax, 0
  push 0 pop eax, ...

 Dadurch erhlt man fr jede verschlsselte Datei einen neuen Decryptor
 und sie damit ein komplett neues Aussehen. Auch kann ( bzw. sollte ) man
 die Art der Verschlsselung ndern. So kann man mit Hilfe verschiedener
 Kombinationen von XOR, ADD / SUB, NEG, NOT, ROR / ROL ganz verschiedene
 Ergebnisse erziehlen. Je mehr die erstellten Verschlsselungen
 voneinander abweichen umso besser.
 
 Wenn man zum Beispiel die Reihenfolge der Instruktionen beibehlt und
 nur die Instruktionen ndert, lassen sich bei nur 2 Alternativen fr
 jede Instruktion schon eine beachtliche Menge an verschiedenen
 Decryptoren erstellen, so dass mehrere Strings zum Erkennen des
 Decryptors kein Ausweg darstellen.

 Des weiteren kann man Trash oder Junk Code in die Routinen mit einbauen.
 Also einfach ein paar Instruktionen mit reinschreiben, die zwar den
 String verndern, aber die Routine nicht in ihrer Ausfhrung beeintrchtigen :

 nop                    ; Trash
 lea esi, decryptor
 add eax, 29a           ; Trash
 mov eax, key
 sub ebp, 200h          ; Trash
 mov ecx, size
 xor edx, ebx           ; Trash

 my_Loop:
  xor edx, 233h         ; Trash
  xor dword ptr [esi], eax
  nop                   ; Trash
  add esi, 4h
  xchg edx, ebx
 loop my_Loop
 add ecx, 39h 

 decryptor:  


 Wie baut man also so eine Poly-Engine auf ?
 Also am leichtesten ist es, wenn man massig IF / CASE Anweisungen verwendet,
 das gibt zwar massig Code, aber man versteht ihn noch =)
 so geht man dann den Decryptor durch, ermittelt fr jede Instruktion
 eine Zufallszahl und whlt anhand der Zahl den entsprechenden Opcode aus.
 Man kann das aber auch mit Hilfe von Tabellen regeln. So nimmt man zum Beispiel
 die Alternativen fr  mov reg, xxxxxh, brint sie alle auf eine Lnge und
 setzt dann nur noch den Register ein ( +0 bis +8 ) und den Wert ein.
 Wenn die Instruktionen alle in einer Tabelle stehen, kann man so
 relativ einfach die entsprechende raussuchen.
 Ganz einfache Poly Engines beinhalten einfach nur 5 bis 10 fertige
 Entschlsselungsroutinen und je nachdem wird eine ausgewhlt und
 eingesetzt. ( Aber mit 10 Strings zum Erkennen und den 10 entsprechenden
 Entschlsselungsroutinen kann man diese relativ einfach ausschalten =)
 Wer es besonders knifflig mag ( freut euch auf die Kopfschmerzen beim coden *g* )
 kann auch Anti-Debugging Tricks polymorph erstellen oder gar den kompletten
 Decryptor fr die Datei mit Hilfe einer Poly Engine realisieren.
 Das ist zwar massig Arbeit und wenn man irgendwo ein paar statische Bytes
 bersieht war sie umsonst :( Wenn man sich zum Beispiel teLock von te
 ansieht wird man geststellen, das die ersten paar Bytes nach der EIP
 immer gleich sind : 

  60            pushad
  E848110000    call 48110000
  C3            ret 
 
 Auch wenn dies nur 11 Bytes sind, die bei dem polymorphen Decryptor
 statisch sind, sind sie ein schnes Erkennungsmerkmal fr den Unpacker,
 da sie nur sehr selten bei "normalen" Dateien direkt nach der EIP auftauchen
 drften. Wenn man dann noch Informationen hinzunimmt ( nicht im Fall von telock )
 wie festgelegte Sektion Namen oder hnliches, ist es eine ziemlich gute Methode
 um den Packer zu bestimmen.

 Selbst wenn jemand glaubt eine Erkennungsmethode fr den Polymorphen Code
 gefunden zu haben, hat er noch massig Arbeit damit, sie an mglichst vielen
 Dateien zu testen.


 Wie kommt man also gegen Polymorphe Crypter / Packer an ?
 Das hngt von vielerlei Faktoren ab, vor allem aber wie gut die Polymorphie ist.
 Wenn immer die gleiche Methode der Verschlsselung verwendet wird, kann der
 Entpacker die Datei analysieren, und nachsehen wo der verschlsselte Code 
 anfngt. Wenn der Decryptor fr die Datei immer gleich ( also nicht polymorph )
 ist, kann man ihn mit etwas Kryptanalyse entdecken.
 Nehmen wir an er ist mit XOR verschlsselt, aber jedesmal mit einem anderen
 Schlssel. Kein Problem. Man Verwendet selber ein XOR auf die ersten paar Bytes
 ( words , dwords je nachdem wie verschlsselt wird ) an, und erhlt einen
 Wert, der unabhngig vom Schlssel immer gleich ist. Damit kann der Crypter
 identifiziert werden und da man den Originalcode kennt auch wieder entpacken.
 Den Anfangswert ermitteln wir also durch das XOR von hintereinanderfolgenden
 Bytes, jeweils mit 2 anderen, bis wir den entsprechenden Wert ermitteln.
 Danach knnen wir aus diesen Bytes den Schlssel auslesen, und den Dateicrypter
 entpacken.
 Bei Add und Sub kann man, wenn man den Originalcode kennt auch relativ einfach den 
 Schlssel ermitteln und so den Packer identifizieren. Hier funktioniert das ganze
 analog zu XOR.


 Originaldecryptor ( xxx ist poly ) :  xx xx xx 32 56 21 56 xx xx xx xx xx xx
 Verschlsselt ( xor 90 :)          :  xx xx xx A2 C6 B1 C6 xx xx xx xx xx xx
                                                 \/  \/ \/
    32 XOR 56 == A2 XOR C6 == 64     <------------'   |  |
    56 XOR 21 == C6 XOR B1 == 77     <----------------'  |
    21 XOR 56 == B1 XOR C6 == 77     <-------------------'

 Nun XOR't man den gefundenen Wert mit dem Originalwer :

  A2 XOR 32 == 90 

 und erhlt somit den Key um den Rest zu entpacken.. simple Mathematik =)

 Als Alternative zur Code-Analyse bietet sich die Emulation an. Mit Hilfe der
 Debug API oder hnlichem den Decryptor fr den Dateidecryptor debuggen, bis
 der echte, der Dateidecryptor entschlsselt ist. Diesen erkennt man sobald die 
 momentane EIP auf den Code des Dateidecryptor zeigt ( Diese kann man ja vorher
 durch eigenes debuggen bzw. entpacken per Hand bestimmen ). Dann knnt ihr den 
 Schlssel fr die Datei auslesen und sie entpacken. Wenn im ersten Decryptor jedoch
 polymorpher Anti Debugging Code vorkommt, ist es leider doch notwendig, eine
 eigene Routine zu schreiben, die den Code analysiert.



 Ok, damit das Ganze noch etwas verstndlicher wird, hier ein kleines
 Crackme. Ich habe das Ganze in fr DOS getippert, da sich das Ganze
 in einer COM Datei einfacher realisieren lsst.
 Das ganze luft wie folgt :
 
  - Entschlsseln des eigenen Codes
  - Datei erneut verschlsseln 
  - Neuen Decrypter erstellen
  - Neue Datei schreiben

 Hmm, ich hab das Gefhl, das klingt etwas wirr. Die Datei ndert
 sich einfach jedesmal selbst. Sie nimmt den eigenen Code, verschlsselt
 ihn neu und schreibt ihn zusammen mit einem neuen Decrypter auf
 die Platte, berschreibt also sich selbst.
 ( hoffe so ist das verstndlicher *g* )

 --- Aufbau der Datei : ---

 [Aufruf des Decryptors]  <-- Call, der jedesmal ber eine andere Distanz geht
 [Verschlsselte Datei ]  <-- "verschlsselt"
 [      Decryptor      ]  <-- wird jedesmal neu erstellt

 
 Ihr knnt euch ja mal daran versuchen, Ziel ist es, die Message,
 die ausgegeben wird, von MSG1 nach MSG2 verndern ( also einfach
 den Offset ndern ) Der Patch sollte mit jeder Version klappen, 
 also nicht nur direkt nach dem Compilieren, sondern auch, wenn
 die Datei schon ein paar mal gestartet wurde. Ich habe hier zwei
 einfache Verschlsselungen gewhlt, damit der Code verstndlicher
 bleibt und die Cryptanalyse nicht zu schwer ist =)
 Am Besten wre es, wenn ihr bisserl Quellcode und nen kleinen
 Text dazu einschicken knntet, wie ihr vorgeht, damit das Kraecker
 Mag auch in der nchsten Ausgabe wieder Material hat... :)
 Bei gengend Resonanz bau ich dieses Crackme eventuell fr die nchste
 Ausgabe des Kraeckers noch etwas aus... :]



;###############[ Cut Here ]############################################

; - Crackme.asm -
; 
; Zum kompilieren ( TASM 5.0 ) :
;
;  tasm crackme /n /p /t /w /z /m4
;  tlink crackme /d /x /t


.model tiny
.code
.486
org 100h                    ; COM Datei

Start:
call Crypt                  ; Call des Entcrypters

;---------------------------; Hier fngt der verschlsselte Teil an
CryptStart:
 mov ah, 3dh                ; eigene Datei zum schreiben ffnen
 mov al, 02h                ; denn diese wollen wir manipulieren
 lea dx, filename
 int 21h

 jc Ende
 
 xchg bx, ax                ; Dateihandle speichern

 pusha                      ; Register speichern
 call PolyEngine            ; Datei neu erstellen
 popa                       ; Register wieder laden

 mov ax, 4200h              ; gehe zum Anfang der Datei
 xor cx,cx
 xor dx,dx
 int 21h

 mov ah, 40h                ; schreibe etwas in die Datei
 lea dx, CryptedCom         ; und zwar den gesamten eigenen Code
 mov cx, word ptr [mySize]  ; der nun einen neuen Entcryptor trgt
 int 21h                    ; und verschlsselt ist

 mov ah, 3eh                ; Datei schlieen ( Handle ist in bx )
 int 21h 

 mov ah, 09h                ; Message ausgeben
 lea dx, MSG1               ; <<== das soll gendert werden auf MSG2
 int 21h

Ende:
 mov ax, 4c00h              ; Programm beenden
 int 21h

                            ; Die Daten
MSG1      db 'Crack me !',10,13,'$'       ; <- normale Ausgabe
MSG2      db 'You cracked me !',10,13,'$' ; <- Ausgabe der gecrackten Version

filename  db 'Crackme.com',0h  ; Der eigene Dateiname
mySize    dw 0h                ; hier speichern wir die Grsse der neuen Datei
CryptType db 0h                ; Die Art wie wir die Datei verschlsseln
LoadType  db 0h
CryptKey  db 0h


; diese Prozedur erstellt die neue COM Datei
; in dem Buffer ( CryptedCom ) am Ende der COM Datei
; ich habe hier fr jede Instruktion nur eine Alternative gewhlt,
; weil ich verdammt faul bin, und weil es so bersichtlich bleibt =)

PolyEngine:

 lea di, CryptedCom

; zuallererst erstellen wir den CALL neu
; dieser wird jedesmal ber eine andere Distanz gefhrt,
; so dass er nicht statisch bleibt

 mov al, 0E8h               ; "Call" - Opcode 
 stosb
 
 xor ax, ax
 in al, 40h                 ; Zufallszahl von 0 bis 255
 shr al, 3h                 ; teilen durch 8 ( Zahl von 0 bis 31 )

                            ; Lnge der Datei ( ohne Crypter & Co )
 mov bx, offset Crypt - offset CryptStart
 add bx, ax                 ; damit geht der Call immer ber eine andere
                            ; Distanz
 xchg ax, bx
 stosw
 add ax, 3
 mov word ptr [mySize], ax  ; Gesamtgroesse speichern
 
 push bx                    ; bx enthlt die "Trash" Bytes, die wir
                            ; spter noch anhngen mssen, damit der call
                            ; auch den decrypter trifft

; zuerst "verschluesseln" wir mal die Datei und schreiben sie
; in unseren Buffer, wir haben hier 2 konstante Arten der Verschlsselung
; man knnte der XOR Verschlsselung auch relativ einfach fr jedes
; Mal einen anderen Key verpassen.

 lea si, CryptStart        
 mov cx, offset Crypt - offset CryptStart

; durch in al, 40h ermitteln wir eine Zufallszahl, dies geht, da
; der Lautsprecher, wohl jedesmal einen anderen Wert liefert, durch
; normales Rauschen & Co..

 in al, 40h                 ; neue Zufallszahl
 shr al, 2h                 ; 1 oder 2
 jc NegCrypt
                            ; speichern wie wir verschluesseln
 mov byte ptr [CryptType], 0

 in al, 40h                 ; Schlssel ermitteln
 mov byte ptr [CryptKey], al
 mov dl, al

 CryptLoop1:
  lodsb
   xor al, dl              ; mit xor "verschlsseln"
  stosb                    ; Zuflliger Schlssel
 loop CryptLoop1

 jmp GoOn

NegCrypt:                   ; speichern wir wir verschluesseln
 mov byte ptr [CryptType], 1
 CryptLoop2:
  lodsb
   neg al                  ; mit einem einfachen neg "verschlsseln"
  stosb
 loop CryptLoop2

; Jetz fangen wir an den Decryptor zu erstellen
; zuerst einige sinnlose Bytes, damit die Groesse nicht immer gleich 
; ist.

GoOn: 

 pop cx                     ; cx sind die Trashbytes zum anhngen
  
 Trash1:
  in al, 40h                ; Zufllige Bytes
  stosb                     ; in den Buffer schreiben
 loop Trash1

 mov ax, word ptr [mySize]  ; Groesse des verschluesselten Teiles
 push ax                    ; im Stack speichern

; Nun erstellen wir den Teil des Decryptors, der den Anfangscode
; ermittelt, ab dem er entschlsseln muss. Da dieser nach dem
; Call auf dem Stack liegt, mssen wir nur den Wert auf den
; sp / esp zeigt in si laden.

 in al, 40h                 ; wir poppen si vom stack, um den Start
 shr al, 1                  ; des verschlsselten codes zu bekommen
 jc GetOffset2

 mov ax, 0565Eh
 stosw
 add word ptr [mySize], 2h
 jmp GoOn2

GetOffset2:                 ; mov si, word ptr [esp]
 mov ax, 08b67h 
 stosw
 mov ax, 02434h
 stosw
 add word ptr [mySize], 4h
GoOn2:   

; Nun laden wir in cx die Groesse des verschlsselten Codes
; entweder indem wir ein mov cx, size machen oder
; indem wir den Wert pushen und dann nach cx poppen

 pop cx                     ; groesse des verschluesselten teiles
 in al, 40h
 shr al, 1
 jc GetSize2
 
 mov al, 068h               ; push Size
 stosb
 mov ax, cx
 stosw
 mov al, 059h               ; pop cx
 stosb
 add word ptr [mySize], 4h
 jmp GoOn3

GetSize2:                   ; mov cx, Size
 mov al, 0B9h
 stosb
 mov ax, cx
 stosw
 add word ptr [mySize], 3h

GoOn3:

; Nun mssen wir die Entschlsselungsschleife generieren
; zuallererst laden wir einen Wert in al
;
; entweder per : mov di, si - lodsb
; oder         : mov al, byte ptr [si]


 push word ptr [mySize]    ; groesse speichern

 in al, 40h
 shr al, 1
 jc LoadByte2 

 mov ax, 0FE8Bh            ; mov di, si
 stosw
 mov al, 0ACh              ; lodsb
 stosb
 add word ptr [mySize], 3h
 mov LoadType, 0
 jmp GoOn4

LoadByte2:
 mov ax, 0048Ah            ; mov al, byte ptr [si]
 stosw
 add word ptr [mySize], 2h
 mov LoadType, 1

GoOn4:                      ; nun wird das Byte in al entsprechen
                            ; der Verschlsselungsart "entschlsselt"
 cmp byte ptr [CryptType],0
 jne Crypt_2

 mov al, 034h               ; xor al, 
 mov ah, byte ptr CryptKey  ; CryptKey
 jmp GoOn5

Crypt_2:
 mov ax, 0D8F6h             ; neg al

GoOn5:
 stosw
 add word ptr [mySize], 2h

; Jetzt mssen wir das Byte in al wieder
; an seinen alten Platz speichern
; entweder per stosb
; oder per mov byte ptr [si], al - inc si

 cmp LoadType, 0
 jne Store2

 mov al, 0AAh               ; stosb
 stosb
 inc word ptr [mySize]
 jmp GoOn6

Store2:
 mov ax, 00488h             ; mov byte ptr [si], al
 stosw
 mov al, 046h               ; inc si
 stosb
 add word ptr [mySize], 3h

GoOn6:

; Jetzt fehlt nur der Loop um das Ganze zu wiederholen
; Zuerst ermitteln wir die Groesse ueber die der Loop gehen muss


 mov bx, word ptr [mySize] ; momentane Grsse
 pop ax                    ; Groesse vor dem Loop-Teil
 sub bx, ax                ; Groesse des Loops
 mov ax, 0FFh              ; wir loopen "rckwrts"
 sub ax, bx                ; der Wert is in al
 dec ax
 mov ah, al

 push ax
 in al, 40h
 shr al, 1
 jc DoLoop2

 pop ax
 mov al, 0E2h              ; loop
 stosw
 add word ptr [mySize], 2
 jmp GoOn7

DoLoop2:                   ; dec cx
 mov al, 049h
 stosb
 pop ax
 dec ah                    ; der Sprung geht ber 1 Byte mehr
 mov al, 075h              ; jnz <CryptStart>
 stosw
 add word ptr [mySize], 3

GoOn7:

; Nun koennen wir den entschluesselten Teil starten,
; wir kehren immer mit einem einfachen "ret" zurck

 mov al, 0C3h               ; Das 'ret' am Ende des Decryptors anhngen
 stosb 
 inc word ptr [mySize]      ; Groesse um eins erhhen, wegen dem Ret
ret


Trash:
 in al, 40h
 

ret


;---------------------------; bis hier ist alles verschlsselt

Crypt:                      ; in der ersten Generation wird
ret                         ; hier noch nichts entschlsselt
                            ; deshalb steht hier nur das ret

CryptedCom:                 ; hierhin speichern wir die verschlsselte
                            ; Datei ( unser Buffer halt *g* )
db 90h dup (0h)

END Start


;###############[ Cut Here ]############################################