TUTORIAL sur CD-Cops v1.31 (Encyclopaedia Universalis 2007) :



Protection commerciale : CD-Cops v1.31
Editeur : Link Data Security (http://www.linkdata.com/)



Outils nécessaires :
OllyDbg v1.10
ImpRec 1.6f
LordPE
Masm v9
Un éditeur hexadécimal

OS: Win XP

Il n'est pas nécessaire d'avoir le CD d'authentification pour appliquer ce tutorial.

Ce tutorial est détaillé de façon suivante :

A) Trouver l'OEP et dumper à ce niveau
B) Restaurer les stolen bytes à l'OEP et les imports

C) Fixer les nanomites

D) Conclusion et remerciements


Introduction :

La protection CD-Cops n'a jamais été très utilisée par les éditeurs de jeux. Elle se rencontre fréquemment chez les éditeurs d'encyclopédie (peut-être la raison en est historique). Elle a été utilisée par Macmillan pour ses dictionnaires électroniques depuis 2002 (cf. http://www.macmillandictionary.com/med-magazine/July2006/40-Profile-Copy-Protection.htm).

D'ailleurs, elle est encore utilisée sur Macmillan English Dictionary, Second edition, For Advanced Learners.
-> une vieille version, d'ailleurs :



CD-Cops se reconnaît par la présence de fichiers aux extensions .QZ_ et .W_X dans le répertoire où se trouve l'exécutable protégé...
Quelques informations sur la protection (en anglais), venant de cdmediaworld :


Protection Info

CD-Cops is a envelope protection which is added to the CD’s main executable. Minute differences are measured to establish the CD-ROM’s fingerprint and to ensure that copies are not accepted. This fingerprint is usually expressed as an 8-digit code or key number.
The CD-Cops software which recognizes and either accepts or rejects the CD is protected by Link’s Code Security, a system which has been in use since 1984 and is known throughout the world as being virtually unbreakable. Link’s Code Security is a legend in the Middle East where piracy is a serious problem.

Additional Info

PPeter - The Hungarian Guru of CD's :
The CD-Cops protection system is not using electronic "fingerprint" code. The check routine only verifies the physical angle between the first and last accessible logical block on the CD. This verification is very good to identify an original disc. The 8-digit code holds the angle information and the check routine compares this with the CD.
CD-Cops is a very good protection system, because the software developers don't need special mastering machine, they only have to add their verifying code to the main application. The angle between the first and last logical block is different for every disc written by CD-R writer, due to the Laser Calibration Area, different CD-R types, etc.

mcLallo :
Commercial ready-made copy protections are always interesting. Here is another one, used to protect CD-ROMs. It's a nice one, timers making sure the program will crash if something takes more time than it should (as if someone would put a breakpoint somewhere and fool around), a couple of checksums to make sure no one's been changing anything in the code, and - of course - some self-modifying code decrypting itself just when it needs to be used. It even claims to be able to separate perfect CD copies from the original CD!


Comment marche la protection CD-Cops ?

EuV12.exe (protégé par CD-Cops) lance EuV12.QZ_ (c'est le debugger de CD-Cops), qui lance finalement EuV12.W_X (le réel exécutable d'Universalis, qui est crypté). EuV12.exe crée aussi dans un dossier temporaire, un exécutable UC16.EXE ainsi qu'une dll, CDCOPS.DLL ...
Si la période de limitation (d'essai) de 45 jours n'est pas dépassée, l'application se lance. Sinon, l'application est bloquée et une boîte apparaît :



Il demande pour lancer l'application, le cd d'authentification, qui, lui, est protégé "physiquement" par CD-Cops...

Pour cracker l'application, il suffit d'unpacker EuV12.W_X et donc de lancer l'application pendant sa période de limitation...

Au départ, j'avais commencé l'étude en debugguant EuV12.exe. Mais, ceci s'est vite avéré lourd : décryptage par parties, émulation des APIs, blocage de l'application lorsqu'un certain temps est dépassé (par exemple, en posant des breakpoints), nécessité d'attacher plusieurs processus, protection anti-attach pour EuV12.W_X, etc...

- Emulation des APIs dans le processus EuV12.exe :





On arrive à ces APIs émulées via des jmp dword ptr [API]....

- Protection Anti-attach pour le processus EuV12.W_X :



Du coup, j'ai tenté une méthode en modifiant directement EuV12.W_X et ça a marché ;).


A) Trouver l'OEP et dumper à ce niveau :

Comme CD-Cops vérifie par des checksums, l'OEP de EuV12.W_X et les différentes sections, j'ai tenté un hook d'APIs au niveau de l'IAT.
J'ai tenté un hook au niveau des APIs USER32.dll ; les APIs KERNEL32.dll ont été "enlevées" de l'Import Table (cf. plus bas).
Pour cela, il faut d'abord repérer le dword 5 de l'IT correspondant à l'entrée USER32.dll (il s'agit du pointeur vers le tableau des adresses physiques des APIs USER32.dll -> Import Address Table). En l'effaçant, le système ne réécrira plus au niveau de l'IAT pour les APIs USER32.dll :) :

