Manual Unpacking

Packer : Neolite 2.0

Objectif :

Cible :

Outils nécessaires :

Fichiers joints :

Index

  1. Introduction
  2. Collecte des infos sur la cible
  3. Dump full et examen du dump sous un éditeur hexa
  4. Recherche de l'original enty-point (OEP)
  5. Recherche de la table des imports
  6. Correction du PE et suppression du code de Neolite dans le dump
  7. Création d'un self-loader dans l'exe packé
    1. Patch de l'exe packé pour rendre la mémoire accessible
    2. patch du crackme en mémoire
  8. Infos pratiques sur Neolite 2.0

1. Introduction

Neolite 2.0 est très facile à unpacker. Apres compression, les sections originales sont gardés intactes, et le code de neolite est inséré dans une nouvelle section ajouté à la suite des autres sous le nom de ".neolit". Même la section des imports est nickel, pour obtenir un dump fonctionnel et désassemblable avec les imports et les datas, nous n'aurons qu'à corriger l'OEP et l'offset de l'import-table. Neolite est un shareware et en version non-enregistré il ajoute à l'exe packé un nag-screen nous indiquant que le programme à été packé par une version non-enregistré de Neolite. L'unpacker est donc vraiment très simple, heureusement pour compliquer un peu la chose, Neolite nous réserve un petit cadeau dans le cas ou nous voudrions faire patch en mémoire dans l'exe packé puisqu'il protège en écriture la mémoire. Nous verrons donc dans ce tutorial, comment restituer l'exe original et aussi comment faire pour contourner la protection d'écriture en mémoire.

2. Collecte des infos sur la cible

On ouvre la cible avec Lord-PE et on récupère les infos suivantes :

Entry-point : 000230CC
Import-Table : RVA = 00023000, size = 00000050


Sections Table :

names		Voffset		Vsize		Roffset		Rsize		Flags
.text		00001000	00003000	00000000	00003000	C0000080
.rdata		00004000	00001000	00000000	00001000	40000080
.data		00005000	00001000	00001000	00001000	C0000040
.rsrc		00006000	0001C8E0	00002000	00001000	40000040
.neolit		00023000	000070CC	00003000	00001C4C	E0000020

L'entry-point et la table des imports actuels, se trouvent bien dans la section .neolit.

3. Dump full et examen du dump sous un éditeur hexa

On lance le crackme et on clique sur Non au nag-screen de Neolite pour lancer le programme. Ensuite on dump avec Lord-PE ou un autre process dumper en s'assurant que ces options soit cochés avant de dumper :

Si on essaye d'ouvrir l'exe avec wdasm, celui-ci nous informera que le programme contient un PE-file non standard et que toutes les datas références seront ignorés. Ceci vient du fait du flag de la section .data qu'il faudra corriger par E0000020 pour résoudre ce problème. On met aussi le flag E0000020 pour la section .text et ou pourra désormais obtenir les datas strings references.

4. Recherche de l'original enty-point (OEP)

On va se servir d'un debugger et tracer en le code pas à pas et repérer le moment ou l'on quitte le segment de neolite pour aller dans la section .text de notre programme, cette section contenant le code du programme.

Avec WinDbg :

On examine le log et on repère le changement de section ici :

00423181 e8ed040000       call    image00400000+0x23673 (00423673)
image00400000+23186:
00423186 fe0576314200 inc byte ptr [image00400000+0x23176 (00423176)] ds:0023:00423176=00
image00400000+2318c:
0042318c ffe0 jmp eax {image00400000+0x1184 (00401184)} => saute vers l'OEP
image00400000+1184:
00401184 55 push ebp
image00400000+1185:
00401185 8bec mov ebp,esp

L'OEP est en 401184 (offset = 1184). On retient aussi l'adresse 0042318c qui marque la fin du loader de neolite, on s'en servira pour créer notre memory patch en déviant le code depuis cette adresse.

5. Recherche de la table des imports

