POSTER 6.3
Reverse Engineering
By Alien & Christal
|
Outil
: wdasm 8.9 :0040C363 8B8D4CFFFFFF mov ecx, dword ptr [ebp+FFFFFF4C]
:0040C369 3B8D48FFFFFF cmp ecx, dword ptr [ebp+FFFFFF48]
:0040C36F 741B je 0040C38C.....................je devient jne
* Possible Reference to String Resource ID=00175: "Invalid Registration Code"
|
:0040C371 68AF000000 push 000000AF
:0040C376 8B1548324800 mov edx, dword ptr [00483248]
:0040C37C 52 push edx
:0040C37D E8CED10300 call 00449550
:0040C382 83C408 add esp, 00000008
* Possible Ref to Menu: POSTER, Item: "New ... "
|
* Possible Reference to String Resource ID=00001: "Create a new poster "
|
:0040C385 B801000000 mov eax, 00000001
:0040C38A EB71 jmp 0040C3FD
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040C36F(C)
|
* Possible StringData Ref from Data Obj ->"081349"
|
:0040C38C 6890734700 push 00477390............vers code ok
pourquoi ? si on descend un peu dans le listing on va trouver une référence a poster.ini * Possible StringData Ref from Data Obj ->"POSTER.INI"
|
:0040C3A7 6898734700 push 00477398
via l'explorateur Windows, on cherche poster.ini et on le trouve dans le répertoire
c:\windows start=081349........au départ cette valeur est a 1 car non enregistré file0=......................garde les fichiers de travail sauvegardé file1=......... file2=......... file3=......... Si on regarde ce qui se passe après le saut vers code ok, on voit que le
soft ouvre le fichier poster.ini, se positionne sur la valeur start, vire le 1 et place 081349. * Possible StringData Ref from Data Obj ->"012248"
|
:0040C1DA 681C734700 push 0047731C
:0040C1DF 8D8554FFFFFF lea eax, dword ptr [ebp+FFFFFF54]
:0040C1E5 50 push eax
:0040C1E6 E8B58E0500 call 004650A0
:0040C1EB 83C408 add esp, 00000008
:0040C1EE 85C0 test eax, eax
:0040C1F0 751B jne 0040C20D................devient jne 0040C38C
encore une autre possibilité :0040C363 8B8D4CFFFFFF mov ecx, dword ptr [ebp+FFFFFF4C] :0040C369 3B8D48FFFFFF cmp ecx, dword ptr [ebp+FFFFFF48] :0040C36F 741B je 0040C38C * Possible Reference to String Resource ID=00175: "Invalid Registration Code" les deux lignes avant le saut sont très belles : :0040C369 3B8D48FFFFFF cmp ecx, dword ptr [ebp+FFFFFF4C] et ca marche aussi . ========================================================================= pour attaquer cette seconde partie on va d'abord ouvrir le fichier poster.ini et
remettre la valeur start a 1 * Possible StringData Ref from Data Obj ->"You have printed %d posters."
|
:00440632 68E0BB4700 push 0047BBE0
:00440637 8D9530FFFFFF lea edx, dword ptr [ebp+FFFFFF30]
:0044063D 52 push edx
En partant de là, on remonte dans le listing jusqu' ici et on trouve des
choses très intéressantes. * Referenced by a CALL at Addresses: |:0042B7D7 , :0042B8DF , :0042B919 , :0043D4E0 | :00440488 55 push ebp :00440489 8BEC mov ebp, esp :0044048B 81ECD4000000 sub esp, 000000D4 :00440491 0FBF054C244800 movsx eax, word ptr [0048244C] :00440498 83F801 cmp eax, 00000001.........devient cmp eax, 00000002 :0044049B 740C je 004404A9 :0044049D 0FBF0D4C244800 movsx ecx, word ptr [0048244C] :004404A4 83F903 cmp ecx, 00000003 :004404A7 7505 jne 004404AE * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0044049B(C) | :004404A9 E9D3010000 jmp 00440681.............vers ret une autre solution c'est de bloquer le compteur pour qu'il reste a 12 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00440583(C) | :0044058A 0FBF0D74314800 movsx ecx, word ptr[00483174]... nbre d'impressions effectuées :00440591 83F90C cmp ecx, 0000000C :00440594 7F12 jg 004405A8 :00440596 668B1574314800 mov dx, word ptr [00483174] :0044059D 6683C201 add dx, 0001....................incrémente le nbre d'impressions :004405A1 66891574314800 mov word ptr [00483174], dx il suffit donc de changer cette valeur en remplaçant le 0001 par 0000 comme
ça le compteur ne compte plus rien et notre valeur restera a 12 impressions. :0044059D 6683C201 add dx, 0001 va devenir sub dx,000A donc 10 - 10 = 0 il nous reste 12 impressions c'est ti pas beau ? * Possible StringData Ref from Data Obj ->"%d"
|
:004405B0 68A0BB4700 push 0047BBA0
:004405B5 8D8D30FFFFFF lea ecx, dword ptr [ebp+FFFFFF30]
:004405BB 51 push ecx
Conclusion |
Lorsque Alien m'a envoyé le programme sur lequel il envisageait de plancher,
j'ai eu le plaisir de le cracker en quelques minutes (mais alors toutes petites les minutes…). Sachant que: Qu'il n'y a pas de références à un call, un saut conditionnel
ou un jump avant l'appel à la Dialog Box, on en vient à se dire qu'il y a forcément eu un
appel indirect sur cette boite... Conclusion :
l'appel se fait très certainement sur l'adresse 00405958 par je sais pas quoi... Bene ! l'appel vient de 0043C4FC. Allons voir : En arrivant en 00401730, ECX vaut 26h, et l'adresse "calculée"
pour le jump donne 004070BE. Un petite intérrogation sur le contenue de cette adresse, et on trouve 00405958
(ou plus exactement 58 59 40 00).
Et valà notre adresse délicatement placée dans une table !. Et voici le résultat dans c:\Windows\Poster.ini Puis le programme va fermer la boite d'enregistrement :
Ces quelques lignes vont nous casser le pied. Si le programme passe par elles alors
que la DialogBox d'enregistrement n'a pas été ouverte, c'est l'application qui va se fermer partiellement,
mais en restant en tache de fond sans possibilité de fermeture autre qu'un CTRL+ALT+SUPPR. On pourrait faire bien plus simple en inversant le saut code valide/code invalide,
mais vous allez voir que l'on va continuer à se faire mal aux cheveux : - il faut récupérer le Handle de la fenêtre (hWnd) Un jeu d'enfant non ? Dans cette routine, et avant d'arriver aux adresses " Good Boy ", il
y a 29 octets à prendre. Si je décide de squatter là, mon patch ne devra pas être plus
long ! Bingo ! Danke Wdasm ! Et Une Item Grisée, Une ! Le patch : La condition en 0040BE73 permet de tester si l'utilisateur est enregistré
ou non, auquel cas le programme saute le patch, et l'Item reste intact pour permettre d'accéder à
celui ci. Si l'utilisateur est enregistré (bonnes informations dans le fichier Poster.ini), l'Item est Grayed. Pour qu'il apparaisse grisé, il faut remplacer 80 par 81 :
Et basta ! On pourrait continuer encore longtemps à apporter des modifications à
l'originale, mais il faudra rapidement trouver de l'espace pour structurer toutes ces patchs, et ne pas continuer
à jouer sur l'empirisme... La section .rsrc étant la dernière, jetons un œil bienveillant sur
celle-ci. Soit A6000 - A5EF0 = 110h, ou encore 272 octets de disponibles à partir
de l'adresse 0x00489000 + 0x252EC = 0x004A52EC.
Le genre de protection juste histoire de s'amuser 5 minutes! et de pouvoir se dire que tout auteur de Shareware
devrait prendre des "cours" de cracking avant de chercher à monter une usine à gaz...
Pour autant, un programme peut encore receler des éléments intéressants, et Alien a eu raison
de continuer.
Pour ma part, je me demandais bien ce que j'allais pouvoir faire de ce truc…
En plein dans ma période " DialogBox ", il m'a semblé qu'il pourrait être amusant
de s'occuper un peu de celle qui s'occupait de l'enregistrement…
Et pour commencer, il fallait trouver l'adresse de cette DialogBoxParamA :
En cherchant " REGISTER " dans le Dead Listing:
* Reference To: USER32.MessageBoxA, Ord:0195h
|
:0040594B FF15B4694800 Call dword ptr [004869B4]
:00405951 33C0 xor eax, eax
:00405953 E9BE160000 jmp 00407016
:00405958 6A00 push 00000000
:0040595A 682EBF4000 push 0040BF2E
:0040595F 8B4508 mov eax, dword ptr [ebp+08]
:00405962 50 push eax
* Possible StringData Ref from Data Obj ->"REGISTER"
|
:00405963 6898684700 push 00476898
:00405968 8B0D50324800 mov ecx, dword ptr [00483250]
:0040596E 51 push ecx
* Reference To: USER32.DialogBoxParamA, Ord:008Eh
|
:0040596F FF1570694800 Call dword ptr [00486970]
Les lignes qui précèdent ne laissent en rien supposer que le programme ait pu passer par la :
:0040594B FF15B4694800 Call USER32.MessageBoxA > pas vu
:00405951 33C0 xor eax, eax
:00405953 E9BE160000 jmp 00407016 > pas pris
Ce type de boite se paramètre de la manière suivante :
int DialogBoxParam(
HINSTANCE hInstance, // handle to application instance
LPCTSTR lpTemplateName, // identifies dialog box template
HWND hWndParent, // handle to owner window
DLGPROC lpDialogFunc, // pointer to dialog box procedure
LPARAM dwInitParam // initialization value
);
push 00000000 hInstance
push 0040BF2E lpTemplateName
mov eax, dword ptr [ebp+08]
push eax hWndParent
push 00476898 ->"REGISTER" lpDialogFunc
mov ecx, dword ptr [00483250]
push ecx dwInitParam
Call USER32.DialogBoxParamA
Jetons un coup d'œil sur la pile :
Stack
KERNEL32!UTUnRegister+09DB at :BFF94407 (SS:EBP 0187:006DFC4C)
KERNEL32!ORD_0079+0757 at :BFF7363B (SS:EBP 0187:006DFC38)
POSTER!.text+0003B4FC at :0043C4FC (SS:EBP 0187:006DFC18)
ð POSTER!.text+072A at :0040172A (SS:EBP 0187:006DF3B4)
Petit trace into le call sur lequel nous fait arriver cette adresse, et :
:0040171C 0F87FE560000 JA 00406E20
:00401722 8B958CF4FFFF MOV EDX,[EBP+FFFFF48C]
:00401728 33C9 XOR ECX,ECX
:0040172A 8A8AEE704000 MOV CL,[EDX+004070EE]
:00401730 FF248D26704000 JMP [ECX*4+00407026] -> Oups !
:00401737 81BD8CF4FFFFBC020000CMP DWORD PTR
Ca roule c't'affaire !
Maintenant un ch'tit coup d'éditeur hexa :
000064B0 4000 1C22 4000 A829 4000 A660 4000 5859 @.."@..)@..`@.
000064C0 4000 711B 4000 4966 4000 8B66 4000 8143 .q.@.If@..f@..C
Ce qui pourrait être fait, maintenant, c'est de remplacer cette valeur par l'adresse de la routine d'enregistrement
de l'utilisateur validé que l'on va décortiquer un brin pour la partie la plus intéressante:
:0040C3A7 PUSH 00477398 > Poster.ini
:0040C3AC LEA ECX,[EBP-00AC] > bon sérial
:0040C3B2 PUSH ECX > poussé sur la pile
:0040C3B3 PUSH 004773A4 > start
:0040C3B8 PUSH 004773AC > POSTER
:0040C3BD CALL [WritePrivateProfileStringA] > écrit
[POSTER]
start=081349
La suite est moins intéressante :
:0040C3C3 MOV WORD PTR [0048244C],0001 > Flag Utilisateur Registered
:0040C3CC PUSH 00 > Bouton MB_OK
:0040C3CE PUSH 004773B4 > Registration (Titre)
:0040C3D3 PUSH 004773C4 > Thank You (message)
:0040C3D8 MOV EDX,[EBP+08] > Handle de la fenêtre
:0040C3DB PUSH ECX > poussé sur la pile
:0040C3DC CALL [MessageBoxA] > Message Box Glop ! Glop !
:0040C3E2 PUSH 00
:0040C3E4 MOV EAX,[EBP+08]
:0040C3E7 PUSH EAX
:0040C3E8 CALL [EndDialog]
On vire en remplaçant Push 00 par JMP 0040C3EE
:0040C3EE MOV EAX,00000001 > Good Boy !
:0040C3F3 JMP 0040C3FD > et on quitte...
:0040C3F5 XOR EAX,EAX
:0040C3F7 JMP 0040C3FD
:0040C3F9 JMP 0040C4A0
:0040C3FB JMP 0040C4A0
:0040C3FD MOV ESP,EBP > en passant par ici.
:0040C3FF POP EBP
:0040C400 RET 0010
Griser l'Item Registration :
Le programme permettant d'entrer différents codes (code : + 2 jours de rabe, code : serial temporaire et
code : number définitif), l'auteur a laissé l'accès à la boite d'enregistrement, même
avec le code définitif entré.
C'est pas BO !
On corrige...
Le principe est simple :
- il faut récupérer le handle des menus
- il faut trouver l'identificateur (ID) de l'item " register "
- et il faut la griser.
Et bien sur, il faut aussi trouver de la place !
On va commencer par ce petit problème.
Ce qui m'est venu à l'esprit en premier, a été de prendre la place de la routine " Code
Invalide " :
017F:0040C35D 89854CFFFFFF MOV [EBP-00B4],EAX
017F:0040C363 8B8D4CFFFFFF MOV ECX,[EBP-00B4]
017F:0040C369 3B8D48FFFFFF CMP ECX,[EBP-00B8]
017F:0040C36F 741B JZ 0040C38C > Jump Good Boy
017F:0040C371 68AF000000 PUSH 000000AF > début de Bad Boy
017F:0040C376 8B1548324800 MOV EDX,[00483248]
017F:0040C37C 52 PUSH EDX
Récupérer le handle de la fenêtre :
Le plus simple est de trouver une API qui en a également besoin, et où, habituellement, le programme
va le mettre en mémoire :
* Reference To: USER32.CreateWindowExA, Ord:0055h
|
:004372EF FF15586A4800 Call dword ptr [00486A58]
:004372F5 A348324800 mov dword ptr [00483248], eax
En [00483248] vous avez EAX qui vient se glisser dans une adresse mémoire (hWnd). Gut !
L'identificateur de l'Item de Menu :
Tutorial [ID=0112h]
Examples [ID=0100h]
About ... [ID=0015h]
Registration [ID=003Ah]
Mon ID est 0x003A
Au tour du handle des menus. Là encore, ce qui m'a paru le plus facile est d'utiliser l'API GetMenu, pour
récupérer ce handle dans EAX.
Et sur la lancée, modification de l'item :
:0040C377 FF3548324800 PUSH DWORD PTR [00483248] > hWnd
:0040C37D E89760B4BF CALL USER32!GetMenu > EAX = hMenu
:0040C382 6A01 PUSH 01 > grayed
:0040C384 6A3A PUSH 3A > ID Item
:0040C386 50 PUSH EAX > hMenu
:0040C387 E8426AB4BF CALL USER32!EnableMenuItem > Done !
:0040C38C 6890734700 PUSH 00477390
:0040C391 8D8554FFFFFF LEA EAX,[EBP-00AC]
Par contre, en relançant l'application, tout enregistré que vous soyez, vous avez droit au grand
retour de notre Item tout ce qu'il y a de plus actif...
Une âme courageuse pourrait mettre un BPM sur le Flag Utilisateur ([0048244C]), attendre que la vérification
du sérial lui ait été favorable, et s'occuper ensuite de modifier l'Item si l'Utilisateur
est bel et bien enregistré. C'est tout à fait possible suite au passage à l'adresse 0040BC51
(bien sur après l'affichage de la fenêtre !). Puis envoyer le programme vers une routine identique
à celle du dessus :
:0040BC51 8B4C85F0 MOV ECX,[EAX*4+EBP-10] > Tout OK
:0040BC55 E919020000 JMP 0040BE73 > jump Patch
:0040BC5A 90 NOP > équilibrage
Il est la réplique du précédent. D'ailleurs, il serait même possible d'envisager que
le même patch puisse servir aussi bien à effacer l'Item Registration après enregistrement,
qu'au lancement de l'application. Tout est histoire de condition. En tout état de cause, pour le moment
je laisse en l'état, et je pars à la recherche d'un peu de place disponible. Quelques lignes en dessous
de l'adresse 0040BC55, je trouve toute une famille de OR [ECX],ECX qui semble n'avoir rien à faire. Un
petit BPR sur un bloc d'au moins une cinquantaine d'octets, et Zou ! C'est parti pour un patch...
:0040BE73 833D4C24480001 CMP DWORD PTR [0048244C],01
:0040BE7A 7515 JNZ 0040BE91
:0040BE7C FF3548324800 PUSH DWORD PTR [00483248]
:0040BE82 E89265B4BF CALL USER32!GetMenu
:0040BE87 6A01 PUSH 01
:0040BE89 6A3A PUSH 3A
:0040BE8B 50 PUSH EAX
:0040BE8C E83D6FB4BF CALL USER32!EnableMenuItem
:0040BE91 51 PUSH ECX
:0040BE92 6859020000 PUSH 00000259
:0040BE97 E9BFFDFFFF JMP 0040BC5B
:0040BE9C 0909 OR [ECX],ECX
:0040BE9E 0909 OR [ECX],ECX
:0040BEA0 0909 OR [ECX],ECX
Et pour ne pas flanquer le souque dans le programme, les 2 push ECX et 00000259 restaurent les octets que le jmp
en 0040BC55 avait écrasé.
Mais il a moyen de faire autrement, bien que de façon assez définitive:
Griser l'item directement dans le fichier sur DD :
Avant modification, le texte de l'Item (en Wide Char Format) et de ses caractéristiques est ainsi :
00090420 1500 2600 4100 6200 6F00 7500 7400 2000 ..&.A.b.o.u.t. .
00090430 2E00 2E00 2E00 0000 8000 3A00 2600 5200 ..........:.&.R.
00090440 6500 6700 6900 7300 7400 7200 6100 7400 e.g.i.s.t.r.a.t.
00090450 6900 6F00 6E00 0000 0000 0000 1000 7800 i.o.n.........x.
00090420 1500 2600 4100 6200 6F00 7500 7400 2000 ..&.A.b.o.u.t. .
00090430 2E00 2E00 2E00 0000 8100 3A00 2600 5200 ..........:.&.R.
00090440 6500 6700 6900 7300 7400 7200 6100 7400 e.g.i.s.t.r.a.t.
00090450 6900 6F00 6E00 0000 0000 0000 1000 7800 i.o.n.........x.
Par contre, plus moyen d'accéder à cet Item, et donc de provoquer un enregistrement par un clic sur
celui ci à la première fois ou l'utilisateur voudra s'enregistrer...
Un peu trop radical, non ?
On peut continuer à faire mumuse...
Virer la DialogBox de départ:
Je n'en vois pas l'utilité. Elle n'apporte rien, que ce soit dans la version Shareware (style : Enregistrez-vous)
soit dans la version Enregistrée. Pour la supprimer, il y a différentes méthodes, mais une
de celles qui est la plus simple à mettre en œuvre, et de la réduire à la taille d'un pixel.
ExeEscope est parfait pour ce genre de travail :
X 9 Y 39 Width 1 Height 1
Trouver de la place :
Regardons ce que le padding a pu nous laisser comme place à la fin de la dernière section de Poster.exe
:
Name Virt Size Virt Offset Raw Size Raw Offset Characteristics
.text 00072ED3 00001000 00073000 00000400 60000020
.rdata 00001BB0 00074000 00001C00 00073400 40000040
.data 0000F5E4 00076000 00009A00 00075000 C0000040
.idata 00002008 00086000 00002200 0007EA00 C0000040
.rsrc 000252EC 00089000 00025400 00080C00 40000040
Adresse de début de la section :
Ad Image Base 00400000 + Virtual Offset 00089000 = 00489000
Le code de la section .rsrc commence à l'offset 0x80C00 pour finir à l'offset 0x80C00 + 0x25400 =
0xA6000, mais la taille du code étant de 0x252EC, il doit être possible de squatter quelques octets
à partir de l'offset 0xA5EF0.
Confirmation en Image :
000A5EC0 0900 4200 6100 6400 2000 5600 6100 6C00 ..B.a.d. .V.a.l.
000A5ED0 7500 6500 0000 0000 0000 0000 0000 0000 u.e.............
000A5EE0 0000 0000 0000 0000 0000 0000 0000 0000 ................
000A5EF0 0000 0000 0000 0000 0000 0000 0000 0000 ................
Espérons que ce sera suffisant...
Bonne Journée