ImageImportDescriptor :

OriginalFirstThunk : 0x00CC3460
TimeDateStamp :     0x00000000 (GMT: Thu Jan 01 00:00:00 1970)
ForwarderChain :    0x00000000
Name :                    0x00CC5354 ("USER32.dll")
FirstThunk :             0x00CC3A54



Il faut donc mettre à 0 le dword, situé en 00CC3074...
Ensuite, il faut se rendre à l'adresse pointée anciennement par ce dword (partie de l'IAT pour les APIs USER32.dll), càd en 00CC3A54. On remplit alors le tableau (de 00CC3A54 à 00CC3C68 avec notre hook (adresse de notre routine). J'ai choisi une adresse dans le PE, à savoir 00400F00 :



Ainsi, au premier appel d'une API USER32.dll, notre routine est appelée et sauvegarde le registre esp courant en 00691000 (section .data).
En dumpant ce dword, on obtient 12FD10. Le début de la pile étant sur XP en 130000, le nombre de dwords poussés sur la pile est (130000-12FD10) / 4 = 2F0 / 4 = BC, d'où cette nouvelle routine :



On dumpe alors la région 691000-6912F0, grâce à LordPE et c'est là que je me dis : "Mais, t'es un boulet, toi ! On pouvait faire directement le dump de la pile sous lordPE..." :



Voici le dump de la pile :



Regardons en 00591AE6 dans un dump d' EuV12.W_X "à la volée" :



Cette routine est appelée par un call en 005A34B5. Un petit "Go to CALL from 005A34B5" nous amène à l'OEP :



005A3400 est l'OEP pour les raisons suivantes :
- C'est la première instruction à être exécutée. Il n'est appelé par aucune routine.
- Il est partiellement détruit, car émulé par CD-Cops (stolen bytes à l'OEP). Il est précédé de nops, qui le sépare de la routine précédente.

Il ne reste plus qu'à dumper au niveau de l'OEP. Pour cela, j'ai utilisé un tls callback :).

Pourquoi utiliser un TLS callback ?

Puisqu'il est executé avant l'OEP, cela permettra de patcher de manière à obtenir une boucle infinie à l'OEP. Cette astuce est utilisée par certains protectors
(EXECryptor 2, Private Exe Protector) pour détecter indirectement la présence d'un debugger (présence d'un breakpoint [int 03 / opcode 0CCh] à l'OEP).
Il est assez marrant de pouvoir retourner cette astuce du tls callback contre d'autres protectors :).

Comment créer manuellement un TLS callback ?

Cette partie est partiellement tirée du texte d'un article, écrit par mars (merci à lui) et traitant ce sujet dans la résolution d'un crackme.
Pour mettre en place un TLS callback, on peut modifier directement le PE header et rajouter facilement cette fonction TLS callback.

La TLS table est composée de de 6 DWORDs à la suite:
-DataBlockStartVa --> 00000000 ou une adresse qui pointe vers zéro
-DataBlockEndVa --> 00000000 ou une adresse qui pointe vers zéro
-IndexVariableVa --> l'adresse indiquée doit pointer vers une adresse writeable
-CallBackTableVa --> l'adresse indiquée doit pointer vers l'adresse de début de notre fonction
-SizeOfZeroFill --> 00000000
-Characteristics --> 00000000

On se moque des 2 premiers DWORD et des 2 derniers DWORD (on va y mettre 0000000).
Ce qui compte, ce sont les deux variables IndexVariableVa et CallBackTableVa, qu'il va falloir renseigner.

Sous LordPE, on remplit dans les directories, l'entrée TlsTable :



Maintenant, on remplit cette TLS Table comme suit :



Occupons-nous du CallBackTableVA (en 400E0C). La table pointée par celui-ci peut contenir zéro, une, ou plusieurs adresses de fonctions callback.
Ici, on veut une fonction qui débute en 400E60.
A l'adresse de la table pointée par la CallbackTableVA càd en 400E50, on écrit donc l'adresse 00400E60 au format little endian. Il doit y avoir des zéro au DWORD suivant pour dire que c'est fini.

L'adresse en 00400E08 correspond à la IndexVariableVa. Elle doit pointer vers une adresse writeable. J'ai choisi un DWORD dans la section .data (ici, 691000).
Attention, le dword en question sera écrasé (par un DWORD nul) !!!
On peut donc choisir une adresse en fin de section .data (remplie de zéro), pour ne pas avoir à restaurer le dword écrasé...

Voici la fonction callback en question :



Elle sert à effectuer une boucle infinie en 005A3415, au niveau du push eax.
On ne le fait pas avant cette adresse, car de toute façon les premières instructions sont émulées...

Il ne faut pas oublier de mettre la section .text writeable pour pouvoir effectuer ce patch par le tls callback :



Il ne reste plus qu'à dumper EuV12.W_X à l'aide de LordPE :



Ensuite, il faut dans ce dump, grâce à un éditeur hexadécimal :
- enlever la TlsTable et la fonction Tls callback.
- corriger l'OEP
- restaurer le push eax en 005A3415, au lieu de la boucle infinie
- restaurer le DWORD en 691000, écrasé lors du Tls callback
et enfin restaurer les stolen bytes à l'OEP...


B) Restaurer les stolen bytes à l'OEP et les imports :

On lance EuV12.exe et on dumpe "à la volée" EuV12.W_X (on l'appellera dump.exe).
On sait maintenant que l'OEP est en 005A3400.
On ouvre donc dump.exe sous Ollydbg et l'on se rend à l'OEP :



En analysant le dump de la pile :



On peut déduire ceci :
- La première valeur poussée sur la pile correspond à une adresse pointant sur la pile.
Les 2 premières instructions sont donc PUSH EBP / MOV EBP,ESP
- Les 3ème et 4ème valeurs poussées sur la pile (006D7758 et 005D490E) permettent de déduire un PUSH 006D7758 en 005A3405 et un PUSH 005D490E en 005A340A.
- Il ne reste plus qu'à trouver la 3ème instruction, de taille 2 bytes. La deuxième valeur poussée sur la pile étant 0, l'instruction PUSH 0 convient donc.
Au final, on obtient ceci :



Les deux valeurs dans le dump de la pile (en vert) sont identiques et correspondent bien aux deux PUSH EAX en 005A341D et en 005A341E.
Il ne reste plus qu'à sauver ces modifications dans dumped.exe .

Toujours dans dump.exe (le dump à la volée), on cherche et sélectionne un call [API] ou jmp [API].
Un click droit Follow in Dump > Memory address permet d'atterrir dans l'IAT.
Elle commence en 010C36D0 et se termine en 010C3CC0 (Size = 5F0) :


...


En ouvrant dump.exe à l'aide de LordPE et en utilisant son Structure Lister, on s'aperçoit qu'il manque dans l'Import Table, les imports KERNEL32.dll (voir aussi imports.txt) :



Il faut donc reconstruire une Import Table avec ImpRec :



On sélectionne dumped.exe et on obtient un dumped_.exe reconstruit :).
On renomme ce dernier et paf, ça plante :(.


C) Fixer les nanomites :

On lance Ollydbg, on désactive tous les types d'exceptions dans Debugging options, pour que l'on s'arrête sur l'exception en question.
On lance l'exécutable et l'on atterrit ici :



Ohhh comme c'est mignon !!! Des nanomites :p.
Et il y en a un paquet...
On fait un Search for > All commands > int3 pour obtenir la liste (cf. int3_table.txt).

On n'oublie de relever de le code d'erreur de l'exception :



On remarquera aussi des sortes de données juste après les int 03. Le code est bien trop propre et structuré pour être honnête :p.



Une autre chose apparaît claire sur plusieurs nanomites : ce sont des instructions sur 6 bytes, qui ont été détruites...

On doit donc retrouver le processus, qui debuggue EuV12.W_X et fait en sorte qu'il exécute indirectement l'instruction détruite...
On lance Universalis (EuV12.exe) que l'on réduit, on lance Ollydbg, on attache le processus EuV12.QZ_ .
Par intuition, je cherche dans le code la constante 80000003 (code d'erreur) :



On obtient :



On met un bp en 00411E9C sur le CMP EAX, 80000003. On clique sur l'application pour l'agrandir...
Bingo ! On breake :).




On voit d'ailleurs bien une liste de comparaison de codes d'erreurs à ce niveau :).
Maintenant on traçe le code jusqu'en 00411F02. On rentre dans ce call et l'on surveille les registres.