Si on examine la section des imports (.rdata) on pourra remarquer que celle-ci est parfaite contenant tous les éléments nécessaires (les noms de DLL et fonctions, la table des imports, les tableaux pointés par les OriginalFirstThunk et FirstThunk). Comme les tableaux des OriginalFirstThunk sont présents et que chaque élément pointe bien vers un nom de fonction, nous n'aurons pas à corriger les tableaux des FirstThunk comme on le faisait d'habitude. (si vous ne comprenez pas pourquoi il n'est pas nécessaire de reconstruire ces tableaux, aller faire un tour sur mon tutorial "Reconstruction de la table des imports").

L'offset de la table des imports dans notre dump pointe pour l'instant vers une table utilisé par le loader de neolite, il faut simplement redéfinir dans le PE l'offset de la table original pour rendre fonctionnel et obtenir les imports du programme original.

Pour retrouver cette table il suffit de chercher visuellement un nom de DLL dans la partie qui contient les noms des fonctions et des DLL. On va prendre pour l'exemple USER32.dll qui se trouve à l'offset 000045BC.

00004520 E6470000 F2470000 FE470000 0E480000 86480000 00000000 A4450000 .G...G...G...H...H.......E..
0000453C 96450000 8E450000 82450000 70450000 B0450000 00000000 34004372 .E...E...E..pE...E......4.Cr
00004558 65617465 46696C65 41004B45 524E454C 33322E64 6C6C0000 93004469 eateFileA.KERNEL32.dll....Di
00004574 616C6F67 426F7850 6172616D 41000302 52656C65 61736544 4300FD00 alogBoxParamA...ReleaseDC...
00004590 47657444 43000201 47657444 6C674974 656D0000 52025365 7454696D GetDC...GetDlgItem..R.SetTim
000045AC 65720000 B900456E 64446961 6C6F6700 55534552 33322E64 6C6C0000 er....EndDialog.USER32.dll..
000045C8 53004465 6C657465 4F626A65 63740000 05025465 78744F75 74410000 S.DeleteObject....TextOutA..
000045E4 F3015365 74546578 74436F6C 6F720000 CE015365 74426B4D 6F646500 ..SetTextColor....SetBkMode.

On sait que cet offset sera spécifié dans la table des imports par l'un des éléments. On lance une recherche sur la valeur hexa BC450000. On trouve cette référence à l'offset 00004454. Là on est sur le 4ème élément d'une des structures de la table des imports. Si on cherche les nom des DLL on n'en trouveras 3 nous aurons donc 3 structures plus une vide dans la table des imports. Il suffit de parcourir les éléments alentours de l'offset on l'on est actuellement pour identifier les 4 structures qui composent cette table :

00004408 00000000 00000000 FFFFFFFF 7E334000 82334000 FFFFFFFF 32344000 ............~3@..3@.....24@.
00004424 36344000 FFFFFFFF B6354000 BA354000 A0440000 00000000 00000000 64@......5@..5@..D..........
00004440 62450000 1C400000 38450000 00000000 00000000 BC450000 B4400000 bE...@..8E...........E...@..
0000445C 84440000 00000000 00000000 1E460000 00400000 00000000 00000000 .D...........F...@..........
00004478 00000000 00000000 00000000 00460000 F4450000 E4450000 D8450000 .............F...E...E...E..
00004494 C8450000 10460000 00000000 3E480000 54480000 64480000 74480000 .E...F......>H..TH..dH..tH..
000044B0 1C480000 2E480000 54450000 28460000 3C460000 4E460000 60460000 .H...H..TE..(F..<F..NF..`F..

L'offset de début de la table commence en 00004434 et s'étend sur 50h bytes.

6. Correction du PE et suppression du code de Neolite dans le dump

Maintenant qu'on à tous les renseignement on corrige le PE :

Notre dump est fonctionnel et désassemblable avec les imports et les datas. Pour faire plus propre on va supprimer la section .neolite du dump :

Remarquez au passage que le nag de Neolite n'est plus là, normal puisqu'il était affiché par le loader de neolite.

7. Création d'un self-loader dans l'exe packé

Passons à la petite subtilité de ce packer qui protège l'accès en mémoire en écriture ce qui est un peu embêtant pour créer un memory patch. Prenons exemple sur le crackme que je survole très vite en vous donnant directement le point de patch :

Pour être Registered :
----------------------

:00401075 7510		jne 00401087	=> Forcer le saut

[note de Anubis: le soft teste la présence du fichier vide de nom "#45465f123" pour être en full version]

a) Patch de l'exe packé pour rendre la mémoire accessible

Si on essaye de patcher la mémoire à cette adresse on aura une erreur d'accès dûe à la protection. Cette protection est établie par le loader de Neolite avec l'api VirtualProtect qui à pour effet de définir le type d'accès sur un espace mémoire. Si on pose un break point sur cette api, voila les différents break :

Au total, 4 appels différents : 423B77, 423B27, 423C00 et 423CED. On pourrait nopper ces 4 appels pour se débarrasser de cette protection, mais on va être plus judicieux. Si on examine minutieusement les breaks et les paramètres passés à VirtualProtect à chaque appel on peux déterminer que le 2eme appel va à un moment s'occuper de la section 401000 (la section qui contient l'instruction à patcher) et lui passer la valeur 20h pour le paramètre qui détermine le type d'accès, si on regarde dans les définitions des constantes windows cette valeur correspond à PAGE_EXECUTE_READ qui donne un accès en lecture et exécution mais pas en écriture comme on a pu le remarquer. On va tout simplement changer cette valeur en PAGE_EXECUTE_READWRITE qui correspond à la valeur 40h et qui cette fois donne un acces en exécution, lecture et écriture. Démonstration, on a ça :

00423b1d 50               push    eax
00423b1e ff75aa push dword ptr [ebp-0x56] => détermine le type d'accès
00423b21 ff755e push dword ptr [ebp+0x5e]
00423b24 ff75ae push dword ptr [ebp-0x52]
00423b27 ff5576 call dword ptr [ebp+0x76] [Call VirtualProtect]

On remplace par :

00423b1d 50               push    eax
00423b1e 6a40 push 0x40 => détermine le type d'accès (PAGE_EXECUTE_READWRITE)
00423b20 90 nop ; équilibre le code
00423b21 ff755e push dword ptr [ebp+0x5e]
00423b24 ff75ae push dword ptr [ebp-0x52]
00423b27 ff5576 call dword ptr [ebp+0x76] [Call VirtualProtect]

On remplace le push dword ptr [ebp-0x56] par push 40. Seulement cette dernière instruction est codée sur 2 bytes alors que la précédente l'était sur 3, on rajoute alors un nop pour équilibrer le code.

Sous un éditeur hexa on patch le crackme packé à l'offset 00003B1E en remplaçant : FF75AA par 6A4090 et voilà plus de problème d'accès mémoire sur la zone de patch. nous pouvons à présent écrire notre self-loader qui ira patcher le crackme et le mettre Registered.

b) patch du crackme en mémoire

Le principe : au moment ou l'on saute vers l'OEP on dévie vers un bout de code que l'on va ajouter, ce bout de code ira modifier les instructions en mémoire pour le crack, ensuite on redirige vers l'OEP, où le programme se déroule avec nos instructions modifiées.

Personnellement je commence par faire tout ça sous un debugger pour voir déjà si ça plante pas et j'en profite pour relever les valeur hexa des instructions ensuite j'effectue les modifs avec un éditeur hexa.

Avant tout il faut chercher de la place libre dans l'exe packé pour y insérer notre code. On pourrait se placer dans l'icône, mais la on vois que Neolite à une belle signature bien longue qui fera largement l'affaire pour y insérer notre code. En 000030E9 qui correspond à l'adresse virtuelle : 4230E9

000030E0 004C4C00 008E3142 004E656F 4C697465 .LL...1B.NeoLite
000030F0 20457865 63757461 626C6520 46696C65 Executable File
00003100 20436F6D 70726573 736F720D 0A436F70 Compressor..Cop
00003110 79726967 68742028 63292031 3939382C yright (c) 1998,
00003120 31393939 204E656F 576F7278 20496E63 1999 NeoWorx Inc
00003130 0D0A506F 7274696F 6E732043 6F707972 ..Portions Copyr
00003140 69676874 20286329 20313939 372D3139 ight (c) 1997-19
00003150 3939204C 65652048 61736975 6B0D0A41 99 Lee Hasiuk..A
00003160 6C6C2052 69676874 73205265 73657276 ll Rights Reserv
00003170 65642E0D 0A00
008B 44240423 05DD3042 ed......D$.#..0B
mov byte ptr [401075], EB 	;(point de patch qui force le saut pour être registered)
jmp eax 		;(eax contient toujours l'adresse de l'OEP on l'on saute)

On effectue les modifications sous un éditeur hexa :

Voila nous avons insérer un self-loader dans l'exe packé après avoir rendu accessible l'écriture en mémoire pour effectuer le patch. Si vous voulez virer le nag de Neolite dans ce crackme :

Pour virer le nag de Neolite :
-----------------------------

:00423A30 50                      push eax		=> nopper
:00423A31 6A00 push 00000000 => nopper
:00423A33 FF551A call [ebp+1A] => nopper

Offset 00003A30 remplacer : 506A00FF551A par 909090909090.

8. Infos pratiques sur Neolite 2.0

00423181 e8ed040000       call    00423673
00423186 fe0576314200 inc byte ptr [00423176]
0042318c ffe0 jmp eax => saute vers l'OEP
00401184 55 push ebp
00401185 8bec mov ebp, esp

&