Auteur : Anarchriz/DREAD Date de Sortie : le 29 avril 1999 (dernière modification le 30 avril 1999) Difficulté : débutant à avancé Cible : CRC algorithme Outils : QEdit 2.1 (le mieux!, Wordpad et quelque prog CRC)
CRC et comment Les modifier
Introduction
Cet essai va essayer de vous montrer comment se débarrasser des CRC.
Beaucoup de Coders/Reversers ne savent pas exactement comment travaillent les CRC et presque personne ne sait comment
les modifier...
Tout d'abord, ce tutorial va vous apprendre comment calculer un CRC de manière générale.
Deuxièmement, la partie Reverse vous apprendra (principalement) comment modifier complètement les
CRC-32.
Vous pourrez employer cela pour casser des certaines protections (Comme les anti-virus). Il semble y avoir des
utilitaires qui puissent 'corriger' un CRCS pour vous, mais je doute qu'ils expliquent aussi comment ils s'y prennent.
J'aimerais vous avertir qu'il y a pas mal de maths employés dans cet essai. Cela ne fera de mal à
personne, et permettra de mieux comprendre le Mécanisme de Reverse Engineering.
Partie 1 : Comment calculer un CRC
Le Code de Redondance Cyclique ou CRC est fréquemment utilisé, et vous l'avez déjà
rencontré, peut être même sans le savoir. Vous vous rappelez tous être tombé sur
un message ennuyant " File corrupted " avec des fichiers RAR, ou d'autres compresseurs, suite à
un mauvais téléchargement, ou à cause d'une de ces @#$% de disquettes...
Le CRC est une valeur calculée sur un morceau de données avant compression. Quand l'archiver déballe
ce fichier, il lit la valeur du CRC précédemment écrit et contrôle la validité
du fichier non décompressé en refaisant le même calcul. Quand les deux résultats correspondent,
il y a de bonne chance que les fichiers soient identiques. Avec les CRC-32,Il y a un risque d'erreur de l'ordre
de 1/2^32 que la vérification ne détecte pas un changement de données.
Comment le calcul est-il fait ? Et bien l'idée principale est de voir le fichier comme un grande série
d'octets divisées par un nombre, et qui vous laissera un Reste, le CRC!
Vous avez toujours un reste (qui peut aussi être zéro), et toujours un diviseur(9/3=3 reste=0; (9+2)/3=3
reste=2) qui peut être sous la forme de X soustractions. Si vous voulez trouver la valeur d'origine il suffit
de multiplier le diviseur (ou additionner X fois) et ajouter le reste.
Voici 2 exemples, le numéro 1 est une soustraction normale, 2&3 Sont particuliers :
-+ (1) 1101 (2) 1010 1010 (3) 0+0=0 0-0=0 1010- 1111 + 1111- 0+1=1 *0-1=1 ----- ---- ---- 1+0=1 1-0=1 0011 0101 0101 *1+1=0 1-1=0
Dans l'exemple (1), la deuxième colonne de droite équivaut à
0-1 =-1, donc un bit a été emprunté au bit d'a coté, et vous donnera cette soustraction
(10+0)-1=1. (C'est comme une soustraction décimale classique sur le papier), Pour les cas particuliers (2&3),
1+1 aurait du normalement donner comme réponse 10, où '1' est la retenue qui transmet une valeur
au bit suivant. Cette valeur est oubliée. Le cas spécial 0-1 aurait normalement dû donner comme
réponse '-1 '. Si vous avez des notions de programmation, cela ressemble, ou mieux, C'EST l'opération
XOR. Regardez maintenant l'exemple d'une division:
Dans une arithmétique normale :
1001/1111000\1101 13 9/120\13 1001 - 09 - | ---- -- | 1100 30 | 1001 - 27 - ---- -- 0110 3 - > le reste 0000 - ---- 1100 1001 - ---- 011 - > 3, le reste Dans l'arithmétique CRC : 1001/1111000\1110 9/120\14 reste 6 1001 - ---- 1100 1001 - ---- 1010 1001 - ---- 0110 0000 - ---- 110 - > le reste( Exemple 3)
Le quotient d'une division n'est pas important et il est inutile de s'en souvenir,
parce que ce n'est qu'un couple de octets inférieurs du bitstring que vous avez voulu calculer pour le CRC.
Ce qui EST important, c'est le reste!
C'est la seule chose qui représente quelque chose d'important par rapport au fichier original. C'est le
fondement du CRC!
Passant au calcul réel du CRC :
Pour exécuter un calcul CRC nous avons besoin de choisir un diviseur, nous l'appellerons le'poly' dorénavant.
La largeur W d'un poly est la position du bit le plus élevé, donc la taille du poly 1001 est 3 et
non pas 4.
Notez que le bit le plus élevé est toujours Un, quand vous devez choisir la taille d'un poly, vous
avez seulement à choisir une valeur pour la partie basse des octets W. Si nous voulons calculer le CRC sur
une bitstring, nous devons nous assurer que tous les octets sont traitées.
Il faut donc ajouter W zéro octets à la fin de la Bitstring.
Dans l'exemple 3, nous allons supposer que la bitstring était 1111.
Regardez un exemple un peu plus grand :
Le poly = 10011, taille de W =4 Bitstring + W zéros = 110101101 + 0000 10011/1101011010000\110000101 (nous ne nous soucions pas du quotient)
10011 |||||||| - -----|||||||| 10011||||||| 10011||||||| - -----||||||| 00001|||||| 00000|||||| - -----|||||| 00010||||| 00000||||| - -----||||| 00101|||| 00000|||| - -----|||| 01010||| 00000||| - -----||| 10100|| 10011|| - -----|| 01110 | 00000 | - -----| 11100 10011- ----- 1111 - > le reste - > le CRC!( Exemple 4)
Il y a 2 choses importantes d'exposées ici :
1. Uniquement quand l'octet de poids fort est l'un des octets de la bitstring, nous le XORons avec le poly, autrement
dit nous 'changeons' seulement la bitstring d'un bit sur la gauche.
2. L'effet du XORRING, quand il XORé avec les octets inférieurs W, est que le bit de poids fort donne
toujours zéro.
Table-Driven Algorithm
Vous devez comprendre qu'un algorithme basé sur le calcul bitwise sera très lent et inefficace.
Il serait beaucoup plus efficace de le calculer sur une base "par octet".
Mais alors nous pouvons seulement accepter les poly's avec une taille d'un multiple de 8 (c'est un octet).
Voyons ca dans un exemple avec un poly d'une taille de 32 octets (W =32) :
3 2 1 0 octet +---+---+---+---+ Pop < - | | | | | < - bitstring avec des W bits zéro ajoutés, cas 32 +---+---+---+---+ 1 <---32 octets---> c'est le poly, 4*8 octets ( Figure 1)
C'est un registre que vous utilisez pour stocker le résultat provisoire
du CRC, je l'appelle le registre CRC ou uniquement Registre dorénavant. Vous changez des octets de la bitstring
du coté droit, et d'autres du côté gauche. Quand un bit change du côté gauche,
le registre entier est XORRED par les W octets bas du poly (dans le cas 32).
En fait, nous faisons exactement la même chose que dans la division ci-dessus.
Regardez l'exemple d'un CRC 8 octets avec 4 octets de changés immédiatement :
Le registre juste avant le changement : 10110100
4 octets sont (en partie supérieure) changés du côté gauche en modifiant 4 nouveaux
Octets du côté droit.
Dans cet exemple 1011 est changé en 1101.
La situation est celle ci :
8 octets du registre CRC : 01001101 4 octets supérieurs sont modifiés : 1011 Nous utilisons le poly : 101011100, taille W=8 Nous calculons la nouvelle valeur du registre. Top Registre ---- -------- 1011 01001101 le top-bit et le registre 1010 11100 + (*1) du poly est XORRED sur le 3ème bit ---- -------- 0001 10101101 résultat du XORRING Nous avons toujours un 1 sur le bit 0 du top-bits : 0001 10101101 résultat précédent 1 01011100 + (*2) le poly est XORRED sur le bit 0 du top octets ---- --------- 0000 11110001 résultat de seconde XORring ^^^^
Maintenant tout le top-bits est à 0, et nous n'avons plus besoin de XORer
avec le poly pour cette séquence.
Vous obtenez la même valeur dans le registre si vous XORez (*1) avec (*2) et le résultat avec le registre.
C'est à cause des propriétés du XOR:
( a XOR b) XOR c = a XOR (b XOR c)
1010 11100 poly sur le bit 3 du top-bits 1 01011100 + poly XORred sur le bit 0 du top-bits ---- -------- 1011 10111100 (*3) résultat du XORRINGLe Le résultat (*3) est XORRED avec le registre 1011 10111100 1011 01001101 + les octets supérieurs et le registre ---- -------- 0000 11110001
Vous voyez ? Le même résultat!
Maintenant (*3) est important, parce que le bit supérieur 1010 est toujours la valeur (*3)=10111100 (pour
les octets inférieurs W=8).
Cela signifie que vous pouvez pré-calculer les valeurs Xorées pour chaque combinaison d'octets supérieurs.
Notez que des octets supérieurs arrivent toujours à zéro après une itération,
cela doit être parce que la combinaison du XORRING y mène.
Revenons à la figure 1.
Pour chaques valeurs du byte supérieur (8 octets) changés, nous pouvons pré calculer une valeur.
Dans ce cas se serait une table de 256 (2^8) entrées de DWORD (32bit). (La table CRC-32 est dans l'annexe)
En pseudo-language notre algorithme est maintenant celui ci :
While (la bitsring n'est pas exhaustive) Begin Top = top_byte of register Register = Register shifted 8 octets left ORred with a new byte from string Register = Register XORred by value from precomputedTable at position Top End
L'Algorithme de la Table
L'algorithme proposé ci-dessus peut être optimisé. Les octets de la bitString n'ont pas besoin
d'aller dans tout le registre avant qu'ils ne soient employés.
Avec ce nouvel algorithme nous pouvons directement XORé un octet d'une Bitstring avec l'octet changé
du registre. Le résultat pointe vers une valeur dans la table pré-calculée qui sera XORRée
avec le registre. Je ne sais pas exactement pourquoi cela donne le même résultat (ca doit avoir un
rapport avec les propriétés de la fonction XOR), mais il a le Grand avantage de ne pas vous obliger
à ajouter des octets nuls / octets de votre Bitstring.
Regardons cet algorithme :
+----< Bitstring (ou fichier) | V 3 2 1 0 octet | +---+---+---+---+ XOR---< | | | | | Registre | +---+---+---+---+ | | | | XOR | ^ V +---+---|---+---+ | | | | | | table pré calculée | +---+---+---+---+ +---> - : : : : : +---+---+---+---+ | | | | | +---+---+---+---+ ( Figurez 2)
The 'reflected' direct Table Algorithm
Pour compliqué un peu les choses, il y a une version 'reflet' de cet Algorithme. Une valeur / registre Reflected
est un bit qui a pivoté autour de son centre. Par exemple 0111011001 est le reflet de 1001101110.
Ils ont inventé cela à cause de l'UART (le chip qui utilise le serial IO),qui envoie chaque octet
avec le bit le moins significatif (le bit 0) d'abord et en dernier le bit le plus significatif (le bit 7), c'est
le contraire de la situation normale. Au lieu de refléter chaque octet avant de le traiter, chaque contraire
est reflété. Un des avantages est d'obtenir un code plus compact lors de la mise en œuvre. Ainsi,
dans le calcul de la table, les octets sont changés par la droite et le poly est reflected. Dans le calcul
du CRC le registre est changé à droite et (bien sûr) la table reflétée est employée.
Bytes sting (ou fichier) - >---+ | 1. Dans la table chaque entrée est reflétée
Octet 3 2 1 0 V 2. Le registre initial est reflété +---+---+---+---+ | 3. Les octets de la Bytes string ne sont | | | | | >---XOR pas reflété, parce que tout le reste l'est. +---+---+---+---+ | | | XOR V ^ | +---+---|---+---+ | | | | | | | table Pré calculée +---+---+---+---+ | : : : : : <---+ +---+---+---+---+ | | | | | +---+---+---+---+ ( Figurez 3)
Notre algorithme est maintenant :
1. Changer le registre directement par un octet 2. XORer l'octet supérieur par un nouvel octet de la Bytes string Pour pointer vers un index dans la table ([0,255]) 3. XORer la valeur de la table dans le registre 4. Goto 1 s'il y a plus d'octets à traiter
Quelques exemples en Assembleur
voici la norme complète CRC-32 : Nom : "CRC-32" Taille : 32 Poly : 04C11DB7 Valeur initiale : FFFFFFFF Reflet : Vrai XORé avec : FFFFFFFF En prime, pour les gens curieux, voici la norme CRC-16 : Nom : "CRC-16" Taille : 16 Poly : 8005 Valeur initiale : 0000 Reflet : Vrai XORé avec : 0000
'XORé avec' est la valeur qui est XORRée avec la valeur finale du
registre avant obtention (comme réponse) du CRC final.
Il y a aussi des 'reversed CRC Poly's' mais ils ne sont pas appropriés pour ce tutorial.
Regardez mes références si vous voulez en savoir plus.
Pour la mise en œuvre en assembleur, j'emploie du code en 32 bits et du codes 16 bits...
Donc vous verrez quelques mélanges entre les deux codes...
Il est facile de convertir cela pour transformer le code en 32 bits.
Notez que la partie assembleur est entièrement écrite pour fonctionner correctement en Java ou en
C dont il est tiré.
Ok. Voici la routine assembleur pour le calcul de la table CRC-32 :
Xor ebx, ebx ; ebx=0, parce qu'il sera employé comme pointeur InitTableLoop : Xor eax, eax ; eax=0 pour nouvelle entrée Mov al, bl ; partie basses 8 octets d'ebx est copiée dans partie ; basses 8 octets d'eax ; Generate Entry Xor cx, cx EntryLoop : test eax, 1 Jz no_topbit Shr eax, 1 Xor eax, poly Jmp entrygoon No_topbit : Shr eax, 1 Entrygoon : Inc cx test cx, 8 Jz entryLoop Mov dword ptr [ebx*4 + crctable], eax Inc bx test bx, 256 Jz InitTableLoop
Notes :
- crctable est un tableau de 256 DWORD
- Eax est changé à droite parce que le CRC-32 emploie l'Algorithme reflété
- donc la partie basse 8 octets est traitée...
Dans du Java ou du C (int est en 32 bits) :
for (int cx=0; cx<8; cx++) { if (eax&&0x1) { eax>>=1; eax^=poly; } else eax>>=1; } crctable[bx]=eax; }
La mise en œuvre pour le calcul du CRC-32 utilise la table :
computeLoop: xor ebx, ebx xor al, [si] mov bl, al shr eax, 8 xor eax, dword ptr[4*ebx+crctable] inc si loop computeLoop xor eax, 0FFFFFFFFh
Notes :
- ds:si pointe vers le buffer où les octets sont traités
- Cx contient le nombre d'octets à traiter
- Eax contient le CRC en cours
- Crctable est la table calculée avec le code ci-dessus
- La valeur initiale du CRC est dans le cas d'un CRC-32 : FFFFFFFF
- Après le calcul complet, le CRC est XORRED avec : FFFFFFFF
En Java ou en C on obtient ceci :
for (int cx=0 ; cx >=8 ;
eax^=crcTable[ebx];
}
eax^=0xFFFFFFFF;
Nous voici arrivé à la fin de la première partie : si vous voulez en savoir encore plus sur
les CRC, je vous suggère de lire la documentation que vous trouverez dans les liens à la fin de ce
texte.
Ok.
Passons à la partie intéressante de ce document : Reverser un CRC-32 :
Partie II: Reversing CRC
Alors que je cherchais une façon de modifier un CRC...
J'ai été coincé plusieurs fois !
J'ai essayé de 'désactiver' le CRC en pensant que l'ordre des octets n'avait pas d'importance quel
que soit l'octet qui arrivait derrière...
Et je me suis rendu compte que je ne pourrait jamais travailler ainsi, parce que l'algorithme CRC est construit
de telle façon qu'il se fiche de l'octet que vous changez
Je me suis rendu compte que je pourrais seulement 'corriger' le CRC après avoir modifié les octets
qui m'intéressaient. Donc il fallait que je transforme le CRC en l'a valeur qui m'intéressait.
Voici mon idée :
Série d'octets : 01234567890123456789012345678901234567890123456789012 Vous voulez intervertir ^ et ^
De la position 9 à 26.
Nous avons aussi besoin de 4 octets supplémentaires (avant la position 30) pour la séquence d'octets
qui changeront la valeur d'origine du CRC après que les octets auront été modifiés.
Quand vous calculez le CRC-32 il n'y a pas de problème jusqu'à l'octet en 9ème position, ensuite
la série d'octets patchés va faire changer radicalement le CRC. Même au-delà de la position
26, où les octets ne sont pas changés, vous n'arrivez jamais à retrouver les valeurs du CRC
original.
En lisant le reste de cet essai, vous comprendrez pourquoi.
En fait il va falloir que vous préserviez le CRC quand vous patcherez une partie du code:
1. Calcul du CRC jusqu'à la position 9, et sauvegarde de cette valeur.
2. Continuer le calcul jusqu'à la position 27 et les 4 extra bytes, sauver le résultat.
3. Utiliser la valeur de 1 pour calculer le CRC des " nouveaux " octets et des 4 extra bytes (ca devrait
donner 27-9+4=22 bytes) et sauver la valeur obtenue.
4. maintenant, nous avons la "nouvelle" valeur du CRC, mais nous voulons que le CRC devienne "l'ancienne"
valeur du CRC. Nous allons utiliser "the reverse algorithm" pour calculer les 4 extra bytes.
Reversing CRC-16
Je pense que, pour rendre les choses plus faciles, vous devez calculer en premier un CRC-16. Nous nous situons,
d'une certaine manière, après le code patché, là ou nous voulons changer le CRC. Nous
connaissons le CRC original (calculé avant le patchage des data) et l'état actuel du registre CRC.
Nous voulons calculer les deux bytestring qui modifie le CRC.
En premier, nous allons calculer 'normalement' le CRC avec les 2 octets inconnus appelé X et Y, pour le
registre je vais prendre a1 a0, la seul non-variable etant zéro (00).
Regardez à nouveau notre dernier algorithme CRC, figure 3, pour mieux comprendre ce que je vais faire.
Ok, nous y sommes :
Prenez les 2-bytestring 'X Y'. Les Bytes sont traités à partir du coté gauche.
Prenez comme registre a1 a0.
Pour une opération XOR, j'écrirai '+' (comme dans le tutorial sur le CRC)
Traitement du premier byte, X: a0+X c'est le calcul du bit supérieur (topbyte) (1) b1 b0 séquence dans la table où pointe le topbyte points à 00 a1 la droite du registre 00+b1 a1+b0 2 précédentes lignes XORred avec les autres Le nouveau registre devient: (b1) (a1+b0) Traitement du second byte, Y: (a1+b0)+Y c'est le calcul du bit supérieur topbyte (2) c1 c0 séquence dans la table où pointe le topbyte points à 00 b1 la droite du registre 00+c1 b1+c0 2 précédentes lignes XORred avec les autres Le registre final est: (c1) (b1+c0) Je vais vous montrer un petite variante: a0 + X =(1) points to b1 b0 in table a1 + b0 + Y =(2) points to c1 c0 in table b1 + c0=d0 new low byte of register c1=d1 new high byte of register (1) (2)
Ne soyez pas inquiet, un exemple plus concert arrive ensuite.
Vous voulez que le registre soit égal à d1 d0 (le CRC original) et vous connaissez la valeur du registre
avant la transformation (a1 a0)...
Quels sont les 2 octets, ou quels X et Y, devez vous prendre pour le calcul du CRC ?
Ok. Nous allons travailler de l'arrière vers l'avant. d0 doit devenir b1+c0 et d1 doit devenir c1. Mais
comment diable !, je vous entends dire, pouvez vous savoir la valeur de l'octet b1 et c0 ? ? ?
Dois je vous rappelez la Table?
Vous pouvez consulter la valeur du WORD C0 C1 dans la Table parce que vous connaissez C1. Donc vous besoin de faire
une routine 'de consultation'. Si vous avez trouvé la valeur, soyez sur de vous rappelez l'index de cette
valeur parce que c'est le moyen de trouver les deux topbytes inconnus, par exemple.( 1)&(2)!
Si maintenant vous avez trouvé c1 c0, comment obtenir b1 b0 ?
Si b1+c0=d0 alors b1=d0+c0!
Utilisez votre routine de consultation pour trouver la valeur de b1 b0. Maintenant nous connaissons tout ce qui
nous est nécessaire pour calculer X et Y!
a1+b0+Y=(2) donc Y=a1+b0+(2) a0+X=(1) donc X=a0+(1)
Non-variable example for CRC-16
Regardons un exemple avec de vraies valeurs:
- registre avant: (a1=)DE (a0=)AD
- registre désiré: (d1=)12 (d0=)34
Regardez l'entrée commençant par 12 dans la table CRC-16 table de l'annexe.
- C'est l'entrée 38h dont la valeur est 12C0. Essayons de trouver une autre entrée commençant
par 12.
Vous ne pouvez pas en trouver parce que nous avons calculé chaque entrée pour chaque valeur possible
du topbyte et qu'il n'y en a que 256.
Maintenant nous connaissons (2)= 38, c1= 12 et c0= C0, donc b1= C0+34=F4, regardons l'entrée de B1 commençant
avec F4.
- c'est l'entrée 4Fh avec la valeur F441.
(1)= 4F, b1= F4 et b0= 41. Toute ces valeurs nécessaires vont permettre de calculer X and Y en faisant:
Y=a1+b0+(2)= DE+41+38 = A7 X=a0+(1) = AD+4F = E2
Conclusion: pour changer le registre CRC-16 de DEAD vers 1234, nous avons besoin
des bytes
E2 A7 (dans cet ordre).
Vous voyez, pour reverser un CRC, vous devez calculer à l'envers, et vous rappeler des valeurs trouvées.
Si vous programmez la table de consultation en assembleur, rappelez vous qu'INTEL sauve les valeurs à l'envers
en Little-Endian format.
Passons au CRC-32
Reversing CRC-32
Le CRC-32 est aussi facile (ou difficile) que le CRC-16. Nous allons travailler avec 4 octets au lieu de deux.
Prenez 4-bytestring X Y Z W , Les octets sont pris à partir de la gauche. Prenez comme registre a3 a2 a1 a0
Notez que a3 est l'octet le plus significatif, et a0 le moins.
Traitement du premier octet, X:
a0+X this is the calculated topbyte (1) b3 b2 b1 b0 sequence in table where the topbyte points at 00 a3 a2 a1 to right shifted register 00+b3 a3+b2 a2+b1 a1+b0 previous 2 lines XORred with eachother Le nouveau registre donne: (b3) (a3+b2) (a2+b1) (a1+b0) Traitement du second octete, Y: (a1+b0)+Y this is the calculated topbyte (2) c3 c2 c1 c0 sequence in table where the topbyte points at 00 b3 a3+b2 a2+b1 to right shifted register 00+c3 b3+c2 a3+b2+c1 a2+b1+c0 previous 2 lines XORred with eachother Le nouveau registre donne: (c3) (b3+c2) (a3+b2+c1) (a2+b1+c0) Traitement du troisième octet, Z: (a2+b1+c0)+Z this is the calculated topbyte (3) d3 d2 d1 d0 sequence in table where the topbyte points at 00 c3 b3+c2 a3+b2+c1 to right shifted register 00+d3 c3+d2 b3+c2+d1 a3+b2+c1+d0 previous 2 lines XORred with eachother Le nouveau registre donne: (d3) (c3+d2) (b3+c2+d1) (a3+b2+c1+d0) Traitement du quatrième octet, W: (a3+b2+c1+d0)+W this is the calculated topbyte (4) e3 e2 e1 e0 sequence in table where the topbyte points at 00 d3 c3+d2 b3+c2+d1 to right shifted register 00+e3 d3+e2 c3+d2+e1 b3+c2+d1+e0 previous 2 lines XORred with eachother Le registre final est égal à: (e3) (d3+e2) (c3+d2+e1) (b3+c2+d1+e0) Je vais vous montrer une méthode différente: a0 + X =(1) pointe vers b3 b2 b1 b0 in table a1 + b0 + Y =(2) pointe vers c3 c2 c1 c0 in table a2 + b1 + c0 + Z =(3) pointe vers d3 d2 d1 d0 in table a3 + b2 + c1 + d0 + W =(4) pointe vers e4 e3 e2 e1 in table b3 + c2 + d1 + e0 =f0 c3 + d2 + e1 =f1 d3 + e2 =f2 e3 =f3 (1) (2) (3) (4) (figure 4) Voici un exemple en valeurs réelles: (utilisez la table CRC-32 de l'index). Prenez comme valeur CRC d'origine a3 a2 a1 a0 -> AB CD EF 66 Prenez comme valeur CRC modifié, f3 f2 f1 f0 -> 56 33 14 78 (valeurs recherchées) First byte of entries entry value e3=f3 =56 -> 35h=(4) 56B3C423 for e3 e2 e1 e0 d3=f2+e2 =33+B3 =E6 -> 4Fh=(3) E6635C01 for d3 d2 d1 d0 c3=f1+e1+d2 =14+C4+63 =B3 -> F8h=(2) B3667A2E for c3 c2 c1 c0 b3=f0+e0+d1+c2=78+23+5C+66=61 -> DEh=(1) 616BFFD3 for b3 b2 b1 b0 Nous avons désormais toutes les valeurs nécessaires : X=(1)+ a0= DE+66=B8 Y=(2)+ b0+a1= F8+D3+EF=C4 Z=(3)+ c0+b1+a2= 4F+2E+FF+CD=53 W=(4)+d0+c1+b2+a3=35+01+7A+6B+AB=8E
(calcul final)
Conclusion: pour changer le CRC-32 de ABCDEF66 vers 56331478 nous avons besoin
de cette séquence d'octets: B8 C4 53 8E
The reverse Algorithm for CRC-32
Si vous regardez le by-hand computation de la séquence d'octets nécessaires, pour changer le registre
CRC de a3 a2 a1 a0 vers f3 f2 f1 f0, il est difficile de transformer celui ci en un bel algorithme compact.
Regardez cette version étendue du calcul final:
Position X =(1) + a0 0 Y =(2) + b0 + a1 1 Z =(3) + c0 + b1 + a2 2 W =(4) + d0 + c1 + b2 + a3 3 f0= e0 + d1 + c2 + b3 4 f1= e1 + d2 + c3 5 f2= e2 + d3 6 f3= e3 7 (figure 5)
C'est la même chose que pour la figure 4, seul quelques valeurs/octets changent.
Ce schéma va nous aider à obtenir un algorithme plus compact.
Si nous prenons un buffer de 8 octets, c'est pour réserver 1 octet, pour chaque ligne que vous voyez dans
la figure 5.
Les octets 0 à 3 sont remplis avec a0 à a3, les octets 4 à 7 sont remplis avec f0 à
f3. Comme précédemment, nous utilisons le dernier octet e3, qui est égal à f3, pour
regarder la valeur correspondante dans la table CRC.
Puis nous XORons cette valeur (e3 e2 e1 e0) sur la position 4 (comme dans la figure 5). Alors nous obtenons automatiquement
la valeur de d3, parce nous avons déjà XORré f3 f2 f1 f0 avec e3 e2 e1 e0, et que f2+e2 =
d3.
Comme nous connaissons maintenant la valeur (4), nous pouvons directement XORer cette valeur avec la position 3.
Maintenant, nous savons que d3 utilise ce résultat pour trouver les valeurs de d3 d2 d1 d0 et XOR sur la
position précédante, qui est la position3 (regardez la figure!).
XOR le nombre trouvé (3) pour la valeur en position 2. Nous connaissons désormais c3parce que nous
avons les valeurs de f1+e1+d2=c3 sur la position 5.
Nous continuons jusqu'à ce que nous XORons b3 b2 b1 b0 sur la position 1. Et voila!
Les octets 0 a 3 du buffer contiennent les octets X et W!
Récapitulons l'algorithme :
1. Dans un buffer de 8 octets, remplir la position 0 à 3 avec a0 à a3 (la valeur de départ
du registre CRC), et la position 4 à 7 avec f0 à f3 (valeur de finale recherchée du registre
CRC).
2. Prendre l'octet en position 7 et l'utiliser pour regarder la valeur complète.
3. XOR cette valeur (dword) sur la position 4
4. XOR le nombre d'entré (byte) sur la position 3
5. Répéter l'étape 2 & 3 trois fois de plus en décrémentant la position
à chaque fois par un.
Source de l'Algorithme
Place aux codes. Ci dessous vous trouverez le source de l'algorithme pour le CRC-en en assembleur (ca n'est pas
bien compliqué de l'adapter à d'autres langage et/ou aux CRC standards). Notez qu'en assembleur (sur
les PC) les DWORD sont écrits et lu dans l'ordre inverse de leur apparition en mémoire.
crcBefore dd (?) wantedCrc dd (?) buffer db 8 dup (?) mov eax, dword ptr[crcBefore] ;/* mov dword ptr[buffer], eax mov eax, dword ptr[wantedCrc] ; Step 1 mov dword ptr[buffer+4], eax ;*/ mov di, 4 computeReverseLoop: mov al, byte ptr[buffer+di+3] ;/* call GetTableEntry ; Step 2 */ xor dword ptr[buffer+di], eax ; Step 3 xor byte ptr[buffer+di-1], bl ; Step 4 dec di ;/* jnz computeReverseLoop ; Step 5 */
Notes:
- les registres eax, di et bx sont utilisés
Implementation of GetTableEntry
crctable dd 256 dup (?) ;doit être définie quelque part & ;et initialisée mov bx, offset crctable-1 getTableEntryLoop: add bx, 4 ;pointe vers (crctable-1)+k*4 (k:1..256) cmp [bx], al ;doit tjs trouver la valeur quelque part jne getTableEntryLoop sub bx, 3 mov eax, [bx] sub bx, offset crctable shr bx, 2 ret
En retour eax contient l'entrée de la table, bx le nombre d'entrée.
Well... vous êtes arrivé à la fin de cet essai. Si vous pouvez penser maintenant : Wow, tous
ces programmes protégés par des CRC peuvent dire 'bye bye'.
Nope. C'est vraiment trop facile de faire un anti-anti-CRC. Pour réussir avec succès le reverse d'un
CRC, vous aurez besoin de connaître précisément dans quelle partie du code le CRC est calculé,
et quel est l'algorithme utilisé. Une simple contre-mesure est d'utiliser deux algorithmes différents
ou une combinaison avec un autre data-protection algorithm.
Quoi qu'il en soit... J'espère que tout ce travail a été intéressant et que vous avez
apprécié de le lire autant que j'ai pu en avoir à l'écrire.
Mes remerciements vont aux beta-testers Douby/DREAD et Knotty Dread pour les commentaires sur mon travail, et qui
m'ont permis de l'améliorer!
Pour un patcher corrigeant un CRC-32 simple, visitez ma webpages:
http://surf.to/anarchriz -> Programming -> Projects
Pour plus d'infos sur DREAD visitez http://dread99.cjb.net
Si vous avez toujours des questions, vous pouvez m'écrire à anarchriz@hotmail.com, ou essayer les channels
#DreaD, #Win32asm, #C.I.A et #Cracking4Newbies (dans cet ordre) sur EFnet (sur IRC).
CYA ALL! - Anarchriz
Index
CRC-16 Table 00h 0000 C0C1 C181 0140 C301 03C0 0280 C241 08h C601 06C0 0780 C741 0500 C5C1 C481 0440 10h CC01 0CC0 0D80 CD41 0F00 CFC1 CE81 0E40 18h 0A00 CAC1 CB81 0B40 C901 09C0 0880 C841 20h D801 18C0 1980 D941 1B00 DBC1 DA81 1A40 28h 1E00 DEC1 DF81 1F40 DD01 1DC0 1C80 DC41 30h 1400 D4C1 D581 1540 D701 17C0 1680 D641 38h D201 12C0 1380 D341 1100 D1C1 D081 1040 40h F001 30C0 3180 F141 3300 F3C1 F281 3240 48h 3600 F6C1 F781 3740 F501 35C0 3480 F441 50h 3C00 FCC1 FD81 3D40 FF01 3FC0 3E80 FE41 58h FA01 3AC0 3B80 FB41 3900 F9C1 F881 3840 60h 2800 E8C1 E981 2940 EB01 2BC0 2A80 EA41 68h EE01 2EC0 2F80 EF41 2D00 EDC1 EC81 2C40 70h E401 24C0 2580 E541 2700 E7C1 E681 2640 78h 2200 E2C1 E381 2340 E101 21C0 2080 E041 80h A001 60C0 6180 A141 6300 A3C1 A281 6240 88h 6600 A6C1 A781 6740 A501 65C0 6480 A441 90h 6C00 ACC1 AD81 6D40 AF01 6FC0 6E80 AE41 98h AA01 6AC0 6B80 AB41 6900 A9C1 A881 6840 A0h 7800 B8C1 B981 7940 BB01 7BC0 7A80 BA41 A8h BE01 7EC0 7F80 BF41 7D00 BDC1 BC81 7C40 B0h B401 74C0 7580 B541 7700 B7C1 B681 7640 B8h 7200 B2C1 B381 7340 B101 71C0 7080 B041 C0h 5000 90C1 9181 5140 9301 53C0 5280 9241 C8h 9601 56C0 5780 9741 5500 95C1 9481 5440 D0h 9C01 5CC0 5D80 9D41 5F00 9FC1 9E81 5E40 D8h 5A00 9AC1 9B81 5B40 9901 59C0 5880 9841 E0h 8801 48C0 4980 8941 4B00 8BC1 8A81 4A40 E8h 4E00 8EC1 8F81 4F40 8D01 4DC0 4C80 8C41 F0h 4400 84C1 8581 4540 8701 47C0 4680 8641 F8h 8201 42C0 4380 8341 4100 81C1 8081 4040 CRC-32 Table 00h 00000000 77073096 EE0E612C 990951BA 04h 076DC419 706AF48F E963A535 9E6495A3 08h 0EDB8832 79DCB8A4 E0D5E91E 97D2D988 0Ch 09B64C2B 7EB17CBD E7B82D07 90BF1D91 10h 1DB71064 6AB020F2 F3B97148 84BE41DE 14h 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 18h 136C9856 646BA8C0 FD62F97A 8A65C9EC 1Ch 14015C4F 63066CD9 FA0F3D63 8D080DF5 20h 3B6E20C8 4C69105E D56041E4 A2677172 24h 3C03E4D1 4B04D447 D20D85FD A50AB56B 28h 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 2Ch 32D86CE3 45DF5C75 DCD60DCF ABD13D59 30h 26D930AC 51DE003A C8D75180 BFD06116 34h 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 38h 2802B89E 5F058808 C60CD9B2 B10BE924 3Ch 2F6F7C87 58684C11 C1611DAB B6662D3D 40h 76DC4190 01DB7106 98D220BC EFD5102A 44h 71B18589 06B6B51F 9FBFE4A5 E8B8D433 48h 7807C9A2 0F00F934 9609A88E E10E9818 4Ch 7F6A0DBB 086D3D2D 91646C97 E6635C01 50h 6B6B51F4 1C6C6162 856530D8 F262004E 54h 6C0695ED 1B01A57B 8208F4C1 F50FC457 58h 65B0D9C6 12B7E950 8BBEB8EA FCB9887C 5Ch 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 60h 4DB26158 3AB551CE A3BC0074 D4BB30E2 64h 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 68h 4369E96A 346ED9FC AD678846 DA60B8D0 6Ch 44042D73 33031DE5 AA0A4C5F DD0D7CC9 70h 5005713C 270241AA BE0B1010 C90C2086 74h 5768B525 206F85B3 B966D409 CE61E49F 78h 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 7Ch 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD 80h EDB88320 9ABFB3B6 03B6E20C 74B1D29A 84h EAD54739 9DD277AF 04DB2615 73DC1683 88h E3630B12 94643B84 0D6D6A3E 7A6A5AA8 8Ch E40ECF0B 9309FF9D 0A00AE27 7D079EB1 90h F00F9344 8708A3D2 1E01F268 6906C2FE 94h F762575D 806567CB 196C3671 6E6B06E7 98h FED41B76 89D32BE0 10DA7A5A 67DD4ACC 9Ch F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 A0h D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 A4h D1BB67F1 A6BC5767 3FB506DD 48B2364B A8h D80D2BDA AF0A1B4C 36034AF6 41047A60 ACh DF60EFC3 A867DF55 316E8EEF 4669BE79 B0h CB61B38C BC66831A 256FD2A0 5268E236 B4h CC0C7795 BB0B4703 220216B9 5505262F B8h C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 BCh C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D C0h 9B64C2B0 EC63F226 756AA39C 026D930A C4h 9C0906A9 EB0E363F 72076785 05005713 C8h 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 CCh 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 D0h 86D3D2D4 F1D4E242 68DDB3F8 1FDA836E D4h 81BE16CD F6B9265B 6FB077E1 18B74777 D8h 88085AE6 FF0F6A70 66063BCA 11010B5C DCh 8F659EFF F862AE69 616BFFD3 166CCF45 E0h A00AE278 D70DD2EE 4E048354 3903B3C2 E4h A7672661 D06016F7 4969474D 3E6E77DB E8h AED16A4A D9D65ADC 40DF0B66 37D83BF0 ECh A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9 F0h BDBDF21C CABAC28A 53B39330 24B4A3A6 F4h BAD03605 CDD70693 54DE5729 23D967BF F8h B3667A2E C4614AB8 5D681B02 2A6F2B94 FCh B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D
Références
Un guide indolore sur les algorithmes de détection d'erreur CRC :
Url : ftp://ftp.adelaide.edu.au/pub/rocksoft/crc_v3.txt
J'ai aussi employé le source aléatoire d'un algorithme CRC-32 pour mieux comprendre l'algorithme
:
cherchez 'CRC.ZIP' ou 'CRC.EXE' ou quelque chose de ce genre avec un ftpsearch (http://ftpsearch.lycos.com?form=advanced)
(c) 1998,1999 par Anarchriz( C'est VRAIMENT la dernière ligne :)
Translated by christal. Septembre 2000