En traçant un tout petit peu, on voit apparaître l'adresse d'une nanomite (0055C15F) en ecx. Comme par hasard !
On voit aussi que les données situées après l'int 03 sont utilisées. Le hasard fait bien les choses :p.
En 00411C86, on a en SS:[EBP-8] :



Dans le dumped d'EuV12.W_X, on a bien :



Que fait cette routine ? Par un petit algorithme, elle se sert des données (le dword) situées juste après l'int 03.
Elle renvoie un numéro dans AX (ici, 0F1). A quoi sert-il ?

En continuant de tracer, on arrive à ceci :



Tiens, tiens, une référence à une API !!!
En 00411CFA, EDX + EAX*4 peut se traduire ainsi : adresse d'origine + numéro de rang * 4.
On a donc à faire à une table (une IAT), qui commence en 9AAD88 et se termine en 9AB354 (taille = 5CCh, soit 173h entrées).
On dumpe donc la table obtenue (imports.dat).
Le numéro de rang est celui, obtenu par l'algorithme précédent.
Ainsi, à l'adresse 9AAD88 + 0F1 * 4 se trouve l'adresse de l'API PostMessageA.
Maintenant, on sait que les nanomites permettent l'émulation d'appel aux APIs.
Mais, est-il question de call [API] ou de jmp [API] ?

