Seconde partie de l'article consacré au défi de Championship Wrestling imaginé par les ACS Boys, Deny et The Gog's. Résumé de l'épisode précédent : on en a bavé mais on a enfin localisé la routine de décryptage qui se trouve en $6000. Nous avons aussi découvert où se trouve l'image encodée sur le disque. Et comble du raffinement, nous avons un peu bidouillé une copie  nous permettant ainsi d’atterrir juste avant le décodage (ou presque) sans devoir tout retracer. Pour ceux n'ayant pas lu la première partie (ou s'étant endormis avant la fin), je vous invite à y jeter un œil tout de même avant d’enchaîner...

La première étape avant d'attaquer les choses sérieuses, sera de récupérer une image décodée du défi afin de pouvoir y apposer notre petite signature. Il serait bon également de sauver sur disque toute la routine de décodage car cela pourrait nous être utile plus tard (mais non je ne spoile pas !). Pour ce faire, nous allons repartir de notre disque modifié. Je rappelle que, quand il nous rend la main, nous sommes juste avant l'exécution de la routine en $1180, routine qui s'occupait de décrypter tout une portion de code  et finissait par nous envoyer en $6000 en utilisant l'astuce de la pile et de la routine HOME.

Pour récupérer la routine, procédons ainsi :

1176 : 4C 59 FF ; on shunte l'appel à la routine HOME donc le saut en $6000
1180G ; on lance l'exécution pour obtenir le décryptage de la routine
      ; de décryptage (vous suivez ou bien ?)
 
