_________________ _.;'_______ ______ _____ __ __ _ __ _ __ _ _ [' [#.15] _|_| Program Cracking \_________/_/ So CalleD _ ______ __ _ _ Rix strike back _/_/ [By:_rix] \ ___________________ __________________ _________ ______/_/_ __ _ __ _ _ _,] Program Cracking ================ Comment les cracks sont-ils concus, et quelles sont les faiblesses de tout ces sharewares que l'on trouve de plus en plus facilement ? Je vais essayer de repondre a ces 2 petites questions, en utilisant pour cela un exemple tout a fait pratique. Nous allons essayer de cracker WinRAR, un utilitaire de compression excellent, qui permet de compresser mieux que WinZIP, et qui en plus, permet de compresser aussi les fichiers ZIP !!! Je vais donc ici vous montrer comment proceder sur une version de WinRAR, en l'occurence la version 2.50 beta 3, que j'ai chargee a l'adresse suivante: http://www.creabel.com/softronic REMARQUE: LES TECHNIQUES QUI SONT EMPLOYEES DANS CET ARTICLE SONT SIMPLEMENT UTILISEES ICI POUR DEMONTRER LES FAIBLES MOYENS MIS EN OEUVRE PAR LA PLUPART DES PROGRAMMEURS POUR PROTEGER LEUR PROGRAMME, DANS LE BUT D'INFORMER CEUX-CI DES TECHNIQUES HABITUELLES EMPLOYEES PAR LES CRACKERS, ET DE LES AIDER A S'EN PROTEGER. LE PROGRAMME WINRAR N'EST ICI UTILISE SIMPLEMENT QU'A TITRE D'EXEMPLE, ET L'AUTEUR DE CET ARTICLE VOUS ENCOURAGE PLUS QUE VIVEMENT A ENREGISTRER TOUT LES PROGRAMMES SHAREWARES QUE VOUS UTILISER. LES OUTILS ========== Pour cela, nous allons avoir besoin de quelques outils tres pratiques et obligatoires pour realiser ce que nous voulons. Tout d'abord, je vous conseille de prendre WIN32DASM, le meilleur debugger-desassembleur sous Windows (ca n'engage que moi ;). ( http://www.expage.com/page/w32dasm ) Ensuite, il sera tres interessant d'avoir un petit editeur hexadecimal, par exemple UltraEdit, qui est tres bien fait. ( http://www.idmcomp.com ) Vous allez voir, qu'avec ces 2 programmes tres puissants, il est assez facile de cracker un petit programme. Cependant, il est conseille d'avoir une certaine connaissance de l'assembleur, etant donne que nous allons devoir analyser certaines parties (petites, rassurez-vous ;), du code du programme. WIN32DASM ========= Avant de commencer le crack proprement dit, je vais d'abord vous expliquer quelques fonctions bien utiles que nous offre WIN32DASM. Une fois WIN32DASM lance, nous pouvons effectuer toute une serie de recherche d'informations sur le programme que nous allons debugger. Pour cela, vous pouvez d'abord charger le fichier WINRAR.EXE dans WIN32DASM, a l'aide de la commande "Open file to disassemble" du menu "Disassembler". Cette commande va prendre un certain temps, car c'est en fait elle qui est responsable de toute la partie desassemblage du programme. Une fois le programme desassemble, nous allons avoir le code de WINRAR.EXE affiche dans la fenetre. Ce code est decompose comme ceci: D'abord les valeurs de l'offset et de la taille du code et des donnees. Ensuite le nombre d'objets du programme (pas trop important pour nous ;) Puis les eventuels menus et boites de dialogues. Viennent ensuite toutes les APIs Windows que le programme utilise, classees suivant la DLL d'ou elles proviennent. Puis enfin, toutes les fonctions qui sont exportees par le programme (pas trop utiles pour nous non plus ;) Au niveau de chaque instruction du programme, chaque ligne est composee d'une adresse, suivie du code hexadecimal de l'instruction, suivie de l'instruction assembleur correspondante. Au niveau des commandes, WIN32DASM va nous permettre d'effectuer des recherches de chaine dans le code, par exemple, grace a la commande "Find Text" du menu "Search". Nous pouvons aussi aller a une adresse precise, avec la commande "Goto Code Location" du menu "Goto". WIN32DASM nous permet aussi d'effectuer les jumps et les calls dans le code desassemble, grace aux commandes du menu "Execute Text". Lorsque WIN32DASM detecte une instruction de saut ou de call, elle est automatiquement affichee en vert, pour plus de facilite ;)) De plus, au debut de chaque procedure, WIN32DASM nous indique a quels endroits du programme cette procedure est appelee. Ensuite, dans la ligne de status en bas de la fenetre, nous pouvons voir des informations tres interessantes, comme l'offset de l'instruction courante dans le fichier EXE original. Nous pouvons aussi visualiser le(s) segment(s) de donnees en hexadecimal, grace a la commande "Hex Display Of Data Object/Segments" du menu "HexData". Enfin, nous pouvons visualiser toutes les ressources utilisees par le programme, grace aux commandes "Menu References", "Dialog References", et "String Data References" du menu "Refs". On peut aussi utiliser plus facilement plusieurs de ces commandes grace a la barre d'icones ;) Maintenant nous allons passer au debugger. Pour lancer le debugging du programme, il nous suffit de cliquer sur la commande "Load Process" du menu "Debug". On a un chtit ecran "Load Dissassembly Dialog" qui apparait, et qui peut servir a entrer des parametres de la ligne de commandes au programme. Ici, dans le cas de WINRAR, on a pas besoin de ca, donc on clique direct sur "Load". Nous avons 3 fenetres qui apparaissent a l'ecran. La 1ere contient toujours le code source desassemble. La 2eme contient l'etat des registres, de la pile, et toutes une serie d'informations sur les process, etc... La 3eme contient le code tel qu'il est present en memoire, et les commandes de debuggage proprement dit. Dans la 2eme fenetre, on peut observer au fur et a mesure de l'execution les modifications que subissent les registres, mais aussi observer le contenu de la pile du programme, et observer des variables qui sont en memoire. Dans la 3eme fenetre, on peut executer le code pas a pas dans les procedures ou non (F7/F8), on peut aussi faire une pause dans l'execution, l'executer normalement (F9), ou encore le patcher, c'est a dire modifier les instructions qui vont etre executees. Une autre fonction tres pratique est la fonction d'ajout de point d'arret dans le programme. Pour cela, il faut voyager avec le curseur dans la fenetre de code et presser sur F2 a l'endroit ou on veut placer un breakpoint. Le debut de la ligne se met alors en jaune pour l'indiquer. Pourquoi une 3eme fenetre alors que on a deja le code dans la 2eme ? Tout simplement parce que cette 3eme fenetre peut nous permettre aussi de debugger les DLLs ou les APIs Windows appelees par le programme, qui ne sont pas presentes dans le code desassemble. METHODES DE BASE ================ Je vais vous donner ici quelques techniques de bases pour trouver les cracks. Faut d'abord savoir que chaque programmeur code son programme comme il le veut , donc il n'y a pas de methode qui marche a tout les coups. Il faut souvent tester, analyser, et essayer, pour se rendre compte que ca ne marche pas et qu'il faut essayer autre chose. Tout d'abord, les programmes stockent souvent les informations de registration selon une de ces manieres: - dans le programme executable lui-meme, en allant modifier une donnee dans le programme, ou bien dans un fichier de donnees quelconque. - dans la base des registres, en mettant une valeur de cle a jour, ou dans un fichier .INI (du aux anciennes versions de Windows, style 3.1, etc) Dans le 1er cas, il va donc souvent s'agir d'aller modifier cette valeur pour simuler la registration. On peut aussi essayer d'inverser les conditions de la registration dans le programme (souvent des comparaison avec des sauts conditionnels). Dans le 2eme cas, on peut aussi evidemment aller modifier les informations dans la base des registres, mais c'est plus risque. Personnellement, je vous conseille d'essayer les inversion de comparaison dans ce cas-la aussi. Comment savoir ou se trouvent les differentes comparaisons dans le programme? On pourrait balayer tout le code du programme, a la recherche de ces comparaisons, ou bien le debugger a partir du debut. Mais cette technique peut etre tres longue, et surtout tres compliquee, car il faut quasiment connaitre tout le fonctionnement du programme en assembleur. Si le programme fait 500 lignes ca va, mais s'il en fait plus, comme 163000 comme WINRAR, ca devient un travail de fou ;)))) De plus, deja comprendre le programme de quelqu'un avec le code source n'est pas evident, mais avec le code assembleur, je vous souhaite bien du courage ;)))) On va donc essayer de proceder plutot dans le sens inverse. C'est a dire rechercher dans le code les endroits qui font intervenir des donnees de registration, et remonter a partir de ces donnres aux comparaisons. Autre chose: Ce n'est pas parce que l'on a trouver une comparaison et qu'on l'a inversee que le crack est parfait. Les programmes sont souvent concus pour posseder plusieurs protections, et la suppression d'une seule n'est pas suffisante. Comme vous le voyez, il n'y a pas vraiment de grande ligne a suivre, cela se fait un peu au feeling. Je vais essayer de vous montrer le chemin que j'ai suivi pour cracker ce programme, avec les faux pas et les bons, pour que vous puissiez comprendre plus efficacement. Au niveau de la methode "generale", je vous conseille de proceder de la maniere suivante: - analyse de la registration au niveau de l'interface - analyse des references - analyse du code relatif au references - analyse des donnees - debugging eventuel - patching - tests ANALYSE DE L'INTERFACE ====================== Pour mieux suivre la suite, je vous conseille de charger cette doc dans un fichier dans votre bloc-notes par exemple, et de faire les tests et les commandes en meme temps que moi. Vous comprendez surement beaucoup mieux ;) Avant de debugger, ce que l'on a trop souvent tendance a faire au debut, nous allons d'abord analyser un petit peu la registration en detail. Lancons WINRAR.EXE normalement (pas dans WIN32DASM I mean ;) Cherchons ou se trouve ce foutu ecran de registration :) Il se trouve dans le menu "Options", commande "Registration". Notons cette petite commande interessante. Ensuite, regardons un petit peu l'interface. On remarque que la barre de titre de la fenetre contient un beau message "evaluation copy". Mettons le aussi de cote. Allons aussi voir une eventuelle information dans la boite "A propos de...", que l'on trouve assez couramment. Ici, pas de chances, la boite de dialogue "About WinRAR..." ne mentionne pas la registration ou non du programme. Il est donc souvent tres interessant de noter le plus d'informations possibles qui sont affectees par la registration, car ces informations doivent probablement subir un test avant d'etre affichee, dans le menu, dans la barre de titre, etc... Maintenant, nous allons essayer d'entrer un numero de registration errone, pour voir comment le programme reagit. Ne vous inquietez pas, tout les programmes permettent des erreurs de registration, c'est si vite arrive, donc pas d'panik ;) Nous lancons donc la commande "Registration" du menu "Options", et nous arrivons devant une belle boite de dialogue. Notons toutes les chaines de caracteres qui sont affichees dans cette boite de dialogue, elle pourront eventuellement nous renseigner sur l'endroit ou la boite de dialogue est appelee dans le programme. Ensuite, entrons un texte et un code de registration, n'importe quoi. Par exemple: "iga" et "iga", et pressons "OK". Nous avons immediatemment une boite de dialogue qui s'amene, toute contente, avec un titre "Warning", ainsi qu'un beau message "Registration failed". Notons evidemment ces chaines de caracteres. Voila, nous avons a peu pres tout note au niveau de l'interface... ANALYSE DES REFERENCES ====================== Nous lancons WIN32DASM et chargeons le programme WINRAR.EXE. Nous allons d'abord analyser les references, c'est a dire essayer de trouver les donnees que nous avons note dans la phase precedente, et de trouver ou ces donnees sont utilisees dans le programme. Allons dans le menu "Refs", et cliquons sur "Menu References". Nous avons une liste de toutes les chaines presentes dans les menus qui sont affichees. Parcourons un peu cette liste, a la recherche de chaines qui pourraient nous interesser. Nous trouvons la chaine "Registration", correspondant a la commande du menu "Options". Cliquons 2x sur cette chaine, WIN32DASM saute directemment aux endroits correspondants dans le code ou cette chaine est referenciee. Nous arrivons a un endroit du code, autour de la ligne d'adresse 0044B3A6, 0044B3AB. A priori, le code qui suit n'as pas l'air tres explicite sur ce qui se passe a cet endroit la du code. En fait, les commandes de menus n'apportent pas souvent d'informations tres utiles, car elles sont justes affichees et n'effectuent aucun traitement. La decision de l'affichage ou non de la commande ne se fait pas au meme endroit dans le code, elle se fait dans la gestion de l'objet du menu, dont nous ne savons pas ou elle se trouve ;) Allons maintenant dans le menu "Refs", et cliquons sur "Dialog References". Parcourons cette liste, a la recherche d'informations. Nous ne trouvons malheureusement pas d'information tres interessante. Allons ensuit dans le menu "Refs", sur "String Data References". Cette liste contient toutes les chaines de caracteres du programme. Elle est assez longue, mais il est tres interessant de la parcourir entierement, car elle apprend souvent plus d'informations que les references de menu ou de boites de dialogue. Premiere chaine interessante, sur la premiere page: "Available in registered version only". Cliquons 2x dessus, et nous sommes amenes dans le code pres de l'adresse 00404670. En remontant un peu dans le code, ainsi qu'en descendant un peu, autour de cette adresse, nous voyons des references vers "Unselect group","Invert selection copy", etc... Cela nous indique que ce message permet de faire des selections de groupe, etc... dans la version enregistree. En observant un peu plus le code juste au dessus de la ligne, nous avons ceci: call 0040A0C0 add esp,0000000C test eax,eax je 00404678 push 00000001 Pourquoi remarquer cette partie du code ? Tout simplement parce que nous y observons une comparaison TEST, sur le registre eax. La commande TEST en assembleur compare le contenu du registre de gauche a celui de droite, en effectuant un AND binaire bit a bit entre les 2 registres. Ici, comme nous faisons un AND binaire entre EAX et EAX, nous obtenons forcement EAX comme valeur resultante. Cette methode est souvent utilisee par des compilateurs, pour optimiser le code, pour savoir si un registre est egal ou non a 0. Donc, si EAX est egal a 0, le processeur saute a l'adresse 00404678. Allons un peu ce qui se passe a cet endroit. Positionons le curseur sur la ligne "je 00404678", et la ligne devient de couleur verte. ( JE est une instruction assembleur qui effectue un saut a l'adresse indiquee si les valeurs comparees dans la derniere comparaison etaient egales ) Maintenant, cliquons sur lancons la commande "Execute Jump" du menu "Execute Text". Nous arrivons donc a l'adresse 00404678. Remontons un petit peu, pour voir ou nous sommes, et oh surprise :))) Nous avons sauter la partie du code qui utilisait la reference !!! Qu'est-ce que cela veut dire ? Cela veut dire que EAX, s'il est different de 0, a cet endroit du programme, indique probablement si le programme est enregistre ou non. Pourquoi EAX ? Retournons a notre instruction de saut JE originale, en pressant "Return From Last Jmp" dans le menu "Execute Text". Un peu au dessus, juste au dessus de la ligne "add esp,0000000C", nous avons une instruction CALL. CALL represente un appel de procedure. Or, dans la programmation habituelle structuree sous Windows, les fonctions (procedures) renvoient leur valeur resultante dans le registre EAX. Donc, apparement, cette fonction renvoie une information concernant la registration ou non du programme. Allons donc un peu voir ce que cette fonction fabrique ;) Nous allons donc sur le CALL, puis nous lancons la commande "Execute Call" du menu "Execute Text". Nous arrivons dans le code de cette procedure. Un petit rappel, la fin de la procedure est toujours signalee par un RET. Ici, nous apercevons un RET quelques lignes plus bas, qui montre donc que cette procedure n'est pas tres longue. Autre information: au debut les procedures, vous appercevrez souvent beaucoup de commandes qui font intervenir [ebp+X] dans des instructions assembleur. Cela est du tout simplement au fait que les parametres des fonctions sont passes sur la pile dans les languages courants, et que ce registre EBP est classiquement utilise pour acceder a ces parametres. De meme, le registre ESP est lui souvent modifie en debut de procedure pour "descendre" la pile, et reserve de l'espace dessus, le plus souvent pour les variables locales de la procedure. Cela se fait au moyen d'une instruction "sub esp,VALEUR". Ce n'est cependant pas le cas dans cette procedure. Cette fonction ne fait donc apparement pas grand chose, exepte un autre CALL, vers l'adresse 0040A064. Allons voir cette 2eme fonction, avec la commande "Execute Call". Nous arrivons a une procedure apparement plus importante. En regardant un petit peu les pages qui suivent, nous appercevons que cette procedure fait appel a une API Windows: l'API RegQueryValueExA (ligne en bleu dans le code). Cette API est en effet une API qui permet d'aller chipoter dans la base des registres... hehe cela devient interessant ;) Mais cette fonction a l'air assez compliquee, donc nous n'insiston pas trop pour l'instant... Vous pouvez revenir 2x en arriere avec la commande "Return From Call", puisque nous avons utiliser 2x la commande "Execute call" ;)) Nous avons donc analyser tout ce qui tourner autour de la 1ere refrence de chaine qui nous semblait interessante. Revenons a la liste des references de chaine, et continuons a la parcourir. Beaucoup plus loin vers la moitie de la liste, nous trouvons la reference suivante: "String Resource ID=00870 : Registration Failed", ainsi que plusieurs autres chaines interessantes derriere !!! Cliquons 2x sur la reference "Registration Failed", pour voir ou cette reference est utilisee par le programme. Nous arrivons autour de l'adresse 00409DC0. Ahhh... Enfin quelques trucs interessants ;) Tout d'abord, nous observons un peu en dessous un bel appel de l'API MessageBoxA. En remontant d'une page, on appercoit aussi notre "Warning", qui etait le titre de la boite de dialogue du message d'erreur... En regardant un peu apres le MessageBoxA, nous appercevons un autre message que l'on aimerai justement bien voir apparaitre ;))) "Correct Registration", suivi de "Thank you for support", puis suivi encore un peu plus loin d'une autre appel a MessageBoxA. Tout cela devient assez interessant n'est-ce pas ;))) D'ou cela provient-il ? Le fait que ces 2 messages soient si proches l'un de l'autre n'est pas du au hasard. Quand le compilateur code une instruction conditionnel du type "if {} else {}", il utilise pour cela une comparaison en assembleur, suivie d'un Jx (JE,JC, sauts conditionnels) vers l'une des solutions. L'autre solution reste dans le deroulement "sequentiel" du code du programme. Donc, vraissemblablement, il doit y avoir de nouveau une comparaison avant ces instructions qui effectue un saut vers l'un ou l'autre des affichages, suivant que le programme est enregistre ou non. Remontons donc un petit peu, vers + ou - l'adresse 00409DA9. Nous retrouvons un suite d'instructions un peu semblable a tantot, a savoir: call 004272C0 add esp,00000008 test eax,eax jne 00409DE1 Apres ces instructions, le processeur continue et execute les instructions qui vont afficher la boite de dialogue avec une erreur de registration. Analysons un peu le JNE, pour voir ou il nous emmene, en utilisant la commande "Execute Jump". Oh surprise :))) Nous nous retrouvons cette fois devant le message "Correct registration" !!! Donc, de nouveau, c'est la comparaison "test eax,eax" qui precede qui decide ou non du fait que le programme est enregistre... Et de nouveau, nous avons un peu avant cela un appel de fonction, qui renvoie sans doute une valeur dans EAX. Remarque: certains personnes auraient tendance a aller essayer de modifier la comparaison qui effectue le saut vers le bon affichage, en l'inversant. Attention ! Si vous ne modifiez que ce saut, vous aurez en effet le bon message "Registration Correct" qui sera affiche, mais les commandes actives dans la version enregistree ne seront toujours pas valide ! En regle generale, il est preferable d'essayer de modifier la source de la comparaison, que la comparaison elle-meme, car il y en a souvent plus que une. ANALYSE D'UNE PROCEDURE INTERESSANTE ==================================== Allons un peu voir ce qui se trouve dans la fonction (commande "Execute Call"). Nous nous rendons compte que cette fonction a l'air assez longue, et qu'elle appelle elle-meme plusieurs autres fonctions... Mais que cherchons nous ? En fait, nous cherchons a atteindre la fin de la procedure, et de voir quelle est la valeur qui est renvoyee via EAX a notre programme principal. Notons cependant l'adresse du CALL vers la fonction: 00409DA9 Nous allons donc parcourir cette fonction, en effectuant tout les jumps internes grace aux commandes "Execute Jump", jusqu'a ce que nous arrivions a la commande RET. Nous ne rentrerons cependant pas dans les CALL de cette procedure, pour ne pas nous perdre ;) Pourquoi ne pas effectuer ceci avec un desassembleur allez vous dire ? Tout simplement pour eviter de perdre inutilement du temps dans des boucles eventuelles, qui pourraient rendre le tout extremement confu ;) Donc, des que la couleur du curseur devient verte (saut), nous regardons s'il ne s'agit pas d'un CALL, et si ce n'est pas le cas, nous regarderons si nous devons executer ce saut ou pas. Nous allons ainsi arriver au 1er test: 0042732A test eax,eax je 00427334 Nous allons essayer d'arriver le plus vite possible vers la fin de la procedure, et si nous ne trouvons rien d'interessant de cette maniere, nous essayerons un autre "parcours" dans la procedure. Pour aller le plus vite, il sufit de regarder les sauts conditionnels, et de voir si l'adresse cible est apres l'adresse courante ou non. Si c'est le cas, nous y allons avec la commande "Execute Jump". Dans ce cas-ci, donc, nous executons le JE. Nous arrivons a ceci xor eax,eax jmp 004275C6 Le XOR est une instruction souvent utilisee dans les compilateurs pour remettre un registre a 0(elle est plus economique en codage hexadecimal). En effet, le XOR effecture un OU EXCLUSIF entre les 2 registres fournis. Or, les 2 registres etant egaux dans ce cas-ci, il en resulte un 0. Effectuons ensuite le saut inconditionnel vers 004275C6. Nous nous retrouvons a la fin de la procedure, apres une serie de depilage peut interessants. Donc, c'est une valeur de 0 qui est retournee a notre programme principal, et nous savons que cette valeur 0 entrainera un affichage d'un message de registration incorrecte. Nous allons donc revenir au test precedent la mise a 0 de EAX, et prendre l'autre parcours, a savoir ne pas effectuer le JE. Pour revenir, nous pouvons utiliser la commande "Return From Last Jump" 2x. Pourquoi ne pas essayer de savoir d'ou provient la valeur qui est dans EAX a cet endroit ? Parce que nous ne sommes peut-etre qu'au debut de la procedure, et que cette valeur n'a peut-etre rien avoir directemment, ou que cette valeur peut encore subir plusieurs modifications avant la fin de la procedure. Nous n'executons donc pas le "je 00427334". Nous arrivons a un 2eme test: cmp dword ptr [ebp-04],0000003C jle 0042733B Si nous n'executons pas ce jump, nous observons que nous revenons au fameux "xor eax,eax", ce qui n'est pas interessant. Nous executons donc ce JLE. Nous avancons un petit peu, jusqu'a: 00427346 mov dword ptr [0045155C],eax test eax,eax jne 00427368 Qu'observons nous la ? Tout d'abord, nous observons de nouveau un TEST sur EAX. Mais, nous observons aussi que le programme accede a une variable globale, situee a l'adresse [0045455C]. Or, un bon programmeur evite le plus possible d'utiliser des variables globales, sauf quand cela est vraiment necessaire. Le plus souvent, il utilise des variables sur la pile, referencees d'une toute autre maniere, a l'aide des EBP ou de ESP. N'oublions pas que une variable quelconque qui indiquerai la registration serait plus probablement globale que locale, etant donne qu'elle doit etre probablement accessible a un grand nombre de fonctions. Si nous n'effectuons pas le JNE, nous nous rendons compte qu'une chaine "Not enough memory" est utilisee, ce qui semble etre un cas particulier, donc nous allons plutot executer le JNE. Nous arrivons a une longue sequence d'instructions, et nous continuons notre parcours, jusqu'a: 004273C9 lea eax,dword ptr [ebp+FFFFFF74] jge 004273DF Executons ce saut, qui nous rapproche probablement de la fin. Nous executons encore le saut suivant un peu plus loin, qui nous rapproche lui aussi de la fin. Nous arrivons a: 0042740C inc ebx inc [ebp-10] cmp ebx,00000005 jl 004273EA Ce saut revient en arriere, probablement parce qu'il s'agit d'une boucle, donc nous ne l'executons pas. Nous continuons a avancer, et nous voyons que nous entrons dans une serie d'instructions plutot difficile a interpreter... C'est souvent la partie la plus critique de l'analyse, car on ne sait pas tres bien quel solution prendre. Rappelez vous toutefois que la procedure doit obligatoirement sortir par un RET, et que donc, quel que soit le chemin, il y aura toujours un moment ou nous arriverons a ce RET. Dans ce cas-ci, plutot que d'executer ces instructions aux risque de s'y perdre, essayer de retourner a la fin de la fonction (adresse 004275C6 provenant du saut du debut avec EAX qui etait remit a 0 par un XOR). Remontons un peu au dessus de cette adresse, pour voir ce qui se passe quand on arrive a la fin de la fonction par un autre chemin. En fait, cette methode est plutot essayer des le debut, car elle nous aurait eviter tout le parcous interne de la procedure. Nous avons suivi un petit peu ce parcours interne comme exemple, pour voir comment se debrouiller lorsqu'il y a divers type de sauts qui etaient interessants. Maintenant revenons au lignes precedants la fin de la fonction. Voila qui devient interessant. Observons la ligne precedente: mov eax,dword ptr [0045157C] Cette ligne place donc comme valeur de retour de notre procedure (puisque EAX n'est plus modifie par apres), la valeur de la variable globale d'adresse 0045157C. La il n'y a plus a hesiter... Nous venons probablement de trouver l'adresse d'un flag qui indique la registration ou non de notre programme ! Resumons: si ce DWORD est a 0, le programme n'est pas enregistre. Si il est different de 0, le programme est bien enregistre ! ANALYSE DES DONNEES =================== Nous allons maintenant aller voir ce que contient effectivement cette petite variable bien interessante ;) Pour cela, nous allons aller dans le menu "HexData", commande "Hex Display of Data Object/Segments". Rendons nous donc a l'adresse 0045157C (il faut cliquer sur Page, pour passer a la page suivante). Nous arrivons a: 00451578 00 00 00 00 xx 00 00 00 00451580 00 00 00 00 00 00 00 00 00451588 00 00 00 00 00 00 00 00 00451590 00 00 00 00 00 00 00 00 00451598 00 00 00 00 C4 F4 E6 E4 Ou l'adresse recherchee est la valeur xx et est egale a 0 ! Cela parait coherent. Le programme n'est pas enregistre, et la valeur du flag est en effet bien egale a 0. Qu'allons nous faire ? bahh, assez logiquement, nous allons essayer de modifier la valeur de ce byte. Comment proceder ? Nous allons essayer d'aller modifier la valeur de ce byte directement dans WINRAR.EXE. Pourquoi directemment dans le fichier .EXE ? Pour etrecertain que la valeur que nous allons mettre sera bien en memoire des la 1ere instruction du programme. Retournons au tout debut du listing du desassemblage. Nous avons la une information interessante: Data Offset = 0004CC00, Data Size = 00007C00 Cela veut dire que notre segment de donnees commence au byte nø0004CC00 dans le fichier WINRAR.EXE . Retournons maintenant dans la fenetre "Hex Display of Data Object/Segments", sur la 1ere page. Nous observons que la 1ere donnee se trouve a l'adresse 0044E000. Notre variable elle, se trouve a l'adresse 0045157C. Nous effectuons donc le calcul 0004CC00+(0045157C-0044E000), avec la calculatrice en mode hexadecimal de Windows (je parie que vous vous en etiez jamais servi hein ;))) Ca nous donne comme resultat: 5017C. Nous allons donc aller modifier le byte nø5017C dans WINRAR.EXE Avant toute modification, je vous conseille de sauvegarder le fichier WINRAR.EXE original, par securite. Nous lancons ensuite UltraEdit, et ouvrons le fichier WINRAR.EXE en mode d'edition hexadecimale (menu "Edit", "Hex Edit"), et nous nous rendons a l'offset 5017C du fichier (l'offset est affiche dans la barre de status en dessous). Nous remarquons que nous retrouvons bien les meme bytes "C4 F4 E6 E4" qui suivaient un peu plus loin dans les donnees en memoire, ce qui confirme qu'on est bien au bon endroit. Qu'allons nous mettre a cet adresse ? Une valeur differente de 0, peut importe. Mettons par exemple "FF FF FF FF", pour etresur ;) Puis sauvegardons le fichier WINRAR.EXE Et si on essayait ? Nous lancons WINRAR.EXE, et... surprise, c'est comme si on avait rien change :((( D'ou cela provient-il ? Cela veut dire que la valeur qui est dans le fichier n'intervient pas lors de l'execution, qu'elle est simplement ecrasee par la valeur reelle du flag a un moment dans le programme. DEBUGGING ========= Nous allons donc maintenant essayer de savoir ou a lieu cette modification de la variable. Pour cela, nous allons relancer WIN32DASM, charger WINRAR.EXE, et lancer le debugger. Nous cliquons donc sur la commande "Load Process" du menu "Debug", puis nous pressons "OK" (aucun parametre). Ensuite, nous allons mettre des points d'arrets a tout les endroits du programme qui font reference a l'adresse 0045157C. Pour cela, nous allons dans le menu "Search", commande "Find Text", et nous tapons l'adresse "0045157C" a rechercher. A la 1ere occurence trouvee, vous pressez annuler, pour ne plus avoir cette fichue boite de dialogue affichee ;), puis vous pressez F2, pour activer un point d'arret a cet endroit. La ligne doit normalement s'afficher en jaune. Puis vous pressez sur F3 pour continuer la recherche, et de nouveau F2... Vous procedez ainsi de suite jusqu'a avoir un message "Text {0045157C} Not Found". Au passage, vous pouvez remarquer que cette adresse a l'air d'etre effectivement beaucoup utilisee avec des comp "[0045157C],00000000", donc probablement des tests qui verifie la registration ;) Maintenant, nous allons lancer le programme, avec la commande "Run" (F9). Aussitot lance, aussitot arrete hehe ;) Normalement, vous etes arrete au breakpoint: 00427554 test eax,eax 00427556 sete al 00427559 and eax,00000001 0042755C mov dword ptr [0045157C],eax <- BREAKPOINT Comme vous le remarquez, la valeur courante de EAX est apparement placee dans la variable, d'ou l'ecrasement que nous avions mentionne tantot... Que faire ? La solution la plus facile serait d'eviter l'ecrasement de notre variable, que nous avons initialisee a FFFFFFFFh en patchant l'executable. Pour cela, nous allons utiliser l'instruction NOP (90h), qui est une instruction qui ne fait rien. Cependant elle occupe des octets, ce qui va nous aider, puisque la structure du fichier .EXE ne sera donc pas modifiee. Placons le curseur exactement sur l'adresse de l'instruction de modification. Au passage, notons: 0042755C A37C154500 mov dword ptr [0045157C],eax Donc, cette instruction est codee de maniere hexadecimale par "A37C154500". Dans la barre de status du dessous, nous notons aussi l'offset de l'instruction dans le fichier executable: "@Offset 00026B5Ch in File:WinRAR.exe" Nous ressortons donc UltraEdit, pour aller modifier les 5 bytes situes a l'offset 00026B5Ch, et aller y ecrire 9090909090h. Mais tout n'est pas termine... Nous pouvons aussi nous servir des lignes precedents la commande MOV. En effet, si nous les regardons plus attentivement, en observant la derniere instruction "and eax,00000001", nous pouvons en tirer comme conclusion que la valeur est une valeur egale soit a 0 soit a 1. En effet, la commande AND est souvent utilisee pour masquer des bits en effectuant un AND logique. Danc ce cas-ci, elle masque tout les bits sauf le premier. Etant donne que la valeur 0 signifie que le programme n'est pas enregistre, nous pouvons donc estimer que la variable a l'adresse 0045157C doit contenir la valeur 1 pour simuler la registration. Nous allons donc avec UltraEdit, aller remplacer la valeur FFFFFFFFh que nous avions ete mettre tantot, par la valeur 00000001, qui est plus appropriee. Puis nous relancons notre programme et... ... et cette fois ca a bien l'air de marcher ;)))) La barre de titre de la fenetrenous l'indique en tout cas. Allons voir dans le menu "Options". Tient tient, l'option "Registration" est toujours presente. Entrons des valeurs pour voir: "iga" "iga" Nous avons bien la boite de dialogue "Correct Registration" qui apparait. Voila qui est fait ;) Il reste cependant une enigme, a savoir le fait que la commande "Registration" soit toujours presente dans les menus. N'ayant pas eu de version enregistree mai seulement des version de demonstration, je ne peut savoir si elle est toujours bien presente dans les versions enregistrees officiellement. Cependant tout les tests concernant le flag de registration semblent montrer la faiblesse du systeme de protection du programme. LE MOT DE LA FIN ================ Pour resumer, nous devons donc modifier les octets suivants dans le programme: offset: 5017C : 00 00 00 01 offset: 26B5C : 90 90 90 90 90 Pour faciliter cette tache, je vous conseille de programmer une petite fonction dans le language de votre choix, que vous pourriez utiliser facilement, en donnant l'offset et le byte a patcher. Voila,j'espere que vous n'avez pas eu trop de mal a comprendre la methode generale expliquee ici ;) Ciao