En ouvrant avec Ollydbg, un dumped d'EuV12.W_X et en faisant un Search for > All intermodular calls, on s'aperçoit qu'il n'y a pas beaucoup d'imports Kernel & User présents. De plus, seuls les appels des imports de la librairie MSL_All-DLL90_x86.dll se font par des jmp [API] :



On peut donc opter pour l'hypothèse selon laquelle seuls des call [API] sont émulés, hypothèse, qui sera confirmée ultérieurement.

Un autre point important est la présence d'une table de nanomites.
On s'en aperçoit, en traçant le call 00411B28, situé en 00411CC6 :



Elle commence à l'adresse pointée en [00418574], càd en 009AB378 et se termine en 9AB8F0.
On dumpe donc la table obtenue (nanotable.dat).

Au départ, j'avais restauré les instructions en me servant de cette table.
Malheureusement, il reste de très nombreuses nanomites...

A quoi sert-elle donc, cette table ?

En analysant sa structure, on s'aperçoit qu'il y a une valeur (un byte), qui sépare chaque adresse de nanomite (en fait, ce sont les adresses+1 des int 03, qui sont stockées) :



Cette table stocke pour chaque nanomite correspondant, le nombre de fois, dont il a été effectué / appelé / debuggué...
Quelle en est la raison ?



