Au hasard des multiples tests d'images disque effectués dernièrement, je suis tombé sur le crack de Championship Wrestling signé Apple Chemical Software (ACS). Et intégré au détour des (nombreux) écrans de présentations ajoutés par le groupe, apparaît un petit défi concocté par Deny et The Gog's, défi consistant en une classique signature d'image. Classique ? Pas tant que ça car ACS nous a réservé quelques petites surprises rendant le défi particulièrement intéressant. À noter qu'à l'époque circulait aussi une version du jeu crackée et signée par un certain Godfather (mais sans défi cette fois)...
Allez, on attaque par la première phase qui est, comme d'habitude, une phase d'observation de ce qui se passe depuis le boot du disque jusqu'au moment où l'on arrive enfin sur le défi. Je dis "enfin" car effectivement, ce ne sont pas moins de trois écrans Hires (voire Double Hires) qui se succèdent avant l'apparition de l'image du défi (voir ci-contre). On comprend vite qu'il va falloir s'enfoncer relativement loin dans la séquence de boot avant de tomber sur la (ou les) routine(s) recherchée(s).
Ce petit jeu de piste, nous menant jusqu'à la localisation (et le code) de la routine de décryptage, fera l'objet de cette première partie, la seconde consistant en la résolution concrète du défi en lui-même. Comme vous allez le voir, cela va être assez longuet mais je vous rassure tout de suite, cela a été aussi laborieux pour moi à écrire que cela sera fastidieux pour vous à lire !
On commence par décortiquer la séquence du Boot 1. On peut l'obtenir en Boot Traçant ou tout simplement en allant checker ce qui se passe avec un éditeur de secteur Piste$00/Secteur$00 (option désassemblage).
0800- 01 ; un seul secteur chargé 0801- E0 60 CPX #$60 ; boot depuis le slot 6 ? 0803- F0 03 BEQ $0808 ; oui alors suite... 0805- 4C EB 08 JMP $08EB ; non ? alors message ! 0808- A9 B6 LDA #$B6 080A- 85 27 STA $27 080C- BD 8C C0 LDA $C08C,X ; $C080 080F- 10 FB BPL $080C 0811- 49 D5 EOR #$D5 0813- D0 F7 BNE $080C 0815- BD 8C C0 LDA $C08C,X ; $C080 0818- 10 FB BPL $0815 081A- C9 AA CMP #$AA 081C- D0 F3 BNE $0811 081E- BD 8C C0 LDA $C08C,X ; $C080 0821- 10 FB BPL $081E 0823- C9 AD CMP #$AD 0825- D0 EA BNE $0811 0827- A9 00 LDA #$00 0829- A0 56 LDY #$56 082B- 84 3C STY $3C 082D- BC 8C C0 LDY $C08C,X ; $C080 0830- 10 FB BPL $082D 0832- 59 D6 02 EOR $02D6,Y 0835- A4 3C LDY $3C 0837- 88 DEY 0838- 99 00 03 STA $0300,Y 083B- D0 EE BNE $082B 083D- 84 3C STY $3C 083F- BC 8C C0 LDY $C08C,X ; $C080 0842- 10 FB BPL $083F 0844- 59 D6 02 EOR $02D6,Y 0847- A4 3C LDY $3C 0849- 91 26 STA ($26),Y 084B- C8 INY 084C- D0 EF BNE $083D 084E- BC 8C C0 LDY $C08C,X ; $C080 0851- 10 FB BPL $084E 0853- 59 D6 02 EOR $02D6,Y 0856- D0 48 BNE $08A0 0858- A0 00 LDY #$00 085A- A2 56 LDX #$56 085C- CA DEX 085D- 30 FB BMI $085A 085F- B1 26 LDA ($26),Y 0861- 5E 00 03 LSR $0300,X 0864- 2A ROL 0865- 5E 00 03 LSR $0300,X 0868- 2A ROL 0869- 45 2B EOR $2B ; EOR #$60 086B- 91 26 STA ($26),Y 086D- C8 INY 086E- D0 EC BNE $085C 0870- E6 27 INC $27 0872- A2 60 LDX #$60 0874- CE F5 08 DEC $08F5 ; nb secteurs à lire 0877- D0 93 BNE $080C 0879- 20 58 FC JSR $FC58 087C- A0 0C LDY #$0C 087E- B9 A5 08 LDA $08A5,Y ; message "ACS-DOS" 0881- 49 12 EOR #$12 ; codé bien entendu... 0883- 20 ED FD JSR $FDED 0886- 88 DEY 0887- 10 F5 BPL $087E 0889- 20 8F 08 JSR $088F 088C- 4C 00 B6 JMP $B600 ; JMP BOOT2 088F- A9 00 LDA #$00 0891- 8D F2 03 STA $03F2 0894- A9 C6 LDA #$C6 0896- 8D F3 03 STA $03F3 0899- 49 A5 EOR #$A5 089B- 8D F4 03 STA $03F4 089E- A9 20 LDA #$20 08A0- 85 E6 STA $E6 08A2- 4C B2 08 JMP $08B2 08A5- B2 ??? 08A6- 32 ??? 08A7- 32 ??? 08A8- 01 1D ORA ($1D,X) 08AA- 16 3F ASL $3F,X 08AC- 01 11 ORA ($11,X) 08AE- 13 ??? 08AF- 32 ??? 08B0- 32 ??? 08B1- B2 ??? 08B2- 20 F2 F3 JSR $F3F2 08B5- A0 94 LDY #$94 08B7- B9 00 04 LDA $0400,Y 08BA- 4D B6 08 EOR $08B6 08BD- 8D B6 08 STA $08B6 08C0- 88 DEY 08C1- D0 F4 BNE $08B7 08C3- C9 94 CMP #$94 08C5- D0 01 BNE $08C8 08C7- 60 RTS 08C8- 6C F2 03 JMP ($03F2) 08CB- A0 C1 LDY #$C1 08CD- C3 ??? 08CE- D3 ??? 08CF- AD C4 CF LDA $CFC4 08D2- D3 ??? 08D3- A0 CD LDY #$CD 08D5- D5 D3 CMP $D3,X 08D7- D4 ??? 08D8- A0 C2 LDY #$C2 08DA- CF ??? 08DB- CF ??? 08DC- D4 ??? 08DD- A0 C6 LDY #$C6 08DF- D2 ??? 08E0- CF ??? 08E1- CD A0 D3 CMP $D3A0 08E4- CC CF D4 CPY $D4CF 08E7- A0 B6 LDY #$B6 08E9- A0 A1 LDY #$A1 08EB- 20 2F FB JSR $FB2F ; INIT 08EE- 20 58 FC JSR $FC58 ; HOME 08F1- A0 00 LDY #$00 08F3- B9 CB 08 LDA $08CB,Y ; blabla... 08F6- 99 04 04 STA $0404,Y ; ...boot slot 6 only! 08F9- C8 INY 08FA- C0 20 CPY #$20 08FC- D0 F5 BNE $08F3 08FE- F0 FE BEQ $08FE |
Première remarque : l'utilisation originelle du softswitch $C080 (en lieu et place de $C08C) pour lire le latch empêche purement et simplement l'image disque de booter sous émulateur (notamment AppleWin). C'était une pratique relativement courante à l'époque que l'on retrouvera notamment sur certains Déplombage Mode d'Emploi. À signaler aussi l'emploi d'un EOR $2B (qui contient normalement #$60 dû au Slot 6) après dénibblelisation, et ce, juste avant de sauver l'octet final. C'était une façon assez classieuse de cacher sur disque ce qui se passe au boot et qu'on retrouvera là encore deci delà sur certaines productions Underground. Ceci ne concerne toutefois que le chargement du Boot 2. Une fois celui-ci chargé, l'EOR #$60 n'a plus lieu d'être et on ne le retrouve pas dans la RWTS. Le Boot 2 parlons-en justement puisque l'on accédera à lui par le très classique JMP $B600 (en $88C) une fois chargé. Dernière petite chose rigolotte à remarquer, le compteur du nombre de secteurs à charger localisé en $8F5 est en plein milieu du code utilisé pour le message d'erreur en cas de boot hors Slot 6. Raffiné n'est-ce pas ?
Pour récupérer le Boot 2, il faut bien sûr shunter le JMP $B600 par un JMP $FF59 par exemple, soit lors du Boot Tracing, soit directement en patchant le Boot Sector sur disque. L'essentiel étant de récupérer le code que voici :
B600- A9 00 LDA #$00 B602- 8D 4B BA STA $BA4B ; Piste B605- 8D 4D BA STA $BA4D ; Buffer Low B608- A9 0F LDA #$0F B60A- 8D 4C BA STA $BA4C ; Secteur B60D- A9 01 LDA #$01 B60F- 8D 4F BA STA $BA4F ; nb Secteurs à charger B612- A9 BF LDA #$BF B614- 8D 4E BA STA $BA4E ; Buffer High B617- A2 60 LDX #$60 B619- A9 06 LDA #$06 B61B- AD E9 C0 LDA $C0E9 B61E- 20 6D B6 JSR $B66D ; RWTS B621- A9 00 LDA #$00 B623- 8D 4D BA STA $BA4D B626- A9 AE LDA #$AE B628- 8D 4E BA STA $BA4E B62B- A9 00 LDA #$00 B62D- A0 01 LDY #$01 B62F- A2 08 LDX #$08 B631- 20 90 BF JSR $BF90 ; appel RWTS B634- 20 80 B5 JSR $B580 ; jmp étape suivante ! |
Un petit coup d’œil sur le contenu en $B580 nous montre qu'il n'a pas encore été chargé. C'est justement l'objet de l'appel (indirect) à la RWTS en $B631. Notons bien ce $BF90 que l'on va retrouver à chaque fois que l'ACS-DOS aura besoin de charger quelque chose. Cet appel indirect utilise les registres A, X et Y pour le passage de paramètres vers la routine en $B66D où l'on retrouve la RWTS en elle-même. On ne se disperse pas trop dans les détails, l'affichage de l'image qui nous intéresse est encore très (très) loin.
B634 : 4C 59 FF ; JMP $FF59 B600G ; exécution de la routine qui va charger la suite et nous rendre la main B580L ; affichage du résultat B580- 2C E9 C0 BIT $C0E9 B583- 2C 52 C0 BIT $C052 B586- 20 80 BF JSR $BF80 B589- EA NOP B58A- 20 F0 BF JSR $BFF0 B58D- A9 C5 LDA #$C5 B58F- 85 1C STA $1C B591- A9 40 LDA #$40 B593- 85 E6 STA $E6 B595- 20 F6 F3 JSR $F3F6 B598- 2C 50 C0 BIT $C050 B59B- 2C 57 C0 BIT $C057 B59E- 2C 55 C0 BIT $C055 B5A1- 4C AA AE JMP $AEAA ; jmp suite AEAA- A0 00 LDY #$00 AEAC- B9 E0 AE LDA $AEE0,Y AEAF- 8D 10 AE STA $AE10 AEB2- B9 E8 AE LDA $AEE8,Y AEB5- 8D 16 AE STA $AE16 AEB8- B9 F0 AE LDA $AEF0,Y AEBB- 8D 08 AE STA $AE08 AEBE- B9 F8 AE LDA $AEF8,Y AEC1- 8D 0C AE STA $AE0C AEC4- C9 FF CMP #$FF AEC6- F0 0B BEQ $AED3 AEC8- C8 INY AEC9- 98 TYA AECA- 48 PHA AECB- 20 03 AE JSR $AE03 AECE- 68 PLA AECF- A8 TAY AED0- 4C AC AE JMP $AEAC AED3- 4C 48 B5 JMP $B548 ; on continue B548- A9 00 LDA #$00 B54A- 85 0E STA $0E B54C- A5 0E LDA $0E B54E- A2 00 LDX #$00 B550- A0 00 LDY #$00 B552- 20 11 F4 JSR $F411 B555- A0 00 LDY #$00 B557- B1 26 LDA ($26),Y B559- 48 PHA B55A- A5 27 LDA $27 B55C- 49 60 EOR #$60 B55E- 85 27 STA $27 B560- 68 PLA B561- 91 26 STA ($26),Y B563- A5 27 LDA $27 B565- 49 60 EOR #$60 B567- 85 27 STA $27 B569- C8 INY B56A- C0 28 CPY #$28 B56C- D0 E9 BNE $B557 B56E- 20 A4 B5 JSR $B5A4 B571- E6 0E INC $0E B573- A5 0E LDA $0E B575- C9 C0 CMP #$C0 B577- D0 D3 BNE $B54C B579- 2C 54 C0 BIT $C054 B57C- 4C B0 B5 JMP $B5B0 ; jmp étape suivante |
Ce qu'il faut bien comprendre, c'est que lors de la résolution d'un tel défi, nul besoin d'analyser et de détailler ce qui se passe à chaque étape.Il y a 3 écrans graphiques avant celui qui nous intéresse, on se doute bien qu'ils vont devoir être chargés, probablement décryptés, de même que les routines graphiques et sonores. Notre objectif est d'arriver jusqu'au chargement de l'image du défi. On va donc progresser d'étape en étape en modifiant le saut vers la prochaine étape par un JMP $FF59 pour récupérer la main afin d'analyser ce qui aura été chargé avant son exécution. Jusqu'au JMP $B5B0 en $B57C, tout a été chargé d'un coup et est visible directement en mémoire. En $B5B0 par contre, cela se corse :
B5B0- A0 00 LDY #$00 B5B2- B9 BE B5 LDA $B5BE,Y B5B5- 49 7F EOR #$7F B5B7- 99 BE B5 STA $B5BE,Y B5BA- C8 INY B5BB- C0 AE CPY #$AE B5BD- D0 8C BNE $B54B B5BF- DF ??? B5C0- 7F ??? B5C1- C6 7F DEC $7F |
Un petit décryptage basique à coup d'EOR. Seul problème ici, la routine qui est décodée suit immédiatement la routine de décodage. Une fois celle-ci terminée, le programme va donc enchaîner avec la suite. La solution ici est d'obtenir en $B5BF un JMP $FF59 après décodage de façon à récupérer la main.
JMP $FF59 = 4C 59 FF. Si on EOR tout ça avec #$7F, on obtient : 33 26 80.
En $B5BF, après décodage, on aurait dû obtenir (sans modification) : (DF 7F C6) EOR #$7F = A0 00 B9
Les plus perspicaces auront aussi noté que le BNE XX en $B5BD est modifié lors du premier passage de la routine de décodage. Le $8C devient $F3 permettant ainsi à la routine de boucler correctement.
B5BF : 33 26 80 ; on remplace par notre JMP $FF59 codé. B580G ; on relance la routine à ce niveau ! On n'oublie pas que ; c'est le JMP $B580 que l'on avait shunté tout à l'heure. ; Il faut donc reprendre au même endroit pour que le tout ; s'exécute normalement. On observe alors l'affichage ; de la première image et on récupère la main ! B5BF : A0 00 B9 ; on n'oublie pas de remettre les octets originaux ! B5B0L ; on peut maintenant voir ce que ça donne B5B0- A0 00 LDY #$00 B5B2- B9 BE B5 LDA $B5BE,Y B5B5- 49 7F EOR #$7F B5B7- 99 BE B5 STA $B5BE,Y B5BA- C8 INY B5BB- C0 AE CPY #$AE B5BD- D0 F3 BNE $B5B2 B5BF- A0 00 LDY #$00 B5C1- B9 00 AE LDA $AE00,Y B5C4- 4D BF B5 EOR $B5BF B5C7- 8D BF B5 STA $B5BF B5CA- C8 INY B5CB- D0 F4 BNE $B5C1 B5CD- EE C3 B5 INC $B5C3 B5D0- AD C3 B5 LDA $B5C3 B5D3- C9 B5 CMP #$B5 B5D5- D0 ED BNE $B5C4 B5D7- AD BF B5 LDA $B5BF B5DA- C9 45 CMP #$45 B5DC- F0 03 BEQ $B5E1 B5DE- 20 C0 BF JSR $BFC0 ; WARNING B5E1- A0 00 LDY #$00 B5E3- B9 00 08 LDA $0800,Y B5E6- 4D D4 B5 EOR $B5D4 B5E9- 8D D4 B5 STA $B5D4 B5EC- B9 00 B6 LDA $B600,Y B5EF- 4D D4 B5 EOR $B5D4 B5F2- 8D D4 B5 STA $B5D4 B5F5- C8 INY B5F6- D0 EB BNE $B5E3 B5F8- C9 D9 CMP #$D9 B5FA- F0 03 BEQ $B5FF B5FC- 20 C0 BF JSR $BFC0 ; WARNING B5FF- 60 RTS ; retour en $B637 |
Cette routine est intéressante car elle effectue une triple vérification : d'abord de la zone $AE00-$B4FF puis de la page $08 (le Boot 1 donc) et enfin de la page $B6 (Boot 2). Si le résultat n'est pas celui escompté, on saute vers une mystérieuse routine en $BFC0. Si tout se passe bien, on finit sur un RTS qui nous renverra en $B637. Souvenez-vous du JSR en $B634. Depuis nous n'avons effectué que des JMP pour aller d'étape en étape. Le RTS nous renvoie donc juste derrière ce JSR là. À noter que la partie en $B637 aura été décodée en même temps que la partie en $B5BF.
Que se passe-t'il si on exécute la routine telle quelle par un $B5BFG ? Je vous invite à le faire mais prenez la précaution de faire une sauvegarde votre disque (ou de votre image) d'abord...
Le petit voyant rouge allumé en bas à droite, ce n'est pas bon signe ! Et oui ACS n'a aucune pitié et efface votre disque si le checksum des pages précédentes n'est pas correct. N'oublions pas que pour arriver jusqu'ici, nous avons modifié d'abord la page $08 puis la page $B6 (pour y mettre à chaque fois un JMP $FF59). Le checksum l'a détecté et il n'est pas content !
Pour continuer, la meilleure solution sera de reprendre directement en $B637. On n'exécutera donc pas le code en $B5BF (aucune importance, ce n'était qu'une vérification). Évidemment avant de passer en $B637, il va d'abord falloir analyser ce qui nous attend :
B637- 20 80 BF JSR $BF80 B63A- A9 00 LDA #$00 B63C- 8D 4D BA STA $BA4D B63F- A9 A0 LDA #$A0 B641- 8D 4E BA STA $BA4E ; chargement en $A000 B644- A9 16 LDA #$16 B646- A0 00 LDY #$00 B648- A2 10 LDX #$10 ; 16 secteurs ! B64A- 20 90 BF JSR $BF90 ; appel RWTS B64D- A0 00 LDY #$00 B64F- 98 TYA B650- 59 00 A0 EOR $A000,Y B653- 99 00 A0 STA $A000,Y ; décryptage de la zone $A000-$A0FF B656- C8 INY B657- D0 F6 BNE $B64F B659- A9 9F LDA #$9F ; on empile une B65B- 48 PHA ; nouvelle adresse B65C- A9 FF LDA #$FF ; de retour pour B65E- 48 PHA ; le RTS de la routine HOME B65F- 4C 58 FC JMP $FC58 ; HOME (puis go en $A000) |
Faisons un premier point : arrivé à cette étape, avant l'exécution de la routine en $B637, seule la première image a été chargée et affichée. Nous avons découvert de plus qu'ACS n'hésitera pas à nous pourrir notre disque en cas de bad checksum rencontré... Méfiance donc.
Notre routine en $B637 que fait-elle ? Elle charge incontestablement la suite par l'appel à la RWTS. On a ensuite droit à un petit décryptage d'une partie de la zone nouvellement chargée (entre $A000 et $A0FF) et à un joli empilement de l'adresse $9FFF. Pourquoi faire me direz-vous ? En sortie de la routine Monitor HOME, le RTS rencontré dépilera l'adresse alors en haut de la pile (donc $9FFF), y ajoutera automatiquement 1 (c'est dans ses gènes) et y sautera : on arrivera donc en $A000. Là encore, cette technique de saut par pile interposée a déjà été rencontrée quelques fois et était bien appréciée par quelques élites de la scène Underground.
Pour découvrir ce qui nous attend en $A000, rien de plus simple :
B65F : 4C 59 FF ; on shunte le JMP HOME (donc le RTS) B637G ; on exécute pour le chargement (et le décryptage) A000L A000- 20 D4 A0 JSR $A0D4 ; vérif ! A003- 2C 55 C0 BIT $C055 A006- A9 00 LDA #$00 A008- 8D 4D BA STA $BA4D A00B- A9 20 LDA #$20 A00D- 8D 4E BA STA $BA4E ; chargement en $2000+ A010- A9 12 LDA #$12 A012- A0 00 LDY #$00 A014- A2 20 LDX #$20 A016- 20 90 BF JSR $BF90 ; appel RWTS A019- 20 51 AE JSR $AE51 A01C- A9 00 LDA #$00 A01E- 85 3C STA $3C A020- A9 20 LDA #$20 A022- 85 3D STA $3D A024- A9 F8 LDA #$F8 A026- 85 3E STA $3E A028- A9 3F LDA #$3F A02A- 85 3F STA $3F A02C- A9 00 LDA #$00 A02E- 85 42 STA $42 A030- A9 20 LDA #$20 A032- 85 43 STA $43 A034- 38 SEC A035- 20 11 C3 JSR $C311 ; AUXMOVE A038- A9 00 LDA #$00 A03A- 8D 4D BA STA $BA4D A03D- A9 20 LDA #$20 A03F- 8D 4E BA STA $BA4E ; chargement en $2000+ A042- A9 14 LDA #$14 A044- A0 00 LDY #$00 A046- A2 20 LDX #$20 A048- 20 90 BF JSR $BF90 ; appel RWTS A04B- 2C 50 C0 BIT $C050 A04E- 2C 57 C0 BIT $C057 A051- 2C 54 C0 BIT $C054 A054- 2C 52 C0 BIT $C052 A057- 2C 5E C0 BIT $C05E A05A- 8D 0D C0 STA $C00D A05D- 20 A8 A0 JSR $A0A8 ; vérif ! A060- 20 04 AE JSR $AE04 ; A063- 8D 0C C0 STA $C00C A066- 2C 55 C0 BIT $C055 A069- 8D 5F C0 STA $C05F A06C- A9 00 LDA #$00 A06E- 8D 4D BA STA $BA4D A071- A9 10 LDA #$10 A073- 8D 4E BA STA $BA4E ; chargement en $1000+ A076- A9 00 LDA #$00 A078- 8D 4D BA STA $BA4D A07B- A9 1A LDA #$1A A07D- A0 00 LDY #$00 A07F- A2 30 LDX #$30 A081- 20 90 BF JSR $BF90 ; appel RWTS A084- A9 BB LDA #$BB ; on empile A086- 48 PHA ; $BBDA pour un A087- A9 DA LDA #$DA ; futur RTS et donc A089- 48 PHA ; sauter en $BBDB A08A- A0 00 LDY #$00 A08C- B9 00 A1 LDA $A100,Y ; déplacement $A100-$A9FF A08F- 99 00 B7 STA $B700,Y ; vers $B700-$BFFF A092- C8 INY A093- D0 F7 BNE $A08C A095- EE 8E A0 INC $A08E A098- EE 91 A0 INC $A091 A09B- AD 91 A0 LDA $A091 A09E- C9 C0 CMP #$C0 A0A0- F0 03 BEQ $A0A5 A0A2- 4C 8A A0 JMP $A08A A0A5- 4C 58 FC JMP $FC58 ; notre RTS vers $BBDB |
Dans cette routine, nous avons plusieurs appels à la RWTS (ça tombe bien, il reste encore beaucoup de choses à charger) et on remarque surtout la sortie utilisant encore une fois la routine HOME pour sauter cette fois en $BBDB (toujours en utilisant la pile). Évidemment, nous pourrions allègrement mettre un JMP $FF59 en $A0A5 et exécuter le tout pour voir où nous allons arriver... Sauf que l'on sait maintenant que cela peut nous péter à la gueule à tout moment... Et ça commence par la routine en $A0D4 appelée dès $A000 :
A0D4- A0 00 LDY #$00 A0D6- 98 TYA A0D7- 59 00 AE EOR $AE00,Y A0DA- 99 00 AE STA $AE00,Y A0DD- 4D D5 A0 EOR $A0D5 A0E0- 8D D5 A0 STA $A0D5 A0E3- 98 TYA A0E4- 59 00 AF EOR $AF00,Y A0E7- 99 00 AF STA $AF00,Y A0EA- 4D D5 A0 EOR $A0D5 A0ED- 8D D5 A0 STA $A0D5 A0F0- C8 INY A0F1- D0 E3 BNE $A0D6 A0F3- C9 58 CMP #$58 A0F5- F0 03 BEQ $A0FA A0F7- 4C C0 BF JMP $BFC0 ; WARNING A0FA- 60 RTS |
La routine décode et en profite pour faire un checksum et nous renvoie vers la routine maudite ($BFC0) si cela ne lui convient pas. Mais on trouve aussi d'autres vérifs dans la routine en $A0A8 :
A0A8- A9 40 LDA #$40 A0AA- 8D 4E BA STA $BA4E A0AD- A9 00 LDA #$00 A0AF- 8D 4D BA STA $BA4D A0B2- A9 17 LDA #$17 A0B4- A0 00 LDY #$00 A0B6- A2 20 LDX #$20 A0B8- 20 90 BF JSR $BF90 ; appel RWTS A0BB- A9 9A LDA #$9A A0BD- 8D 4D BA STA $BA4D A0C0- A9 7F LDA #$7F A0C2- 8D 4E BA STA $BA4E A0C5- A9 19 LDA #$19 A0C7- A0 00 LDY #$00 A0C9- A2 11 LDX #$11 A0CB- 20 90 BF JSR $BF90 ; appel RWTS A0CE- EA NOP A0CF- EA NOP A0D0- EA NOP A0D1- 4C B8 AE JMP $AEB8 ; vérif |
En fait on ne tarde pas à être noyé sous les routines qui s'appellent entre elles, font des vérifs etc. Notre objectif étant de parvenir à l'image du défi, pas d'entrer dans tous les sous-programmes, on va donc utiliser les grands moyens : arrivé en $AA05, on doit donc sauter en $BBDB. Or on voit que la partie commençant en $A08A déplace (entre autre) la page $A5 vers la page $BB. Comme les appels à la RWTS précédents ne chargent rien dans cet espace, on peut légitimement penser que la partie qui sera en $BB00 est déjà en mémoire en $A500 (elle a normalement été chargée précédemment par la routine en $B637)
Qu'avons-nous en $A5DB ?
A5DB- A0 00 LDY #$00 A5DD- B9 00 11 LDA $1100,Y ; décodage A5E0- 59 00 A0 EOR $A000,Y A5E3- 99 00 11 STA $1100,Y ; vers $1100+ A5E6- 4D DC BB EOR $BBDC A5E9- 8D DC BB STA $BBDC A5EC- C8 INY A5ED- D0 EE BNE $A5DD A5EF- C9 21 CMP #$21 A5F1- D0 BB BNE $A5AE ; bad boy A5F3- A9 11 LDA #$11 A5F5- 48 PHA A5F6- A9 7F LDA #$7F A5F8- 48 PHA ; $117F empilé A5F9- 4C 58 FC JMP $FC58 ; pour un RTS vers $1180 |
Ce code est très probablement celui qui sera exécuté en $BBDB. L'idée géniale (hum), c'est de chercher sur le disque si nous n'avons pas directement ce code quelque part. On l'a vu, les différents checksum mis en place par ACS posent problème dès lors que nous modifions le code pour reprendre la main. Si on localise le code en $BBDB sur disque, il suffira de mettre un 4C 59 FF au début de la routine pour prendre la main au moment où le code sautera à cette adresse. Ce qui nous permet de tenter cette approche audacieuse (rehum), c'est que le code en lui-même n'est pas encrypté (il a juste été déplacé) et surtout que les checksums qui posent problèmes ne vérifieront pas cette partie avant qu'elle ne soit exécutée.
Faisons donc une recherche Hexa sur le disque avec la chaine : A0 00 B9 00 11 (début de la routine en $A5DB donc en $BBDB).
Croisons les doigts et bingo ! Nous trouvons le tout à l'emplacement : T$16/S$05/P :$DB. Notez que la position ($DB) dans le secteur correspond bien...
À partir d'une copie de la disquette de Championship Wrestling non modifiée (donc pas de modification sur le boot sector ou autre), on va donc changer le JMP $FC58 (4C 58 FC) en position $F9 par un 4C 59 FF (JMP $FF59). On n'a pas à nous occuper du checksum découlant du décodage vers $1100+ car il ne contrôle pas la partie que l'on vient de modifier. On aurait aussi pu effectuer la modif au début de la routine en $BBDB directement mais autant récupérer la main à la fin du décodage, nous permettant ainsi d'avoir lisible la partie qui nous intéresse à savoir $1100+ et donc le $1180, prochaine étape de notre chemin de croix.
Relancez votre disquette fraîchement modifiée, et sous vos yeux ébahis, le JMP Monitor vous rend la main juste avant le chargement (ou est-ce le décodage ? Mystère pour le moment...) de l'image du défi ! Nous n'arrivons pas ici par hasard. Si vous reprenez la routine en $A000, on repère deux chargements en $2000 et un déplacement en mémoire auxiliaire entre les deux. La seconde image étant une "Double Hires", la mémoire auxiliaire est logiquement utilisée. Nous avons ensuite encore un chargement pour finalement arriver vers le déplacement mémoire vu précédemment ($A100+ vers $B700+). Il est vrai que notre JMP $FF59 aurait très bien pu arriver après l'affichage du défi. Il aurait alors fallu "remonter" un peu pour trouver ce que nous cherchons. Mais cette méthode "empirique" de localisation, avec un peu d'expérience, est très efficace pour circonscrire une zone recherchée dans les méandres des multiples sous-routines d'un programme. La preuve !
Nous pouvons donc repartir maintenant sur une nouvelle base en étant nettement plus avancé dans le chargement de notre défi. D'ailleurs, par curiosité, il est toujours bon de vérifier régulièrement ce que contiennent les pages Hires.
Arrivé à ce point, en page 2, pas de surprise, nous trouvons la troisième image, celle affichée juste avant le défi. En page 1 par contre, apparaît un joli garbage. Il ne faut pas être devin pour prophétiser que nous tenons - sans doute - notre image encore encryptée. Un moyen simple de le vérifier ? Remettez en $BBF9 le 4C 58 FC attendu (nous avions mis 4C 59 FF directement sur le disque rappelez-vous) pour éviter de nous faire mal si on tombait sur un checksum vérifiant cette portion. Passez ensuite sous affichage Hires/Page 1 ($C050/$C057) et (en aveugle) tapez 1180G pour reprendre là où le programme s'était interrompu. Vous assistez en direct au décodage de l'image. On voit d'ailleurs qu'il y a plusieurs passes avant l'obtention de l'image finale. Cela promet pour la seconde partie (ou pas)...
Le gros intérêt de notre modification disque c'est qu'il suffit de rebooter pour se retrouver au même point, c'est à dire juste avant le décodage de l'image de défi. Profitez-en d'ailleurs pour inspecter le contenu de la mémoire en $2000+ et repérer les premiers octets. Faites ensuite une petite recherche sur disque pour voir si ces octets, par un heureux hasard, ne seraient pas quelques part. Idem pour $3000+
2000 : 17 3F 0A 1B 6F 75 3000 : 80 29 70 49 00 4E |
Vous avez trouvé la chaîne "2000" en T$1B/S$00 et la "3000" en T$1C/S$00 ? Bravo, vous venez (très) probablement de localiser l'image cryptée sur disque. Cela nous sera utile au moment de la réécrire signée (et ré-encryptée). Elle occupe donc les Pistes $1B et $1C au complet. Non, ce n'est pas jouer aux devinettes, c'est juste une question de bon sens. On peut aussi s'emmerder à remonter pour trouver quand et où l'image encryptée a été chargée en suivant les appels à la RWTS, repérer les paramètres passés etc. Parfois, il faut le faire, mais ici ce n'est pas nécessaire donc autant en profiter pour gagner du temps !
Allez, on reboote notre disquette modifiée pour se retrouver une fois encore avant le grand saut en $1180 et on inspecte un peu ce qui s'y passe :
1180- A0 00 LDY #$00 1182- B9 00 BB LDA $BB00,Y 1185- 4D 81 11 EOR $1181 1188- 8D 81 11 STA $1181 118B- C8 INY 118C- C0 DA CPY #$DA 118E- F0 03 BEQ $1193 1190- 4C 82 11 JMP $1182 1193- C9 B8 CMP #$B8 ; checksum OK ? 1195- F0 03 BEQ $119A 1197- 4C 1D AF JMP $AF1D ; bad boy ! 119A- 4C 00 11 JMP $1100 ; vers la suite |
Et encore un nouveau checksum qui vérifie une partie de la page $BB cette fois. Bizarrement celui-ci ne check que jusqu'en $BBD9. Notre modification ayant eu lieu en $BBF9, elle ne posera aucun problème avec ce checksum. Plutôt bon à prendre ! Passons donc à la suite en $1100 :
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 ; décryptage Part I 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 1129- A9 4C LDA #$4C 112B- 85 00 STA $00 112D- A9 08 LDA #$08 112F- 85 01 STA $01 1131- A9 70 LDA #$70 1133- 85 02 STA $02 1135- 20 00 70 JSR $7000 ; décryptage part II 1138- A9 04 LDA #$04 113A- 8D DE 60 STA $60DE 113D- A9 C8 LDA #$C8 113F- 8D DF 60 STA $60DF 1142- 4C A0 11 JMP $11A0 ; vers suite 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 11A0- A0 00 LDY #$00 ; nettoyage... 11A2- 98 TYA 11A3- 99 00 70 STA $7000,Y 11A6- 99 00 71 STA $7100,Y 11A9- 99 00 72 STA $7200,Y 11AC- 99 00 73 STA $7300,Y 11AF- 99 00 74 STA $7400,Y 11B2- 99 00 75 STA $7500,Y 11B5- 99 00 76 STA $7600,Y 11B8- C8 INY 11B9- F0 03 BEQ $11BE 11BB- 4C A2 11 JMP $11A2 11BE- 4C 60 11 JMP $1160 ; vers suite 1160- A9 11 LDA #$11 ; 1162- 85 0F STA $0F 1164- A9 EF LDA #$EF 1166- 85 0E STA $0E 1168- A0 00 LDY #$00 116A- A9 A9 LDA #$A9 116C- 51 0E EOR ($0E),Y 116E- 48 PHA ; on empile $F6 EOR $A9 = $5F 116F- A0 10 LDY #$10 1171- A9 FA LDA #$FA 1173- 51 0E EOR ($0E),Y ; on empile $05 EOR $FA = $FF 1175- 48 PHA 1176- 4C 80 02 JMP $0280 ; mettre JMP $FF59 |
Certains espéraient sans doute la fin de la quête, ce n'est pas tout à fait le cas... Ici nous sommes face aux routines qui vont décrypter non pas l'image du défi mais la routine qui va elle-même décrypter notre défi. Et vous savez quoi ? Et bien on s'en fout ! En effet, pour le moment nul besoin de comprendre ce que font ces routines, tout ce qui nous intéresse, c'est de savoir qu'on en sort en sautant en $280. On remarque bien évidemment l'empilement juste au-dessus du JMP de deux valeurs qu'il sera facile de récupérer une fois notre saut vers le Monitor effectué. Un indice chez vous : c'est $5FFF qui est empilé, ce qui laisse présager d'un RTS nous conduisant vers $6000.
1176 : 4C 59 FF ; on met un JMP Monitor à la place du JMP $280 1180G ; et on relance ! 280L ; on admire le résultat 0280- A9 21 LDA #$21 0282- 8D F2 03 STA $03F2 ; vector reset 0285- A9 02 LDA #$02 0287- 8D F3 03 STA $03F3 ; redéfini 028A- 49 A5 EOR #$A5 028C- 8D F4 03 STA $03F4 028F- A9 21 LDA #$21 0291- 8D F0 03 STA $03F0 ; idem 0294- A9 02 LDA #$02 0296- 8D F1 03 STA $03F1 ; pour le vector BRK ! 0299- 60 RTS ; sans surprise le RTS nous envoie vers $6000 ! |
Et nous voilà enfin face à la routine en $6000 qui est LA routine de décryptage de l'image car (et vous pouvez le vérifier) avant son exécution, l'image en Page 1 n'est toujours pas décryptée... Un rapide coup d'œil sur son contenu avant de rendre l'antenne :
6000- 20 9B 02 JSR $029B ; lalala ! 6003- A9 00 LDA #$00 6005- 85 10 STA $10 6007- A9 00 LDA #$00 6009- 85 0E STA $0E 600B- A9 20 LDA #$20 600D- 85 0F STA $0F ; décodage phase 1 : (A) = (A) EOR (A+1) 600F- A2 20 LDX #$20 6011- A0 00 LDY #$00 6013- B1 0E LDA ($0E),Y 6015- C8 INY 6016- 51 0E EOR ($0E),Y 6018- 88 DEY 6019- 91 0E STA ($0E),Y 601B- C8 INY 601C- D0 F5 BNE $6013 601E- E6 0F INC $0F 6020- CA DEX 6021- D0 EE BNE $6011 6023- E6 10 INC $10 6025- AD F1 20 LDA $20F1 6028- 49 11 EOR #$11 ; check 1 602A- D0 DB BNE $6007 602C- A5 10 LDA $10 602E- C9 0A CMP #$0A ; check 1b 6030- D0 FE BNE $6030 ; boucle infinie ; décodage phase 2 : (A) EOR XX 6032- A9 00 LDA #$00 6034- 85 0F STA $0F 6036- A9 20 LDA #$20 6038- 85 10 STA $10 603A- A2 20 LDX #$20 603C- A0 00 LDY #$00 603E- B1 0F LDA ($0F),Y 6040- 48 PHA 6041- 98 TYA 6042- 18 CLC 6043- 65 10 ADC $10 6045- 48 PHA 6046- 8A TXA 6047- 6A ROR 6048- 85 11 STA $11 604A- 68 PLA 604B- 2A ROL 604C- 2A ROL 604D- 2A ROL 604E- 45 11 EOR $11 6050- 85 11 STA $11 6052- 68 PLA 6053- 45 11 EOR $11 6055- 91 0F STA ($0F),Y 6057- 88 DEY 6058- D0 E4 BNE $603E 605A- E6 10 INC $10 605C- CA DEX 605D- D0 DD BNE $603C ; décodage phase 3 : 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 6079- A9 00 LDA #$00 607B- 85 0E STA $0E 607D- A9 B7 LDA #$B7 607F- 85 0F STA $0F 6081- A0 3D LDY #$3D 6083- A9 C5 LDA #$C5 6085- 91 0E STA ($0E),Y ; $C5 en $B73D 6087- A0 46 LDY #$46 6089- A9 E7 LDA #$E7 608B- 91 0E STA ($0E),Y ; $E7 en $B746 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 60A1- 4C 00 C6 JMP $C600 ; reboot 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 60C3- 4C 21 02 JMP $0221 ; bad boy "ACS Lead Again !" 60C6- 2C 54 C0 BIT $C054 60C9- EA NOP 60CA- 2C 10 C0 BIT $C010 60CD- A9 02 LDA #$02 60CF- 20 20 64 JSR $6420 ; go to check 4 60D2- A0 00 LDY #$00 60D4- B9 00 AA LDA $AA00,Y 60D7- C8 INY 60D8- 59 00 AA EOR $AA00,Y 60DB- 88 DEY 60DC- 99 00 04 STA $0400,Y 60DF- C8 INY 60E0- D0 F2 BNE $60D4 60E2- EE D6 60 INC $60D6 60E5- EE DA 60 INC $60DA 60E8- EE DE 60 INC $60DE 60EB- AD DE 60 LDA $60DE 60EE- C9 08 CMP #$08 60F0- F0 03 BEQ $60F5 60F2- 4C D4 60 JMP $60D4 60F5- 20 2F FB JSR $FB2F 60F8- A9 60 LDA #$60 60FA- 85 2B STA $2B 60FC- 4C 00 BB JMP $BB00 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 649A- 6C F2 03 JMP ($03F2) ; reboot ! 649D- A9 A0 LDA #$A0 649F- 8D 20 64 STA $6420 64A2- A9 D1 LDA #$D1 64A4- 8D 21 64 STA $6421 64A7- A9 B9 LDA #$B9 64A9- 8D 22 64 STA $6422 64AC- 4C 20 64 JMP $6420 |
Passez en mode Hires, Page 1 et lancez la routine par $6000G et vous assistez au décryptage en direct. C'est comme tout à l'heure sauf que cette fois, nous sommes bien au cœur même du décodage en l'exécutant.
On peut déjà remarquer sans plus entrer dans les détails, qu'il y a différentes vérifications qui sont effectuées, vérifications qu'il faudra contourner d'une façon ou d'une autre. Mais tout ceci fera l'objet de la partie II normalement en ligne demain ou après-demain... Suspens !
Et pour finir, à l'occasion d'une fausse manœuvre ou d'un checksum assassin, il pourra très bien vous arriver de tomber sur ceci ! La routine se trouve en $221 et on y arrive, notamment, en cas de Reset intempestif ou de BRK ravageur ! C'est toute la mémoire qui sera alors rempli d'ACS !