C600G ; on peut alors booter un DOS 3.3 (n'oubliez pas de changer de disque...)  
 
BSAVE ROUTINE DECODAGE,A$6000,L$500 ; on sauve la routine décryptée !
                                    ; ne soyez pas pingre sur la portion à
                                    ; enregistrer car il faut aussi sauver
                                    ; la partie qui se trouve en $6400+

Pour obtenir l'image décryptée, cela commence pareil :

1176 : 4C 59 FF
1180G
6079 : 4C 59 FF ; on stoppe la routine juste après le décodage
 
C600G           ; boot d'un DOS 3.3
 
BSAVE IMAGE,A$2000,L$2000 ; sauvegarde de toute la Page Hires 1

Attention : si vous signez l'image en utilisant Blazzing Paddle par exemple, faites bien attention car l'image sera alors sauvegardée en 33 secteurs (et non 34). Il faudra bien veiller à mettre à zéro les octets entre $3FF8 et $3FFF (non sauvegardés par Blazzing Paddle car inutiles) avant de réencoder l'image. Nous avons besoin d'une Page Hires complète de $2000 octets car c'est ce que la routine de décodage attendra...

Ces étapes préliminaires effectuées, nous allons pouvoir entrer concrètement dans le vif du sujet et reprendre point par point les 3 phases de décodage survolées dans la première partie. Afin de simplifier le tout, nous allons nommer ces phases P,Q et R qui correspondront aux phases successives 1,2 et 3 exécutées lors du décodage. Pour chacune de ces trois phases de décodage, nous devrons générer un code inverse pour l'encodage. Nous obtiendrons alors les 3 phases d'encodage correspondantes que nous appellerons P',Q' et R'.

Première phase : P

6003-   A9 00       LDA   #$00
6005-   85 10       STA   $10     ; compteur initialisé à 00
 
6007-   A9 00       LDA   #$00
6009-   85 0E       STA   $0E     ; zone à traiter (buffer)
600B-   A9 20       LDA   #$20
600D-   85 0F       STA   $0F     ; commence en $2000
 
; décodage phase 1 : P => (Buffer) =  (Buffer) EOR (Buffer+1) 
 
600F-   A2 20       LDX   #$20    ; $20 pages mémoire ($20-$3F)
 
6011-   A0 00       LDY   #$00
 
6013-   B1 0E       LDA   ($0E),Y ; A<-(Buff)
6015-   C8          INY
6016-   51 0E       EOR   ($0E),Y ; A<- A EOR (Buff+1)
6018-   88          DEY
6019-   91 0E       STA   ($0E),Y ; A->(Buff)
601B-   C8          INY
601C-   D0 F5       BNE   $6013
 
601E-   E6 0F       INC   $0F     ;
6020-   CA          DEX
6021-   D0 EE       BNE   $6011   ; il reste des pages à traiter ?
 
6023-   E6 10       INC   $10     ; on incrémente le compteur
6025-   AD F1 20    LDA   $20F1   ; on récupère le contenu de $20F1
6028-   49 11       EOR   #$11    ; est-il égal à #$11 ?
602A-   D0 DB       BNE   $6007   ; non ? alors on refait un cycle complet
 
602C-   A5 10       LDA   $10     ; si oui
602E-   C9 0A       CMP   #$0A    ; on vérifie que le compteur est bien à 10
6030-   D0 FE       BNE   $6030   ; sinon boucle infinie

Petit aparté sur le EOR (Exclusive OR) : une des propriétés fondamentales de cet opérateur binaire est sa réversibilité : si on a A EOR B = C, alors C EOR B = A. Pour passer de C à A ou de A à C, il faut donc utiliser exactement le même argument B pour le EOR.

Pour traduire en français notre première phase de décodage, on dirait : on prend un octet d'une page mémoire donnée, on fait un EOR avec l'octet qui le suit immédiatement et on remet le tout à l'emplacement du premier. Chaque octet est donc dépendant de celui qui le suit immédiatement. Et ainsi de suite. Pour inverser la routine (donc encoder cette fois), il faudra toujours faire un EOR entre un octet et celui qui le suit mais en partant non plus du premier d'une page mémoire mais du dernier. Et remonter jusqu'au premier. En effet, le premier octet d'une page mémoire étant celui qui est décodé en premier, il est donc logique qu'il soit celui encodé en dernier. Et inversement, le dernier décodé (le $FF) devra être le premier encodé si on veut obtenir la routine inverse.

Il est également important de comprendre que les pages mémoire du buffer d'entrée (donc toute la zone de la Page graphique 1) sont indépendantes entre elles lors du décodage. Quand l'index Y devient supérieur à $FF, il repasse à $00 (c'est un registre 8 bits). Ce qui fait que le dernier octet d'une page mémoire donnée (exemple $21FF) sera décodé (EORé) avec le premier de la même page ($2100). Il n'y a pas d'interconnexion entre les pages mémoire. On pourra donc les traiter lors de l'encodage dans l'ordre croissant (comme pour le décodage) ou bien dans n'importe quel ordre. On va se simplifier la vie et reprendre l'ordre croissant.

Ce qui nous amène pour notre phase d'encodage P' (ceux ne pigeant pas pourquoi elle s'appelle P "prime" sont condamnés à tout relire !) à un code qui ressemble quasiment à celui du décodage, seule nuance, on part de l'octet $FF et on redescend vers $00 pour chaque page mémoire :

** ENCODAGE : PHASE P' **
 
         LDA   #$00
         STA   COMPT       
 
:B6      LDX   #$20       
 
         STX   ADDH
         LDA   #$00
         STA   ADDB       
 
         LDY   #$FF       ; on part de $FF
 
:B5      LDA   (ADDB),Y   ; (Buff)
         INY
         EOR   (ADDB),Y   ; EOR (Buff + 1)
         DEY
         STA   (ADDB),Y   ; remis dans Buff
         DEY              ; on décrémente
         CPY   #$FF       ; vers 00 (inclus)
         BNE   :B5        
 
         INC   ADDH
         DEX
         BNE   :B5        
 
         INC   COMPT
         LDA   COMPT
         CMP   #$0A
         BNE   :B6

Nous incrémentons un compteur afin d'effectuer 10 fois le même cycle de codage . Rappelez-vous dans la routine de décodage, il était vérifié à la fin que l'octet en $20F1 correspondait bien à #$11 au bout de 10 exécutions de la même routine. Et là vous allez me dire : "oui mais le #$11 qui doit être en $20F1, il sort d'où ?"

Le décodage de l'image du défi contient trois phases successives P puis Q puis R (on va le savoir...). Pour encoder, il faut effectuer également 3 phases (P',Q' et R') mais dans l'ordre inverse : la première phase de décodage devra correspondre à notre dernière phase d'encodage, et la dernière de décodage à notre première partie du codage. Vous l'aurez compris, la seconde phase reste à la même place ! Nous aurons donc pour l'encodage trois phases dans cette ordre : R' puis Q' puis enfin P'. Le rapport avec le #$11 ? Et bien cet octet ($20F1) qui est vérifié dans la phase 1 du décodage devra donc être issu des phases d'encodage R' et Q'. Pour le moment, mettons juste dans un coin de notre cerveau qu'il y a une vérif qui traîne dans le coin...

Seconde phase  : Q

; décodage phase 2 : Q => (A) EOR XX
 
6032-   A9 00       LDA   #$00
6034-   85 0F       STA   $0F
6036-   A9 20       LDA   #$20     ; zone à traiter commence en $2000
6038-   85 10       STA   $10      ; $20 pages à traiter
603A-   A2 20       LDX   #$20     ; compteur de pages (qu'on va décrémenter)
 
603C-   A0 00       LDY   #$00
 
603E-   B1 0F       LDA   ($0F),Y  ; A<-(Buff)
6040-   48          PHA            ; sauve A
6041-   98          TYA            ; A<-Y
6042-   18          CLC
6043-   65 10       ADC   $10      ; on lui additionne la page courante
6045-   48          PHA            ; on sauve A'
6046-   8A          TXA            ; compteur dans A
6047-   6A          ROR            ; rotation droite
6048-   85 11       STA   $11      ; sauve résultat dans TEMP
604A-   68          PLA            ; on récupère le A'
604B-   2A          ROL            ; rotation gauche
604C-   2A          ROL            ; rotation gauche
604D-   2A          ROL            ; rotation gauche
604E-   45 11       EOR   $11      ; EOR avec TEMP
6050-   85 11       STA   $11      ; sauve TEMP
6052-   68          PLA            ; on restaure A (c'est à dire (Buff))
6053-   45 11       EOR   $11      ; EOR avec TEMP
6055-   91 0F       STA   ($0F),Y  ; sauve dans (Buff)
6057-   88          DEY            ;
6058-   D0 E4       BNE   $603E    ; on boucle pour faire toute la page mémoire
 
605A-   E6 10       INC   $10      ; on incrémente Buff
605C-   CA          DEX            ; il reste des pages à traiter ?
605D-   D0 DD       BNE   $603C    ; oui on boucle

Le petit rappel sur le EOR de tout à l'heure, va nous permettre de bien comprendre que cette phase Q de décodage, ne fait que prendre un A (issu du Buffer), calculer un B à partir de la page courante et du compteur, pour ensuite faire un A EOR B et générer notre fameux C. Pour encoder et revenir vers A, il faudra donc le C (on le connaît puisque ce sera un octet de l'image) et calculer le B de la même manière que la routine ci-dessus. Et oui de la même manière car il nous faut le même B ! Peu importe la façon alambiquée à coup de ROR et autre ROL utilisée par la routine de décodage, il suffit de reprendre exactement la même routine pour l'encodage ! Magique et pratique... Ce qui nous donne :

** ENCODAGE : PHASE Q' **     
 
         LDA   #$00
         STA   ADDB
         LDA   #$20
         STA   ADDH
         TAX              
 
:B4      LDY   #$00       
 
:B3      LDA   (ADDB),Y
         PHA
         TYA
         CLC
         ADC   ADDH
         PHA
         TXA
         ROR
         STA   TEMP
         PLA
         ROL
         ROL
         ROL
         EOR   TEMP
         STA   TEMP
         PLA
         EOR   TEMP
         STA   (ADDB),Y
         DEY
         BNE   :B3        
 
         INC   ADDH
         DEX
         BNE   :B4

Oui c'est exactement la même routine que celle du décodage. Et il va se passer la même chose pour la phase R, troisième et dernière que nous allons voir maintenant :

Troisième phase : R

; décodage phase 3 : R  => A = A EOR (#$78 EOR Y) 
 
605F-   A9 00       LDA   #$00
6061-   85 0E       STA   $0E
6063-   A9 20       LDA   #$20
6065-   85 0F       STA   $0F
6067-   AA          TAX               
 
6068-   A0 00       LDY   #$00
606A-   98          TYA
606B-   49 78       EOR   #$78
606D-   51 0E       EOR   ($0E),Y
606F-   91 0E       STA   ($0E),Y
6071-   C8          INY
6072-   D0 F6       BNE   $606A       
 
6074-   E6 0F       INC   $0F
6076-   CA          DEX
6077-   D0 EF       BNE   $6068

On retrouve encore une fois notre A EOR B = C avec un B qui sera fixe pour chaque paire A/C. Ici il est simplement calculé par rapport à la valeur de l'index Y. La routine d'encodage R' correspondante sera donc exactement la même encore une fois :

** ENCODAGE R'**        
 
         LDA   #$00
         STA   ADDB
         LDA   #$20
         STA   ADDH     
 
         TAX            
 
:B2      LDY   #$00
:B1      TYA
         EOR   #$78
         EOR   (ADDB),Y
         STA   (ADDB),Y
         INY
         BNE   :B1      
 
         INC   ADDH
         DEX
         BNE   :B2

Pour obtenir notre routine d'encodage finale, il faudra donc réunir les trois phases dans cet ordre : R' Q' P' qui s'effectueront successivement sur le même buffer. Chaque phase (ré)encryptant le résultat de la précédente (sauf pour R' évidemment qui partira, elle, d'une image décodée).

Hélas tout n'est pas encore terminé, car la routine de décodage contient aussi différentes vérifications nous conduisant chacune, si le résultat n'est pas celui escompté, dans le mur !

La première concerne donc le fameux #$11 qui doit être en $20F1 après les deux premières phases d'encodage (R' et Q'). Puisque ces deux phases effectuent chacune un EOR avec une valeur fixe pour l'octet en $20F1, on en déduit donc que ce #$11 dépendra directement (et uniquement) de l'octet de départ en $20F1 de notre image signée !

 

Pour bien se rendre compte de quoi on parle, sur cette capture d'écran, j'ai mis l'octet $20F1 à $FF. On obtient donc 7 bits consécutifs à 1 donc un petit trait blanc. C'est celui qui apparaît juste en-dessous et à droite du "TOUJOURS PERSONNE ?". Sur l'image originale, cet octet est à $80 (donc rien à l'écran).

 

 

 

Sur cette première version de l'image modifiée, sans même le faire exprès, l'octet $20F1 était inchangé. On obtenait donc #$11 lors de l'encodage après les phases R' et Q' et la vérification au décodage me laissait donc tranquillement passer...

 

 

Vous l'aurez compris, pour passer le check 1, une des solutions possibles est tout simplement de faire en sorte de ne rien écrire en $20F1. Simple et efficace...

La seconde vérification concerne la routine de décodage en elle-même :

608D-   A0 00       LDY   #$00
608F-   B9 00 60    LDA   $6000,Y
6092-   4D 8E 60    EOR   $608E
6095-   8D 8E 60    STA   $608E
6098-   C8          INY
6099-   C0 8E       CPY   #$8E
609B-   D0 F2       BNE   $608F
609D-   C9 B8       CMP   #$B8   ; check 2 (routine décodage)
609F-   F0 03       BEQ   $60A4  ; gentil garçon tu peux continuer!
60A1-   4C 00 C6    JMP   $C600  ; bad boy = reboot

Elle vérifie donc par un checksum si la section $6000-$608D est intacte. Et donc, si par exemple, nous n'avons pas modifié le #$11 de la vérif précédente qui est en plein dedans. Ce qui veut donc forcément dire que modifier le #$11 entrainera d'office une modification du #$B8 pour que le checksum ne nous arrête pas. Vous voyez que l'affaire se complique...

La troisième vérif toujours contenue dans la routine de décodage a lieu après l'exécution des trois phases et s'intéresse à la Page Hires 1. C'est donc une vérification de l'image décodée :

60A4-   A0 00       LDY   #$00
60A6-   B9 00 20    LDA   $2000,Y
60A9-   4D A5 60    EOR   $60A5
60AC-   8D A5 60    STA   $60A5
60AF-   C8          INY
60B0-   D0 F4       BNE   $60A6
60B2-   EE A8 60    INC   $60A8
60B5-   AD A8 60    LDA   $60A8
60B8-   C9 40       CMP   #$40
60BA-   D0 EA       BNE   $60A6
60BC-   AD A5 60    LDA   $60A5
60BF-   C9 DE       CMP   #$DE   ; check 3 (image décodée)
60C1-   F0 03       BEQ   $60C6  ; cours chercher bonheur !
60C3-   4C 21 02    JMP   $0221  ; bad boy = "ACS Lead Again !"

Un checksum à base d'EOR (décidément...) est effectué sur toute la page graphique entre $2000 et $3FFF. Le résultat doit être égal à $DE sinon badaboom...
Or cette fois, peu importe la modification effectuée sur l'image, même un petit gribouillis dans un coin, entraînera une valeur différente pour le checksum (sauf pour les veinards qui tomberont par hasard sur la même).

Mais est-ce bien sûr ? Ne pourrait-on pas s'arranger pour retrouver $DE et ce, peu importe l'image et la signature ?

Rappel numéro 2 :  EOR est une opération dite commutative : A EOR B = B EOR A et associative (A EOR B) EOR C = A EOR (B EOR C). Autre propriété intéressante : A EOR 00 = A.

Comment utiliser tout ceci pour le cas qui nous intéresse ? Vais-je arrêter de poser des questions dont je connais les réponses ?
Lors de la vérification, tous les octets entre $2000 et $3FFF vont être EORés les uns avec les autres. On aura donc : A EOR B EOR C EOR D .... EOR X = $DE, X étant le dernier octet vérifié à savoir celui en $3FFF. Vous comprenez maintenant pourquoi je vous avais demandé tout à l'heure de mettre des zéro entre $3FF8 et $3FFF.
Or l'associativité de EOR fait que l'on peut regrouper tous les octets AVANT celui en $3FFF par une valeur que nous appellerons Y. On a donc : A EOR B EOR C EOR D EOR E .... EOR W = Y (W étant l'octet en $3FFE).
Et Y EOR X = $DE ! Y va dépendre directement de notre propre image signée. Et on peut très facilement le calculer nous même en utilisant la même routine que le checksum made in ACS. Une fois Y trouvé, on pourra donc en déduire X car X = $DE EOR Y ! Et une fois X obtenu, i suffira alors de mettre cette valeur en $3FFF pour que le checksum final soit $DE peu importe notre image !
Et comme les octets à partir de $3FF8 jusqu'en $3FFF ne sont pas visibles à l'écran, on pourra donc mettre la valeur que l'on souhaite en $3FFF, l'image n'en sera pas altérée pour autant ! Classe n'est-ce pas ?

Mais ce n'est pas tout car il existe une quatrième vérification :

60CF-   20 20 64    JSR   $6420 ; got to check 4
 
6420-   4C 88 64    JMP   $6488
 
6488-   A0 00       LDY   #$00
648A-   B9 00 60    LDA   $6000,Y
648D-   4D 89 64    EOR   $6489
6490-   8D 89 64    STA   $6489
6493-   C8          INY
6494-   D0 F4       BNE   $648A
6496-   C9 04       CMP   #$04    ; check 4
6498-   F0 03       BEQ   $649D   ; good guy
649A-   6C F2 03    JMP   ($03F2) ; reboot !

C'est de nouveau une vérification consistant à checker l'intégrité de la routine de décodage. Là, on ne fait plus dans la dentelle puisque c'est toute la page $60 qui est vérifiée. De nouveau, cette vérif posera problème si on tient absolument à modifier l'octet $20F1. Mais si on est malin, on le laissera à $80, sa valeur par défaut, et on éliminera d'un coup le problème des vérifications 1, 2 et 4 !

Voici le programme final d'encryptage de l'image que je vous propose. Il contient bien entendu les 3 phases d'encodage telles que décrites précédemment et dans le bon ordre (R' Q' P'). En début de routine, j'ai incorporé le calcul de checksum de l'image avant encodage afin de générer la valeur avec laquelle il faudra faire un EOR #$DE pour obtenir le contenu définitif de $3FFF. Vous remarquerez que ce checksum est calculé entre $2000 et $3FFF. N'oubliez pas de vérifier que votre image signée contient bien des $00 entre $3FF8 et $3FFF (et pas quelques $FF qui traînent). Le programme affiche également la valeur en $20F1 après les phases R' et Q'.

    1
    2          LST   OFF
    3          ORG   $1000
    4
    5 ADDB     EQU   $18
    6 ADDH     EQU   $19
    7 TEMP     EQU   $1A
    8
    9 PRBYTE   EQU   $FDDA
   10 COUT     EQU   $FDED
   11
   12 ** CALCUL CHECKSUM IMAGE
   13
   14
   15          LDY   #00
   16          STY   TEMP
   17
   18 BC1      LDA   $2000,Y
   19          EOR   TEMP
   20          STA   TEMP
   21          INY
   22          BNE   BC1
   23          INC   BC1+2
   24          LDA   BC1+2
   25          CMP   #$40
   26          BNE   BC1
   27
   28          LDA   TEMP
   29          JSR   PRBYTE
   30
   31          LDA   #$8D
   32          JSR   COUT
   33
   34
   35 ** CODAGE PART I : R'
   36
   37          LDA   #$00
   38          STA   ADDB
   39          LDA   #$20
   40          STA   ADDH
   41
   42          TAX
   43
   44 :B2      LDY   #$00
   45 :B1      TYA
   46          EOR   #$78
   47          EOR   (ADDB),Y
   48          STA   (ADDB),Y
   49          INY
   50          BNE   :B1
   51
   52          INC   ADDH
   53          DEX
   54          BNE   :B2
   55
   56
   57 ** CODAGE PART II : Q'
   58
   59          LDA   #$00
   60          STA   ADDB
   61          LDA   #$20
   62          STA   ADDH
   63          TAX
   64
   65 :B4      LDY   #$00
   66
   67 :B3      LDA   (ADDB),Y
   68          PHA
   69          TYA
   70          CLC
   71          ADC   ADDH
   72          PHA
   73          TXA
   74          ROR
   75          STA   TEMP
   76          PLA
   77          ROL
   78          ROL
   79          ROL
   80          EOR   TEMP
   81          STA   TEMP
   82          PLA
   83          EOR   TEMP
   84          STA   (ADDB),Y
   85          DEY
   86          BNE   :B3
   87
   88          INC   ADDH
   89          DEX
   90          BNE   :B4
   91
   92 ** CODAGE PART III : P'
   93
   94          LDA   $20F1
   95          JSR   PRBYTE
   96
   97          LDA   #$00
   98          STA   TEMP
   99
  100 :B6      LDX   #$20
  101
  102          STX   ADDH
  103          LDA   #$00
  104          STA   ADDB
  105
  106
  107          LDY   #$FF
  108
  109 :B5      LDA   (ADDB),Y
  110          INY
  111          EOR   (ADDB),Y
  112          DEY
  113          STA   (ADDB),Y
  114          DEY
  115          CPY   #$FF
  116          BNE   :B5
  117
  118          INC   ADDH
  119          DEX
  120          BNE   :B5
  121
  122          INC   TEMP
  123          LDA   TEMP
  124          CMP   #$0A
  125          BNE   :B6
  126
  127          JMP   $3D0
  128
  129 FIN      RTS

Il suffit donc de charger l'image signée en mémoire. De charger le programme ci-dessus, de l'exécuter ($1000G). De noter la première valeur retournée. Elle sera à EORer avec $DE pour obtenir le bon $3FFF. Il faudra bien sûr recharger l'image (car elle aura été encryptée entre temps), fixer le $3FFF et recharger la routine d'encodage (qui ne peut s’exécuter deux fois de suite) pour, enfin, lancer l'encryptage final. Tout ceci n'est pas très optimisé je vous l'accorde mais j'ai préféré tout mettre dans le même programme même si cela oblige à quelques acrobaties.

Exemple pour ma première image signée (qui n'écrase pas le $20F1) : le programme d'encodage retourne 9D et 11. Pour le #$11, on ne revient pas dessus... Le $9D correspond donc au checksum de la zone $2000-$3FFF. Pour obtenir la valeur à écrire en $3FFF, il suffit de faire : $9D EOR $DE soit $43. Pour info, vous pouvez mettre ce $43 sur n'importe quel octet entre $3FF8 et $3FFF (du moment que les autres sont à zéro). Quand vous relancez le programme d'encodage avec l'image signée (et le $3FFF modifié), il doit afficher maintenant DE et 11. Cette fois, tout est OK, l'image en Page 1 est celle à sauver sur disque !

Petit aperçu de l'image après encodage (ou avant décodage c'est selon...). Même signée et donc modifiée, vous devriez obtenir un truc à peu près ressemblant...

Il reste ensuite l'étape de l'écriture sur Championship Wrestling. Tout se passe en accès direct, Piste $1B / Secteur $00. Pour un total de 34 secteurs. L'intégralité des pistes $1B et $1C sera donc occupée par l'image.

Vu que le fameux #$11 n'a pas été modifié et le checksum image rectifié, tous les checks doivent donner leur feu vert et le défi est officiellement résolu... Faites quand même une copie avant le lancement final au cas où...

Bonus pour les fous...

Que se passe-t'il, par contre, si on n'est pas malin mais plutôt maso voire carrément vicieux et qu'on se débrouille pour que la signature écrase l'octet $20F1 (comme sur celle qui illustre le début de cette partie) ? Il faudra alors modifier la routine en $6000 pour y placer les bonnes valeurs sur les différentes vérifs rencontrées. Ceux ayant lu jusqu'au bout la partie I, doivent se souvenir que la routine en $6000 est elle-même cryptée et subit un double décodage avant d'être exécutée. Allez jeter un petit coup d'œil au code en $7000 par exemple et vous allez comprendre votre douleur...

Donc on résume : si on modifie l'image et l'octet $20F1, il faudra modifier la routine de décodage pour fixer les checks 1,2 et 4. Le check 3, lui, sera réglé par l'octet $3FFF que l'on disposera à la bonne valeur (comme expliqué précédemment). Or modifier ces 3 checks nous oblige à réencoder la routine de décryptage en elle-même pour l'écrire sur disque. Et là c'est l'usine à gaz assurée...

Il reste toutefois une autre possibilité : ne pas toucher à la routine de décodage telle qu'elle apparaît sur le disque mais la patcher une fois chargée (et décryptée) pour y modifier les 3 octets de comparaison des vérifs problématiques. Problème : où incorporer et comment exécuter ce patch ? Or ACS dans son envie de (trop) bien faire nous a laissé une petite porte entrouverte. Rappelez-vous le tout début de la routine de décodage en $6000 :

6000-   20 9B 02    JSR   $029B  ; <--- hihihi
 
029B-   8D E9 C0    STA   $C0E9  ; allumage drive (fake)
029E-   A9 02       LDA   #$02   ; on empile
02A0-   48          PHA
02A1-   A9 7F       LDA   #$7F   ; $27F pour sauter en
02A3-   48          PHA          ; $280 quand le RTS
02A4-   4C 58 FC    JMP   $FC58  ; de HOME sera exécuté
 
0280-   A9 21       LDA   #$21   ; on a déjà rencontré cette
0282-   8D F2 03    STA   $03F2  ; routine qui dispose les vecteurs
0285-   A9 02       LDA   #$02   ; RESET et BRK
0287-   8D F3 03    STA   $03F3  ; pour renvoyer vers $221
028A-   49 A5       EOR   #$A5   ;
028C-   8D F4 03    STA   $03F4
028F-   A9 21       LDA   #$21
0291-   8D F0 03    STA   $03F0
0294-   A9 02       LDA   #$02
0296-   8D F1 03    STA   $03F1
0299-   60          RTS          ; retour à l'appelant

La routine en $280, déjà rencontrée précédemment, est de nouveau appelée après le coup de l'allumage du drive. À ce sujet d'ailleurs, je pense qu'ACS brouille les pistes en allumant le drive pour essayer de faire croire que quelque chose se charge (alors qu'en fait notre image cryptée est déjà entièrement en mémoire). Pas mal le coup du vrai-faux accès disque !
L'idée ici, c'est d'utiliser la routine en $280 et de la modifier afin qu'elle patche la routine de décodage ! Elle est exécutée au moment idéal pour notre rustine. Et vu qu'il ne nous faut patcher que trois octets, on a largement la place de l'incorporer ici.
Est-ce pour autant que la routine en $280 est lisible sur le disque ? Hélas non, mais elle est cryptée d'une façon plutôt simple (et en une seule fois) contrairement à la routine en $6000.

La routine de décryptage d'ailleurs, la voici ou la revoici car nous l'avons déjà aperçue dans la partie I :

1100-   A2 00       LDX   #$00
1102-   BD D0 11    LDA   $11D0,X
1105-   85 0F       STA   $0F
1107-   A9 FF       LDA   #$FF
1109-   49 FF       EOR   #$FF
110B-   85 0E       STA   $0E
110D-   85 10       STA   $10
110F-   BD E0 11    LDA   $11E0,X
1112-   85 11       STA   $11
1114-   A0 00       LDY   #$00
1116-   98          TYA
1117-   5D F0 11    EOR   $11F0,X
111A-   51 0E       EOR   ($0E),Y
111C-   91 10       STA   ($10),Y
111E-   88          DEY
111F-   D0 F5       BNE   $1116
1121-   E8          INX
1122-   E0 0C       CPX   #$0C
1124-   F0 03       BEQ   $1129
1126-   4C 02 11    JMP   $1102
 
11D0-   12 13 14 15 16 17 18 19 1A 1B 1C 1D 00
11E0-   02 60 70 71 72 73 03 19 61 62 63 64 00
11F0-   01 45 D3 79 18 56 99 44 12 19 33 35 00
        **

La page 2 est décryptée en même temps que les pages indexées en $11E0+ ($60, $70, $71 etc.).
Le décryptage est simple : on prend une valeur fixe suivant la page ($01 pour la page $02) et on fera un EOR entre cette valeur, chaque octet de la page et l'index de l'octet dans la page (ouf !).

Exemple concret pour les octets qui nous intéressent en $280+ :

$280 : octet final = A9. Octet sur disque = A9 EOR $01 (valeur fixe) EOR $80 (position de l'octet dans la page) = $28
$281 : octet final = 21. Octet sur disque = 21 EOR $01 (valeur fixe) EOR $81 (position de l'octet dans la page) = $A1
$282 : octet final = 8D. Octet sur disque = 8D EOR $01 (valeur fixe) EOR $82 (position de l'octet dans la page) = $0E
$283 : octet final = F2. Octet sur disque = F2 EOR $01 (valeur fixe) EOR $83 (position de l'octet dans la page) = $70

Nous n'avons pas calculé ces octets pour rien. Il nous suffit maintenant de faire une recherche Hexa sur le disque avec 28 A1 0E 70 pour localiser l'emplacement de la page $02 codée. Et on trouve le tout en T :$1A/S :$02/P :$80 !

Il nous reste maintenant à trouver les bonnes valeurs pour écrire notre patch. Pour ma seconde image, celle qui écrase le $20F1, le programme d'encodage retourne : FA et 16. Le $16 sera donc la valeur à intégrer dans la routine de décodage à la place du $11. Quant au $FA, il nous permet de calculer notre valeur à placer en $3FFF à savoir : $FA EOR $DE = $24

Il nous faut aussi trouver les valeurs pour les checks 2 et 4, ceux qui vérifient l'intégrité de la routine de décodage. Je vous avais conseillé (bien) plus haut de sauver sur disque cette routine. C'est maintenant qu'elle va nous servir !

Chargez là en mémoire ainsi que votre image encodée depuis le DOS. Nous partons à la pêche à la bonne valeur du check 2 :

29B : 60  ; on met un RTS en $29B (et oui il n'a y rien là)
6029 : 16 ; on met notre "nouvelle" valeur pour le "$11"
609D : 4C 59 FF ; pour récupérer la main juste après le check 2 !
6000G ; on lance la routine qui va encrypter l'image en Page 1
608E  ; on récupère la bonne valeur du check 2 !

De mon côté j'obtiens $BF.  Rebootez votre disk DOS contenant votre image encodée et la routine de décodage (on doit repartir sur des bases propres), rechargez le tout et cette fois, on cherche la bonne valeur du check 4 :

29B : 60  ; on met un RTS en $29B
6029 : 16 ; on met notre bonne valeur pour le check 1
609E : BF ; on met la bonne valeur pour le check 2
6496 : 4C 59 FF ; pour récupérer la main juste après le check 4 !
6000G ; on lance la routine qui va encrypter l'image en Page 1
6489  ; on récupère la bonne valeur du check 4 !

J'obtiens 03. Nous avons donc maintenant toutes les valeurs pour générer notre patch et la routine en $280 devient après modification :

0280-   A9 16       LDA   #$16
0282-   8D 29 60    STA   $6029 ; pour le check 1
0285-   A9 BF       LDA   #$BF
0287-   8D 9E 60    STA   $609E ; pour le check 2
028A-   49 BC       EOR   #$BC  ; -> A = 03
028C-   8D 97 64    STA   $6497 ; pour le check 4
028F-   A9 21       LDA   #$21  ; ne change pas...
0291-   8D F0 03    STA   $03F0 ;
0294-   A9 02       LDA   #$02  ;
0296-   8D F1 03    STA   $03F1 ;
0299-   60          RTS

Passons à la toute dernière étape : encoder ces modifications avant de les écrire sur disque. Le plus simple sera d'utiliser un éditeur de secteur pour placer directement les valeurs  Piste$1A/Secteur$02 à partir de la position $80 une fois EORées. Pour l'encodage, on refait l'inverse de tout à l'heure avec nos nouvelles valeurs, ce qui donne pour mon cas perso :

P$80 : 28 96       (A9 EOR 80 EOR 01 = 28 / 16 EOR 81 EOR 01 = 96)
P$82 : 0E AB E5  (8D EOR 82 EOR 01 = 0E / 29 EOR 83 EOR 01 = AB / 60 EOR 83 EOR 01 = E5)
P$85 : 2D 38      (A9 EOR 85 EOR 01 = 2D / BF EOR 86 EOR 01 = 38)
P$87 : 0B 17 E8  (8D EOR 87 EOR 01 = 0B / 9E EOR 88 EOR 01 = 17 / 60 EOR 89 EOR 01 = E8
P$8A : C2 36      (49 EOR 8A EOR 01 = C2 / BC EOR 8B EOR 01 = 36)
P$8C : 00 1B EB (8D EOR 8C EOR 01 = 00 / 97 EOR 8D EOR 01 = 1B / 64 EOR 8E EOR 01 = EB)

Voilà, il ne reste plus qu'à tester et ça marche ! J'avais prévenu qu'il fallait être maso pour aller jusqu'ici...

Je ne sais pas pour vous, mais perso, je n'en peux plus donc je rends l'antenne sous vos applaudissements (ou vos ronflements). Comme d'habitude, voici le disque de travail contenant les divers trucs de cet article (le programme "Encode" avec son source, la "Routine Décodage" ainsi que les diverses images). Et bien sûr, un peu d'autosatisfaction avec les deux versions signées par mes soins de Championship Wrestling, la première allant au plus simple, la seconde dite masochiste !

Adieu !