Lorsque ce nombre atteint 010h, CD-Cops patche directement le code au niveau de la nanomite correspondante, et ce pour un gain de performance...
Comme le debugger EuV12.QZ_ doit patcher au niveau du thread principal de EuV12.W_X, il doit faire appel à l'API VirtualProtectEx (il faut tracer à l'intérieur du call 00411D89) :



Et pour patcher, il utilise la fonction non documentée NtWriteVirtualMemory du noyau (SSDT) :



A la première utilisation, CD-Cops utilise le couple classique LoadLibraryA / GetProcAddress pour obtenir l'adresse de celle-ci, puis l'émule :




L'appel à cette fonction se fait en 0040A888 :





Cette fonction du noyau est en fait appelée par l'API WriteProcessMemory. Il suffit de jetter un oeil à l'adresse de cette dernière :





Le prototype de la fonction NtWriteVirtualProcess est d'ailleurs très proche de celui de l'API WriteProcessMemory :

NtWriteVirtualMemory (

ProcessHandle
BaseAdress
Buffer
NumberofBytesToWrite
NumberofBytesWritten ) ;

Pour savoir, à quel endroit s'effectue le patch, il suffit de regarder les paramètres dans la pile, avant l'appel à cette fonction :





EuV12.QZ_ patche donc EuV12.W_X, au niveau du nanomite incriminé. Ce patch se fait en 0055C15F sur 6 bytes (nombre de bytes, correspondant à un call [API] ).
L'opcode E8 correspond à un call, notre hypothèse était donc valide !
Pour équilibrer le code, un nop est ajouté.



Beurk !!!
Un appel direct à l'API PostMessageA sera donc effectué par la suite...
L'adresses des APIs étant OS-dépendant, ce comportement est donc à éviter ! (Il suffit de dumper à l'OEP comme précédemment pour ne pas avoir ce problème).

Qu'est-ce que la SSDT ?


System Service Dispatch Table

SSDT ou System Service Dispatch Table est un tableau de descripteur de service situé dans le Noyau de windows, utilisé pour diriger des appels système vers un traitement approprié : table d'adressage des API.

Point d'accroche dans la table SSDT

"SSDT hooking" Le "crochetage" de la table SSDT en vue de sa modification est une des techniques fréquemment utilisées par les rootkits. En modifiant cette table, ils peuvent réorienter l'exécution vers leur code au lieu du module de traitement (fonction) originellement appelé. Certaines de ces fonctions sont crochetées tant * par les rootkits malsains que les anti-rootkits

Sur Windows XP, cette table est en lecture seule, mais cette protection peut être contournée.

Exemples d'appel système détourné :

* Pour les thread, processus, (et jobs) : NtAssignProcessToJobObject, NtCreateThread et NtTerminateThread, NtOpenProcess et NtTerminateProcess...etc
* pour les fichiers : NtQueryDirectoryFile, NtDeleteFile...etc
* pour les pilotes : NtLoadDriver et NtUnloadDriver
* Pour la mémoire : NtProtectVirtualMemory et NtWriteVirtualMemory
* Divers : NtCreateKey, NtReplaceKey

Par voie de conséquence, cette table du noyau est l'une des plus contrôlées par les utilitaires de détection des rootkits. Sa restauration en cas de modification non désirée est une protection supplémentaire que certains anti-rootkits proposent.

http://fr.wikipedia.org/wiki/System_Service_Dispatch_Table


Dernier détail à comprendre, mais qui a son importance...

A quoi sert l'instruction en 00411C9B (Un byte a été isolé en cl...) ?





C'est en codant une première routine que j'ai remarqué, que le byte isolé est toujours 01, sauf au niveau de ces trois nanomites (le byte isolé est alors de 0) :



Pour trouver à quoi correspondent ces 3 nanomites (004E8557, 004E858C & 004E85A7), il suffit de breaker au niveau du traitement d'une exception, provoquée par une nanomite et de faire croire qu'elle correspond à l'une de celles-ci. En forçant pour obtenir un patch du code par CD-Cops, on obtient :



Ce flag sert donc à éviter un patch sur 6 bytes et donc l'ajout d'un nop, puisque l'instruction initiale tient sur 5 bytes...
Il s'agit donc d'un appel indirect à une API du type :

