Cela faisait longtemps qu'on ne s'était pas donné rendez-vous pour un petit défi. Surprise, c'est sur Apple II GS que ça se passe, et vous allez le voir, cela change tout ! Oubliez en effet tout ce qui a été vu jusqu'à présent pour aborder un tel challenge, pourtant classique en apparence : signer une fois encore une image. Ici, nous ne sommes plus sur 8 bits, ce n'est plus une page Hires mais Super-Hires propre au GS et en plus, il nous faudra même utiliser les Tool Sets (quelle hérésie...) pour arriver à nos fins. Bref, en attaquant ce défi, je savais que j'allais en baver, croyant ne pas connaître grand-chose au GS. En fait, j'avais tord : je n'y connaissais absolument rien ! Et pour aller au bout de l'aventure, il m'a fallu tout réapprendre (ou presque). Plutôt que de vous abreuver de longues pages d'explications arides ou de listings bien longuets (rassurez-vous, il y en aura quand même), je vous propose cette fois un compte-rendu un peu plus vivant, car illustré de screenshots. Le GS peut afficher de vraies couleurs, autant en profiter !
Partie I : réussir à lancer le programme.
Car oui, premier problème : passer cet écran ! La première fois que j'ai lancé le programme, j'ai tout simplement cru que la disquette était morte. On a en effet droit à un festival de sons (couinements de drives) et lumières (diodes des lecteurs qui s'allument dans tous les sens) nous rappelant les heures les plus sombres du support magnétique ! En fait, au boot du disque, nous sommes tout simplement face au Piège à Con tendu par l'équipe de GS On the Rocks et qui était une des marques de fabrique de la série. Pour ceux qui ne sont pas au courant, tout s'arrête ici et ils balanceront de rage la disquette à la benne. Les autres savent, eux, qu'il suffit de taper lsd (pour Lyon Software Diffusion, les créateurs de la série) pour passer à la suite. Bien entendu tout ceci n'a rien à voir avec le défi et sert juste de mise en bouche.
Car le défi en lui-même, le voici. Il apparaît lors du lancement du programme principal (le lecteur de fichiers texte) et nous invite, comme d'habitude je dirais, à signer une image. Gronk (et cie) nous promet même le champagne en cas de réussite. Bon, puisqu'il me semble avoir lu que la caisse avait été gagnée à l'époque, nous allons donc nous attaquer au défi, non pas pour la picole, mais pour la beauté du geste. En attendant, on notera (nous sommes aussi là pour bosser...) que le programme du défi utilise une interface graphique standard et repose donc sur les Tools GS.
Un petit coup l’œil au contenu de la disquette sous le Finder GS/OS et on remarque immédiatement les trois fichiers PICx (type ANI) ainsi que le programme principal LSD.SYS16. Le type des fichiers PICx n'est pas standard pour des images et leur taille (variable) laisse supposer que les fichiers sont compressés. Info, intox ou tout simplement fausse-route ?
Que se passe-t'il si on veut lancer directement LSD.SYS16 depuis le Finder ? Et bien l'écran ci-contre apparaît et le programme boucle sur lui-même. Second problème donc : je comptais utiliser GSBug (le debugger GS) pour m'attaquer au défi mais il nécessite GS/OS. Or GS On The Rocks semble ne pas trop l'apprécier.
Puisque le programme rechigne à aller plus loin, on va tout simplement utiliser GSBug afin de voir ce qui se passe un peu. On l'active et on passe en mode SINGLE STEP pour voir qu'effectivement le code boucle sur la vérification d'une adresse qui, non égale à zéro, nous renvoie encore et encore sur la comparaison. Plutôt que de tracer pour découvrir d'où provient le contenu de cette adresse, essayons plutôt la méthode bourrine (qui nous caractérise) et tentons de forcer le passage.
Cela tombe bien, GSBug possède une fonction permettant de skiper une instruction. On zappe donc le branchement obligatoire (BRL) pour passer à la suite... Et on rend la main au programme.
On se retrouve alors avec cet écran. On remarque que les deux messages (le classique en cas de boot direct, et le message nous invitant à relancer l'application) s'affichent simultanément. Mais le programme, et c'est le plus important, se poursuit normalement et s'exécute correctement. Et depuis le Finder cette fois... Rappelez-vous la manip à faire, car nous aurons à l'effectuer à chaque fois que nous voudrons exécuter le programme depuis GS/OS.
Partie II : localiser la routine mystère.
La première chose la plus évidente, une fois qu'on a réussi à lancer le programme dans les conditions voulues, c'est bien évidemment de regarder un peu à l'intérieur... En fait pour résoudre ce défi, nous allons devoir à la fois travailler en direct live avec GSBug mais aussi avoir une vision plus globale du code assembleur de LSD.SYS16 afin de pouvoir étudier son fonctionnement à tête reposée. Pour ce faire nous allons utiliser Orca/Disassembler qui permet d'afficher le code sous l'interface graphique typique GS/OS. Il permet également d'avoir une vision rapide des segments utilisés par le programme. On remarque d'ailleurs immédiatement qu'un des trois segments s'appelle tout simplement pic. De quoi évidemment attirer notre attention. La méthode n'est pas spécialement technique mais plutôt pleine de bon sens. J'ai souvent vanté dans les défis précédents les bienfaits de l'observation. Ici encore, après quelques minutes à scroller à l'intérieur du code du segment pic, on remarque rapidement un LDA #$ABCD trop étrange pour être honnête.
Ou encore, comme ci-contre, un EOR #$4321 tout aussi louche. Et quand on voit apparaître juste au-dessus un LDA suivi d'un EOR puis d'un STA, on repense immédiatement aux défis sur 8 bits et aux routines de décodage rencontrées... Bien évidemment, il va maintenant falloir vérifier tout ça en live avec GSBug.
Nouvelle difficulté propre au GS : l'allocation mémoire étant dynamique, un loader s'occupe de placer le programme en RAM lors de son chargement à un emplacement libre. Les adresses mémoire vues plus haut avec le code désassemblé ne sont en fait que des offsets. Pour localiser réellement en mémoire telle ou telle portion de code, il faudra ajouter son offset à l'adresse réelle de son segment. Heureusement, GSBug est fourni avec un petit Classic Desk Accessory (programme résident) permettant de fournir ces précieuses informations pour chaque programme chargé actuellement en mémoire : j'ai nommé Loader Dumper. Attention de bien utiliser une version compatible avec votre GS/OS. Pour la 6.0.1, il faudra Loader Dumper 4, celui fourni avec GSBug 1.5 ne fonctionnant pas...
On repère notre programme parmi tous les processus actuellement en mémoire et on récupère son Identificateur unique. Ici l'UserID est $1002.
Grâce à cet UserID, on obtient ensuite les adresses de début et fin des segments. Il ne reste plus alors qu'à ajouter les offsets des instructions qui nous intéressent pour obtenir leurs adresses mémoire et poser des breakpoints dessus ! Exemple : ici le segment 3 (pic) commence en $0F6C3D. On veut obtenir l'adresse du EOR #$4321 qui nous titille. L'offset correspondant est : $0552. L'adresse en mémoire sera donc : $0F6C3D+0552 soit $0F718F !
On vérifie et effectivement, on retrouve bien notre EOR #4321 ! Attention, le code ayant été totalement relogé en mémoire par le loader, il est parfois peu évident de le reconnaître. En effet, toutes les adresses mémoire (hors Page Zéro) et tous les sauts dans le code même seront différents de ce qui est affiché avec un désassembleur.
Pour en revenir à notre EOR #4321, une méthode simple pour vérifier que nous sommes bien dans la routine recherchée consiste tout simplement à le remplacer par des NOP. Si nous observons un résultat différent de celui normalement obtenu, on pourra considérer que nous sommes bien au bon endroit.
Je pense que le résultat parle de lui-même. L'image n'est clairement plus décryptée correctement. Notre EOR se situe bien au cœur de la routine qui nous intéresse.
Partie III : le décryptage de l'image.
Une fois la routine localisée, il suffit de poser judicieusement un breakpoint (en gros juste avant qu'elle ne commence) pour ensuite la suivre à la trace, instruction par instruction grâce aux fonctionnalités de GSBug. Ne pas s'abstenir évidemment d'afficher les adresses en Page Zéro utilisées par le programme pour bien comprendre ce qui s'y passe. Il faut en fait subtilement mélanger analyse en dead listing (avec le code obtenu par désassemblage) et le traçage en live qui permet lui de voir la variation des valeurs en direct. Je vous colle ci-après le code complet de la routine agrémenté de quelques petits commentaires. Ceux qui regardent juste les images peuvent directement passer à la suite !
00/03BD: A9 01 00 LDA #0001 ; image 1 00/03C0: 85 DB STA DB 00/03C2: A9 03 00 LDA #0003 ; 3 images à traiter 00/03C5: 85 F9 STA F9 00/03C7: 38 SEC 00/03C8: E5 DB SBC DB 00/03CA: 70 03 BVS 03CF {+03} 00/03CC: 49 00 80 EOR #8000 00/03CF: 30 03 BMI 03D4 {+03} 00/03D1: 82 94 02 BRL 0668 {+294} 00/03D4: A5 DB LDA DB 00/03D6: 3A DEC 00/03D7: 0A ASL 00/03D8: 0A ASL 00/03D9: AA TAX 00/03DA: BD D4 08 LDA 08D4,X 00/03DD: A8 TAY 00/03DE: BD D6 08 LDA 08D6,X 00/03E1: AA TAX 00/03E2: 98 TYA 00/03E3: 85 AD STA AD 00/03E5: 86 AF STX AF 00/03E7: 64 B1 STZ B1 00/03E9: 64 B3 STZ B3 00/03EB: F4 00 00 PEA 0000 00/03EE: 7B TDC 00/03EF: 18 CLC 00/03F0: 69 AB 00 ADC #00AB 00/03F3: 48 PHA 00/03F4: 22 25 36 01 JSL 013625 ; FONCTION OPEN PRODOS 16 (fichier PICx) 00/03F8: B0 03 BCS 03FD {+03} 00/03FA: 82 03 00 BRL 0400 {+03} 00/03FD: 82 35 03 BRL 0735 {+335} 00/0400: F4 00 00 PEA 0000 00/0403: 7B TDC 00/0404: 18 CLC 00/0405: 69 AB 00 ADC #00AB 00/0408: 48 PHA 00/0409: 22 52 36 01 JSL 013652 ; FCT GET_EOF (End of file) 00/040D: A5 AD LDA AD 00/040F: 85 D1 STA D1 ; retour = taille du fichier (lo) 00/0411: A5 AF LDA AF 00/0413: 85 D3 STA D3 ; (hi) 00/0415: 48 PHA 00/0416: 48 PHA 00/0417: F4 00 00 PEA 0000 00/041A: F4 20 7D PEA 7D20 00/041D: AD 00 00 LDA 0000 00/0420: 48 PHA 00/0421: F4 00 C0 PEA C000 00/0424: F4 00 00 PEA 0000 00/0427: F4 00 00 PEA 0000 00/042A: A2 02 09 LDX #0902 00/042D: 22 00 00 E1 JSL E10000 NewHandle(Size/4,MemID,Attr,@loc):H 00/0431: 8D 9E 09 STA 099E 00/0434: 68 PLA 00/0435: FA PLX 00/0436: 85 CD STA CD 00/0438: 86 CF STX CF 00/043A: A5 DB LDA DB 00/043C: 3A DEC 00/043D: 0A ASL 00/043E: 0A ASL 00/043F: AA TAX 00/0440: A5 CD LDA CD 00/0442: 9D C8 08 STA 08C8,X 00/0445: A5 CF LDA CF 00/0447: 9D CA 08 STA 08CA,X 00/044A: A0 02 00 LDY #0002 00/044D: B7 CD LDA [CD],Y 00/044F: AA TAX 00/0450: A7 CD LDA [CD] 00/0452: 85 E3 STA E3 00/0454: 86 E5 STX E5 00/0456: 48 PHA ; réserve place pour le Handle retourné 00/0457: 48 PHA ; (4 octets) 00/0458: D4 D3 PEI D3 ; Size (hi) 00/045A: D4 D1 PEI D1 ; Size (lo) 00/045C: AD 00 00 LDA 0000 ; 00/045F: 48 PHA ; ID 00/0460: F4 00 C0 PEA C000 ; Attribut 00/0463: F4 00 00 PEA 0000 ; Pointer to Loc 00/0466: F4 00 00 PEA 0000 ; Pointer to Loc 00/0469: A2 02 09 LDX #0902 00/046C: 22 00 00 E1 JSL E10000 NewHandle(Size/4,MemID,Attr,@loc):H 00/0470: 8D 9E 09 STA 099E 00/0473: 68 PLA ; handle 00/0474: FA PLX ; handle 00/0475: 85 C9 STA C9 ; sauvegarde 00/0477: 86 CB STX CB 00/0479: A0 02 00 LDY #0002 00/047C: B7 C9 LDA [C9],Y 00/047E: AA TAX 00/047F: A7 C9 LDA [C9] 00/0481: 85 DF STA DF ; memory block (lo) 00/0483: 86 E1 STX E1 ; memory block (hi) 00/0485: A5 DF LDA DF 00/0487: 85 AD STA AD 00/0489: A5 E1 LDA E1 00/048B: 85 AF STA AF 00/048D: A5 D1 LDA D1 00/048F: 85 B1 STA B1 00/0491: A5 D3 LDA D3 00/0493: 85 B3 STA B3 00/0495: F4 00 00 PEA 0000 00/0498: 7B TDC 00/0499: 18 CLC 00/049A: 69 AB 00 ADC #00AB 00/049D: 48 PHA 00/049E: 22 2F 36 01 JSL 01362F ; FONCTION READ 00/04A2: B0 03 BCS 04A7 {+03} 00/04A4: 82 03 00 BRL 04AA {+03} 00/04A7: 82 8B 02 BRL 0735 {+28B} 00/04AA: F4 00 00 PEA 0000 00/04AD: 7B TDC 00/04AE: 18 CLC 00/04AF: 69 AB 00 ADC #00AB 00/04B2: 48 PHA 00/04B3: 22 39 36 01 JSL 013639 ; CLOSE 00/04B7: F4 00 00 PEA 0000 00/04BA: F4 20 00 PEA 0020 ; 32 octets 00/04BD: A5 D1 LDA D1 00/04BF: A6 D3 LDX D3 00/04C1: 22 A7 2A 01 JSL 012AA7 ; "soustraction" D3/D1-0000/0020 -> D3/D1 00/04C5: 68 PLA 00/04C6: FA PLX 00/04C7: 85 D1 STA D1 ; D1 contient la taille du fichier - 32 00/04C9: 86 D3 STX D3 ; décryptage PHASE 1 (ascendante) A = A EOR (A-2)' 00/04CB: A9 01 EF LDA #EF01 ; valeur de départ pour A' 00/04CE: 85 D9 STA D9 00/04D0: 64 D7 STZ D7 ; on part de 00 00/04D2: A5 D7 LDA D7 00/04D4: A2 00 00 LDX #0000 00/04D7: C9 00 00 CMP #0000 00/04DA: 10 01 BPL 04DD {+01} 00/04DC: CA DEX 00/04DD: DA PHX 00/04DE: 48 PHA 00/04DF: A5 DF LDA DF ; adresse mémoire début 00/04E1: A6 E1 LDX E1 ; fichier chargé 00/04E3: 22 9C 2A 01 JSL 012A9C ; "addition" E1/DF+0000/D7 -> C7/C5 00/04E7: 68 PLA 00/04E8: FA PLX 00/04E9: 85 C5 STA C5 00/04EB: 86 C7 STX C7 00/04ED: A7 C5 LDA [C5] ; *** A 00/04EF: 45 D9 EOR D9 00/04F1: 85 D9 STA D9 00/04F3: A5 D9 LDA D9 00/04F5: 87 C5 STA [C5] ; *** 00/04F7: A5 D9 LDA D9 00/04F9: 0A ASL 00/04FA: 85 D9 STA D9 00/04FC: A5 D9 LDA D9 00/04FE: 49 21 43 EOR #4321 00/0501: 85 D9 STA D9 ; A' = ASL + EOR #$4321 00/0503: E6 D7 INC D7 ; on incrémente de 2 00/0505: E6 D7 INC D7 ; (on traite un WORD à chaque fois) 00/0507: A5 D7 LDA D7 00/0509: A2 00 00 LDX #0000 00/050C: C9 00 00 CMP #0000 00/050F: 10 01 BPL 0512 {+01} 00/0511: CA DEX 00/0512: C5 D1 CMP D1 ; taille buffer à traiter (D1-2 max donc) 00/0514: D0 02 BNE 0518 {+02} 00/0516: E4 D3 CPX D3 00/0518: F0 03 BEQ 051D {+03} 00/051A: 82 B5 FF BRL 04D2 {-4B} ; on boucle ; décryptage PHASE 2 (descendante) A = A EOR (A+2)' 00/051D: A9 CD AB LDA #ABCD , valeur de départ pour A' 00/0520: 85 D9 STA D9 00/0522: A5 D1 LDA D1 ; on part de D1 00/0524: 85 D7 STA D7 ; 00/0526: A5 D7 LDA D7 00/0528: A2 00 00 LDX #0000 00/052B: C9 00 00 CMP #0000 00/052E: 10 01 BPL 0531 {+01} 00/0530: CA DEX 00/0531: DA PHX 00/0532: 48 PHA 00/0533: A5 DF LDA DF 00/0535: A6 E1 LDX E1 00/0537: 22 9C 2A 01 JSL 012A9C ; "addition" E1/DF+0000/D7 -> C7/C5 00/053B: 68 PLA 00/053C: FA PLX 00/053D: 85 C5 STA C5 00/053F: 86 C7 STX C7 00/0541: A7 C5 LDA [C5] ; *** 00/0543: 45 D9 EOR D9 00/0545: 85 D9 STA D9 00/0547: A5 D9 LDA D9 00/0549: 87 C5 STA [C5] ; *** 00/054B: A5 D9 LDA D9 ; 00/054D: 0A ASL 00/054E: 85 D9 STA D9 00/0550: A5 D9 LDA D9 00/0552: 49 21 43 EOR #4321 00/0555: 85 D9 STA D9 ; A' = ASL + EOR #$4321 00/0557: C6 D7 DEC D7 00/0559: C6 D7 DEC D7 00/055B: A5 D7 LDA D7 00/055D: C9 00 00 CMP #0000 ; (buffer traité jusqu'à 2) 00/0560: F0 03 BEQ 0565 {+03} 00/0562: 82 C1 FF BRL 0526 {-3F} 00/0565: A9 00 7D LDA #7D00 00/0568: 85 D5 STA D5 00/056A: 48 PHA ; return data (number of bytes unpacked) 00/056B: D4 E1 PEI E1 ; Pointer to packed data (hi) 00/056D: D4 DF PEI DF ; Pointer to packed data (lo) 00/056F: D4 D1 PEI D1 ; size of packed data 00/0571: 7B TDC 00/0572: 18 CLC 00/0573: 69 E3 00 ADC #00E3 00/0576: A2 00 00 LDX #0000 00/0579: DA PHX ; Pointer to pointer to unpacked data (hi) 00/057A: 48 PHA ; Pointer to pointer to unpacked data (lo) 00/057B: 7B TDC 00/057C: 18 CLC 00/057D: 69 D5 00 ADC #00D5 00/0580: A2 00 00 LDX #0000 00/0583: DA PHX ; Pointer to DWORD contening Size (hi) 00/0584: 48 PHA ; Pointer to DWORD contening Size (lo) 00/0585: A2 03 27 LDX #2703 ; Tool Number (UnPackBytes) 00/0588: 22 00 00 E1 JSL E10000 UnPackBytes(@Buff,BfSz,@StartPtr,@Sz):Size 00/058C: 8D 9E 09 STA 099E 00/058F: 68 PLA ; size 00/0590: 85 DD STA DD 00/0592: A5 DD LDA DD 00/0594: A2 00 00 LDX #0000 00/0597: C9 00 00 CMP #0000 00/059A: 10 01 BPL 059D {+01} 00/059C: CA DEX 00/059D: DA PHX 00/059E: 48 PHA 00/059F: A5 DF LDA DF 00/05A1: A6 E1 LDX E1 00/05A3: 22 9C 2A 01 JSL 012A9C ; addition 00/05A7: 68 PLA 00/05A8: FA PLX 00/05A9: 85 DF STA DF 00/05AB: 86 E1 STX E1 00/05AD: A5 DB LDA DB 00/05AF: 3A DEC 00/05B0: 0A ASL 00/05B1: 0A ASL 00/05B2: 0A ASL 00/05B3: 0A ASL 00/05B4: 0A ASL 00/05B5: AA TAX 00/05B6: F4 68 08 PEA 0868 00/05B9: A9 68 08 LDA #0868 00/05BC: 18 CLC 00/05BD: 86 F7 STX F7 00/05BF: 65 F7 ADC F7 00/05C1: 48 PHA 00/05C2: A5 E1 LDA E1 00/05C4: 48 PHA 00/05C5: A5 DF LDA DF 00/05C7: 48 PHA 00/05C8: F4 20 00 PEA 0020 ; les fameux 32 derniers octets 00/05CB: 22 A9 23 01 JSL 0123A9 ; move Palettes block 00/05CF: D4 CB PEI CB 00/05D1: D4 C9 PEI C9 00/05D3: A2 02 10 LDX #1002 00/05D6: 22 00 00 E1 JSL E10000 DisposeHandle(H) 00/05DA: 8D 9E 09 STA 099E 00/05DD: A5 DB LDA DB 00/05DF: 3A DEC 00/05E0: 0A ASL 00/05E1: 0A ASL 00/05E2: 0A ASL 00/05E3: 0A ASL 00/05E4: AA TAX 00/05E5: 86 F7 STX F7 00/05E7: 48 PHA 00/05E8: A2 04 17 LDX #1704 00/05EB: 22 00 00 E1 JSL E10000 GetMasterSCB():SCB 00/05EF: 8D 9E 09 STA 099E 00/05F2: 68 PLA 00/05F3: A6 F7 LDX F7 00/05F5: 9D 38 08 STA 0838,X 00/05F8: A5 DB LDA DB 00/05FA: 3A DEC 00/05FB: 0A ASL 00/05FC: 0A ASL 00/05FD: 0A ASL 00/05FE: 0A ASL 00/05FF: AA TAX 00/0600: 86 F7 STX F7 00/0602: A0 02 00 LDY #0002 00/0605: B7 CD LDA [CD],Y 00/0607: AA TAX 00/0608: A7 CD LDA [CD] 00/060A: 85 F3 STA F3 00/060C: 86 F5 STX F5 00/060E: A6 F7 LDX F7 00/0610: A5 F3 LDA F3 00/0612: 9D 3A 08 STA 083A,X 00/0615: A5 F5 LDA F5 00/0617: 9D 3C 08 STA 083C,X 00/061A: A5 DB LDA DB 00/061C: 3A DEC 00/061D: 0A ASL 00/061E: 0A ASL 00/061F: 0A ASL 00/0620: 0A ASL 00/0621: AA TAX 00/0622: A9 A0 00 LDA #00A0 00/0625: 9D 3E 08 STA 083E,X 00/0628: A5 DB LDA DB 00/062A: 3A DEC 00/062B: 0A ASL 00/062C: 0A ASL 00/062D: 0A ASL 00/062E: 0A ASL 00/062F: AA TAX 00/0630: A9 40 08 LDA #0840 00/0633: 18 CLC 00/0634: 86 F7 STX F7 00/0636: 65 F7 ADC F7 00/0638: A2 40 08 LDX #0840 00/063B: 85 F3 STA F3 00/063D: 86 F5 STX F5 00/063F: A9 00 00 LDA #0000 00/0642: 87 F3 STA [F3] 00/0644: A0 02 00 LDY #0002 00/0647: A9 00 00 LDA #0000 00/064A: 97 F3 STA [F3],Y 00/064C: A9 C8 00 LDA #00C8 00/064F: A0 04 00 LDY #0004 00/0652: 97 F3 STA [F3],Y 00/0654: A9 40 01 LDA #0140 00/0657: A0 06 00 LDY #0006 00/065A: 97 F3 STA [F3],Y 00/065C: A5 DB LDA DB 00/065E: C5 F9 CMP F9 00/0660: F0 06 BEQ 0668 {+06} 00/0662: 1A INC 00/0663: 85 DB STA DB 00/0665: 82 6C FD BRL 03D4 {-294} |
On a donc une routine qui va effectuer, pour chacune des trois images affichées par le programme, la série d'actions suivantes (et dans cet ordre) :
- lecture du fichier PICx depuis le disque et détermination de sa taille (utilisation des fonctions OPEN/READ/GET_EOF de Prodos 16).
- première phase de décryptage : EOR ascendant effectué sur l'ensemble du fichier sauf les 32 derniers octets suivant la formule : A<= A EOR (A-2)'. Grosso modo, on fait un EOR entre un WORD et celui qui le précède légèrement modifié (d'où le "prime"). Le tout premier WORD est eoré avec #EF01 (valeur fixe).
- seconde phase de décryptage : EOR descendant effectué sur l'ensemble du fichier sauf les 31 derniers octets (oui 31 et non 32 cette fois !) suivant la formule : A<= A EOR (A+2)'. Le dernier WORD est eoré avec #ABCD (valeur fixe encore).
- pour obtenir le "prime", deux petites manipulations de bits sont effectuées : A' = (ASL A) EOR #$4321.
- troisième phase : le résultat obtenu, après les deux phases de décodage par EOR, est envoyé vers la fonction UnPackBytes du Tool Set 3 (Miscellanous Tool Set) !
- Le résultat final est la partie "pixel" d'une image Super-Hires. Il ne contient ni les 200 Scanline Control Bytes ni les Color Tables ! Ceux-ci sont ajoutés par la suite par le programme . Toutefois, les 32 octets de fin de fichier, non traités par le EOR (ou presque), correspondent à la palette principale de l'image.
À la lecture de ce petit descriptif, on comprend tout de suite que pour notre propre programme d'encodage, il faudra utiliser la fonction inverse à UnPackBytes à savoir (je vous le donne en mille) : PackBytes. Ce qui implique bien sûr l'utilisation des Tools GS ! Terminé les petits trucs faits à l'arrache sous Merlin comme au bon vieux temps du 8 bits !
Partie IV : récupérer l'image !
En effet, pour pouvoir la signer, il va déjà falloir avoir une version vierge de l'image. Bien entendu, elle n'est pas fournie sur le disque ! Nous devrons la récupérer nous-même. La solution la plus simple est d'intercepter l'image (ou plutôt) la zone mémoire où elle est stockée juste après l'appel de la fonction UnPackBytes. Si vous avez lu ce qui précède, vous savez que c'est la dernière étape du décryptage et donc que l'image est sous sa forme quasi-définitive après son exécution. On pose donc un breakpoint sur l'adresse de la fonction. Une fois le breakpoint déclenché, on en profite pour checker l'adresse mémoire contenant l'adresse (oui c'est un pointeur d'un pointeur...) où sera unpacked notre image. Cette adresse est importante car c'est cette zone que nous allons ensuite sauvegarder sous forme de fichier. Ne reste plus alors qu'à exécuter la fonction UnPackBytes en mode SINGLE STEP. Et à effectuer la sauvegarde.
Entrée alors en scène d'un nouvel utilitaire, le CDA Nifty List et son complément NiftyFile. Juste après avoir exécuter la fonction UnPackBytes, on lance le CDA et on sauve la zone unpacked. En fait, vous remarquerez sur l'écran ci-contre que je sauve directement $8000 bytes. À ce stade, nous avons en fait uniquement la partie "pixel" de l'image qui a été décryptée. Celle-ci fait $7D00 octets. Il nous manque $300 octets de données valides (les Scan Lines et les Color Tables). La fin du fichier sera donc pour le moment occupée par des datas ne correspondant à rien. Nous les remplacerons bientôt. Pour info, le "C1" de la ligne de commande permet de fixer directement le type de fichier sur PICTURE. Cela nous servira lorsqu'il faudra ouvrir l'image avec un logiciel de dessin.
Si vous essayez de visualiser directement l'image récupérée précédemment, voici à peu près ce que vous devriez voir... Contrairement aux apparences, nous sommes sur la bonne voie ! Il manque certes les infos de Scan Lines et les Palettes de couleurs mais le reste y est. Il nous faut maintenant récupérer les données manquantes depuis l'image originale.
L'idée la plus évidente est d'attendre tout simplement que le soft la charge, la décrypte et l'affiche à l'écran. Il suffit alors de passer sous GSBug pour stopper le programme. Puis de relancer Nifty Lift pour enfin sauver la partie qui nous intéresse.
On récupère directement les infos dans la partie Super-Hires de la mémoire (en bank $E1). On ne prend que les $300 derniers octets (entre $9D00 et $9DFF) et on sauve le tout sur disk. Là, vous allez sûrement vous demander pourquoi ne pas sauver toute l'image à ce moment-là uniquement et éviter le cirque précédent ? La réponse est simple et vous l'avez même sous les yeux : sauver toute la page Super-Hires inclurait la barre de menu de l'interface graphique du soft. Alors oui c'est sûr que ce qui est en-dessous (si tant est qu'il existe quelque chose) ne se verra de toute façon pas et on pourrait très bien effacer ensuite la barre par l'intermédiaire d'un soft de dessin (que nous allons de toute façon devoir utiliser pour la signature). C'est une approche, qui d'ailleurs, marcherait parfaitement dans le cas présent. Toutefois, je préfère systématiquement récupérer l'image juste après son décryptage. C'est la méthode la plus sûre (pour l'intégrité de l'image) et la plus universelle.
Bien, une fois nos $300 octets récupérés (et seulement ceux-là !), la suite, je suppose que vous l'imaginez vous-même. Nous allons coller cette partie à la fin de notre pseudo image sauvée précédemment, à la place des $300 octets de pur garbage. Le couple "pixel"/"info palettes" de l'image finale sera ainsi recomposé !
Mais avant de recoller les morceaux, nous allons devoir utiliser un utilitaire bien connu des utilisateurs Prodos, le bien nommé Block Warden. Il va nous permettre de fixer un petit défaut de notre dump de $300 octets. En effet, nous avons capturé les infos de l'écran principal contenant notre image mais aussi celles concernant la barre de menu. Attention, je ne parle pas ici de la partie "pixel" mais bien des "infos palettes". Regardez d'ailleurs avec Block Warden les premiers octets qui correspondent aux premières lignes de l'image.
Voici à quoi ressemblerait notre image finale si nous ne modifions pas les premiers octets. Remarquez, cela ne se verra pas. En fait, cela n'a même aucune importance car une fois signée, nous n'allons encrypter et coder que la partie "pixel" de l'image. Mais autant faire les choses proprement. La nuance est subtile avec la partie précédente où je voulais avoir l'intégralité de l'image, y compris les éventuels pixels "cachés" sous la barre de menu. Ici, nous avons bien l'image en entier, ce sont simplement les couleurs qui ne sont pas les bonnes pour les premières lignes de l'image.
On revient à Block Warden, avec lequel on uniformise les premiers octets à $08 comme les autres (utilisation de la palette de couleurs 8). Il ne reste plus maintenant qu'à regrouper notre dump fixé avec la partie "pixel" de l'image.
Pour ce faire, on va de nouveau utiliser l'extension NiftyFile sous Nifty List. L'idée est simple : on charge notre pseudo image récupérée tout à l'heure, on remplace les $300 derniers octets par notre dump modifié précédemment et on sauve le tout. Nous avions déjà un fichier de $8000 octets donc aucun problème de place mémoire.
Et voici le résultat : nous avons enfin une image propre et complète. Elle va maintenant nous servir pour deux choses : la première, évidente, sera son utilisation comme base pour l'ajout de notre signature. Mais avant cela, non modifiée, elle nous sera indispensable pour la mise au point du programme d'encodage. En repartant de cette image, nous devrons être capables, avec notre routine, de générer le même fichier PIC1 que celui utilisé par le programme...
Partie V : le programme de codage.
Pour ce faire, notre programme devra être exactement l'inverse du programme de décodage (logique...). Y compris au niveau de la chronologie des évènements. Nous avions trois phases pour le décodage, nous allons donc retrouver trois phases pour le codage :
- Packer/compresser la partie "pixel" de l'image (les $7D00 premiers octets) avec la fonction du Tool Set 3, PackBytes.
- Appliquer la phase 1 de codage : A<= A EOR (A+2)'. C'est exactement la même formule que pour le décodage phase 2. Par contre, nous avions une phase 2 descendante pour le décodage, l'inverse sera donc une phase 1 ascendante.
- Ensuite viendra la phase 2 de codage : A<=A EOR (A-2)'. Là encore, la phase 2 de codage sera l'inverse de la phase 1 de décodage. Nous avions une phase 1 ascendante. Notre phase 2 sera donc descendante.
- Comme vu précédemment, on passe de A à A' - donc de (A-2) à (A-2)' - en faisant un simple (ASL A) EOR #$4321 . Que ça soit pour le décodage ou le codage, la même formule est appliquée.
- La seule difficulté (qui m'aura fait m'arracher les cheveux que je n'ai plus) sera de bien "borner" les algos. En effet, il faut que les phases inverses s'effectuent exactement sur le même nombre d'octets.
Je vous livre ci-après le code source de ma routine d'encodage. J'ai utilisé les routines Text Tool pour la sortie caractères, afin de simplifier le tout et ne pas devoir utiliser le Tool Set QuickDrawII (ce qui aurait alourdi considérablement le code). Pas de fenêtre, pas de souris, mais un truc sobre et efficace, à l'ancienne quoi...
;............................................................... ; DEFI GS ON THE ROCKS 4 (SIGNATURE IMAGE 1) ; WWW.CTRL-POMME-RESET.FR ; v0.5 - ORCA/M ;............................................................... KEEP test MCOPY test.mac ; fichier MACRO (généré avec macgen) DeRefLoc GEQU $0 HandleBuffer GEQU $10 VC5 GEQU $C5 VC7 GEQU $C7 VD1 GEQU $D1 VD3 GEQU $D3 VD7 GEQU $D7 VD9 GEQU $D9 VDF GEQU $DF VE1 GEQU $E1 TEMP GEQU $A0 VDC GEQU $B0 Main START PHK ; data bank PLB ; = code bank _TLStartup ; Tools Locator on _MTStartup ; Miscellanous Tools on PHA _MMStartup ; Memory Manager on Pullword MyID PHA PHA PushLong $100 ; direct Page memory request PushWord MyID PushWord #$C005 ; locked, fixed, aligned, fixed bank PushLong #0 ; bank 0 _NewHandle ; Direct Page PullLong DeRefLoc LDA [DeRefLoc] PHA PLD ; direct Page Register PEA MSG1|-16 PEA MSG1 _WriteCString ; message intro + open image JSL $E100A8 ; ProDOS16 entry point DC I2'$10' ; commande OPEN DC I4'OpenParms'; parmtable pour OPEN BCC s1 ; open OK ? BRL Error ; sinon erreur s1 PEA MSGOK|-16 PEA MSGOK _WriteCString ; OPEN OK PEA MSG2|-16 PEA MSG2 _WriteCString ; message allocate memory LDA refnum STA refnum2 ; pour close STA refnum3 ; pour read PEA $0000 PEA $0000 ; push longword for result PEA $0001 PEA $0000 ; push size = $10000 (128ko) LDA MyID PHA ; push ID PEA $C000 ; attributes for block PEA $0000 PEA $0000 ; location (0=default) _NewHandle ; allocation mémoire BCC s2 BRL Error s2 PLA PLX STA HandleBuffer STX HandleBuffer+2 PEA MSGOK|-16 PEA MSGOK _WriteCString PEA MSG3|-16 PEA MSG3 _WriteCString ; message read file LDA [HandleBuffer] STA Buffer LDY #2 LDA [HandleBuffer],Y STA Buffer+2 LDA #32768 ; lecture image complète ($8000) STA NbBytes LDA #0000 STA NbBytes+2 JSL $E100A8 DC I2'$0012' ; commande READ DC I4'ReadParms' BCC s3 BRL Error s3 PEA MSGOK|-16 PEA MSGOK _WriteCString ; lecture OK ;............................................................... ;encryptage + package ;............................................................... PEA MSG5|-16 PEA MSG5 _WriteCString ; message Packing PHA ; result space PEA Buffer|-16 PEA Buffer PEA Size|-16 PEA Size ; adresse WORD contenant size LDA Buffer CLC ADC #$8000 ; calcul adresse output buffer STA OutBuff STA VDF ; on sauve le buffer (lo) pour la suite LDA Buffer+2 ADC #$0000 STA OutBuff+2 STA VE1 PHA ; output buffer (hi) LDA OutBuff PHA ; output buffer (lo) PEA $8000 ; output buffer size _PackBytes BCC s4 BRL Error s4 PLA TAX AND #01 BEQ s5 PEA MSG90|-16 PEA MSG90 _WriteCString ; message erreur size impaire BRL Fin s5 TXA STA VD1 ; nb packed bytes STZ VD3 CLC ADC #$20 ; on ajoute 32 octets avant sauvegarde STA WriteSize ; sauve nb bytes packed pour WRITE STZ WriteSize+2 ; (hi=0) PEA MSGOK|-16 PEA MSGOK _WriteCString ; Packing OK JSL $E100A8 DC I2'$0014' ; commande CLOSE DC I4'CloseParms' PEA MSG4|-16 ; Message Encryptage PEA MSG4 _WriteCString ; ; on colle la palette à la fin LDA VD1 LDX #0000 CMP #0000 BPL suite1t DEX suite1t PHX PHA LDA VDF LDX VE1 JSL ADDITION PLA PLX STA VC5 STX VC7 PEA PALETTE|-16 PLA STA TEMP+2 PEA PALETTE PLA STA TEMP LDY #00 BB1 LDA [TEMP],Y STA [VC5],Y INY INY CPY #32 BNE BB1 ; début double encryptage par EOR ; D1 = nb Packed Bytes après Fonction PackBytes ; phase 1 : ascendante (inverse descendante) ; de 2 à D1 (D1 EOR ABCD) LDA #02 ; on part de 2 (0 non traité) STA VD7 LDA VD1 STA VDC DEC VD1 ; D1-2 DEC VD1 Boucle1 LDA VD7 LDX #0000 CMP #0000 BPL suite1 DEX suite1 PHX PHA LDA VDF LDX VE1 JSL ADDITION PLA PLX STA VC5 STX VC7 LDY #2 LDA [VC5],Y ; au max D1-2 + 2 = D1 ASL A EOR #$4321 STA VD9 LDA [VC5] EOR VD9 STA [VC5] INC VD7 INC VD7 LDA VD7 LDX #0000 CMP #0000 ; BPL suite2 DEX suite2 CMP VDC ; D1 (DONC D1-2 MAX traité) BNE suite3 CPX VD3 suite3 BEQ suite4 BRL Boucle1 suite4 LDA VDC LDX #0000 CMP #0000 BPL suite4b DEX suite4b PHX PHA LDA VDF LDX VE1 JSL ADDITION PLA PLX STA VC5 STX VC7 LDA [VC5] EOR #$ABCD STA [VC5] ; on fixe le dernier WORD DEC VD1 DEC VD1 ; VD1 passe à -4 ; phase 2 : descendante (inverse ascendante) ; de 0 à D1-2 (0 EOR EF01) LDA VD1 ; D1-4 STA VD7 Boucle2 LDA VD7 LDX #0000 CMP #0000 BPL suite5 DEX suite5 PHX PHA LDA VDF LDX VE1 JSL ADDITION PLA PLX STA VC5 STX VC7 LDA [VC5] ; DI-4 au max ASL A EOR #$4321 STA VD9 LDY #2 LDA [VC5],Y ; donc D1-4 + 2 = D1-2 ! EOR VD9 STA [VC5],Y DEC VD7 DEC VD7 LDA VD7 CMP #$FFFE ; VD7 = 0 inclus BEQ suite6 BRL Boucle2 suite6 LDA [VDF] ; on fixe le premier EOR #$EF01 ; WORD STA [VDF] ; fin encryptage PEA MSGOK|-16 PEA MSGOK _WriteCString ; OK encryptage PEA MSG6|-16 PEA MSG6 _WriteCString ; Message Sauvegarde JSL $E100A8 DC I2'$0001' DC I4'CreateParms'; commande CREATE JSL $E100A8 DC I2'$10' DC I4'OpenParms2' ; commande OPEN BCS Error LDA refnum4 ; ref du file ouvert STA refnum5 ; pour write STA refnum2 ; pour close LDA OutBuff STA WriteData LDA OutBuff+2 STA WriteData+2 ; Buffer Write JSL $E100A8 DC I2'$13' DC I4'WriteParms' ; commande WRITE BCS Error PEA MSGOK|-16 PEA MSGOK _WriteCString ; Save OK JSL $E100A8 DC I2'$14' DC I4'CloseParms' ; commande CLOSE BRA Fin Error PEA MSGERROR|-16 PEA MSGERROR _WriteCString Fin ANOP PEA MSG99|-16 PEA MSG99 _WriteCString ; message fin PHA ; space for result PEA $0000 ; pas d'echo _ReadChar PushWord MyID _DisposeAll ; ménage ! PushWord MyID _MMShutDown ; Memory Manager off _MTShutDown ; Miscellanous Tools off _TLShutDown ; Tools Locator off _Quit QuitParams BRK ADDITION CLC ADC 04,S STA 04,S TXA ADC 06,S STA 06,S RTL MyID DS 2 QuitParams DC I4'0' DC I'0000' MSG1 DC C'Resolution DEFI GS ON THE ROCKS 4 v0.5',I1'$8D,$8A' DC C'Ouverture Image : ',I1'0' MSG2 DC C'Allocation memoire : ',I1'0' MSG3 DC C'Lecture Image : ',I1'0' MSG4 DC C'Encryptage : ',I1'0' MSG5 DC C'Packing : ',I1'0' MSG6 DC C'Sauvegarde Image Packed et Encrypted : ',I1'0' MSG90 DC C'ERREUR : taille image packed non paire !',I1'$8D,$8A,0' MSG99 DC C'Appuyez sur une touche pour quitter...',I1'0' MSGOK DC C'OK',I1'$8D,$8A,0' MSGERROR DC C'ERREUR !',I1'0' OpenParms ANOP refnum DS 2 DC I4'FileName' DS 4 FileName DC I1'11' DC C'1/PICSIGNED' CloseParms ANOP refnum2 DS 2 ReadParms ANOP refnum3 DS 2 Buffer DS 4 NbBytes DS 4 DS 4 CreateParms ANOP DC I4'SaveFileName' DC I2'$E3' ; access code DC I2'$C2' ; type file ($C2 comme l'original) DC I4'0' ; aux type DC I2'2' ; storage type (sapling file) DC I2'0' ; création date DC I2'0' ; création time OpenParms2 ANOP refnum4 DS 2 DC I4'SaveFileName' DS 4 SaveFileName DC I1'11' DC C'1/PICRYPTED' WriteParms ANOP refnum5 DS 2 WriteData DS 4 WriteSize DS 4 DS 4 Size DC I2'$7D00' ; nb de bytes à packer (pixels only) OutBuff DS 4 PALETTE DC I1'$00,$01,$51,$09,$62,$0A,$62,$0B' DC I1'$73,$0B,$73,$0C,$84,$0D,$95,$0E' DC I1'$A6,$0F,$31,$08,$52,$0C,$00,$01' DC I1'$00,$04,$21,$07,$31,$09,$FF,$0F' END |
La routine de cryptage est loin d'être optimisée car j'ai surtout cherché à la calquer sur celle de décodage. Ce qui explique aussi le nom peu orthodoxe des variables utilisées (j'ai repris les adresses Page Zéro du code original). Pour le reste, l'essentiel est composé de l'appel aux différentes fonctions Tool Sets et ProDOS 16 (pour la gestion des fichiers). À ce sujet, j'ai limité l'utilisation des macros propres à ORCA/M pour la gestion de ces fonctions afin de garder un code source proche de celui obtenu après assemblage. Pour une utilisation sous Merlin 16, il faudra sans doute faire quelques adaptations.
À noter que le programme ne laisse pas le choix du nom des fichiers ni en entrée ni en sortie. Il faudra penser à les renommer en fonction des besoins.
Partie VI : signer l'image.
Pour la signature, j'ai commencé par utiliser Deluxe Paint II pour ajouter un petit gribouillis sur l'image. Puis, je me suis aperçu (tardivement) que la sauvegarde ne pouvait s'effectuer qu'au format propriétaire compressé. Il nous faut, je vous le rappelle, un format non compressé, le package et l'encodage se faisant par notre routine.
J'ai donc repris l'image sauvée avec Deluxe Paint sous PaintWorks Gold qui lui propose l'option "Sreen Format" lors de la sauvegarde. Bien évidemment, signer directement l'image sous PaintWorks marche aussi. Vous pouvez également éditer l'image avec votre logiciel favori puis la convertir ensuite avec les multiples utilitaires disponibles sous GS/OS faisant le job. Bref, je laisse l'artiste en vous s'exprimer librement. Ce qui compte par contre, c'est d'avoir une image finale non compressée et sauvée au format écran (donc avec l'organisation mémoire classique).
Et... je vous laisse découvrir le superbe résultat obtenu avec la première version a priori fonctionnelle de mon programme d'encodage. Pourtant, je vous garantie qu'avec l'image non retouchée, tout était OK, elle était ré-encryptée et affichée correctement. Pourquoi donc avec une image modifiée, nous subissons un problème évident de palette (alors que l'image utilise forcément celle par défaut) ? Y'aurait-il une vérification cachée quelque part ? Là encore, il m'a fallu un peu de temps pour comprendre le problème. Et il faut remonter un peu en arrière pour en trouver l'origine !
Explication : la routine de décodage des images de GS On The Rocks 4 décrypte les octets par groupe de deux. Elle travaille en 16 bits, donc sur des WORD. Les deux boucles principales de décryptage utilisent le taille de l'image cryptée (moins 32) comme valeur de base. Les compteurs de boucles étant incrémentés (ou décrémentés) de deux à chaque passage, si cette taille est paire, pas de problème, on aura bien exécution d'un nombre de boucles donné. C'est le cas avec l'image d'origine (qui fait 15866 octets). Avec une taille impaire par contre, les problèmes commencent. Les boucles de décodage deviennent infinies car les conditions de fin ne sont jamais vérifiées. Pour éviter ce type de plantage, j'avais donc intégré à ma routine de codage une détection sur la parité de la taille après PackedBytes de façon à ajouter artificiellement un octet supplémentaire si nécessaire, de façon à toujours obtenir une taille d'image paire.
Or, en ajoutant cet octet, j'ai engendré un autre problème touchant la palette intégrée au fichier (les fameux 32 octets de fin). L'octet supplémentaire ajouté après la partie "pixel" de l'image cryptée, décale d'autant la palette, modifiant totalement la correspondance des couleurs. D'où ce superbe bleu observé ci-dessus. On ne peut pas non plus ajouter l'octet supplémentaire à la fin du fichier car la taille de la partie cryptée (obtenue, je vous le rappelle, par taille fichier - 32) est alors faussée pour le décodage. Seule solution viable : avoir une vraie taille d'image Packed paire ! Cette taille dépendant directement de l'image à compresser, il faudra systématiquement vérifier si, après PackedBytes, la taille obtenue est correcte. La dernière version de mon code inclut cette vérification. Hélas, pour savoir si une image est OK , il n'y a pas d'autre solution que de lancer la routine et d'attendre le verdict.
Voici ce qu'affiche le programme de codage/package lorsque la taille ne lui convient pas ! Dans ce cas, l'image ne pourra pas être encodée. Il faudra en essayer une autre.
Exemple : cette image-ci donnera un résultat incorrect (c'est d'ailleurs celle à l'origine de l'effet bleuté vu précédemment).
Celle-ci non plus ne passera pas... Essayons de modifier légèrement la couleur, ou le positionnement de la signature. Méthode empirique obligatoire !
Bingo, nous avons un gagnant !
Il suffit maintenant de renommer le fichier PICRYPTED obtenu en PIC1 et de remplacer celui sur le disque GS On The Rocks 4 !
L'instant de vérité : le programme du défi décode cette fois parfaitement l'image... Vous remarquerez la subtile différence avec l'image d'au-dessus qui ne passait pas l'encodage. Au fait, finalement pas de test caché, pas de checksum fourbe, rien... Game Over !
Petit récapitulatif des outils utilisés pour mener à bien ce défi :
- GSBug version 1.6 (Apple Computer) et System Loader Dumper 4 (Greg Branche).
- ORCA/Disassembler 1.2 (Paul Elseth/Byte Works). Fait partie du package Opus ][ vendu par Syndicomm. Oui, continuer à vendre des softs d'il y a 30 ans plus de 80$ pour une version totalement dématérialisée, c'est limite abusé... Comme alternative, vous pouvez utiliser l'excellent The Flaming Bird Disassembler (Ferox/Phoenix Corporation).
- Block Warden 2.5 (Glen Bredon).
- Nifty Lift 3.4 (David A. Lyons) avec l'extension NiftyFile 0.99 (Jonah Stich).
- Deluxe Paint II 2.01 (Dan Silva/Electronic Arts) et PaintWorks Gold 1.0 (Luc Barthelet & Henri Lamiraux/Version Soft).
- ORCA/M Macro Assembler 2.1.0 (Byte Works). Alternative possible : Merlin 16 (Glen Bredon) disponible et utilisable gratuitement lui...
- Mon programme de codage/package en version 0.5 non optimisé, probablement bourré de fautes de conception mais qui a le mérite de sortir une image cryptée ! Disponible avec ses sources sur le disk de travail en fin d'article.
Pour trouver tous ces programmes (tout au moins ceux pour lesquels personne ne vous réclamera des sous), les sources habituelles (Asimov, What is the Apple IIGS, Mac GUI City, Apple IIGS France, Brutal Deluxe, etc.) sont vos amies. Pour les autres...
Et pour finir quelques remarques en guise de conclusion :
- Cet article n'a pas vocation à vous apprendre à maîtriser chaque utilitaire utilisé pour la résolution du défi. La plupart sont complexes et demandent une certaine habitude pour être exploités au maximum. Je n'ai d'ailleurs fait qu'effleurer leurs possibilités. Je vous invite à vous référer à leur doc respective pour en avoir un aperçu plus complet (et obtenir des éclaircissements sur certaines commandes utilisées si besoin est).
- De même, certaines étapes n'ont pas été détaillées ou même mentionnées (manipulations de disquettes, changements de prefix, modifications de file type et bien sûr toutes les phases de tracing sous GSBug avec suivi des adresses mémoire, etc.). Le but était surtout de proposer un aperçu général du processus pour mener à bien le défi plutôt qu'un véritable mode d'emploi à suivre point par point.
- Attention aux infos trouvées dans les divers ouvrages parus sur le GS. J'ai perdu beaucoup de temps sur la fonction PackBytes car le type d'un des arguments référencés était incorrect ! En cas de doute, recoupez les infos et utilisez notamment les Toolbox Reference signés Apple comme juge de paix !
- Aussi curieux que cela puisse paraître, ma petite expérience de la programmation et de la bidouille sous Windows aura été un vrai plus pour aborder ce défi (et me servira sans doute par la suite). Avec les Tool Sets, la programmation GS ressemble beaucoup à la programmation sous un OS moderne. Et par extension, on retrouve à peu près les même méthodes pour aborder le côté sombre de la force...
- Bien qu'ayant travaillé essentiellement sous ActiveGS, j'ai pris soin de n'utiliser que des techniques ou des softs typiquement GS pour chacune des étapes. Avoir recours par exemple à Ciderpress ou à un éditeur Hexa aurait simplifié et accéléré les choses certes mais était totalement hors défi. Par contre, il est bien évident qu'un émulateur (en vitesse illimité) permet de gagner beaucoup de temps pour les boot du système. Se retrouver sous environnement ORCA en moins de 10 secondes a été un gros plus lors de la mise au point du programme de codage par exemple.
- Quant au défi en lui-même, merci (avec quelques décennies de retard) à Gronk et à son équipe pour ce sympathique challenge. Même si dans l'absolu, il était relativement simple à résoudre, à chaque étape, il m'aura posé un problème spécifique. Une bonne façon de défricher le terrain pour la suite...
Téléchargements :
- GS On The Rocks 4 (original). N'oubliez pas de taper lsd pour passer le piège à con.
- GS On The Rocks 4 (signé par mes soins).
- Et comme toujours le disque de travail contenant les différentes images, le programme de codage (appelé test) et son code source (en différentes versions).
"Quelle délivrance d'appuyer sur le bouton Publier et d'en avoir enfin fini avec cet article" (blogueur anonyme)
envoyé le 07-12-2012 à 17 h 13 min
je ne regarde pas je ne regarde pas, je vais faire tout seul je vais faire tout seul
envoyé le 07-12-2012 à 17 h 40 min
🙂
Je pense que tu iras sans doute beaucoup plus vite que moi !
Après coup, n’hésite pas à revenir poster ici tout ce que tu auras fait différemment, les outils utilisés, etc.
Toute info supplémentaire sera la bienvenue !