Obligement - L'Amiga au maximum

Vendredi 24 mai 2019 - 04:53  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Liens
 · Liste jeux Amiga
 · Quizz
 · Téléchargements
 · Trucs et astuces


Articles

 · Actualité (récente)
 · Actualité (archive)
 · Comparatifs
 · Dossiers
 · Entrevues
 · Matériel (tests)
 · Matériel (bidouilles)
 · Points de vue
 · En pratique
 · Programmation
 · Reportages
 · Tests de jeux
 · Tests de logiciels
 · Tests de compilations
 · Articles divers

 · Articles in english
 · Articles en d'autres langues


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Logiciels
 · Jeux
 · Scène démo
 · Divers


Jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Partenaires

Annuaire Amiga

Amedia Computer

Relec

Hit Parade


Contact

David Brunet

Courriel

 


Programmation : C - Le greffeur
(Article écrit par Frédéric Mazué et extrait d'Amiga News Tech - février 1992)


Votre pantagruélique serviteur revient faire des siennes dans les pages de votre cher ANT, suite à une absence prolongée pour cause de programmation intense d'utilitaires déments.

Et puisque l'on parle d'utilitaires, celui que je vous propose aujourd'hui, n'est pas dément, mais presque. Il s'agit d'un programme capable de greffer des programmes sur des programmes. Autrement dit, vous disposez d'un excellent programme, mais vous regrettez que lors de son lancement, telle ou telle fonction ne soit pas accomplie : il vous suffit de programmer vous-même ladite fonction et de la greffer sur le programme fautif grâce à l'utilitaire du mois.

Restez sages !

Je vois venir certains petits malins, qui ont immédiatement compris qu'il leur sera possible de se servir de cet utilitaire (au demeurant génial) afin de répandre d'odieux virus coquilles tout partout. Oui cela est possible, mais je maudis les coupables jusqu'à la 52e incarnation. Voilà !

Comment cela se peusse ?

Puisque l'Amiga est multitâche, n'en déplaise aux bidouilleurs de démos (que je maudis cette fois jusqu'à la 104e incarnation), il n'est pas possible de savoir à l'avance à quelle adresse mémoire sera chargé tel ou tel programme.

Pour résoudre cette difficulté, les développeurs de l'Amiga ont employé la solution suivante : un programme exécutable n'est pas sauvé tel quel sur disquette. Ainsi, toutes les adresses de saut ne sont pas réelles mais comptées à partir de l'adresse 0. Lors du chargement du programme en mémoire, AmigaDOS se charge d'ajouter à chaque adresse répertoriée, l'adresse absolue de chargement et le programme se trouve relogé et donc en état de fonctionner. Bien entendu, pour que cela se fasse correctement, le fichier binaire contient, en plus du code lui-même, toute une pagaille de renseignements que nous verrons plus loin.

Ce n'est pas tout. En plus de ceci, et pour des raisons analogues (bonne gestion de la mémoire), un programme peut être constitué en seul bloc ou en plusieurs. Ce sont les segments ou "hunks".

Pour y voir clair, entrez sous CLI la commande suivante :

type c:list opt h

Vous obtiendrez un dépôt (dump) héxa de la commande List, qui devrait ressembler à celui reproduit ci-dessous :

C
  • "000003F3" signifie que l'on a à faire à un programme exécutable.
  • "00000000" ne signifie rien d'important.
  • "00000002" signifie que le programme est en deux segments.
  • "00000000" est le numéro du premier segment chargé.
  • "00000001" est le numéro du second segment chargé. Donc, il y a deux segments à charger, puisque qu'en informatique 0 plus 1 égale 2, comme chacun sait.
  • "0000004F" est la longueur en mots longs du premier segment.
  • "00000961" est la longueur en mots longs du second segment.
  • "000003E9" est un identificateur particulier.
Si le programme comportait un nombre plus élevé de segments, trois par exemple, on aurait 00000003 à la place de 00000002, suivi de 00000000, 00000001 et 00000002, puis trois mots longs donnant leur longueur respective, et ainsi de suite. Vous avez compris ? Je continue.

Pour parvenir à nos fins, il suffit de connaître tous les identificateurs existants (ou plutôt ceux utilisés dans un fichier exécutable) et de savoir comment chaque segment est structuré.

Nous allons donc passer tout ça en revue, sachant que les renseignements proviennent de la Bible de l'Amiga, donc qu'il vaut mieux se méfier. Mais c'est le seul livre à ma connaissance où il soit possible de trouver ces renseignements, depuis que Commodore a décrété que l'AmigaDOS Technical Manual était devenu obsolète. Il fallait oser...

Revue des troupes

$3E9 hunk_code : précède une partie de programme susceptible de fonctionner une fois relogé en mémoire. L'identificateur $3E9 est suivi du nombre de mots longs de cette partie de programme, puis du code lui-même.

$3EA hunk_data : précède une partie de "datas" (données initialisées). Le reste est comme ci-dessus.

$3EB hunk_bss : précède une zone BSS. Il s'agit d'une zone de mémoire sans valeur initiale particulière que le système alloue dynamiquement avant de la mettre à disposition du programme. L'identificateur $3EA n'est donc suivi que de la taille de cette zone, exprimée en mots longs.

$3EC hunk_reloc32 : précède la fameuse table de relogement. Ceci nous intéresse de près. Derrière $3EC se trouvent le nombre d'offsets à corriger, le numéro de segment concerné et enfin, les offsets eux-mêmes. Ces informations se répètent autant de fois que nécessaire (jamais plus souvent qu'il n'y a de segments !) et la fin de la table est marquée par un nombre d'offsets égal à 0.

Pour greffer un programme sur un autre, il suffit donc de modifier l'en-tête afin de déclarer la présence du segment supplémentaire, puis d'incrémenter de 1 chaque numéro de segment concerné par un relogement. C'est aussi simple que cela.

$3ED hunk_relocl6 et $3EE hunk_reloc8 : ces deux identificateurs sont tout à fait similaires au précédent, si ce n'est qu'ils concernent des relogements sur 16 ou 8 bits respectivement. Les tables elles-mêmes sont toujours constituées de mots longs.

$3F0 hunk_symbol : précède une table de symboles susceptibles d'intéresser un débogueur. D'après la Bible de l'Amiga, cette table de mots longs est conclue par un mot long nul. Je suis désolé, mais j'ai vu de mes yeux des tables de symboles conclues par deux mots longs nuls. Mais la Bible de l'Amiga sans erreurs ne serait pas la Bible...

$3F1 hunk_debug : dans cette table peut être mis à peu près n'importe quoi qui peut aussi intéresser un débogueur en tant qu'informations supplémentaires à celles contenues dans "hunk_symbol". Voyez par exemple ce qu'en fait le Lattice (pardon le SAS) lorsque, vous compilez et reliez avec les options de débogage activées. $3F1 est suivi du nombre de mots longs constituants la table, puis des longs mots eux-mêmes. Aucun format n'est donc imposé.

$3F3 hunk_header : c'est le "mot de passe" qui, placé en début de fichier, assure le DOS qu'il a bien à faire à un programme exécutable.

Y'en a d'autres

Il existe encore d'autres identificateurs, qui concernent les fichiers objets et les overlays. Le programme que je vous propose devrait pouvoir greffer sans problème sur un programme comportant des overlays, mais je n'ai pas eu le temps d'essayer (bon d'accord, je n'ai pas eu le courage).

Comment écrire la greffe

C'est très simple. Il faut l'écrire en assembleur avec un macro-assembleur (SEKA ist streng verboten !), en deux segments dont un bidon (Cf. exemples), sauver les registres au début, les récupérer puis sauter avec un JMP dans le segment bidon.

Et ça marche

Parfaitement. J'ai même greffé un programme sur Excellence!, qui "pèse" tout de même la bagatelle de 221 segments. Lorsque vous avez greffé un programme, rien ne vous empêche, si ça vous amuse, de le greffer une fois de plus ou d'en greffer un autre, il n'y a pas de limite.

Deux exemples

Le premier : Cls. Vous pouvez le greffer sur une commande CLI quelconque, List par exemple, en procédant comme suit :

Greffeur c:List QuelquePart:Cls Ram:SuperList

Vous obtenez en RAM: l'exécutable SuperList. Si vous lancez ce programme, vous avez un effacement du CLI courant avant le listage du répertoire.

Le second : CLI_Regs. Avez-vous essayé de tracer sous Monam2 une commande CLI telle que List ? Oui ? Alors vous avez constaté que cela n'était pas possible, car les commandes CLI utilisent le contenu des registres transmis par le CLI. Greffez donc CLI_Regs sur vos commandes CLI et vous pourrez les tracer ! Il est vrai que cela plantera (beaucoup) plus tard au cours du traçage, mais pour une autre raison... que je vous laisse élucider.

Ces deux exemples sont sur notre disquette ANT 30 et en fin d'article.

Ça marche toujours ?

Non, certains programmes, comme par exemple Deluxe Paint III, ont des segments trafiqués. En ce cas, le greffeur le signale et essaie quand même, mais vous aurez presque à coup sûr un gourou lorsque vous essaierez le résultat (c'est pas ma faute !).

Le listing

Écrit en C, il ne présente aucune difficulté de compréhension si vous maîtrisez convenablement les pointeurs, sinon bonsoir, je suis fatigué.

Listing 1 : Greffeur.c

/******************************************************************/
/*                                                                */
/* Programme: Greffeur.c                                          */
/*                                                                */
/* Auteur: L'incroyable HUNK F Mazué pour ANT le 04/11/1991       */
/*                                                                */
/* Fontion: Greffer un éxécutable écrit par vous sur n'importe    */
/*          quel éxécutable de votre choix                        */
/*                                                                */
/* Remarque: Vous êtes vivement prié de ne pas vous servir de cet */
/*           utilitaire de grande classe afin de répandre         */
/*           d'odieux virus coquilles tout partout....            */
/*                                                                */
/* Utilisation: Greffeur            */
/*                                                                */
/* Ecrit en SAS 5.10                                              */
/*                                                                */
/* Compiler et linker d'un seul coup par:                         */
/*                         lc -d0 -m0t -v -y -Ln greffeur         */
/*                                                                */
/* VIVE LES POINTEURS !                                           */
/*                                                                */
/******************************************************************/
     

#include 
#include 
#include 
#include 
#include 

struct FileLock *LockGreffe;  /* because longword alignment */
struct FileLock *LockSource;
struct FileLock *LockTarget;

struct FileInfoBlock *InfoGreffe;  
struct FileInfoBlock *InfoSource;
struct FileInfoBlock *InfoTarget;

LONG *Source;
LONG *Greffe;
LONG *Target;
LONG *Fixe;
LONG *FixeSource;
LONG *FixeGreffe;

BPTR source;
BPTR greffe;
BPTR target;

VOID CleanUp();
LONG *Reloc(LONG *,int);
LONG *Code(LONG *);
LONG *Debug(LONG *);
LONG *Symbol(LONG *);
LONG *Data(LONG *);
LONG *Bss(LONG *);
LONG *Overlay(LONG *);


VOID main(int argc, char **argv)
{
   
   int i;
   int nbr_hunk = 0;
   int compteur;
   LONG *pointeur;
   LONG Test;
   
   printf("\nGreffeur de Programmes !!!\n");
   printf("Par F Mazué, L'incroyable HUNK !\n");
   
   if(argc != 4)
   {
      printf("La ligne doit avoir comme paramètres 3 noms de fichiers\n\n");
      printf("D'abord le fichier source qui doit être éxécutable\n");
      printf("Ensuite le fichier à greffer sur le fichier source\n");
      printf("             ce fichier doit aussi être éxécutable\n");
      printf("Enfin le fichier cible\n");
      CleanUp();
   }
   
   InfoGreffe=(struct FileInfoBlock *)
   AllocMem(sizeof(struct FileInfoBlock),MEMF_PUBLIC|MEMF_CLEAR);
   if(!InfoGreffe)
   {
      CleanUp();
   }
   
   InfoSource=(struct FileInfoBlock *)
   AllocMem(sizeof(struct FileInfoBlock),MEMF_PUBLIC|MEMF_CLEAR);
   if(!InfoSource)
   {
      CleanUp();
   }
   
   InfoTarget=(struct FileInfoBlock *)
   AllocMem(sizeof(struct FileInfoBlock),MEMF_PUBLIC|MEMF_CLEAR);
   if(!InfoTarget)
   {
      CleanUp();
   }
   
   LockSource = (struct FileLock *)Lock(argv[1],SHARED_LOCK);
   Examine(LockSource,InfoSource);
   
   if(InfoSource->fib_DirEntryType >0)
   {
      printf("Nom de fichier ou de répertoire incorrect\n");
      CleanUp();
   }
   
   LockGreffe = (struct FileLock *)Lock(argv[2],SHARED_LOCK);
   Examine(LockGreffe,InfoGreffe);
   
   if(InfoGreffe->fib_DirEntryType >0)
   {
      printf("Nom de fichier ou de répertoire incorrect\n");
      CleanUp();
   }
   
   
   Source=(LONG *)AllocMem(InfoSource->fib_Size,MEMF_PUBLIC|MEMF_CLEAR);
   if(!Source)
   {
      CleanUp();
   }
   FixeSource=Source;
   source=Open(argv[1],MODE_OLDFILE);
   Read(source,Source,InfoSource->fib_Size);
   
   Greffe=(LONG *)AllocMem(InfoGreffe->fib_Size,MEMF_PUBLIC|MEMF_CLEAR);
   if(!Greffe)
   {
      CleanUp();
   }
   FixeGreffe=Greffe;
   greffe=Open(argv[2],MODE_OLDFILE);
   Read(greffe,Greffe,InfoGreffe->fib_Size);
   
   Target=(LONG *)AllocMem(InfoGreffe->fib_Size+InfoSource->fib_Size,
   MEMF_PUBLIC|MEMF_CLEAR);
   if(!Greffe)
   {
      CleanUp();
   }
   
   Fixe=Target;
   
   target=Open(argv[3],MODE_NEWFILE);
   if(!target)
   {
      CleanUp();
   }
   
   LockTarget = (struct FileLock *)Lock(argv[3],ACCESS_WRITE);
   
   if(*Source != 0x3F3 || *Greffe != 0x3F3)
   {
      printf("\nIl faut des fichiers éxécutables !!\n");
      CleanUp();
   }
   
   pointeur = Greffe+2;
   
   if(*pointeur !=2)
   {
      printf("\nLa Greffe ne rempli pas les conditions nécéssaires\n");
      printf("Opération terminée... désolé\n");
   }
         
   *Target++=*Source++;            /* 3F3 */
   *Target++=*Source++;            /* Nom du HUNK (à priori NULL) */
   
   nbr_hunk=*Source;
   printf("\nLe programme Source contient %d HUNKS\n",nbr_hunk);
   
   *Source +=1;                    /* un HUNK de plus */
   *Target++=*Source++;            /* Nbre de HUNK placé */
   *Target++=*Source++;            /* N° du premier HUNK chargé */
   
   compteur = *Source;
   compteur +=1;
   
   if(compteur != nbr_hunk)
   {
      printf("\n le nombre de HUNKS de la source et le nombre de HUNKS\n");
      printf("effectivement chargés ne se correspondent pas !\n");
      printf("\nOn essaie quand même !  [RETURN]\n");
      getchar();
   }
   
   
   *Source +=1;              /* Dernier HUNK à charger plus 1 */
   *Target++=*Source++;
   
   
   /* ici Source pointe sur le premier identificateur de la source */
   
   pointeur=Greffe+5;        /* Taille du HUNK à ajouter */
   
   printf("\nLa Taille du Hunk greffé est %d octets\n",*pointeur*4);
   
   *Target++=*pointeur++;
   
   for(i=0;ifib_Size);  
   if(FixeGreffe) FreeMem(FixeGreffe,InfoGreffe->fib_Size);
   
   if(LockSource) UnLock(LockSource);
   if(LockGreffe) UnLock(LockGreffe);
   if(LockTarget) UnLock(LockTarget);
   
   if(source) Close(source);
   if(greffe) Close(greffe);
   if(target) Close(target);
   
   if(InfoSource) FreeMem(InfoSource,sizeof(struct FileInfoBlock));
   if(InfoGreffe) FreeMem(InfoGreffe,sizeof(struct FileInfoBlock));
   if(InfoTarget) FreeMem(InfoTarget,sizeof(struct FileInfoBlock));
   
   exit(0);  /* Fin du match ! */
}
           

LONG *Reloc(LONG *pointeur,int deplacement)
{
   int i;
   int encore=1;
   int limite;
      
   *Target++=*pointeur++;  /* 0x3EC  est mis*/
   
   while(encore)
   {
      
      limite=*pointeur;
      
      *Target++=*pointeur++; /* nbr de correction placé */
      *pointeur +=deplacement;          /* HUNK corrigé */
      *Target++=*pointeur++; /* et placé */                   
      
      for(i=0;i

Listing 2 : Cls.s

;
;                 *** CLS ***
;
; Programme d'effacement de la fenetre
; CLI ou SHELL courante par F MAZUE


; offsets d'EXEC.LIBRARY

OldOpenLibrary = -408   ;une fois n'est pas coutume !
CloseLibrary   = -414


; offsets de DOS.LIBRARY

Output         = -60
Write          = -48


 section 1,code

 movem.l d0-d7/a0-a6,-(sp)

 tst.l d0 

 beq erreur
 
 move.l $04,a6    
 lea dosname(pc),a1       
 jsr OldOpenLibrary(a6) 
 beq erreur             
 
 move.l d0,a6           
 
 jsr Output(a6)         

 lea debut(pc),a0       
 move.l a0,d2           
                          
 moveq #(fin-debut),d3  

 jsr Write(a6)          
 
 move.l a6,a1           
 move.l $04,a6          
 jsr CloseLibrary(a6)   
 
erreur
 
 movem.l (sp)+,d0-d7/a0-a6
 jmp suite
 
 
dosname
 dc.b 'dos.library',0
 even

debut
 dc.b $9b,";H"           ; ATTENTION, respecter les majuscules !
 dc.b $9b,"J"  
fin 

 section 2,code
suite
 nop 

Listing 3 : CLI_Regs.s

******************************************************************
*                                                                *
* prog: reg_cli.s                                                *
*                                                                *
* Auteur: F Mazué pour ANT le 04/11/1991                         *
*                                                                *
* Programme qui initialise les registres A2,A5,A6 afin qu'il     *
* soit possible de tracer sous le debogueur de votre choix la    *
* commande CLI sur laquelle il sera greffé.                      *                                   *
*                                                                *
* Necessite le programme 'Greffeur' du même auteur               *
*                                                                *
******************************************************************

 incdir "include/"          ;mettez ici votre path personnel
 include "exec/exec.i"
 include "exec/exec_lib.i"
 include "exec/execbase.i"
 include "libraries/dos.i"
 include "libraries/dosextens.i"
 
 
 section 1,code
 
 movem.l d0/a0,-(sp)   ; Obligatoirement INDISPENSABLE !
 
 move.l 4,a6           ; execbase
 move.l LibList(a6),a0
 lea dosname,a1
 CALLEXEC FindName     ; chercher et trouver la dos.library et 
                       ; obtenir la valeur de DOSBASE
                       ; c'est dans ce cas une solution préférable
                       ; à ouvrir puis fermer la librairie
                       
 
 move.l d0,a0          ; cf RMK 'libraries/dosextens.h (ou .i)
 move.l dl_A2(a0),a2   ;
 move.l dl_A5(a0),a5   ;
 move.l dl_A6(a0),a6   ;
 
 movem.l (sp)+,d0/a0
 jmp dummy
 
dosname DOSNAME
 even
 
 section 2,code
dummy
 rts                   ; n'importe quoi ou autre chose ....


[Retour en haut] / [Retour aux articles]