call @1
@1: jmp dword [API]

En recherchant à l'aide d'un Search for > All commands > jmp dword ptr [010C3A70], (010C3A70 pointe en 77D5A4EE, càd l'API DdeInitializeA), on arrive ici :



Et comme par hasard, ce jmp [API] n'est appelé à aucun endroit, dans un dump "brut".
Le code restauré donne donc :



Avant la restauration des nanomites, on va récapituler les différentes étapes :

- Le processus EuV12.QZ_ utilise une boucle classique :




L'API ContinueDebugEvent permet à un debugger de reprendre le thread qu'il avait précédemment suspendu dans le programme cible quand celui-ci avait rencontré un "Debugging evenement", et de reprendre le cours normal de l'application.
L'API WaitForDebugEvent attend qu'un Debugging Event survienne dans le process en cours de debuggage.

tiré de Dossier n°9 : Les Debug APIs, par christal

Lorsque EuV12.W_X rencontre une int 03, un debugging event est créé et l'on breake alors en 004133DB dans EuV12.QZ_ .

- L'API GetThreadContext permet de retrouver le context d'un thread spécifié.



Voici la strucure du context :



Celui-ci est alors sauvé via lpContext (Un pointeur vers une structure CONTEXT, qui reçoit le context approprié du thread spécifié).





En bleu, on a donc la sauvegarde de l'eip et en rouge, celle d'esp...

- Ensuite, EuV12.QZ_ détermine si le debugging event correspond bien à une exception de type breakpoint (int 03), via une liste de comparaison de codes d'erreurs :



- Lorsqu'une nanomite est effectuée, un algorithme permet à partir du dword suivant l'opcode 0CCh de l'int 03, d'obtenir un numéro (celui de l'API correspondante)...
Un flag est aussi calculé (flag 0 pour une émulation d'un call @1 / @1: jmp [API], flag 1 pour une émulation d'un call [API]).



- EuV12.QZ_ détermine si l'adresse de la nanomite en question est dans la table (grâce au call 00411B28 en 00411CC6).



Si la nanomite fait partie de cette table, on incrémente alors le compteur correspondant, dans cette table. Bien évidemment, toutes les namomites ne sont pas dans cette table...

- A partir du numéro de l'API (position dans une IAT), l'adresse de l'API est retrouvée...

- Via le call 00410D8C en 00411D11 et l'utilisation de la fonction NtWriteVirtualMemory, la pile de EuV12.W_X est patchée en esp-4 par eip+04+flag. Ceci permet un retour au code de EuV12.W_X après l'exécution de l'API correspondante. Ce patch se fait de la même manière que celui au niveau du code de EuV12.W_X par CD-Cops.

- CD-Cops soustrait 4 à esp du context sauvé :



- Si la nanomite est dans la table et qu'elle a été appelée 10h fois, CD-Cops patche directement le code de EuV12.W_X (l'int 03) en call API / nop.



Le patch se fait comme précédemment en appelant en 00411D72, la routine commençant en 00410D8C.
Il n'y a pas de patch (sans le nop) en call API (flag = 0), puisque les 3 cas de nanomites respectant cette condition ne sont pas dans cette table...

- CD-Cops change l'eip du context sauvé en mettant l'adresse de l'API à exécuter :



- L'API SetThreadContext met le context dans le thread spécifié :

Sur la pile, on trouve lpContext (un pointeur vers une structure CONTEXT, qui contient le context à mettre dans le thread spécifié) :

Voici le nouveau context (modifié par CD-Cops) :



Ainsi, EuV12.W_X reprendra la main directement au niveau de l'API et au premier ret, retournera au bon endroit dans le code de EuV12.W_X, grâce à la modification de la pile. Il s'agit donc d'une émulation des appels aux APIs.

- EuV12.QZ_ rend la main au processus cible, grâce à l'API ContinueDebugEvent.
La boucle est bouclée :).

Passons à la restauration de ces nanomites, grâce à une routine.

