Nous revoilà pour la suite (et la fin) de la résolution du défi de la saga Archives ! Pour ceux qui auraient raté un épisode, le début est par ici !
On commence par une présentation générale de ce que nous devons concrètement faire :
- récupérer le texte décrypté.
- le modifier pour y ajouter notre signature (on fera le service minimum).
- ré-encrypter le texte.
- récupérer la portion mémoire entre $6000 et $6600 juste avant l’exécution de la routine en $6000.
- y incorporer à partir de $61A7 notre texte fraîchement ré-encrypté.
- regrouper entre $7000-$7600 les octets provenant de $6000 et de $6300+ (un sur deux).
- encoder en 4:4 ce qui est entre $7000 et $7600.
- sur les 12 pages mémoire obtenues, faire un EOR entre chaque octet et sa position dans la page.
- écrire à partir de P$22/S$00 les 12 secteurs avec ces 12 pages.
A) Récupération du texte décrypté et de la mémoire entre $6000 et $6600 :
On va faire d'une pierre deux coups et tout récupérer en même temps. Pour cela, on refait exactement le même parcours que décrit dans la première partie. Je le résume rapidement :
- boot de la disquette modifiée qui nous rend la main au moment de sauter en $8344.
- on modifie le STA 0200,X par un STA 2000,X ($8356) et on shunte le JMP $0200 ($835E).
- 8344G
- on shunte les ROR (qui modifient le RTS) dans la routine en $2000 : $2C en $2014, $2018 et $201B.
- 2000G
- on shunte le JSR $6000 en $81EC.
- Arrivé à ce moment là, il faut booter un DOS (C600G) et on peut ainsi sauver la mémoire entre $6000 et $6600 :
- ] BSAVE D6000-6600,A$6000,L$600
- On repasse sous Monitor et on lance manuellement la routine en $6000 de façon à décoder le texte cette fois :
- 6000G
- On peut alors sauver le texte décodé qui commence en $6500. Pour la longueur, on sauve $28x$18 = $3C0 octets.
- ] BSAVE TEXT-DEC,A$6500,L$3C0
B) Ajout de la signature au texte décrypté :
On va directement utiliser DiskFixer pour éditer le texte (en éditant le fichier TEXT-DEC sauvé précédemment). On va se baser sur le mot "PERSONNE" pour se positionner correctement sur la bonne ligne. Attention il n’apparaît pas directement, Deckard a utilisé les codes ASCII "CTRL" qui ne s'affichent pas sous DiskFixer. Une fois le texte modifié, on n'oublie pas de le sauver. On n'oublie pas non plus d'utiliser les codes ASCII INVERSE pour notre signature. Cela fait partie de l'énoncé du défi je le rappelle...
C) Cryptage du texte modifié :
Cette fois, on n'y échappera pas, il va falloir faire chauffer Merlin pour coder un petit programme qui fera exactement l'inverse de la routine de décryptage. J'ai d'ailleurs utilisé comme base cette routine. J'ai également utilisé les mêmes adresses Page Zéro comme variables (attention, elles ne correspondent bien évidemment plus aux mêmes données que pour le décodage). Allez encore une petite liste avec cette fois le fonctionnement du programme de codage :
- on récupère un caractère du texte à afficher.
- on le teste pour connaître son attribut ($E0/$80/$40/$00).
- si cet attribut est différent de celui en cours, on envoie un $00 ainsi que le code correspondant à l'attribut ($08/$01/$04/$02) vers la partie routine d'encodage.
- on supprime les 3 bits forts (7-6-5) du caractère. On obtient donc un nombre compris entre $01 et $3F.
- ce nombre va servir d'index et on récupère ainsi dans une table la valeur correspondante. Et c'est finalement cette valeur qui va être encodée à son tour.
- s'il ne reste plus de caractères (donc texte fini par un 00), on envoie le marqueur de fin, c'est à dire un double 00 qu'on encode également.
L'encodage est bien évidemment l'exact opposé du décodage mais repose (forcément) sur le même principe (en gros, une valeur est codée sur deux demi-octets consécutifs). Pour la construction de la table d'encodage, et bien, on prend l'opposé encore une fois de la table de décodage. Cette fois c'est la valeur même du caractère (après mise à 0 des bits 5-6-7 ) qui sert directement de valeur d'entrée. Et en retour, on obtient donc l'index. Techniquement, ce n'est donc pas une suite de caractères qui sont encodés mais une suite d'index correspondants à la table de décodage...
À noter que deux valeurs (entre $01 et $3F donc) n'avaient pas d'index associé dans la table de décodage. Ce sont les valeurs $1E et $26. Et ce $26, une fois l'attribut ajouté devient $E6 soit le caractère "f" utilisé abondamment dans le texte de présentation du défi !
Ce n'est bien entendu pas un bug mais une dernière subtilité de notre ami Deckard ! Pour régler le problème, il suffit de mettre la même valeur que pour l'entrée $06 !
Explication : $06 = 00000110 et $26 = 00100110. Comme de toute façon l'attribut (ici $E0 = 11100000) sera ajouté (par un ORA voir explication dans la première partie), que ça soit $06 ou $26 le résultat sera le même, à savoir : 11100110 soit $E6 !
Si arrivé à ce stade, vous êtes encore en train de lire et si en plus vous avez à peu près compris le charabia de ce dernier paragraphe, merci de m'écrire pour vous faire connaître, car vous êtes réellement impressionnant !
Bref, le plus simple c'est de passer directement au code source que j'ai essayé de commenter le plus possible :
1 ORG $2000 2 LST OFF 3 4 SOURCE EQU $08 5 DEST EQU $06 6 VALEUR EQU $EC 7 INDSOUR EQU $EE ; INDEX DE LA SOURCE 8 INDDEST EQU $EF ; INDEX DE LA DESTINATION 9 COMENCO EQU $ED ; COMPTEUR ENCODAGE 10 ATTRIBP EQU $1B , ATTRIBUT PRECEDENT 11 ATTRIB EQU $1A ; ATTRIBUT DU CARACTERE 12 CARACT EQU $1C ; CARACTERE COURANT 13 14 LDA #$00 15 STA COMENCO ; COMPTEUR ENCODAGE 16 STA SOURCE 17 LDA #$A7 18 STA DEST 19 LDA #$61 ; 20 STA DEST+1 ; DESTINATION 21 LDA #$65 ; SOURCE 22 STA SOURCE+1 23 LDA #$80 24 STA ATTRIBP ; ATTRIBUT TEXTE PRECEDENT 25 26 27 LDY #$00 28 STY INDSOUR ; INDEX SOURCE 29 STY INDDEST ; INDEX DESTINATION 30 31 BOUCLEPR LDY INDSOUR ; ON RECUPERE L'INDEX SOURCE 32 LDA (SOURCE),Y ; LECTURE CARACTERE A ENCODE 33 BEQ FIN ; SI C'EST 00 ON A FINIT ! 34 STA CARACT ; ON LE SAUVE 35 INY ; ON PREPARE LE SUIVANT 36 BNE SUITE 37 INC SOURCE+1 38 SUITE STY INDSOUR ; ON SAUVE INDEX SOURCE 39 40 AND #$E0 ; ON TEST LES BITS 5-6-7 41 CMP #$E0 ; 111000000 ? 42 BNE SUITE1 43 LDA #$08 ; SI OUI ON FIXE L'ATTRIBUT 44 STA ATTRIB ; 45 JMP GO 46 47 SUITE1 LDA CARACT 48 AND #$80 ; ON TEST LE BIT 7 49 CMP #$80 ; 10000000 ? 50 BNE SUITE2 51 LDA #$01 ; SI OUI ATTRIBUT = 1 52 STA ATTRIB 53 JMP GO 54 55 SUITE2 LDA CARACT 56 AND #$40 ; BIT 6 57 CMP #$40 ; 01000000 58 BNE SUITE3 59 LDA #$04 ; ATTRIBUT = 4 60 STA ATTRIB 61 JMP GO 62 63 SUITE3 LDA #$02 ; BIT 5-6-7 A 0 ? 64 STA ATTRIB ; ATTRIBUT = 2 65 66 GO CMP ATTRIBP ; EST-CE LE MEME ATTRIBUT 67 BEQ SUITE4 ; SI OUI ON NE CHANGE RIEN 68 LDA #$00 ; SI NON ON ENCODE LE CHGT 69 JSR ENCODE ; UN 00 POUR AMORCER 70 LDA ATTRIB 71 STA ATTRIBP 72 JSR ENCODE ; + L'ATTRIBUT CODE 73 74 SUITE4 LDA CARACT ; ON RECUPERE LE CARACTERE 75 AND #$3F ; 00111111 - ON GARDE LES 6 BITS 76 TAX ; ON TRANSFORME EN INDEX DE TABLE 77 LDA TABLE,X ; ON PREND LA VALEUR ASSOCIE 78 JSR ENCODE ; ON ENCODE LA VALEUR ! 79 JMP BOUCLEPR ; ON BOUCLE ! 80 81 FIN LDA #$00 ; SI C'EST LA FIN 82 JSR ENCODE ; ON ENCODE 83 LDA #$00 ; DEUX ZERO 84 JSR ENCODE ; DE SUITE 85 RTS ; ET ON QUITTE ! 86 87 ENCODE LDY INDDEST ; ON RECUPERE L'INDEX POUR LA DEST 88 STA VALEUR ; ON SAUVE LA VALEUR A ENCODE 89 LDA COMENCO ; COMPTEUR ENCODAGE 90 BEQ TYPE0 ; 0 ? 91 CMP #$01 ; 1 ? 92 BEQ TYPE1 93 CMP #$02 ; 2 ? 94 BEQ TYPE2 95 96 TYPE3 LDA VALEUR ; OU TYPE 3 97 CLC 98 ADC (DEST),Y ; ON AJOUTE 99 STA (DEST),Y ; DIRECTEMENT 100 INY ; DEST + 1 101 BNE S1 102 INC DEST+1 103 S1 LDA #$00 ; REMISE A ZERO COMPTEUR 104 STA COMENCO 105 STY INDDEST 106 RTS 107 108 TYPE0 LDA VALEUR ; TYPE 0 109 ASL ; ON NE GARDE 110 ASL ; QUE LES 6 BITS FAIBLES 111 STA (DEST),Y ; ON SAUVE 112 INC COMENCO ; TYPE ENCODAGE + 1 113 STY INDDEST 114 RTS 115 116 TYPE1 LDA VALEUR ; TYPE 1 117 AND #$30 ; ON NE GARDE QUE LES BITS 4-5 118 LSR ; ON LES DECALLE 119 LSR ; 120 LSR ; 121 LSR ; JUSQU'A OBTENIR 000000XX 122 CLC 123 ADC (DEST),Y ; ON LES AJOUTE 124 STA (DEST),Y ; A LA DESTINATION 125 INY ; DEST + 1 126 BNE S2 127 INC DEST+1 128 S2 LDA VALEUR ; ON REPREND LA VALEUR 129 AND #$0F ; ON NE GARDE QUE LES BITS 0-3 130 ASL ; ON DECALLE POUR OBTENIR 131 ASL 132 ASL 133 ASL ; XXXX0000 134 STA (DEST),Y ; ON SAUVE 135 INC COMENCO ; TYPE + 1 136 STY INDDEST 137 RTS 138 139 TYPE2 LDA VALEUR ; TYPE 2 140 AND #$3C ; ON OBTIENT : 00XXXX00 141 LSR ; DECALLAGE 142 LSR ; POUR OBTENIR 0000XXXX 143 CLC 144 ADC (DEST),Y ; ON AJOUTE A DEST 145 STA (DEST),Y 146 INY ; DEST + 1 147 BNE S3 148 INC DEST+1 149 S3 LDA VALEUR ; ON REPREND LA VALEUR 150 AND #$03 ; ON NE GARDE QUE BIT 0-1 151 ASL ; OUI JE SAIS J'AURAIS 152 ASL ; PU DECALLER DANS L'AUTRE 153 ASL ; SENS MAIS C'EST PLUS CLAIR 154 ASL ; JE TROUVE ! 155 ASL ; ON OBTIENT DONC 156 ASL ; XX000000 157 STA (DEST),Y ; ET ON SAUVE ! 158 INC COMENCO ; TYPE + 1 159 STY INDDEST 160 RTS 161 162 TABLE HEX 00,0C,07,3A,2F,13,24,10,06,0F,27,0A,29,23 163 HEX 30,31,01,15,17,3C,04,09,3E,03,35,21,18,0B 164 HEX 28,32,38,26,38,14,20,1D,33,22,24,0E,3D,2A 165 HEX 2B,08,3B,34,2D,36,19,1E,05,1C,39,02,2E,2C 166 HEX 25,11,12,1A,1B,37,0D,16 |
Quelques petites notes encore à propos de ce code source.
- j'ai utilisé les mêmes adresses de destination (donc texte codé vers $61A7) et de source (texte avant codage en $6500) que celles utilisées par la routine de décodage, afin de faciliter les tests. Vous trouverez d'ailleurs sur la disquette de travail en fin d'article, la routine de décodage de Deckard issue du défi dans laquelle a été virée toute la partie écrasement/nettoyage du code). Exécutable en $6109, ce fichier DECODAGE1 (qui se charge en $6000) permet ainsi de tester immédiatement si le texte a été correctement encrypté.
- évidemment, il ne fallait pas espérer la même optimisation de mon code que celle rencontrée dans le défi. Ici le but est d'avoir quelque chose de fonctionnel avec chaque étape bien détaillée.
- pour bien comprendre l'encryptage, il faut regarder en parallèle ce qui se passe dans la partie décryptage (voir article partie I). Le point fondamental à comprendre c'est que la plupart des valeurs sont encodées à cheval sur deux octets. De même les attributs des caractères (inverse/clignotant/normal) sont intégrés et encodés avec les valeurs.
Utilisation de tout cela (on fera référence ici aux fichiers présents sur la disquette de travail fournie plus bas) :
- on charge notre DUMP mémoire de la partie $6000-$6600 (fichier D6000-6600).
- on charge notre programme CODAGE1 (issu du source précédent) exécutable en $2000.
- on charge notre TEXTE modifié avec notre superbe signature (fichier TEXT-DEC) en $6500.
- on exécute le codage ($2000G sous Monitor).
- on nettoie par des 00 ce qui est entre $6500 et $6600 (à savoir une partie de notre texte décodé). On va utiliser une petite ligne de commande sous Monitor : *6500 : 00 N 6501<6500.6600M. Cette partie n'est pas obligatoire mais c'est plus propre...
- À cet instant, on a donc entre $6000 et $6600, le programme de décryptage du défi et notre texte codé. On sauve l'espace mémoire (il sera utilisé par la partie suivante) :
- ] BSAVE PHASE1,A$6000,L$600
D) Et on emballe le tout...
On touche (presque) au but (si si) ! Une fois le texte ré-encrypté et placé en $61A7, il ne reste plus qu'à faire faire au tout (routine de décryptage + texte encrypté) le chemin inverse de celui qui l'a emmené là !
Première étape : on regroupe ce qui est en $6000+/$6300+ vers $7000 un octet sur deux. Un source vaut mieux que de longs discours donc hop :
1 ORG $2000 2 LST OFF 3 4 SOURCE1 EQU $06 5 SOURCE2 EQU $08 6 DESTI EQU $18 7 TEMP EQU $1A 8 9 LDA #$00 10 STA SOURCE1 11 STA SOURCE2 12 STA DESTI 13 LDA #$60 14 STA SOURCE1+1 15 LDA #$63 16 STA SOURCE2+1 17 LDA #$70 18 STA DESTI+1 19 20 21 LDX #$00 22 23 B1 LDY #$00 24 25 B2 LDA (SOURCE1),Y ; $6000+ 26 STY TEMP 27 LDY #$00 28 STA (DESTI),Y ; $7000+ 29 INC DESTI 30 BNE S1 31 INC DESTI+1 32 S1 LDY TEMP 33 LDA (SOURCE2),Y ; $6300+ 34 LDY #$00 35 STA (DESTI),Y ; $7001+ 36 INC DESTI 37 BNE S2 38 INC DESTI+1 39 S2 LDY TEMP 40 INY 41 BNE B2 42 INX 43 CPX #$06 44 BEQ FIN 45 INC SOURCE1+1 46 INC SOURCE2+1 47 JMP B1 48 49 FIN RTS |
Pas besoin de commentaires particuliers sur ce code je pense, c'est vrai que c'est très moche mais ça a au moins le mérite d'être plutôt limpide au niveau compréhension. Ceci correspond au programme GATHERING sur la disquette de travail. On charge donc PHASE1 et on lance ce code en $2000. On n'a plus qu'à sauver ce qui a été écrit entre $7000 et $7600 (dans PHASE2 par exemple).
Seconde étape : on applique à PHASE2 le codage de type 4:4 et dans la foulée le EOR (reportez-vous à la partie I de l'article pour savoir de quoi on parle hein...). Pas besoin de détailler le EOR, tout le monde connaît. Quant au codage/décodage type 4:4, on le rencontre dans n'importe quelle routine de lecture bas niveau (standard), car c'est le codage utilisé par les infos VOLUME/PISTE/SECTEUR/CHECKSUM dans le champ adresse de chaque secteur. C'est donc un codage courant dans le monde Apple II que l'on repère vite. Le principe est simple : on code les bits pairs d'un côté et les impairs de l'autre. Chaque octet est donc réparti sur deux octets. Pas optimal certes mais ultra rapide et facile à mettre en œuvre, on ne va donc pas s'en plaindre !
Voici le code source (avec en commentaires, ce qui se passe au niveau des bits lors de ce fameux codage) :
1 ORG $2000 2 LST OFF 3 4 SOURCE EQU $06 5 DESTI1 EQU $08 6 DESTI2 EQU $18 7 TEMP EQU $1A 8 9 LDA #$00 10 STA SOURCE 11 STA DESTI1 12 STA DESTI2 13 LDA #$70 14 STA SOURCE+1 15 LDA #$40 16 STA DESTI1+1 17 LDA #$46 18 STA DESTI2+1 19 20 **** CODAGE TYPE 4:4 **** 21 22 LDX #$00 23 B1 LDY #$00 24 25 B2 LDA (SOURCE),Y ; A = XYXYXYXY. On commence par les impairs X 26 LSR ; après décalage droite A = 0XYXYXYX 27 ORA #$AA ; $AA = 10101010 (sert à "encapsuler" les bits!) 28 STA (DESTI1),Y ; après le ORA, A=1X1X1X1X 29 LDA (SOURCE),Y ; A = XYXYXYXY (on s'occupe des bits pairs Y) 30 ORA #$AA ; $AA = 10101010 31 STA (DESTI2),Y ; après le ORA, A = 1Y1Y1Y1Y 32 33 INY 34 BNE B2 35 INC SOURCE+1 36 INC DESTI1+1 37 INC DESTI2+1 38 INX 39 CPX #$06 40 BNE B1 41 42 **** EOR **** 43 44 LDA #$40 45 STA DESTI1+1 46 47 LDX #$00 48 49 B4 LDY #$00 50 B3 STY TEMP ; Y = index de l'octet dans la page mémoire courante 51 LDA (DESTI1),Y 52 EOR TEMP ; EOR de l'octet avec sa position dans la page 53 STA (DESTI1),Y 54 INY 55 BNE B3 56 INX 57 CPX #$0C 58 BEQ FIN 59 INC DESTI1+1 60 JMP B4 61 62 FIN RTS |
Le résultat de ce source, c'est le programme QUATQUAT que l'on applique sur PHASE2 en mémoire (toujours par un $2000G). On obtient alors l'équivalent de 12 secteurs (on avait 6 pages mémoire qui ont été "doublées" par le 4:4) à partir de l'adresse $4000. Il ne reste plus alors qu'à écrire ces secteurs directement sur une copie propre d'Archives 10 à partir de la piste $22, secteur $00 à l'aide du programme ACCÈS DIRECT par exemple (ordre croissant).
Et voilà c'est fini... Incroyable non ? ! Il ne reste plus qu'à rebooter le tout, d'appuyer sur ESC et de contempler le joli graffiti (qui doit normalement s'afficher, enfin j'espère...). Tant d'efforts pour ça !
E) Le mot de la (vraie) fin
On ne va tout de même pas se quitter comme ça après tout ce temps passé ensemble, voici donc quelques remarques supplémentaires (dans une ultime liste) :
- oui effectivement Deckard avait raison de prévenir, c'était bien chiant ! Je me suis bien pris la tête sur certaines parties...
- on ne peut que lui rendre hommage pour la qualité de son code et le soin apporté au défi.
- pourquoi spécifiquement devoir écrire en INVERSE alors ? Vu que le cryptage oblige à recoder tout le texte, même ce qu'on ne modifie pas, écrire en inverse ou en normal ne rend pas spécialement le défi plus compliqué. Ou plutôt devrais-je dire, même si on avait écrit en normal, cela n'aurait pas été plus simple pour autant...
- Deckard a écrit une routine complète de lecture bas niveau en y incorporant un EOR. Seulement, ce EOR n'arrivant qu'après la phase de Post-Nibblelisation, il ne pose pas vraiment de problème pour le (re)cryptage. Et surtout pas besoin de routine d'écriture spécifique (ouf).
- That's All Folks...
Télécharger l'image .dsk de la face 1 de Archives 10 avec le défi accompli !
Retrouvez sur la disquette de travail les sources et les binaires des programmes de cryptage. Ainsi que les différents fichiers auxquels il est fait allusion dans l'article.
envoyé le 01-11-2011 à 21 h 54 min
Balaise tu es! Et Deckard aussi.
envoyé le 01-11-2011 à 23 h 02 min
Je suis certain qu’à l’époque, je n’aurais jamais réussi…
Quant à Deckard, son talent n’est plus à démontrer !
envoyé le 11-01-2012 à 23 h 57 min
Je découvre seulement maintenant que le défi a été relevé!
Félicitations Arnaud 😉
J’avoue avoir éclaté de rire en arrivant sur le « Tant d’efforts pour ça ! ». C’est vrai que ces défis étaient encore pires que le cracking puisqu’après bien des efforts on n’avait même pas un jeu à disposition!
JM
envoyé le 12-01-2012 à 9 h 52 min
J’en ai bavé pour ce défi, ça c’est sûr !
En tout cas, il m’a permis de redécouvrir (ou découvrir) plein de choses.
C’était d’ailleurs un peu le but lorsque je me suis attaqué à tous ces défis. Me remettre à niveau.
Aujourd’hui, grâce à la documentation massive que l’on peut trouver (vu que tout a été écrit ou presque sur l’Apple II), je m’aperçois qu’à l’époque, j’étais vraiment nul. Et que je ne maîtrisais pas grand chose…
Aujourd’hui non plus d’ailleurs mais j’y travaille…
Je suis juste un peu plus lent qu’il y a 25 ans 😉