Suivez-nous sur X

|
|
|
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
|
|
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
|
|
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
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
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 :
Vous obtiendrez une recopie du contenu (dump) en hexadécimal de la commande List, qui devrait ressembler à celui reproduit ci-dessous :
- "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 <executable> <greffe> <résultat> */
/* */
/* 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;i<compteur;i++)
{
*Target++=*Source++; /* Tranfert des longueurs de segment de la source */
}
/* ici Source pointe sur le premier identificateur de la source */
pointeur=Greffe+7;
/******* TRAITEMENT DE LA GREFFE ********/
while(*pointeur != 0x3F2) /* Fin d'un segment ? */
{
switch(*pointeur) /* Quel segment ? */
{
case 0x3EC:
pointeur=Reloc(pointeur,0);
printf("Relogement 32 bits de la greffe effectué\n");
break;
case 0x3ED:
pointeur=Reloc(pointeur,0);
printf("Relogement 16 bits de la greffe effectué\n");
break;
case 0x3EE:
pointeur=Reloc(pointeur,0);
printf("Relogement 8 bits de la greffe effectué\n");
break;
case 0x3E9:
pointeur=Code(pointeur);
printf("Transfert du code de la greffe effectué\n");
break;
case 0x3F0:
pointeur=Symbol(pointeur);
printf("Table de symboles de la greffe traitée\n");
break;
case 0x3F1:
pointeur=Debug(pointeur);
printf("Table de debogage de la greffe traitée\n");
break;
default:
*Target=*pointeur++;
break;
}
}
*Target++=*pointeur++;
/********* TRAITEMENT DE LA SOURCE ********/
printf("\n\n*** Traitement de la source ***\n\n");
/* *Source 1 0x0FFFFFFF c'est pour filtrer les segments mémoire Chip etc ... */
while((Test=(*Source & 0x0FFFFFFF)) != 0x3F2 || compteur !=0)
{
switch(Test)
{
case 0x3EA:
Source=Data(Source);
printf("\nHunk Data traité\n");
compteur -=1;
break;
case 0x3EB:
Source=Bss(Source);
printf("\nHunk Bss traité\n");
compteur -=1;
break;
case 0x3EC:
Source=Reloc(Source,1);
printf("Relogement 32 bits effectué\n");
break;
case 0x3ED:
Source=Reloc(Source,1);
printf("Relogement 16 bits effectué\n");
break;
case 0x3EE:
Source=Reloc(Source,1);
printf("Relogement 8 bits effectué\n");
break;
case 0x3E9:
Source=Code(Source);
printf("\nHunk Code traité\n");
compteur -=1;
break;
case 0x3F0:
Source=Symbol(Source);
printf("Hunk Symbol traité\n");
break;
case 0x3F1:
Source=Debug(Source);
printf("Hunk debug traité\n");
break;
case 0x3F5:
printf("\nAttention, la source contient des Overlays\n");
printf("Le résultat n'est pas garanti...\n");
Source=Overlay(Source);
printf("Overlay traité\n");
break;
default:
*Target++=*Source++;
break;
}
}
*Target++=*Source;
Write(target,Fixe,(int)Target-(int)Fixe); /* Et voilà ! */
printf("\n\nOPERATION TERMINEE F Mazué vous salue\n\n");
CleanUp();
}
VOID CleanUp()
{
if(FixeSource) FreeMem(FixeSource,InfoSource->fib_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<limite;i++)
{
*Target++=*pointeur++;
}
if(*pointeur==0)
{
*Target++=*pointeur++; /* pour le 0 final */
break;
}
}
return(pointeur);
}
LONG *Code(LONG *pointeur)
{
int limite,i;
*Target++=*pointeur++; /* 0x3E9 est mis*/
limite = *pointeur; /* nombre de longs mots à copier */
*Target++=*pointeur++; /* Ce nombre lui-même copié */
for(i=0;i<limite;i++)
{
*Target++=*pointeur++;
}
return(pointeur);
}
LONG *Debug(LONG *pointeur)
{
int limite,i;
*Target++=*pointeur++; /* 0x3F1 est mis */
limite = *pointeur; /* nombre de longs mots à copier */
*Target++=*pointeur++; /* Ce nombre lui-même copié */
for(i=0;i<limite;i++)
{
*Target++=*pointeur++;
}
return(pointeur);
}
LONG *Symbol(LONG *pointeur)
{
int perpet=1;
LONG Test;
*Target++=*pointeur++; /* 0x3F0 est mis */
while(perpet)
{
switch(Test=(*pointeur & 0x0FFFFFFF))
{
case 0x3E9: /* cf article */
case 0x3EA:
case 0x3EB:
case 0x3EC:
case 0x3ED:
case 0x3EE:
case 0x3F1:
case 0x3F2:
case 0x3F5:
return(pointeur);
default:
*Target++=*pointeur++;
break;
}
}
}
LONG *Data(LONG *pointeur)
{
int limite,i;
*Target++=*pointeur++; /* 0x3EA est mis*/
limite = *pointeur; /* nombre de longs mots à copier */
*Target++=*pointeur++; /* Ce nombre lui-même copié */
for(i=0;i<limite;i++)
{
*Target++=*pointeur++;
}
return(pointeur);
}
LONG *Bss(LONG *pointeur)
{
*Target++=*pointeur++; /*0x3EB mis */
*Target++=*pointeur++; /* Longueur mise */
return(pointeur);
}
LONG *Overlay(LONG *pointeur)
{
int limite,i;
*Target++=*pointeur++; /* 0x3F5 est mis*/
limite = *pointeur; /* nombre de longs mots à copier */
*Target++=*pointeur++; /* Ce nombre lui-même copié */
*Target++=*pointeur++; /* Niveau d'Overlay */
for(i=0;i<limite;i++)
{
*Target++=*pointeur++;
}
*Target++=*pointeur++; /* fin d'overlay */
return(pointeur);
}
|
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 ....
|
Mise à jour de mai 2025 : une archive contenant le listing adapté à vbcc, et avec l'exécutable
compilé par vbcc, a été réalisée par Yann-Gaël Guéhéneuc et est disponible sur
obligement.free.fr/files/antgrafting.lha.
|