Pour restaurer les nanomites, j'ai utilisé la LDE (Length Disassembler Engine) de BeatriX, pour effectuer un début de désassemblage.
Une recherche par pattern est en effet très inefficace, puisque l'on retombera dans de nombreux cas, en plein milieu d'instructions...
Ici, la LDE renvoie la longueur de l'instruction et pour tomber sur une nanomite, il suffit que pour une certaine adresse, la LDE renvoie une longueur de 1 et que l'opcode correspondant soit 0CCh.
Comme, il n'y a pas de junk code ou autres bizarreries dans le code de EuV12.W_X, il suffit d'implémenter la recherche de nanomites, en sautant linéairement d'instruction en instruction, en ajoutant la longueur renvoyée par la LDE...

Voici le descriptif de cette LDE :


Voici un LDE (Length Disassembler Engine) pour calculer la taille des instructions 32 bits ou 64 bits. Il est capable de lire toutes les instructions intel référencées dans les manuels intel de novembre 2006 et notamment les FPU, MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2 et VMX.

Syntaxe pour désassembler du 32 bits :

push 0
push Address2Disasm
call LDE

Syntaxe pour désassembler du 64 bits :

push 64
push Address2Disasm
call LDE

Valeurs de retour : Il renvoie la taille de l’instruction en situation normale ou -1 si l’instruction rencontrée est invalide.

Remarques : Ce LDE est un code relogeable et ne nécessite pas d’être stocké dans une section inscriptible.

Lien direct vers LDE64 : http://binary-reverser.org/tools/LDE64/

Voici quelques explications sur ma routine (nanomite.asm, commenté et assemblable sous Masm v9) :

La routine va d'abord copier l'IAT d'EuV12.W_X à la suite de notre routine, en enlevant les séparateurs entre les différentes librairies. Ainsi, elle devient identique à l'IAT de EuV12.QZ_, utilisée pour la correspondance avec les nanomites.
Ensuite, on va parcourir le code à la recherche d'une nanomite (int 03).
Lorsque la longueur renvoyée par la LDE est égale à 1 et que l'opcode est bien 0CCh, on a bien une nanomite.
Si ce n'est pas le cas, on augmente l'adresse à "désassembler" de la taille de l'instruction précédente...
Dans le cas où la LDE renvoie le code -1 (erreur), on incrémente et on finira bien par retomber dans une situation normale...

Lorsque l'on tombe sur une nanomite, on applique l'algorithme permettant de retrouver l'API correspondante (via son numéro), ainsi que son flag.
L'algorithme a tout simplement été rippé...

Si le flag est égal à 1, on a à faire à un call [API].
Si le flag est égal à 0, on a à faire à un call @1 / @1: jmp [API]. Il faut donc dans ce cas, rechercher le jmp [API] correspondant...

On patche en conséquence et on continue jusqu'à la fin du code...
Il ne reste plus qu'à dumper la section .text et à la remettre dans le dump précédent.

NB : La routine assemblée est à copier dans la section .data (writeable) sous Ollydbg (Binary > Binary paste).

On lance alors le dump restauré et par miracle, l'application se lance sans problème :p.

Bye-bye CD-Cops ;).



D) Conclusion et remerciements :

