Obligement - L'Amiga au maximum

Mercredi 24 avril 2024 - 04:15  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

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

Articles in english


Réseaux sociaux

Suivez-nous sur X




Liste des 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,
ALL


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


Galeries

Menu des galeries

BD d'Amiga Spécial
Caricatures Dudai
Caricatures Jet d'ail
Diagrammes de Jay Miner
Images insolites
Fin de jeux (de A à E)
Fin de Jeux (de F à O)
Fin de jeux (de P à Z)
Galerie de Mike Dafunk
Logos d'Obligement
Pubs pour matériels
Systèmes d'exploitation
Trombinoscope Alchimie 7
Vidéos


Téléchargement

Documents
Jeux
Logiciels
Magazines
Divers


Liens

Associations
Jeux
Logiciels
Matériel
Magazines et médias
Pages personnelles
Réparateurs
Revendeurs
Scène démo
Sites de téléchargement
Divers


Partenaires

Annuaire Amiga

Amedia Computer

Relec


A Propos

A propos d'Obligement

A Propos


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 une recopie du contenu (dump) en hexadécimal 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 de décalages à corriger, le numéro de segment concerné et enfin, les décalages 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 de décalages é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 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 (ce n'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       */
/*                                                                */
/* Fonction: 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 <exec/types.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <stdio.h>

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 trois 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 segment (à priori NULL) */
   
   nbr_hunk=*Source;
   printf("\nLe programme Source contient %d HUNKS\n",nbr_hunk);
   
   *Source +=1;                    /* un segment de plus */
   *Target++=*Source++;            /* Nbre de segment placé */
   *Target++=*Source++;            /* N° du premier segment chargé */
   
   compteur = *Source;
   compteur +=1;
   
   if(compteur != nbr_hunk)
   {
      printf("\n le nombre de segmentS 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 segment à charger plus 1 */
   *Target++=*Source++;
   
   
   /* ici Source pointe sur le premier identificateur de la source */
   
   pointeur=Greffe+5;        /* Taille du segment à ajouter */
   
   printf("\nLa Taille du segment 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;          /* segment 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


; décalages d'EXEC.LIBRARY

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


; décalages 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]