La restauration au niveau des nanomites aurait pu être perturbée par la présence d'int 03 (de bourrage), servant à délimiter des blocs de code (à séparer certaines routines de sub-routines). Je suppose que ceci dépend du compilateur utilisé. Ici, l'application a été développée en cpp (Il suffit de regarder dans les String Data References de EuLaunch.exe, qui, lui, n'est pas protégé, les nombreuses références à des composants *.cpp) et les blocs de code sont uniquement séparés par des nops ;).
Si ce n'était pas le cas, il aurait fallu étudier avec la LDE, ce qui suivait les int 03... Présence d'autres int 03 à la suite ou de bytes aléatoires (instructions invalides) puis d'instructions valides, 6 bytes après l'int 03 ?

Plus de détails sur les autres mécanismes de protection (encryption / checksum / GetTickCount-trick) sont disponibles dans les tutoriaux de McLallo (CD-Cops,
Another ready-made protection annihilated) et de LaptoniC (CD-Cops v1.76). Ils sont disponibles dans le dossier docs.

L'étude de CD-Cops constitue une bonne initiation aux nanomites et permettrait d'attaquer des nanomites plus complexes, comme celles d'Armadillo (émulation de sauts). La méthode employée devrait rester valable sur Armadillo. L'utilisation d'un debugger (émulant le comportement de celui d'Armadillo) ou des veh (vectored exception handler) , c'est crade :p.
Le seul souci pourrait apparaître avec les int 03 de bourrage, insérés par certains compilateurs. Mais, même dans ce cas, il est possible de ne pas se laisser avoir avec la table d'Armadillo, qui contient de fausses nanomites. Il suffit de désassembler linéairement à l'aide de la LDE à la suite du saut jusqu'à l'endroit où l'on se retrouve en exécutant ce saut. Si la différence est nulle, c'est bien une nanomite. Si ce n'est pas le cas, on saute (aléatoirement) en plein milieu d'une instruction et ce n'est pas une nanomite. Bref, ce n'est qu'une hypothèse :p.
Un tutorial, qui parle de nanomites d'Armadillo : http://www.reteam.org/papers/e72.pdf

Même si la version 13 d'Universalis (édition 2008) est protégée avec CD-Cops v1.82, la protection demeure exactement identique.
Reste à savoir si Universalis va changer sa protection suite à la sortie des cracks...
Ils étaient passés des dongles à Safecast, puis à CD-Cops. Que vont-ils faire ?
Leur produit édition 2009 est maintenant terminé (la compilation des launchers se fait en Juin / Juillet) et la protection de leur produit (les fichiers créés par CD-Cops) se fait vers Août / Septembre. C'est donc en train de se jouer ces mois-ci pour l'édition 2009 ;). Suspense...
La seule chose, qui m'échappe, c'est qu'ils utilisent de vieilles versions de CD-Cops (1.31 & 1.82), alors que sur le site de Link Data Security, on voit qu'une version 3 était déjà sortie en Septembre 2003...



L'éditeur d'Encyclopaedia Universalis aurait-il fait une réserve de protections, qu'ils écouleraient bien plus tard ?

EDIT : J'ai été grandement déçu par la protection d'Universalis (l'édition 2009). CD-Cops a été remplacé par une protection minable (faite maison) en java !!!
Il suffit de modifier un minimum, Application.class dans universalis-dvd-app3.jar, pour coutourner cette protection.
Non seulement, la protection est lame, mais, en plus, il est désormais possible de faire une copie du CD d'authentification, puisque la "protection" se contente de vérifier la présence de la chaîne "22024012618190721396961752119927502051841399111229" dans le fichier 00000001.TMP du CD d'authentification, dont le volume est KEY CD. Bien évidemment, ce n'était pas le cas avec CD-Cops...
Reste plus qu'à attendre l'édition 2010, en espérant que la protection soit beaucoup plus intéressante. Et pourquoi pas un CD-Cops v3 ?



Au final, même si CD-Cops n'est en quelque sorte qu'un Armadillo allégé, ce n'est pas forcémment un mauvais choix. En effet, contrairement à Safecast, les CDs d'authentification produits par CD-Cops restent très difficiles à cloner / émuler avec les outils actuels (CloneCD / Alcohol 120 % / BlindRead / Daemon Tools)...

Bien évidemment, les unpackers / decrypters publiques présents sur le net ne marchent pas...
On peut les trouver sur http://www.gamecopyworld.com/cd-cops.htm.

Pour la petite anecdote, en lisant le readme de CD-Cops Emulator v1.0, je suis tombé sur ça :



Ca m'a bien fait rire. Analyst, qui se vante d'être dans beaucoup de greetz, a oublié de nous citer celui-là ;-).


Remerciements :

BeatriX pour sa LDE
mars
Christal et tous ses compères, R!SC, +Frog's Print & +Spath, FRET et bien évidemment, tous les membres de FFF ;)

Voilà c'est fini...



"There is a crack, a crack in everything... That's how the light gets in."

uLysse_31, le 07-08-2008




Copyright © uLysse_31