==Phrack Inc.==
Volume 0x0b, Issue 0x3f, Phile #0x09 of 0x14
|=------=[ Embedded ELF Debugging : the middle head of Cerberus ]=------=|
|=----------------------------------------------------------------------=|
|=------------=[ The ELF shell crew <elfsh@devhell.org> ]=--------------=|
|=----------------------------------------------------------------------=|
|=--------------=[ Traduit par TboWan pour arsouyes.org ]=--------------=|
|=----------------------------------------------------------------------=|
I. Introduction au déboguage endurci
A. Travaux précédents & limites
B. Au delà de PaX et ptrace
C. Amélioration de l'interface
II. Air de jeu du déboguage embarqué
A. Injection dans un processus
B. Alternance de scripting ELF sur disque et en mémoire
C. Déboguage proprement dit : dumping, backtrace, breakpoints
D. Génération d'analyseurs dynamiques
III. De meilleures redirections ELF multi-architecture
A. CFLOW : redirection de fonction statique résistante à PaX
B. Revision de la technique ALTPLT
C. Technique ALTGOT : Le complément RISC
D. Technique EXTPLT : post-liaison de fonction inconnues
E. Algorithmes compatibles IA32, SPARC32/64, ALPHA64, MIPS32
V. Déboguage contraint
A. relocalisation ET_REL en mémoire
B. Relocalisation ET_REL dans ET_DYN
C. Extentions d'exécutables statiques
D. Algorithmes indépendants de l'architecture
VI. Passé et présent
VII. Remerciements
VIII. Références
-------[ I. Introduction au déboguage sous contraintes
Dans le passé, la manipulation de binaires s'est surtout
portee sur l'écriture de virus, le cracking, le déploiement
de backdoors, et la création de minuscules exécutable camouflés.
A part les outils du projet GNU comme binutils qui inclu le GNU
debugger [1] (qui s'intéresse plus à la portabilité qu'aux
fonctionnalités), aucune suite majeure de manipulation de binaire
n'existe. Depuis près de dix ans, le format ELF a été un succès
et la plupart des systèmes et distributions UNIX se basent dessus.
Cependant, les outils existants ne tirent pas avantages du
format et la plupart des logiciels de reverse engineering ou de
débugging sont soit trop spécifiques à une architecture, ou ne
font tout simplement pas attention à la structure du binaire pour
extraire ou rediriger des informations.
Depuis notre première publication sur le shell ELF, nous avons
tellement amélioré la nouvelle suite qu'il est maintenant temps
de publier un second article de fond mettant l'accent sur les
avancées dans les techniques ELF statiques et à la volée. Nous
allons expliquer en grands détails les 8 nouvelles
fonctionnalités de manipulations de binaires qui intersectent la
méthodologie existante du reverse engineering. Ces techniques
permettent un nouveau type d'approche du déboguage et l'extension
de logiciels proprietaire en environnement contraint.
Nous avons travaillé sur beaucoup d'architectures (x86, alpha,
sparc, mips) et nous nous sommes concentrés sur les environements
contraints où les binaires sont liés pour inclure des protections
de sécurité (comme les binaires endurcis de gentoo) dans les
machines protégées par PaX [2]. Ceci signifie que notre débugger
reste utilisable quand il est injecté dans un processus (local ou)
distant.
----[ A. Travaux précédents & limites
Dans la première partie de la série d'articles Cerberus, nous
avions introduit une nouvelle technique résidente appelée
l'injection ET_REL. Elle consistait en la compilation d'un source
en C vers un objet relogeable (.o) et de son injection dans un
programme sans les sources sur architecture INTEL et SPARC pour le
format ELF32.
Nous avons amélioré cette technique pour qu'à la fois les
binaires 32 et 64 bits soient supportés en ajoutant un support
pour alpha64 et sparc64. Nous avons aussi travaillé sur
l'architecture MIPS r5000 et fournissons maintenant un
debut d'environnement pour cette architecture. Nous
permettons maintenant aussi l'injection ET_REL dans les objets
ET_DYN (librairies partagées), notre technique est donc
complètement compatible avec des environnement complètement
randomisés comme ceux fournis par les Gentoo endurcies en
activant PaX sur le système d'exploitation Linux. Nous avons
aussi travaillé sur d'autres OS comme les BSD's, Solaris et HP-UX
et le code y a été compilé et testé régulièrement aussi.
Une innovation majeure de notre suite de manipulation de binaires
basée sur le débugging est l'absence d'utilisation de ptrace. Nous n'utilisons
pas de résidence dans le noyau comme [8] pour que même les
utilisateurs sans privilèges puisse l'utiliser. Enfin, l'environment n'est pas
dépendant d'API optionelles du système d'exploitation.
Les débogueurs existants se base sur l'appel système ptrace pour
que le processus de déboguage s'attache au programme débogué et
permette des manipulations internes variables du processus comme
la copie de mémoire, le placement de points d'arrets, le
backtracking, etc. Nous proposons les mêmes fonctionnalités sans
utiliser l'appel système.
Les raisons pour lesquelles nous n'utilisons pas ptrace sont
simples et multiples. Tout d'abord, beaucoup de systèmes endurcis
ou embarqués ne l'implémentent pas. C'est le cas des systèmes
basés sur grsecurity, des systèmes de production, ou les systèmes
sur téléphones dont le système d'exploitation est basé sur ELF
mais sans interface pour ptrace.
La deuxième raison de ne pas utiliser ptrace est la pénalité de
performance due à ce système de déboguage. Nous n'avons aucune
penalite de performances puisque notre débogueur se trouve dans le
même processus. Nous fournissons une technique complète en mode
utilisateur qui n'a pas besoin d'accéder à la mémoire noyau, elle
est donc utile dans toutes les étapes de test de pénétration
quand on veut déboguer un logiciel sensible dans un environnement
endurci et qu'aucune mise à jour du système n'est possible.
Nous permettons d'injecter du code C dans un nouveau fichier
binaire (dans une optique statique) ou dans un processus (en mode
à la volée) en utilisant une API unifiee. Quand cela est necessaire,
nous n'utilisons que des techniques ELF qui réduisent
les indices sur le disque et fonctionnent uniquement en mémoire.
----[ B. Au delà de PaX et ptrace
Un autre point clef de notre suite est la technique de
redirection grandement améliorée. Nous pouvons rediriger
quasiment tous les flots de contrôle, que le code soit ou non
dans le binaire lui même (technique CFLOW) ou dans une librairie
dont le binaire dépend (notre précédent travail présentais une
nouvelle technique d'hijacking, ALTPLT).
Nous avons amélioré cette technique et sommes passé à travers
plusieurs réécritures et fournissons à présent une
implémentation complète et indépendante de l'architecture. Nous
avons completé ALTPLT par une nouvelle technique appellée ALTGOT
qui rend possible l'hijacking d'une fonction puis l'appel de son
code original sur des machines Alpha, Mips et RISC.
Nous avons aussi créé une nouvelle technique appellée EXTPLT qui
autorise des fonctions inconnues (pour lesquelles il n'y a aucune
information de liaison du tout dans le fichier ELF) utilisant un
algorithme de post-liaison compatible avec les objets ET_EXEC et
ET_DYN.
----[ C. Amélioration de l'interface
Notre implémentation du débogueur ELF embarqué est un prototype.
Il faut comprendre qu'il est réellement utilisable mais est
encore au stade du développement. Tout le code présenté ici
fonctionne. Cependant, nous ne sommes pas omniscients et vous
pouvez rencontrer des problèmes. Dans ce cas, envoyez-nous un
email pour que nous puissions voir comment faire un patch.
La seule supposition que nous avons faite est la capacité de lire
le programme débogué. Dans tous les cas, vous pouvez aussi
déboguer en mémoire un binaire illisible sur disque en chargeant
le débogueur via la variable LD_PRELOAD. Cependant, les fonctionalites
du debugger sont plus interessantes quand le binaire est lisible.
Puisque le débogueur fonctionne dans le même espace d'adressage,
vous pouvez toujours lire la mémoire [3] [4] et restaurer le programme
binaire bien que nous ne l'avons pas encore implemente nous meme.
Le langage de communication central pour la suite Embedded ELF
Debugger (e2dbg) est le langage de script ELFsh. Nous l'avons
étendu avec des boucles et des contrôles conditionnels, un
support transparent pour les variables typées dynamiquement (comme en
python). La commande source (pour exécuter un script dans la
session courante) et les macros défines par l'utilisateur (la
commande scriptdir) sont aussi supportées.
Nous avons aussi développé une pile pair à pair appellée
Distributed Update Management Protocol - DUMP - qui permet de
lier plusieurs instances du débogueur via le réseau, mais ses
capacités ne font pas l'objet de cet article. Pour être complets,
nous supportons maintenant des sessions multi-utilisateurs (en
parallèle ou partagées) et les changements d'environnements en
utilisant la commande workspace.
Nous allons voir l'utilisation de cette interface dans la
première partie de ce papier. Dans la seconde partie, nous
donnons des détails techniques sur l'implémentation de ces
fonctionnalités sur de multiples architectures. La dernière
partie est dédiée aux avancées techniques les plus récentes que
nous avons développé ces dernières semaines sur le déboguage
contraint de binaires protégés. Le dernier algorithme du papier
est indépendant de l'architecture et constitue le coeur du moteur
de relocation dans ELFsh.
-------[ II. Air de jeu du déboguage embarqué
---[ A. Injection dans un processus
Nous avons différentes techniques d'injection du débogueur dans
un processus débogué. Ils vont donc partager le même espace
d'adressage et le débogueur sera capable de lire ses propres
données et code pour récupérer et changer les informations du
processus débogué.
Puisque ELF shell est composé a l'heure actuelle de 40 000 lignes
de code, nous n'avons pas voulu tout recoder pour permettre la
modification de binaires. Nous avons utilisé des astuces qui nous
ont permi de sélectionner si la modification sera faite en mémoire ou
sur le disque. Le truc consiste en 10 lignes de code. En considérant que
la macro PROFILE n'est pas obligatoire, voici le code exact :
(libelfsh/section.c)
========= BEGIN DUMP 0 =========
void *elfsh_get_raw(elfshsect_t *sect)
{
ELFSH_PROFILE_IN(__FILE__, __FUNCTION__, __LINE__);
/* sect->parent->base is always NULL for ET_EXEC */
if (elfsh_is_debug_mode())
{
sect->pdata = (void *) sect->parent->base + sect->shdr->sh_addr;
ELFSH_PROFILE_ROUT(__FILE__, __FUNCTION__, __LINE__, (sect->pdata));
}
if (sect)
ELFSH_PROFILE_ROUT(__FILE__, __FUNCTION__, __LINE__, (sect->data));
ELFSH_PROFILE_ERR(__FILE__, __FUNCTION__, __LINE__,
"Invalid parameter", NULL);
}
========= END DUMP 0 =========
Quelle est cette technique ? C'est assez simple : si le drapeau
du débogueur interne indique le mode statique (modification sur le
disque), alors, on retourne le pointeur vers le cache des données
interne d'ELFsh pour la section de données (ou de code) que nous
voulons accéder.
Cependant, si nous sommes en mode dynamique (modification du
processus), alors nous retournons juste l'adresse de la section.
Le débogueur fonctionne dans le même processus et pensera donc
que l'adresse retournée est un buffer en lecture (ou écriture).
Nous pouvons réutiliser toute l'API de ELF shell en faisant juste
attention à l'utilisation de la fonction elfsh_get_raw() quand
nous accédont au pointeur ->data. La sélection processus/sur
disque est transparente pour tout le code du debogueur/elfsh.
L'idée d'injecter du code directement dans le processus n'est pas
nouvelle et ça fait quelques années maintenant que nous
l'étudions. L'injection de code embarqué est aussi utilisée par
la communauté du cracking sous Windows [12] pour éviter la
plupart des protection contre le traçage et le déboguage, mais
nous n'avons vu nulle part d'implémentation d'un débogueur
complet, capable de telles fonctionnalités avancées comme
l'injection ET_REL ou la redirection de fonctions sur des
architectures multiples, à la fois sur disque et en mémoire, dans
un seul code.
---[ B. Alternance de scripts ELF sur disque et en mémoire
(y compris avec la linkmap)
Nous avons deux approches pour insérer le débogueur dans le
programmé débogué. Quand on utilise une entrée DT_NEEDED et qu'on
redirige le main débogué vers le point d'entrée main du débogueur
ET_DYN, nous injectons aussi differentes sections pour
permettre de faires des techniques essentielles comme EXTPLT.
Ceci sera expliqué en détail dans la prochaine partie.
La deuxième approche implique l'utilisation du LD_PRELOAD sur le
programme débogué et de placer des points d'arrets (soit via
l'opcode 0xCC sous x86, via l'opcode équivalent sur les autres
architectures, ou en redirigeant des fonctions, ce qui est
disponible sur beaucoup d'architectures et pour de nombreux
genres de fonctions dans cette suite).
Puisqu'une modification du binaire est nécessaire de toute
façons, nous allons utiliser la technique DT_NEEDED pour ajouter
les dépendances de librairies, et toutes les injections de
sections et redirections décrites dans cet article avant le
débogueur proprement dit.
La technique du LD_PRELOAD est particulièrement plus utile quand
vous ne pouvez pas lire le binaire que vous voulez déboguer. Le
choix du type d'injection du débogueur est laissé à
l'utilisateur, qui le fera en fonction des besoins du moment.
Voyons comment utiliser le débogueur embarqué et sa commande
"mode" qui fait la sélection en mémoire/sur disque. Ensuite, nous
imprimons la Table Globale d'Offset [NDT : Global Offset Table -
.got]. D'abord, la mémoire GOT est affichée, ensuite, nous
revenons en mode static et la GOT sur disque est affichée :
========= BEGIN DUMP 1 =========
(e2dbg-0.65) list
.::. Working files .::.
[001] Sun Jul 31 19:23:33 2005 D ID: 9 /lib/libncurses.so.5
[002] Sun Jul 31 19:23:33 2005 D ID: 8 /lib/libdl.so.2
[003] Sun Jul 31 19:23:33 2005 D ID: 7 /lib/libtermcap.so.2
[004] Sun Jul 31 19:23:33 2005 D ID: 6 /lib/libreadline.so.5
[005] Sun Jul 31 19:23:33 2005 D ID: 5 /lib/libelfsh.so
[006] Sun Jul 31 19:23:33 2005 D ID: 4 /lib/ld-linux.so.2
[007] Sun Jul 31 19:23:33 2005 D ID: 3 ./ibc.so.6 # e2dbg.so renamed
[008] Sun Jul 31 19:23:33 2005 D ID: 2 /lib/tls/libc.so.6
[009] Sun Jul 31 19:23:33 2005 *D ID: 1 ./a.out_e2dbg # debuggee
.::. ELFsh modules .::.
[*] No loaded module
(e2dbg-0.65) mode
[*] e2dbg is in DYNAMIC MODE
(e2dbg-0.65) got
[Global Offset Table .::. GOT : .got ]
[Object ./a.out_e2dbg]
0x080498E4: [0] 0x00000000 <?>
[Global Offset Table .::. GOT : .got.plt ]
[Object ./a.out_e2dbg]
0x080498E8: [0] 0x0804981C <_DYNAMIC@a.out_e2dbg>
0x080498EC: [1] 0x00000000 <?>
0x080498F0: [2] 0x00000000 <?>
0x080498F4: [3] 0x0804839E <fflush@a.out_e2dbg>
0x080498F8: [4] 0x080483AE <puts@a.out_e2dbg>
0x080498FC: [5] 0x080483BE <malloc@a.out_e2dbg>
0x08049900: [6] 0x080483CE <strlen@a.out_e2dbg>
0x08049904: [7] 0x080483DE <__libc_start_main@a.out_e2dbg>
0x08049908: [8] 0x080483EE <printf@a.out_e2dbg>
0x0804990C: [9] 0x080483FE <free@a.out_e2dbg>
0x08049910: [10] 0x0804840E <read@a.out_e2dbg>
[Global Offset Table .::. GOT : .elfsh.altgot ]
[Object ./a.out_e2dbg]
0x08049928: [0] 0x0804981C <_DYNAMIC@a.out_e2dbg>
0x0804992C: [1] 0xB7F4A4E8 <_r_debug@ld-linux.so.2 + 24>
0x08049930: [2] 0xB7F3EEC0 <_dl_rtld_di_serinfo@ld-linux.so.2 + 477>
0x08049934: [3] 0x0804839E <fflush@a.out_e2dbg>
0x08049938: [4] 0x080483AE <puts@a.out_e2dbg>
0x0804993C: [5] 0xB7E515F0 <__libc_malloc@libc.so.6>
0x08049940: [6] 0x080483CE <strlen@a.out_e2dbg>
0x08049944: [7] 0xB7E01E50 <__libc_start_main@libc.so.6>
0x08049948: [8] 0x080483EE <printf@a.out_e2dbg>
0x0804994C: [9] 0x080483FE <free@a.out_e2dbg>
0x08049950: [10] 0x0804840E <read@a.out_e2dbg>
0x08049954: [11] 0xB7DAFFF6 <e2dbg_run@ibc.so.6>
(e2dbg-0.65) mode static
[*] e2dbg is now in STATIC mode
(e2dbg-0.65) # Here we switched in ondisk perspective
(e2dbg-0.65) got
[Global Offset Table .::. GOT : .got ]
[Object ./a.out_e2dbg]
0x080498E4: [0] 0x00000000 <?>
[Global Offset Table .::. GOT : .got.plt ]
[Object ./a.out_e2dbg]
0x080498E8: [0] 0x0804981C <_DYNAMIC>
0x080498EC: [1] 0x00000000 <?>
0x080498F0: [2] 0x00000000 <?>
0x080498F4: [3] 0x0804839E <fflush>
0x080498F8: [4] 0x080483AE <puts>
0x080498FC: [5] 0x080483BE <malloc>
0x08049900: [6] 0x080483CE <strlen>
0x08049904: [7] 0x080483DE <__libc_start_main>
0x08049908: [8] 0x080483EE <printf>
0x0804990C: [9] 0x080483FE <free>
0x08049910: [10] 0x0804840E <read>
[Global Offset Table .::. GOT : .elfsh.altgot ]
[Object ./a.out_e2dbg]
0x08049928: [0] 0x0804981C <_DYNAMIC>
0x0804992C: [1] 0x00000000 <?>
0x08049930: [2] 0x00000000 <?>
0x08049934: [3] 0x0804839E <fflush>
0x08049938: [4] 0x080483AE <puts>
0x0804993C: [5] 0x080483BE <malloc>
0x08049940: [6] 0x080483CE <strlen>
0x08049944: [7] 0x080483DE <__libc_start_main>
0x08049948: [8] 0x080483EE <printf>
0x0804994C: [9] 0x080483FE <free>
0x08049950: [10] 0x0804840E <read>
0x08049954: [11] 0x0804614A <e2dbg_run + 6>
========= END DUMP 1 =========
Il y a beaucoup de choses à voir dans ce dump. Tout d'abord, vous
pouvez vérifier qu'il fait effectivement ce qu'il est supposé
faire en regardant les premières entrées de la GOT qui sont
réservées pour la linkmap et la fonction rtld dl-resolve. Ces
entrées sont remplies à l'exécution, donc, la version statique de
la GOT contien des pointeurs NULL pour celles-là. Cependant, la
GOT en mémoire a ces entrées qui sont remplies.
En plus, la nouvelle version de l'éditeur de lien de GNU insère
de multiples sections GOT dans les binaires ELF. La section .got
gère les pointeurs vers les données externes, tandis que la
.got.plt gère les pointeurs vers les fonctions externes. Dans les
premières version de LD, ces deux sections étaient fusionnées.
Nous supportons les deux conventions.
Enfin, vous pouvez voir à la fin dans la section .elfsh.altgot.
Ça fait partie de notre technique ALTGOT et sera expliquée comme un
algorithme indépendant dans la prochaine partie du papier. La
technique ALTGOT permet une augmentation de la taille de la GOT.
Ça permet différentes choses suivant l'architecture sur laquelle
on se trouve. Sous x86, ALGOT est seulement utilisée quand EXTPLT
l'est aussi, pour pouvoir ajouter des fonctions supplémentaires
au programme hôte. Sur MIPS et ALPHA, ALTGOT permet de rediriger
une fonction externe (PLT) sans perdre l'adresse réelle de la
fonction. Nous développerons ces deux techniques dans la partie
suivante.
---[ C. Déboguage proprement dit : dumping, backtrace, breakpoints
Quand nous déboguons en utilisant un débogueur embarqué dans le
processus débogué, nous n'avons pas besoin de ptrace, et nous ne
pouvons pas modifier si facilement l'espace d'adressage du
processus. C'est pourquoi nous devons effectuer de petits
changements statiques : nous pouvons par exemple ajouter le débogueur
comme une dépendance DT_NEEDED (dans le cas ou nous n'utilisons pas
LD_PRELOAD). Le débogueur va aussi surcharger certains
gestionnaires de signaux (SIGTRAP, SIGINT, SIGSEGV ..) pour qu'il
puisse garder le contrôle de ces évenements.
Nous pouvons rediriger des fonctions en utilisant tout autant les
techniques CFLOW ou ALTPLT utilisant des modifications sur
disque, pour récupérer le contrôle au moment désiré. Nous pouvons
évidement mettre des breakpoints lors de l'exécution, mais cela
demande de mprotect la zone de code si elle n'était pas en
écriture à ce moment. Nous avons des idées pour ne plus être
embêtés par mprotect mais ça n'a pas été implémenté dans cette
version (0.65). (En fait, beaucoup d'utilisations de l'appel
systèle mprotect sont incompatible avec une des options de PaX).
Heureusement, en assumant pour l'instant que nous avons un accès
en lecture vers le programme débogué, on peut copier le fichier
et désactivé cette option.
Voici comment la dépendance DT_NEEDED est ajoutée :
========= BEGIN DUMP 2 =========
elfsh@WTH $ cat inject_e2dbg.esh
#!../../vm/elfsh
load a.out
set 1.dynamic[08].val 0x2
set 1.dynamic[08].tag DT_NEEDED
redir main e2dbg_run
save a.out_e2dbg
========= END DUMP 2 =========
Voyons maintenant la section .dynamic modifiée du binaire, où les
entrées DT_NEEDED supplémentaires ont été ajoutée grace à la
technique DT_DEBUG publiée il y a deux ans [0] :
========= BEGIN DUMP 3 =========
elfsh@WTH $ ../../vm/elfsh -f ./a.out -d DT_NEEDED
[*] Object ./a.out has been loaded (O_RDONLY)
[SHT_DYNAMIC]
[Object ./a.out]
[00] Name of needed library => libc.so.6 {DT_NEEDED}
[*] Object ./a.out unloaded
elfsh@WTH $ ../../vm/elfsh -f ./a.out_e2dbg -d DT_NEEDED
[*] Object ./a.out_e2dbg has been loaded (O_RDONLY)
[SHT_DYNAMIC]
[Object ./a.out_e2dbg]
[00] Name of needed library => libc.so.6 {DT_NEEDED}
[08] Name of needed library => ibc.so.6 {DT_NEEDED}
[*] Object ./a.out_e2dbg unloaded
========= END DUMP 3 =========
Voyons maintenant comment la fonction main est redirigée vers la
fonction hook_main. Vous pouvez noter les deux octets écrasés
entre les deux jmp de hook_main. Cette technique est aussi
disponible sur l'architecture MIPS, mais ce dump vient de
l'implémentation IA32 :
========= BEGIN DUMP 4 =========
elfsh@WTH $ ../../vm/elfsh -f ./a.out_e2dbg -D main%40
[*] Object ./a.out_e2dbg has been loaded (O_RDONLY)
08045134 [foff: 308] hook_main + 0 jmp <e2dbg_run>
08045139 [foff: 313] hook_main + 5 push %ebp
0804513A [foff: 314] hook_main + 6 mov %esp,%ebp
0804513C [foff: 316] hook_main + 8 push %esi
0804513D [foff: 317] hook_main + 9 push %ebx
0804513E [foff: 318] hook_main + 10 jmp <main + 5>
08045139 [foff: 313] old_main + 0 push %ebp
0804513A [foff: 314] old_main + 1 mov %esp,%ebp
0804513C [foff: 316] old_main + 3 push %esi
0804513D [foff: 317] old_main + 4 push %ebx
0804513E [foff: 318] old_main + 5 jmp <main + 5>
08048530 [foff: 13616] main + 0 jmp <hook_main>
08048535 [foff: 13621] main + 5 sub $2010,%esp
0804853B [foff: 13627] main + 11 mov 8(%ebp),%ebx
0804853E [foff: 13630] main + 14 mov C(%ebp),%esi
08048541 [foff: 13633] main + 17 and $FFFFFFF0,%esp
08048544 [foff: 13636] main + 20 sub $10,%esp
08048547 [foff: 13639] main + 23 mov %ebx,4(%esp,1)
0804854B [foff: 13643] main + 27 mov $<_IO_stdin_used + 43>,(%esp,1)
08048552 [foff: 13650] main + 34 call <printf>
08048557 [foff: 13655] main + 39 mov (%esi),%eax
[*] No binary pattern was specified
[*] Object ./a.out_e2dbg unloaded
========= END DUMP 4 =========
Exécutons maintenant le programme débogué dans lequel on a
injecté le débogueur.
========= BEGIN DUMP 5 =========
elfsh@WTH $ ./a.out_e2dbg
The Embedded ELF Debugger 0.65 (32 bits built) .::.
.::. This software is under the General Public License V.2
.::. Please visit http://www.gnu.org
[*] Sun Jul 31 17:56:52 2005 - New object ./a.out_e2dbg loaded
[*] Sun Jul 31 17:56:52 2005 - New object /lib/tls/libc.so.6 loaded
[*] Sun Jul 31 17:56:53 2005 - New object ./ibc.so.6 loaded
[*] Sun Jul 31 17:56:53 2005 - New object /lib/ld-linux.so.2 loaded
[*] Sun Jul 31 17:56:53 2005 - New object /lib/libelfsh.so loaded
[*] Sun Jul 31 17:56:53 2005 - New object /lib/libreadline.so.5 loaded
[*] Sun Jul 31 17:56:53 2005 - New object /lib/libtermcap.so.2 loaded
[*] Sun Jul 31 17:56:53 2005 - New object /lib/libdl.so.2 loaded
[*] Sun Jul 31 17:56:53 2005 - New object /lib/libncurses.so.5 loaded
(e2dbg-0.65) b puts
[*] Breakpoint added at <puts@a.out_e2dbg> (0x080483A8)
(e2dbg-0.65) continue
[..: Embedded ELF Debugger returns to the grave :...]
[e2dbg_run] returning to 0x08045139
[host] main argc 1
[host] argv[0] is : ./a.out_e2dbg
First_printf test
The Embedded ELF Debugger 0.65 (32 bits built) .::.
.::. This software is under the General Public License V.2
.::. Please visit http://www.gnu.org
[*] Sun Jul 31 17:57:03 2005 - New object /lib/tls/libc.so.6 loaded
(e2dbg-0.65) bt
.:: Backtrace ::.
[00] 0xB7DC1EC5 <vm_bt@ibc.so.6 + 208>
[01] 0xB7DC207F <cmd_bt@ibc.so.6 + 152>
[02] 0xB7DBC88C <vm_execmd@ibc.so.6 + 174>
[03] 0xB7DAB4DE <vm_loop@ibc.so.6 + 578>
[04] 0xB7DAB943 <vm_run@ibc.so.6 + 271>
[05] 0xB7DA5FF0 <e2dbg_entry@ibc.so.6 + 110>
[06] 0xB7DA68D6 <e2dbg_genericbp_ia32@ibc.so.6 + 183>
[07] 0xFFFFE440 <_r_debug@ld-linux.so.2 + 1208737648> # sigtrap retaddr
[08] 0xB7DF7F3B <__libc_start_main@libc.so.6 + 235>
[09] 0x08048441 <_start@a.out_e2dbg + 33>
(e2dbg-0.65) b
.:: Breakpoints ::.
[00] 0x080483A8 <puts@a.out_e2dbg>
(e2dbg-0.65) delete 0x080483A8
[*] Breakpoint at 080483A8 <puts@a.out_e2dbg> removed
(e2dbg-0.65) b
.:: Breakpoints ::.
[*] No breakpoints
(e2dbg-0.65) b printf
[*] Breakpoint added at <printf@a.out_e2dbg> (0x080483E8)
(e2dbg-0.65) dumpregs
.:: Registers ::.
[EAX] 00000000 (0000000000) <unknown>
[EBX] 08203F48 (0136331080) <.elfsh.relplt@a.out_e2dbg + 1811272>
[ECX] 00000000 (0000000000) <unknown>
[EDX] B7F0C7C0 (3086010304) <__guard@libc.so.6 + 1656>
[ESI] BFE3B7C4 (3219371972) <_r_debug@ld-linux.so.2 + 133149428>
[EDI] BFE3B750 (3219371856) <_r_debug@ld-linux.so.2 + 133149312>
[ESP] BFE3970C (3219363596) <_r_debug@ld-linux.so.2 + 133141052>
[EBP] BFE3B738 (3219371832) <_r_debug@ld-linux.so.2 + 133149288>
[EIP] 080483A9 (0134513577) <puts@a.out_e2dbg>
(e2dbg-0.65) stack 20
.:: Stack ::.
0xBFE37200 0x00000000 <(null)>
0xBFE37204 0xB7DC2091 <vm_dumpstack@ibc.so.6>
0xBFE37208 0xB7DDF5F0 <_GLOBAL_OFFSET_TABLE_@ibc.so.6>
0xBFE3720C 0xBFE3723C <_r_debug@ld-linux.so.2 + 133131628>
0xBFE37210 0xB7DC22E7 <cmd_stack@ibc.so.6 + 298>
0xBFE37214 0x00000014 <_r_debug@ld-linux.so.2 + 1208744772>
0xBFE37218 0xB7DDDD90 <__FUNCTION__.5@ibc.so.6 + 49>
0xBFE3721C 0xBFE37230 <_r_debug@ld-linux.so.2 + 133131616>
0xBFE37220 0xB7DB9DF9 <vm_implicit@ibc.so.6 + 304>
0xBFE37224 0xB7DE1A7C <world@ibc.so.6 + 92>
0xBFE37228 0xB7DA8176 <do_resolve@ibc.so.6>
0xBFE3722C 0x080530B8 <.elfsh.relplt@a.out_e2dbg + 38072>
0xBFE37230 0x00000014 <_r_debug@ld-linux.so.2 + 1208744772>
0xBFE37234 0x08264FF6 <.elfsh.relplt@a.out_e2dbg + 2208758>
0xBFE37238 0xB7DDF5F0 <_GLOBAL_OFFSET_TABLE_@ibc.so.6>
0xBFE3723C 0xBFE3726C <_r_debug@ld-linux.so.2 + 133131676>
0xBFE37240 0xB7DBC88C <vm_execmd@ibc.so.6 + 174>
0xBFE37244 0x0804F208 <.elfsh.relplt@a.out_e2dbg + 22024>
0xBFE37248 0x00000000 <(null)>
0xBFE3724C 0x00000000 <(null)>
(e2dbg-0.65) continue
[..: Embedded ELF Debugger returns to the grave :...]
First_puts
The Embedded ELF Debugger 0.65 (32 bits built) .::.
.::. This software is under the General Public License V.2
.::. Please visit http://www.gnu.org
[*] Sun Jul 31 18:00:47 2005 - /lib/tls/libc.so.6 loaded
[*] Sun Jul 31 18:00:47 2005 - /usr/lib/gconv/ISO8859-1.so loaded
(e2dbg-0.65) dumpregs
.:: Registers ::.
[EAX] 0000000B (0000000011) <_r_debug@ld-linux.so.2 + 1208744763>
[EBX] 08203F48 (0136331080) <.elfsh.relplt@a.out_e2dbg + 1811272>
[ECX] 0000000B (0000000011) <_r_debug@ld-linux.so.2 + 1208744763>
[EDX] B7F0C7C0 (3086010304) <__guard@libc.so.6 + 1656>
[ESI] BFE3B7C4 (3219371972) <_r_debug@ld-linux.so.2 + 133149428>
[EDI] BFE3B750 (3219371856) <_r_debug@ld-linux.so.2 + 133149312>
[ESP] BFE3970C (3219363596) <_r_debug@ld-linux.so.2 + 133141052>
[EBP] BFE3B738 (3219371832) <_r_debug@ld-linux.so.2 + 133149288>
[EIP] 080483E9 (0134513641) <printf@a.out_e2dbg>
(e2dbg-0.65) linkmap
.::. Linkmap entries .::.
[01] addr : 0x00000000 dyn : 0x0804981C -
[02] addr : 0x00000000 dyn : 0xFFFFE590 -
[03] addr : 0xB7DE3000 dyn : 0xB7F0AD3C - /lib/tls/libc.so.6
[04] addr : 0xB7D95000 dyn : 0xB7DDF01C - ./ibc.so.6
[05] addr : 0xB7F29000 dyn : 0xB7F3FF14 - /lib/ld-linux.so.2
[06] addr : 0xB7D62000 dyn : 0xB7D93018 - /lib/libelfsh.so
[07] addr : 0xB7D35000 dyn : 0xB7D5D46C - /lib/libreadline.so.5
[08] addr : 0xB7D31000 dyn : 0xB7D34BB4 - /lib/libtermcap.so.2
[09] addr : 0xB7D2D000 dyn : 0xB7D2FEEC - /lib/libdl.so.2
[10] addr : 0xB7CEB000 dyn : 0xB7D2A1C0 - /lib/libncurses.so.5
[11] addr : 0xB6D84000 dyn : 0xB6D85F28 - /usr/lib/gconv/ISO8859-1.so
(e2dbg-0.65) exit
[*] Unloading object 1 (/usr/lib/gconv/ISO8859-1.so)
[*] Unloading object 2 (/lib/tls/libc.so.6)
[*] Unloading object 3 (/lib/tls/libc.so.6)
[*] Unloading object 4 (/lib/libncurses.so.5)
[*] Unloading object 5 (/lib/libdl.so.2)
[*] Unloading object 6 (/lib/libtermcap.so.2)
[*] Unloading object 7 (/lib/libreadline.so.5)
[*] Unloading object 8 (/home/elfsh/WTH/elfsh/libelfsh/libelfsh.so)
[*] Unloading object 9 (/lib/ld-linux.so.2)
[*] Unloading object 10 (./ibc.so.6)
[*] Unloading object 11 (/lib/tls/libc.so.6)
[*] Unloading object 12 (./a.out_e2dbg) *
.:: Bye -:: The Embedded ELF Debugger 0.65
========= END DUMP 5 =========
Comme vous venez de le voir, l'utilisation du débogueur est assez
similaire aux autres débogueurs. La différence tient dans la
technique d'implémentation qui permet un déboguage sur des
systèmes embarqués et endurcis où ptrace n'est pas présent ou est
désactivé.
On nous a dit [9] que l'appel système sigaction active la
possibilité d'exécution pas à pas sans utiliser ptrace. Nous
n'avons pas eu le temps de l'implémenté mais nous fournirons un
débogueur pas à pas dans un futur proche. Puisque cet appel n'est
pas filtré par grsecurity et semble assez portable sous Linux,
BSD, Solaris et HP-UX, ça vaut certainement la peine de le tester.
---[ D. Génération d'analyseurs dynamiques
Évidement, les outils comme ltrace[7] peuvent maintenant être
fait en utilisant des scripts elfsh pour des architectures
multiples puisque tout le travail de redirections à été fait.
Nous pensons aussi que la suite peut être utilisée dans des
instrumentations de logiciels dynamiques. Puisque nous supportons
des architectures multiples, nous laissons la porte ouverte à
d'autres équipes de développement pour coder ce genre de
modules ou d'extensions au sein de la suite ELF shell.
Nous n'avons pas eu le temps d'inclure un script d'example qui le
fait, mais nous le ferons bientôt. Le genre de choses
intéressantes qui peuvent être faites et améliorée en utilisant
la suite prendrait leur inspiration dans des projets comme fenris
[6]. Ceci pourrait être fait pour de multiples architectures
puisque le type de format d'instruction est intégré dans le
moteur de script, en utilisant l'abstraction du code de libasm
(qui est maintenant inclu comme source dans elfsh).
Nous n'avons pas encore traité le chiffrement pour l'instant,
mais des API prometteuses [5] pourrait être implémentées aussi
pour de multiples architectures très facilement.
-------[ III. Meilleures redirections ELF multi-architecture
Dans la première édition du Cerberus ELF interface [0], nous
avions présenté une technique de redirection appelée ALTPLT.
Cette technique n'est pas suffisante puisqu'elle ne permet que
des redirections du PLT sur des fonctions existantes du programme
binaire, les fonctions utilisables pour étendre le logiciel sont
donc limitées.
De plus, nous avons remarqué un bogue dans l'implémentation de la
technique ALTPLT publiée précédement : sur l'architecture SPARC,
quand on appelait une fonction originale, la redirection était
enlevée et le programme continuait à fonctionner comme si aucun
détournement n'était installé. Ce bogue vient du fait que Solaris
n'utilise pas le champt r_offset pour calculer ses
relocations mais calcule l'offset dans le fichier en
multipliant la taille de l'entrée du PLT par l'offset de
relocation placé sur la pile au moment de la résolution
dynamique.
Nous avons trouvé une solution à ce problème. Cette solution
consiste à mettre quelques corrections spécifiques à
l'architecture au début de la section ALTPLT. Cependant, une
telle correction est trop dépendante de l'architecture et nous
avons commencé à pensé à une technique alternative pour
implémenter ALTPLT. Tout en implémentant la technique DT_DEBUG en
modifiant certaines entrées de la section .dynamic, nous avons
découvert que beaucoup d'autres entrées sont écrasables et nous
fournissent une technique très forte et indépendante de
l'architecture pour redirriger l'accès à des sections variables.
Plus précisément, quand on patche l'entrée DT_PLTREL, nous sommes
capable de fournir notre propre pointeur. DT_PLTREL est une
entrée DEPENDANTE de l'architecture et la documentation à son
sujet est très petite, pour ne pas dire inexistante.
Elle pointe en fait sur la section de l'exécutable qui est
relogee à l'exécution (par exemple la GOT sous x86 et mips,
PLT sous sparc et alpha). En changeant cette entrée, nous sommes
capable de fournir notre propre PLT ou GOT, qui nous permet de
l'étendre par la suite.
Regardons d'abord la technique CFLOW, nous reviendrons ensuite
sur les redirections relatives au PLT en utilisant les
modifications de DT_PLTREL.
---[ A. CFLOW : redirection de fonction statique résistante à PaX
CFLOW est une technique simple mais efficace pour rediriger des
fonctions qui sont localisées dans le programme hôte et qui n'ont
pas d'entrée dans la PLT.
Voyons maintenant le fichier hôte que nous allons utiliser pour
les tests :
========= BEGIN DUMP 6 =========
elfsh@WTH $ cat host.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int legit_func(char *str)
{
printf("legit func (%s) !\n", str);
return (0);
}
int main()
{
char *str;
char buff[BUFSIZ];
read(0, buff, BUFSIZ-1);
str = malloc(10);
if (str == NULL)
goto err;
strcpy(str, "test");
printf("First_printf %s\n", str);
fflush(stdout);
puts("First_puts");
printf("Second_printf %s\n", str);
free(str);
puts("Second_puts");
fflush(stdout);
legit_func("test");
return (0);
err:
printf("Malloc problem\n");
return (-1);
}
========= END DUMP 6 =========
Nous allons ici rediriger la fonction legit_func, localisée dans
le fichier host.c par la fonction hook_funk localisée dans
l'objet relogeable.
Jetons un coup d'oeuil au fichier relogeable que nous allons
injecter dans le binaire précédant.
========= BEGIN DUMP 7 =========
elfsh@WTH $ cat rel.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int glvar_testreloc = 42;
int glvar_testreloc_bss;
char glvar_testreloc_bss2;
short glvar_testreloc_bss3;
int hook_func(char *str)
{
printf("HOOK FUNC %s !\n", str);
return (old_legit_func(str));
}
int puts_troj(char *str)
{
int local = 1;
char *str2;
str2 = malloc(10);
*str2 = 'Z';
*(str2 + 1) = 0x00;
glvar_testreloc_bss = 43;
glvar_testreloc_bss2 = 44;
glvar_testreloc_bss3 = 45;
printf("Trojan injected ET_REL takes control now "
"[%s:%s:%u:%u:%hhu:%hu:%u] \n",
str2, str,
glvar_testreloc,
glvar_testreloc_bss,
glvar_testreloc_bss2,
glvar_testreloc_bss3,
local);
free(str2);
putchar('e');
putchar('x');
putchar('t');
putchar('c');
putchar('a');
putchar('l');
putchar('l');
putchar('!');
putchar('\n');
old_puts(str);
write(1, "calling write\n", 14);
fflush(stdout);
return (0);
}
int func2()
{
return (42);
}
========= END DUMP 7 =========
Comme vous pouvez le voir, l'objet relogeable utilise des
fonctions inconnues comme write et putchar. Ces fonctions n'ont
pas de symbole, d'entrée dans la PLT, d'entrée dans la GOT, ou
même une entrée relogeable dans le programme hôte.
Nous pouvons l'appeller quand même en utilisant la technique
EXTPLT qui sera expliquée comme technique indépendante dans la
prochaine partie du papier. Pour l'instant, nous nous concentrons
sur la technique CFLOW qui permet de rediger legit_func vers
hook_func. Cette fonction n'a pas d'entrée en PLT et nous ne
pouvons pas utiliser une simple infection de PLT.
Nous avons développé une technique qui ne craint pas PaX pour les
redirections sur disque de ce genre de fonctions. Elle consiste à
mettre un bon vieux jmp au début de legit_func et de rediriger le
flot vers notre code. ELFsh fera attention à bien exécuter part
les octets écrasés ailleur et rendra le contrôle à la fonction
redirigée, juste après le détournement par jmp, pour qu'aucune
restauration à l'exécution ne soit nécessaire et qu'elle reste
immunisée sur le disque contre PaX.
Quand ces techniques sont utilisées dans le débogueur directement
en mémoire et pas sur le disque, elles cassent les protection
mrprotect de PaX, ce qui veut dire que ce flag doit être
désactivé si vous voulez rediriger le flot directement en
mémoire. Nous avons utilisé l'appel système mrprotect sur de
petites zones de code pour pouvoir changer quelques instructions
spécifiques pour la redirection. Cependant, nous pensons que
cette technique est plus intéressante pour le déboguage et pas
pour autre chose, ce n'est donc pas, pour nous, une priorité de
l'améliorer.
Voyons le petit script ELFsh pour cet exemple :
========= BEGIN DUMP 8 =========
elfsh@WTH $ file a.out
a.out: ELF 32-bit LSB executable, Intel 80386, dynamically linked, \
not stripped
elfsh@WTH $ cat relinject.esh
#!../../../vm/elfsh
load a.out
load rel.o
reladd 1 2
redir puts puts_troj
redir legit_func hook_func
save fake_aout
quit
========= END EXAMPLE 8 =========
La sortie du binaire ORIGINAL est la suivante :
========= BEGIN DUMP 9 =========
elfsh@WTH $ ./a.out
First_printf test
First_puts
Second_printf test
Second_puts
LEGIT FUNC
legit func (test) !
========= END DUMP 9 ===========
Injectons maintenant le bazar :
========= BEGIN DUMP 10 ========
elfsh@WTH $ ./relinject.esh
The ELF shell 0.65 (32 bits built) .::.
.::. This software is under the General Public License V.2
.::. Please visit http://www.gnu.org
~load a.out
[*] Sun Jul 31 15:30:14 2005 - New object a.out loaded
~load rel.o
[*] Sun Jul 31 15:30:14 2005 - New object rel.o loaded
~reladd 1 2
Section Mirrored Successfully !
[*] ET_REL rel.o injected succesfully in ET_EXEC a.out
~redir puts puts_troj
[*] Function puts redirected to addr 0x08047164 <puts_troj>
~redir legit_func hook_func
[*] Function legit_func redirected to addr 0x08047134 <hook_func>
~save fake_aout
[*] Object fake_aout saved successfully
~quit
[*] Unloading object 1 (rel.o)
[*] Unloading object 2 (a.out) *
.:: Bye -:: The ELF shell 0.65
========= END DUMP 10 =========
Exécutons maintenant le binaire modifié.
========= BEGIN DUMP 11 =========
elfsh@WTH $ ./fake_aout
First_printf test
Trojan injected ET_REL takes control now [Z:First_puts:42:43:44:45:1]
extcall!
First_puts
calling write
Second_printf test
Trojan injected ET_REL takes control now [Z:Second_puts:42:43:44:45:1]
extcall!
Second_puts
calling write
HOOK FUNC test !
Trojan injected ET_REL takes control now [Z:LEGIT FUNC:42:43:44:45:1]
extcall!
calling write
legit func (test) !
elfsh@WTH $
========= END DUMP 11 =========
Bien. legit_func a clairement été redirigée vers hook_func, et
elle a bien fait attention à rappeller legit_func en utilisant la
technique du vieux symbole décrite dans la première édition des
articles de Cerberus.
Regardons le code original de legit_func et celui redirigé en
utilisant la technique CFLOW sous x86 :
========= BEGIN DUMP 12 =========
080484C0 legit_func + 0 push %ebp
080484C1 legit_func + 1 mov %esp,%ebp
080484C3 legit_func + 3 sub $8,%esp
080484C6 legit_func + 6 mov $<_IO_stdin_used + 4>,(%esp,1)
080484CD legit_func + 13 call <.plt + 32>
080484D2 legit_func + 18 mov $<_IO_stdin_used + 15>,(%esp,1)
========= END DUMP 12 =========
Maintenant, le code modifié :
========= BEGIN DUMP 13 =========
080484C0 legit_func + 0 jmp <hook_legit_func>
080484C5 legit_func + 5 nop
080484C6 legit_func + 6 mov $<_IO_stdin_used + 4>,(%esp,1)
080484CD legit_func + 13 call <puts>
080484D2 legit_func + 18 mov $<_IO_stdin_used + 15>,(%esp,1)
080484D9 legit_func + 25 mov 8(%ebp),%eax
080484DC legit_func + 28 mov %eax,4(%esp,1)
080484E0 legit_func + 32 call <printf>
080484E5 legit_func + 37 leave
080484E6 legit_func + 38 xor %eax,%eax
========= END DUMP 13 =========
Nous créons une nouvelle section .elfsh.hooks dont la donnée est
un tableau de bouts de codes de détournement comme celui-ci :
========= BEGIN DUMP 14 =========
08042134 hook_legit_func + 0 jmp <hook_func>
08042139 old_legit_func + 0 push %ebp
0804213A old_legit_func + 1 mov %esp,%ebp
0804213C old_legit_func + 3 sub $8,%esp
0804213F old_legit_func + 6 jmp <legit_func + 6>
========= END DUMP 14 =========
Puisque nous voulons pouvoir rappeller la fonction originale
(legit_func), nous avons rajouté les octets écrasés, juste après
le premier jmp, et ensuite, nous rappellons legit_func avec le
bon offset (pour ne pas avoir de récursion dans le détournement
puisque la fonction a été hijackée), comme vous pouvez le voir
commancant au symbole old_legit_func de l'exemple 14.
Cette technique du vieux symbole est cohérente avec la technique
ALTPLT qui a été publiée dans le premier article. Nous pouvons
tout autant utiliser l'appel old_funcname() à l'intérieur du code
injecté pour rappeler la bonne fonction hijackée, et nous le
faisons sans un seul octet de restauration à l'exécution. C'est
pour cela que la technique CFLOW ne craint pas PaX.
Pour l'architecture MIPS, la technique CFLOW est assez similaire,
nous pouvons voir sont résultat aussi (dump 15 est le binaire
original et dump 16 celui modifié) :
======== BEGIN DUMP 15 =========
400400 <func>: lui gp,0xfc1
400404 <func+4>: addiu gp,gp,-21696
400408 <func+8>: addu gp,gp,t9
40040c <func+12>: addiu sp,sp,-40
400410 <func+16>: sw ra,36(sp)
[...]
======== END DUMP 15 =========
Le code de fonction modifié est maintenant :
======== BEGIN DUMP 16 =========
<func>
400400: addi t9,t9,104 # registre T9 comme fonction cible
400404: j 0x400468 <func2> # JMP Direct vers la fonction hook
400408: nop # delay slot sous MIPS
40040c: addiu sp,sp,-40 # le code de fonction original
400410: sw ra,36(sp)
400414: sw s8,32(sp)
400418: move s8,sp
40041c: sw gp,16(sp)
400420: sw a0,40(s8)
======== END DUMP 16 =========
La fonction func2 peut être n'importe laquelle, pourvu qu'elle ai
le même nombre et type de paramètres. Quand la fonction func2
veut appeler la fonction originale (func), elle saute vers le
symbole old_func qui pointe à l'intérieur de l'entrée de la
section .elfsh.hooks pour ce détournement CFLOW. Voici à quoi
ressemble un détournement d'entrée sous architecture MIPS :
======== BEGIN DUMP 17 =========
<old_func>
3ff0f4 addi t9,t9,4876
3ff0f8 lui gp,0xfc1
3ff0fc addiu gp,gp,-21696
3ff100 addu gp,gp,t9
3ff104 j 0x400408 <func + 8>
3ff108 nop
3ff10c nop
======== END DUMP 17 ===========
Comme vous pouvez le voir, les trois instructions qui ont été
écrasées pour installer le détournement CFLOW au début de func()
sont maintenant localisées dans l'entrée de hook, pointée par le
symbole old_func. Le registre T9 est aussi remi pour qu'on puisse
revenir dans une situation sûre juste avant de sauter en arrière
vers func + 8.
---[ B. Technique ALTPLT revue
La technique ALTPLT v1 a été présentée dans le papier "the
Cerberus ELF Interface" [0]. Comme déjà dit, elle n'était pas
satisfaisante puisque le détournement était retiré dès le premier
appel à la fonction originale.
Puisque sous SPARC, les 4 premières entrées du PLT sont
réservées, il y a de la place pour 12 instructions qui feront
tout ce dont on a besoin (en fait, la première entrée PLT) quand
ALTPLT+0 prendra le contrôle.
ALTPLTv2 fonctionne en fait avec 12 instructions, mais il y avait
besoin de réencoder la première entrée ALTPLT avec le code de
PLT+0 (qui est relogé à l'exécution sous SPARC avant que le main
n'aie le contrôle, ce qui explique pourquoi nous ne pouvons pas
le patcher statiquement sur le disque).
Mais ce comportement, il casse PaX, et son implantation sont très
dépendante de l'architecture à cause de sa partie en assembleur
SPARC. Pour ceux qui voudrait le voir, nous avons laissé le code
de tout ceci dans l'arborescence du source d'ELFsh dans
libelfsh/sparc32.c .
Pour l'architecture ALPHA64, il fournis presque l'équivalents
dans ses instructions respectives, et cette fois,
l'implémentation est dans libelfsh/alpha64.c .
Comme vous pouvez le voir dans le code (que nous ne reproduiront
pas pour garder l'article clair), ALTPLTv2 est un vrai merdier et
nous avions besoin d'éviter tout ce code assembleur qui demandait
trop d'efforts pour un futur portage de cette technique sur
d'autres architectures.
Alors, nous avons découvert le truc du DT_PLTREL du .dynamic et
nous avons essayé de voir ce qu'il se passe quand on change cette
entrée du .dynamic dans le binaire hôte. Changer l'entrée
DT_PLTREL est très intéressante puisqu'elle est entièrement
indépendante de l'architecture et fonctionne donc partout.
Voyons donc à quoi ressemble la table d'en-tête de sections et la
section .dynamic utilisée dans la technique très simple ALTPLTv3.
Nous utilisons la section .elfsh.altplt comme miroir de la .plt
originale comme expliqué dans notre premier papier. Les autres
sections .elfsh.* ont déjà été expliquées ou le seront juste
après le log.
La sortie (modifiée) du binaire ressemble à ceci :
=============== BEGIN DUMP 18 ================
[SECTION HEADER TABLE .::. SHT is not stripped]
[Object fake_aout]
[000] 0x00000000 ------- foff:00000000 sz:0000000 link:00
[001] 0x08042134 a-x---- .elfsh.hooks foff:00000308 sz:0000016 link:00
[002] 0x08043134 a-x---- .elfsh.extplt foff:00004404 sz:0000048 link:00
[003] 0x08044134 a-x---- .elfsh.altplt foff:00008500 sz:0004096 link:00
[004] 0x08045134 a--ms-- rel.o.rodata.str1.32 foff:12596 sz:4096 link:00
[005] 0x08046134 a--ms-- rel.o.rodata.str1.1 foff:16692 sz:4096 link:00
[006] 0x08047134 a-x---- rel.o.text foff:00020788 sz:0004096 link:00
[007] 0x08048134 a------ .interp foff:00024884 sz:0000019 link:00
[008] 0x08048148 a------ .note.ABI-tag foff:00024904 sz:0000032 link:00
[009] 0x08048168 a------ .hash foff:00024936 sz:0000064 link:10
[010] 0x080481A8 a------ .dynsym foff:00025000 sz:0000176 link:11
[011] 0x08048258 a------ .dynstr foff:00025176 sz:0000112 link:00
[012] 0x080482C8 a------ .gnu.version foff:00025288 sz:0000022 link:10
[013] 0x080482E0 a------ .gnu.version_r foff:00025312 sz:0000032 link:11
[014] 0x08048300 a------ .rel.dyn foff:00025344 sz:0000016 link:10
[015] 0x08048310 a------ .rel.plt foff:00025360 sz:0000056 link:10
[016] 0x08048348 a-x---- .init foff:00025416 sz:0000023 link:00
[017] 0x08048360 a-x---- .plt foff:00025440 sz:0000128 link:00
[018] 0x08048400 a-x---- .text foff:00025600 sz:0000736 link:00
[019] 0x080486E0 a-x---- .fini foff:00026336 sz:0000027 link:00
[020] 0x080486FC a------ .rodata foff:00026364 sz:0000116 link:00
[021] 0x08048770 a------ .eh_frame foff:00026480 sz:0000004 link:00
[022] 0x08049774 aw----- .ctors foff:00026484 sz:0000008 link:00
[023] 0x0804977C aw----- .dtors foff:00026492 sz:0000008 link:00
[024] 0x08049784 aw----- .jcr foff:00026500 sz:0000004 link:00
[025] 0x08049788 aw----- .dynamic foff:00026504 sz:0000200 link:11
[026] 0x08049850 aw----- .got foff:00026704 sz:0000004 link:00
[027] 0x08049854 aw----- .got.plt foff:00026708 sz:0000040 link:00
[028] 0x0804987C aw----- .data foff:00026748 sz:0000012 link:00
[029] 0x08049888 aw----- .bss foff:00026760 sz:0000008 link:00
[030] 0x08049890 aw----- rel.o.bss foff:00026768 sz:0004096 link:00
[031] 0x0804A890 aw----- rel.o.data foff:00030864 sz:0000004 link:00
[032] 0x0804A894 aw----- .elfsh.altgot foff:00030868 sz:0000048 link:00
[033] 0x0804A8E4 aw----- .elfsh.dynsym foff:00030948 sz:0000208 link:34
[034] 0x0804AA44 aw----- .elfsh.dynstr foff:00031300 sz:0000127 link:33
[035] 0x0804AB24 aw----- .elfsh.reldyn foff:00031524 sz:0000016 link:00
[036] 0x0804AB34 aw----- .elfsh.relplt foff:00031540 sz:0000072 link:00
[037] 0x00000000 ------- .comment foff:00031652 sz:0000665 link:00
[038] 0x00000000 ------- .debug_aranges foff:00032324 sz:0000120 link:00
[039] 0x00000000 ------- .debug_pubnames foff:00032444 sz:0000042 link:00
[040] 0x00000000 ------- .debug_info foff:00032486 sz:0006871 link:00
[041] 0x00000000 ------- .debug_abbrev foff:00039357 sz:0000511 link:00
[042] 0x00000000 ------- .debug_line foff:00039868 sz:0000961 link:00
[043] 0x00000000 ------- .debug_frame foff:00040832 sz:0000072 link:00
[044] 0x00000000 ---ms-- .debug_str foff:00040904 sz:0008067 link:00
[045] 0x00000000 ------- .debug_macinfo foff:00048971 sz:0029295 link:00
[046] 0x00000000 ------- .shstrtab foff:00078266 sz:0000507 link:00
[047] 0x00000000 ------- .symtab foff:00080736 sz:0002368 link:48
[048] 0x00000000 ------- .strtab foff:00083104 sz:0001785 link:47
[SHT_DYNAMIC]
[Object ./testsuite/etrel_inject/etrel_original/fake_aout]
[00] Name of needed library => libc.so.6 {DT_NEEDED}
[01] Address of init function => 0x08048348 {DT_INIT}
[02] Address of fini function => 0x080486E0 {DT_FINI}
[03] Address of symbol hash table => 0x08048168 {DT_HASH}
[04] Address of dynamic string table => 0x0804AA44 {DT_STRTAB}
[05] Address of dynamic symbol table => 0x0804A8E4 {DT_SYMTAB}
[06] Size of string table => 00000127 bytes {DT_STRSZ}
[07] Size of symbol table entry => 00000016 bytes {DT_SYMENT}
[08] Debugging entry (unknown) => 0x00000000 {DT_DEBUG}
[09] Processor defined value => 0x0804A894 {DT_PLTGOT}
[10] Size in bytes for .rel.plt => 000072 bytes {DT_PLTRELSZ}
[11] Type of reloc in PLT => 00000017 {DT_PLTREL}
[12] Address of .rel.plt => 0x0804AB34 {DT_JMPREL}
[13] Address of .rel.got section => 0x0804AB24 {DT_REL}
[14] Total size of .rel section => 00000016 bytes {DT_RELSZ}
[15] Size of a REL entry => 00000008 bytes {DT_RELENT}
[16] SUN needed version table => 0x80482E0 {DT_VERNEED}
[17] SUN needed version number => 001 {DT_VERNEEDNUM}
[18] GNU version VERSYM => 0x080482C8 {DT_VERSYM}
=============== END DUMP 18 ================
Comme vous pouvez le voir, plusieurs sections ont été copiées
etétendues, et leurs entrées da ns .dynamic ont été changées.
Ceci s'applique pour .got (DT_PLTGOT), .rel.plt (DT_JMPREL),
.dunsym (DT_SYMTAB), et .dynstr (DT_STRTAB). Changer ces entrées
nous fournis une nouvelle technique ALTPLT sans aucune ligne
d'assembleur.
Bien sûr, la technique ALTPLT version 3 n'a besoin d'aucune
informations non-obligatoire comme la section debug. Ça peut
sembler évident, mais certaines personnes nous ont vraiment posé
la question.
---[ C. Technique ALTGOT : Le complément RISC
Sous architecture MIPS, les appels vers les entrées de PLT
sont fait différement. En fait, au lieu d'une instruction call
directe sur l'entrée, un jmp indirect est utilisé pour utiliser
l'entrée GOT liée à la fonction désirée. Si cette entrée est
remplie, alors, la fonction est appelée directement. Par défaut,
les entrées GOT contiennent un pointeur vers les entrées PLT.
Pendant l'exécution, par la suite, le linker dynamique est
appelé pour reloge la section GOT (MIPS, x86) ou la section PLT
(sous SPARC ou ALPHA).
Voici un log d'assembleur MIPS qui le prouve sur un bête
helloworld qui utilise printf :
00400790 <main>:
400790: 3c1c0fc0 lui gp,0xfc0 # Met GP à base de GOT
400794: 279c78c0 addiu gp,gp,30912 # address + 0x7ff0
400798: 0399e021 addu gp,gp,t9 # utilise t9 (= main)
40079c: 27bdffe0 addiu sp,sp,-32
4007a0: afbf001c sw ra,28(sp)
4007a4: afbe0018 sw s8,24(sp)
4007a8: 03a0f021 move s8,sp
4007ac: afbc0010 sw gp,16(sp)
4007b0: 8f828018 lw v0,-32744(gp)
4007b4: 00000000 nop
4007b8: 24440a50 addiu a0,v0,2640
4007bc: 2405002a li a1,42
4007c0: 8f828018 lw v0,-32744(gp)
4007c4: 00000000 nop
4007c8: 24460a74 addiu a2,v0,2676
4007cc: 8f99803c lw t9,-32708(gp) # charge l'entrée
# GOT de printf
4007d0: 00000000 nop
4007d4: 0320f809 jalr t9 # y saute
4007d8: 00000000 nop
4007dc: 8fdc0010 lw gp,16(s8)
4007e0: 00001021 move v0,zero
4007e4: 03c0e821 move sp,s8
4007e8: 8fbf001c lw ra,28(sp)
4007ec: 8fbe0018 lw s8,24(sp)
4007f0: 27bd0020 addiu sp,sp,32
4007f4: 03e00008 jr ra # retour de la fonction
4007f8: 00000000 nop
4007fc: 00000000 nop
On note que le registre de pointeur global %gp contient toujours
l'adresse de base de la section GOT sous MIPS, plus ou moins un
certain offset fixé, dans notre cas 0x7ff0 (0x8000 sous ALPHA).
Pour pouvoir appeler une fonction dont l'adresse n'est pas
connue, les entrées GOT sont remplies et ensuite, le jmp indirect
sous MIPS n'utilise plus l'entrée PLT. Qu'apprenons nous de ceci ?
Simplement que nous ne pouvons plus nous baser sur un
détournement classique du PLT puisque le code de l'entrée PLT ne
sera pas appelé si l'entrée GOT est déjà remplie, ce qui veut
dire que nous ne détourneront la fonction que la première fois.
À cause de ceci, nous détournerons les fonction en patchant le
GOT sous MIPS. Cependant, il ne résoud pas le problème de
rappeller la fonction originale. Pour permetre ces rappels, nous
alons juste ajouter le symbols old_ sur l'entrée PLT réelle, pour
que nous puissions toujours accéder aux morceaux de code du
méchanismes de liaison dynamique même si la GOT a été modifiée.
Voyons les résultats détaillés de la technique ALTGOT sous
architecture ALPHA et MIPS. Ceci a été fait sans aucune ligne
d'assembleur, ce qui la rend très portable :
========= BEGIN DUMP 19 =========
elfsh@alpha$ cat host.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
char *str;
str = malloc(10);
if (str == NULL)
goto err;
strcpy(str, "test");
printf("First_printf %s\n", str);
fflush(stdout);
puts("First_puts");
printf("Second_printf %u\n", 42);
puts("Second_puts");
fflush(stdout);
return (0);
err:
printf("Malloc problem %u\n", 42);
return (-1);
}
elfsh@alpha$ gcc host.c -o a.out
elfsh@alpha$ file ./a.out
a.out: ELF 64-bit LSB executable, Alpha (unofficial), for NetBSD 2.0G,
dynamically linked, not stripped
========= END DUMP 19 =========
Exécution du binaire original :
========= BEGIN DUMP 20 =========
elfsh@alpha$ ./a.out
First_printf test
First_puts
Second_printf 42
Second_puts
========= END DUMP 20 ==========
Regardons encore une fois l'objet relogeable que nous allons
injecter :
========= BEGIN DUMP 21 =========
elfsh@alpha$ cat rel.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int glvar_testreloc = 42;
int glvar_testreloc_bss;
char glvar_testreloc_bss2;
short glvar_testreloc_bss3;
int puts_troj(char *str)
{
int local = 1;
char *str2;
str2 = malloc(10);
*str2 = 'Z';
*(str2 + 1) = 0x00;
glvar_testreloc_bss = 43;
glvar_testreloc_bss2 = 44;
glvar_testreloc_bss3 = 45;
printf("Trojan injected ET_REL takes control now "
"[%s:%s:%u:%u:%hhu:%hu:%u] \n",
str2, str,
glvar_testreloc,
glvar_testreloc_bss,
glvar_testreloc_bss2,
glvar_testreloc_bss3,
local);
old_puts(str);
fflush(stdout);
return (0);
}
int func2()
{
return (42);
}
========= END DUMP 21 =========
Comme vous pouvez le voir, l'objet relogeable rel.c utilise le
symbole old_ qui signifie qu'il se base sur la technqiue ALTPLT.
Cependant, nous ne faisont pas encore appel à la technique EXTPLT
sous ALPHA et MIPS, nous ne pouvons donc pas encore appeler des
fonctions inconnues à partir du binaire sur ces architectures.
Notre rel.c est une copie de celui de l'example 7 sans l'appel
aux fonction inconnues write et putchar de l'example 7.
Maintenant, nous injectons le tout :
========= BEGIN DUMP 22 =========
elfsh@alpha$ ./relinject.esh > relinject.out
elfsh@alpha$ ./fake_aout
First_printf test
Trojan injected ET_REL takes control now [Z:First_puts:42:43:44:45:1]
First_puts
Second_printf 42
Trojan injected ET_REL takes control now [Z:Second_puts:42:43:44:45:1]
Second_puts
========= END DUMP 22 ==========
La list des section sous ALPHA est la suivante. Un coup d'oeil à
la section injectée est recommandé :
========= BEGIN DUMP 23 =========
elfsh@alpha$ elfsh -f fake_aout -s -p
[*] Object fake_aout has been loaded (O_RDONLY)
[SECTION HEADER TABLE .::. SHT is not stripped]
[Object fake_aout]
[000] 0x000000000 ------- foff:00000 sz:00000
[001] 0x120000190 a------ .interp foff:00400 sz:00023
[002] 0x1200001A8 a------ .note.netbsd.ident foff:00424 sz:00024
[003] 0x1200001C0 a------ .hash foff:00448 sz:00544
[004] 0x1200003E0 a------ .dynsym foff:00992 sz:00552
[005] 0x120000608 a------ .dynstr foff:01544 sz:00251
[006] 0x120000708 a------ .rela.dyn foff:01800 sz:00096
[007] 0x120000768 a------ .rela.plt foff:01896 sz:00168
[008] 0x120000820 a-x---- .init foff:02080 sz:00128
[009] 0x1200008A0 a-x---- .text foff:02208 sz:01312
[010] 0x120000DC0 a-x---- .fini foff:03520 sz:00104
[011] 0x120000E28 a------ .rodata foff:03624 sz:00162
[012] 0x120010ED0 aw----- .data foff:03792 sz:00000
[013] 0x120010ED0 a------ .eh_frame foff:03792 sz:00004
[014] 0x120010ED8 aw----- .dynamic foff:03800 sz:00352
[015] 0x120011038 aw----- .ctors foff:04152 sz:00016
[016] 0x120011048 aw----- .dtors foff:04168 sz:00016
[017] 0x120011058 aw----- .jcr foff:04184 sz:00008
[018] 0x120011060 awx---- .plt foff:04192 sz:00116
[019] 0x1200110D8 aw----- .got foff:04312 sz:00240
[020] 0x1200111C8 aw----- .sdata foff:04552 sz:00024
[021] 0x1200111E0 aw----- .sbss foff:04576 sz:00024
[022] 0x1200111F8 aw----- .bss foff:04600 sz:00056
[023] 0x120011230 a-x---- rel.o.text foff:04656 sz:00320
[024] 0x120011370 aw----- rel.o.sdata foff:04976 sz:00008
[025] 0x120011378 a--ms-- rel.o.rodata.str1.1 foff:04984 sz:00072
[026] 0x1200113C0 a-x---- .alt.plt.prolog foff:05056 sz:00048
[027] 0x1200113F0 a-x---- .alt.plt foff:05104 sz:00120
[028] 0x120011468 a------ .alt.got foff:05224 sz:00072
[029] 0x1200114B0 aw----- rel.o.got foff:05296 sz:00080
[030] 0x000000000 ------- .comment foff:05376 sz:00240
[031] 0x000000000 ------- .debug_aranges foff:05616 sz:00048
[032] 0x000000000 ------- .debug_pubnames foff:05664 sz:00027
[033] 0x000000000 ------- .debug_info foff:05691 sz:02994
[034] 0x000000000 ------- .debug_abbrev foff:08685 sz:00337
[035] 0x000000000 ------- .debug_line foff:09022 sz:00373
[036] 0x000000000 ------- .debug_frame foff:09400 sz:00048
[037] 0x000000000 ---ms-- .debug_str foff:09448 sz:01940
[038] 0x000000000 ------- .debug_macinfo foff:11388 sz:12937
[039] 0x000000000 ------- .ident foff:24325 sz:00054
[040] 0x000000000 ------- .shstrtab foff:24379 sz:00393
[041] 0x000000000 ------- .symtab foff:27527 sz:02400
[042] 0x000000000 ------- .strtab foff:29927 sz:00948
[Program header table .::. PHT]
[Object fake_aout]
[00] 0x120000040 -> 0x120000190 r-x => Program header table
[01] 0x120000190 -> 0x1200001A7 r-- => Program interpreter
[02] 0x120000000 -> 0x120000ECA r-x => Loadable segment
[03] 0x120010ED0 -> 0x120011510 rwx => Loadable segment
[04] 0x120010ED8 -> 0x120011038 rw- => Dynamic linking info
[05] 0x1200001A8 -> 0x1200001C0 r-- => Auxiliary information
[Program header table .::. SHT correlation]
[Object fake_aout]
[*] SHT is not stripped
[00] PT_PHDR
[01] PT_INTERP .interp
[02] PT_LOAD .interp .note.netbsd.ident .hash .dynsym .dynstr
.rela.dyn .rela.plt .init .text .fini .rodata
[03] PT_LOAD .data .eh_frame .dynamic .ctors .dtors .jcr .plt
.got .sdata .sbss .bss rel.o.text rel.o.sdata
rel.o.rodata.str1.1 .alt.plt.prolog .alt.plt
.alt.got rel.o.got
[04] PT_DYNAMIC .dynamic
[05] PT_NOTE .note.netbsd.ident
[*] Object fake_aout unloaded
========= END DUMP 23 =========
Les segments sont étendus comme il faut. On le voit à la
corrélation entre SHT et PHT : toutes les bornes sont correctes.
La section .alt.plt.prolog est là pour implémenter ALTPLTv2 sous
ALPHA. Ceci pourra patcher à la volée les octets de la première
entrée ALTPLT avec ceux de la première entrée PLT la première
fois que l'entrée ALTPLT sera appelée (quand on appelera une
fonction originale à partir d'une fonction de détournement la
première fois).
Quand nous avons découvert comment faire ALTPLTv3 (sans une ligne
d'assembleur), la .alt.plt.prolog est devenue une section de
bourrage pour que GOT et ALTGOT soit bien aligné sur une certaine
taille qui est nécessaire pour installer ALTPLT à cause de
l'encodage des instruction ALPHA pour un saut indirect du flux de
contrôle.
---[ D. Technique EXTPLT : post-liaison de fonctions inconnues
Cette technique est l'une des ameliorations majeures dans la nouvelle
version d'ELFsh. Elle fonctionne sur les fichiers ET_EXEC et ET_DYN,
inclus quand l'injection est faite directement en mémoire. EXTPLT
consiste à ajouter une nouvelle section (.elfsh.extplt) pour que
nous puissions ajouter des entrées pour les nouvelles fonctions.
Quand on couple l'extension par copie de .rel.plt, .got,
.dynsym, et .dynstr, cela permet de placer des entrées de
relocation qui correspondent aux besoin des nouveaux couples
ALTPLT/ALTGOT. Regardons les informations de relocation
additionnelles en utilisant la commande elfsh -r.
Tout d'abord, regardons la table de relocation originale du
binaire :
========= BEGIN DUMP 24 =========
[*] Object ./a.out has been loaded (O_RDONLY)
[RELOCATION TABLES]
[Object ./a.out]
{Section .rel.dyn}
[000] R_386_GLOB_DAT 0x08049850 sym[010] : __gmon_start__
[001] R_386_COPY 0x08049888 sym[004] : stdout
{Section .rel.plt}
[000] R_386_JMP_SLOT 0x08049860 sym[001] : fflush
[001] R_386_JMP_SLOT 0x08049864 sym[002] : puts
[002] R_386_JMP_SLOT 0x08049868 sym[003] : malloc
[003] R_386_JMP_SLOT 0x0804986C sym[005] : __libc_start_main
[004] R_386_JMP_SLOT 0x08049870 sym[006] : printf
[005] R_386_JMP_SLOT 0x08049874 sym[007] : free
[006] R_386_JMP_SLOT 0x08049878 sym[009] : read
[*] Object ./testsuite/etrel_inject/etrel_original/a.out unloaded
========= END DUMP 24 =========
Regardons mainteant la table modifiée :
========= BEGIN DUMP 25 =========
[*] Object fake_aout has been loaded (O_RDONLY)
[RELOCATION TABLES]
[Object ./fake_aout]
{Section .rel.dyn}
[000] R_386_GLOB_DAT 0x08049850 sym[010] : __gmon_start__
[001] R_386_COPY 0x08049888 sym[004] : stdout
{Section .rel.plt}
[000] R_386_JMP_SLOT 0x0804A8A0 sym[001] : fflush
[001] R_386_JMP_SLOT 0x0804A8A4 sym[002] : puts
[002] R_386_JMP_SLOT 0x0804A8A8 sym[003] : malloc
[003] R_386_JMP_SLOT 0x0804A8AC sym[005] : __libc_start_main
[004] R_386_JMP_SLOT 0x0804A8B0 sym[006] : printf
[005] R_386_JMP_SLOT 0x0804A8B4 sym[007] : free
[006] R_386_JMP_SLOT 0x0804A8B8 sym[009] : read
{Section .elfsh.reldyn}
[000] R_386_GLOB_DAT 0x08049850 sym[010] : __gmon_start__
[001] R_386_COPY 0x08049888 sym[004] : stdout
{Section .elfsh.relplt}
[000] R_386_JMP_SLOT 0x0804A8A0 sym[001] : fflush
[001] R_386_JMP_SLOT 0x0804A8A4 sym[002] : puts
[002] R_386_JMP_SLOT 0x0804A8A8 sym[003] : malloc
[003] R_386_JMP_SLOT 0x0804A8AC sym[005] : __libc_start_main
[004] R_386_JMP_SLOT 0x0804A8B0 sym[006] : printf
[005] R_386_JMP_SLOT 0x0804A8B4 sym[007] : free
[006] R_386_JMP_SLOT 0x0804A8B8 sym[009] : read
[007] R_386_JMP_SLOT 0x0804A8BC sym[011] : _IO_putc
[008] R_386_JMP_SLOT 0x0804A8C0 sym[012] : write
[*] Object fake_aout unloaded
========= END DUMP 25 =========
Comme vous le voyez, les fonctions _IO_putc (nom interne de
putchar) et write ont été utilisée dans l'objet injecté. Nous
avons du les insérer dans le binaire hôte pour que le binaire
final puisse fonctionner.
La section .elfsh.relplt est copiée à partir de la section
.rel.plt mais avec une taille double pour avoir de la place pour
des entrées additionnelles. Même si nous n'étendons qu'une des
table de relocation, les deux doivent être copiées, parce que sur
les fichier ET_DYN, le rtld reclame que les deux tables soient
adjacentes en mémoire, nous ne pouvons donc pas juste copier
.rel.plt mais avons aussi besoin de .rel.dyn (ou .rel.got) juste
avant la copie de .rel.plt. C'est pourquoi vous pouvez voir
.elfsh.reldyn et .elfsh.relplt .
Quand on a besoin de plus de symboles, d'autres sections sont
déplacées après le BSS, incluant .dynsym et .dynstr.
---[ E. Algorithmes compatibles IA32, SPARC32/64, ALPHA64, MIPS32
Introduisons maintenant les détails des algorithmes des
techniques montrées dans les exemples des paragraphes précédents.
Nous couvrons ici tous les pseudos algorithmes pour la
redirection ELF. Les algorithmes détaillés sur le déboguage plus
contraint sont donné à la fin de la partie suivante.
Puisque les techniques ALTPLT et ALTGOT sont si complémentaires,
nous les avons implémentées dans un seul algorithme que nous
donnons ici. Il n'y a aucune condition sur l'architecture SPARC
puisque c'est l'architecture par défaut dans la liste.
L'algorithme principal de ALTPLTv3 / ALTGOT (libelfsh/altplt.c)
se trouve dans elfsh_build_plt() et elfsh_relink_plt() et est
comme suit.
Il pourrait probablement être nettoyé si le code était remplacé
par des morceaux dédiés aux architectures mais ça dupliquerait du
code, nous l'avons donc laissé comme suit :
Algorithme Multiarchitecture ALTPLT / ALTGOT
+--------------------------------------------+
0/ IF [ ARCH = MIPS ET PLT n'est pas trouvé AND
Le fichier est dynamique ]
[
- récupère l'adresse de base de la section .text
- Trouve la signature d'opcode MIPS pour le PLT embarqué
à l'intérieur du PLT
- Modifier le SHT pour inclure l'en-tête de la section PLT
]
1/ SWITCH sur l'architecture ELF
[
MIPS:
* Insère la section .elfsh.gotprolog
* Insère la section .elfsh.padgot
ALPHA:
* Insère la section.elfsh.pltprolog
DEFAULT:
* Insère la section.elfsh.altplt (copie du .plt)
]
2/ IF [ ARCH est (MIPS ou ALPHA ou IA32) ]
[
* Insère la section .elfsh.altgot (copie du .got)
]
3/ POUR CHAQUE ENTRÉE (ALT)PLT :
[
IF [ PREMIÈRE entrée PLT ]
[
IF [ARCH est MIPS ]
[
* Insère des paires d'instructions ld/st dans
.elfsh.gotprolog pour copier les adresses de
variables externes placé dans GOT par le RTLD
dans la section ALTGOT. Voir le gestionnaire
d'altplt MIPS dans libelfsh/mips32.c
]
ELSE IF [ ARCH est IA32 ]
[
* Réencode la première entrée PLT en utilisant la
différence d'adresse GOT - ALTGOT (pour nous
replacer dans ALTGOT au lieu de GOT)
]
]
IF [ ARCH est MIPS ]
* Injecte le symbole OLD sur l'entrée PLT courante
ELSE
* Injecte le symbole OLD sur l'entrée ALTPLT courante
IF [ ARCH est ALPHA ]
* Change l'entrée de relocalisation pointant vers
l'entrée courante
IF [ ARCH est IA32 ]
* Réencode l'entrée courante du PLT et ALTPLT
]
4/ SWITCH sur l'architecture ELF
[
MIPS:
IA32:
* Change l'entrée DT_PLTGOT de l'adresse GOT vers ALTGOT
* Change les relocations GOT correspondantes
SPARC:
* Change l'entrée DT_PLTGOT de l'adresse PLT vers ALTPLT
* Change les relocations PLT correspondantes
]
Sous MIPS, il n'y a pas de table de relocation dans les
binaires ET_EXEC. Si nous voulons changer la relocation qui
fait référence à GOT dans le code MIPS, nous devons chercher
après un pattern dans le code tel qu'on puisse le modifier avec
la différence ALTGOT-GOT. Ils sont facilement trouvés puisque le
patch est toujours sur le même pattern d'instruction suivant :
3c1c0000 lui gp,0x0
279c0000 addiu gp,gp,0
Le champ 0 dans ces instructions devrait être changé au moment
de la liaison quand elles correspondront aux relocations MIPS
HI16 et LO16. Cependant, cette information n'est pas disponible
dans une table pour les binaires ET_EXEC, nous avons donc besoin
de les retrouver dans le code binaire. C'est plus facilement
faisable sous RISC puisque toutes les instructions font la mêem
taille, les faux positifs sont donc vraiment improbables. Une
fois qu'on trouve ces patterns, on les corrige avec la différence
ALTGOT-GOT dans les champs de la table de relocation. Bien
sur, nous ne changeront pas TOUTES les références à GOT dans le
code, puisque ça ne ferait que déplacer la GOT, sans
détournement. On corrige donc ces références dans les premiers
0x100 octets du .text et dans .init, .fini, ce qui concerne
uniqument les références aux entrées GOT réservées (remplies par
les adresses virtuelles dl-resolve et les adresses de linkmap).
De cette façon, nous obligeons le code original à utiliser la
section ALTGOT quand il accès aux entrées réservées (puisqu'elles
ont été relocalisées à l'exécution dans ALTGOT et pas dans GOT)
et les entrées GOT originales quand il accès aux entrées des
fonctions (ont peux alors détourner les fonctions en modifiant la
GOT).
Algorithme EXTPLT
+-----------------+
L'algorithme EXTPLT rentre bien dans l'algorithme précédent.
Nous avons juste besoin d'ajouter deux étapes dans l'algo
précédent :
Étape 2bis : Insèrer la section EXTPLT (copie de PLT) sur les
architectures supportées.
Étape 5 : Faire une copie miroir (et étendue) de la section
de liaison dynamique sur les architectures supportées. Regardons
un peut plus en détail l'algorithme implémenté dans
libelfsh/extplt.c.
* Copie les sections .rel.got (.rel.dyn) et .rel.plt après le
BSS, dans une section de taille double. Ces deux sections
doivent rester adjacentes en mémoire pour que EXTPLT
fonctionne aussi sur les objet ET_DYN.
* Mettre à jours les entrées DT_REL et DT_JUMPREL dans .dynamic
* Copier les sections .dunsym et .dynstr avec une taille double.
* Mettre à jours les entrées DT_SUMTAB e tDT_STRTAB dans .dynamic
Une fois que ces opérations sont faites, nous avons assez de
place dans toutes ces sections orientées liaison dynamique et
nous pouvons y ajouter à la demande des symboles dynamiques, des
noms de symboles, et des entrées de relocalisation nécessaires
pour ajouter des entrées PLT supplémentaires dans la section
EXTPLT.
Maintenant, chaque fois que nous rencontrons un symbole inconnu
dans le processus de relocation d'un objet ET_REL dans un
objet ET_EXEC ou ET_DYN, nous pouvons utiliser l'algorithme
REQUESTPLT, comme implémenté dans la fonction
elfsh_request_pltent() du fichier libelfsh/extplt.c file :
* Vérifier la place dans les copies des sections EXTPLT, RELPLT,
DYNSYM, DYNSTR, et ALTGOT.
* Initialiser l'entrée ALTGOT par la nouvelle entrée EXTPLT
allouée.
* Encoder l'entrée EXTPLT pour utiliser l'entrée ALTGOT.
* Insèrer une entrée de relocation dans .elfsh.relplt pour
la nouvelle entrée ALTGOT.
* Ajouter la taille de l'entrée de relocation à la valeur de
l'entrée DT_PLTRELSZ dans la section .dynamic.
* Insèrer le symbole manquant dans .elfsh.dynsym avec le nom
inséré dans la section .elfsh.dynstr.
* Ajouter la longueur du nom de symbole à la valeur de l'entrée
DT_STRSZ dans la section .dynamic.
Cet algorithme est appelé à partir de l'algorithme principal
d'injection et de relocation ET_REL chaque fois que l'objet
ET_REL utilise une fonction inconnue dont le symbole n'est pas
présent dans le programme hôte. Le nouvel algorithme d'injection
ET_REL est donné à la fin de la partie sur le déboguage contraint
de l'article.
ALgorithme CFLOW
+-----------------+
Cette technique est implémentée en utilisant des morceaux dédiés
aux architectures, mais l'algorithme global est le même pour
toutes les architectures :
- Créer la section .elfsh.hooks (une seule fois)
- Trouver le nombre d'octets aligné sur la taille de l'instruction :
* avec libasm sous IA32
* manuellement sur machine RISC
- Insèrer les entrées HOOK à la demande (voir le dump CFLOW pour le format)
- Insèrer JMP vers l'entrée hook dans le prologue de la fonction détournée.
- Aligner le JUMP sur la taille d'instruction avec des nop dans le prologue détourné.
- Insèrer les symboles hook_funcname et old_funcname dans
l'entrée hook pour être capable de rappeler la fonction
originale.
Cette technique passe Pax puisqu'elle n'a besoin d'aucune étape
de restauration d'octets pendant l'exécution. Nous pouvons
détourner l'adresse de notre choix en utilisant la technqiue
CFLOW, cependant, exécuter les octets originaux dans l'entrée
hook au lieu de leur place originale ne fonctionnera pas si on
place le détournement sur une instruction de branchement relatif.
En fait, les branchements relatifs seront résolu vers une
mauvaise adresse virtuelle nous nous exécutons leurs opcode au
mauvais endroit (dasn le .elfsh.hooks au lieu de leur place
originale) dans le processus. Souvenez-vous en quand vous placez
des détournements CFLOW : ils ne sont pas fait pours détourner
les instructions de branchements conditionnels.
-------[ V. Déboguage contraint
Dans les environnements actuels, les binaires endurcis sont
souvent du type ET_DYN. Nous devons supporter ce type d'injection
puisqu'il permet des modifications de librairies aussi puissantes
que les modifications d'exécutables. De plus, certaines
distributions sont fournies avec un ensemble de binaires par
défaut compilé en ET_DYN, comme les gentoo endurcies.
Une autre amélioration que nous voulions faire est la
relocation ET_REL en mémoire. L'algorithme pour celle-ci est
le même que pour l'injection sur disque, mais cette fois le
disque n'est pas changé, ça réduit donc les traces comme dans
[12]. On pense que ce type d'injection peut être utilisé dans les
exploits et le backdooring de processus sans toucher au disque
dur. Diabolique non ?!
Nous sommes au courant d'une autre implémentation de l'injection
ET_REL en mémoire [10]. La notre supporte plus d'architectures et
se couple avec la technique EXTPLT directement en mémoire, ce qui
n'a encore jamais été implémenté à notre connaissance.
Une dernière technique que nous voulions implémenter concerne
l'extension et le déboguage d'exécutables statiques. Nous avons
développé cette nouvelle technique que nous avons appelé
l'algorithme EXTSTATIC. Elle permet l'injection statique en
utilisant libc.a quand une fonction ou du code est manquant. Le
même algorithme que celui de l'injection ET_REL est utilisé sauf
que plus d'un fichier relogeable pris de libc.a est injecté à la
fois en utilisant un algorithme de dépendances récursives.
---[ A. Relocation ET_REL en mémoire
Vu qu'on veut que l'utilisateur puisse fournir son propre gestionnaire
de breakpoints, nous permettons un mapping direct d'un objet
ET_REL en mémoire. Pour cela, nous utilisons des zones mmap
supplémentaire, en faisant toujours attention à ne pas casser
PaX : nous ne mappons aucune zone à la fois en exécution ET en
écriture.
Dans e2dbg, les breakpoints peuvent être implémentés de deux
façons. Soit un opcode dépendant de l'architecture (comme 0xCC
sous IA32) est utilisé sur l'adresse désirée, ou alors les
primitives CFLOW/ALTPLT peuvent être utilisées à l'exécution.
Dans le second cas, l'appel système mprotect doit être utilisé
pour pouvoir modifier le code à l'exécution. Cependant, nous
pourrons nous passer de mrprotect bientôt pour l'injection à la
volée quand la technique CFLOW sera assez forte pour respecter
Pax en static et à l'exécution.
Jetons un coup d'oeil à un binaire qui n'utilise que printf et
puts pour mieux comprendre ces concepts :
========= BEGIN DUMP 26 =========
elfsh@WTH $ ./a.out
[host] main argc 1
[host] argv[0] is : ./a.out
First_printf test
First_puts
Second_printf test
Second_puts
LEGIT FUNC
legit func (test) !
========= END DUMP 26 =========
Nous utilisons un petit script elfsh comme e2dbg pour créer un
autre fichier avec le débogueur injecté dedans, en utilisant les
techniques elfsh habituelles. Jetons-y un coup d'oeil :
========= BEGIN DUMP 27 =========
elfsh@WTH $ cat inject_e2dbg.esh
#!../../vm/elfsh
load a.out
set 1.dynamic[08].val 0x2 # entrée pour DT_DEBUG
set 1.dynamic[08].tag DT_NEEDED
redir main e2dbg_run
save a.out_e2dbg
========= END DUMP 27 =========
Nous exécutons ensuite le binaire modifié.
========= BEGIN DUMP 28 =========
elfsh@WTH $ ./aout_e2dbg
The Embedded ELF Debugger 0.65 (32 bits built) .::.
.::. This software is under the General Public License V.2
.::. Please visit http://www.gnu.org
[*] Sun Jul 31 16:24:00 2005 - New object ./a.out_e2dbg loaded
[*] Sun Jul 31 16:24:00 2005 - New object /lib/tls/libc.so.6 loaded
[*] Sun Jul 31 16:24:00 2005 - New object ./ibc.so.6 loaded
[*] Sun Jul 31 16:24:00 2005 - New object /lib/ld-linux.so.2 loaded
[*] Sun Jul 31 16:24:00 2005 - New object /lib/libelfsh.so loaded
[*] Sun Jul 31 16:24:00 2005 - New object /lib/libreadline.so.5 loaded
[*] Sun Jul 31 16:24:00 2005 - New object /lib/libtermcap.so.2 loaded
[*] Sun Jul 31 16:24:00 2005 - New object /lib/libdl.so.2 loaded
[*] Sun Jul 31 16:24:00 2005 - New object /lib/libncurses.so.5 loaded
(e2dbg-0.65) quit
[..: Embedded ELF Debugger returns to the grave :...]
[e2dbg_run] returning to 0x08045139
[host] main argc 1
[host] argv[0] is : ./a.out_e2dbg
First_printf test
First_puts
Second_printf test
Second_puts
LEGIT FUNC
legit func (test) !
elfsh@WTH $
========= END DUMP 28 =========
Ok, c'était facile. Et si on voulais faire quelque chose de plus
intéressant comme de l'injection ET_REL en mémoire ? Nous allons
utiliser la commande profile pour découvrir la fonctionnalité
d'auto-profiling [NDT : ~ auto-description] d'e2dbg. Cette
commande est toujours utile pour en apprendre plus sur le
débogueur lui-même, et pour des problèmes de déboguage interne
qui peuvent apparaitre pendant le développement.
Notre léger pattern matching d'appel de fonctions rend la sortie
plus compréhensible qu'un affichage brute des informations de
profiling et a prix peu d'heures pour l'implémenter en utilisant
les macros ELFSH_PROFILE_{OUT,ERR,ROUT} dans libelfsh-internals.h et
libelfsh/error.c.
Nous allons aussi afficher la liste de linkmap. Les premiers champs
de la linkmap sont indépendant de l'OS. Il y a beaucoup d'autres champs
internes que nous n'avons pas affiché mais beaucoup d'informations
peuvent être récoltée à partir de là.
Voici le truc en action :
========= BEGIN DUMP 29 =========
elfsh@WTH $ ./a.out_e2dbg
The Embedded ELF Debugger 0.65 (32 bits built) .::.
.::. This software is under the General Public License V.2
.::. Please visit http://www.gnu.org
[*] Sun Jul 31 16:12:48 2005 - New object ./a.out_e2dbg loaded
[*] Sun Jul 31 16:12:48 2005 - New object /lib/tls/libc.so.6 loaded
[*] Sun Jul 31 16:12:48 2005 - New object ./ibc.so.6 loaded
[*] Sun Jul 31 16:12:48 2005 - New object /lib/ld-linux.so.2 loaded
[*] Sun Jul 31 16:12:48 2005 - New object /lib/libelfsh.so loaded
[*] Sun Jul 31 16:12:48 2005 - New object /lib/libreadline.so.5 loaded
[*] Sun Jul 31 16:12:48 2005 - New object /lib/libtermcap.so.2 loaded
[*] Sun Jul 31 16:12:48 2005 - New object /lib/libdl.so.2 loaded
[*] Sun Jul 31 16:12:48 2005 - New object /lib/libncurses.so.5 loaded
(e2dbg-0.65) linkmap
.::. Linkmap entries .::.
[01] addr : 0x00000000 dyn : 0x080497D4 -
[02] addr : 0x00000000 dyn : 0xFFFFE590 -
[03] addr : 0xB7E73000 dyn : 0xB7F9AD3C - /lib/tls/libc.so.6
[04] addr : 0xB7E26000 dyn : 0xB7E6F01C - ./ibc.so.6
[05] addr : 0xB7FB9000 dyn : 0xB7FCFF14 - /lib/ld-linux.so.2
[06] addr : 0xB7DF3000 dyn : 0xB7E24018 - /lib/libelfsh.so
[07] addr : 0xB7DC6000 dyn : 0xB7DEE46C - /lib/libreadline.so.5
[08] addr : 0xB7DC2000 dyn : 0xB7DC5BB4 - /lib/libtermcap.so.2
[09] addr : 0xB7DBE000 dyn : 0xB7DC0EEC - /lib/libdl.so.2
[10] addr : 0xB7D7C000 dyn : 0xB7DBB1C0 - /lib/libncurses.so.5
(e2dbg-0.65) list
.::. Working files .::.
[001] Sun Jul 31 16:24:00 2005 D ID: 9 /lib/libncurses.so.5
[002] Sun Jul 31 16:24:00 2005 D ID: 8 /lib/libdl.so.2
[003] Sun Jul 31 16:24:00 2005 D ID: 7 /lib/libtermcap.so.2
[004] Sun Jul 31 16:24:00 2005 D ID: 6 /lib/libreadline.so.5
[005] Sun Jul 31 16:24:00 2005 D ID: 5 /lib/libelfsh.so
[006] Sun Jul 31 16:24:00 2005 D ID: 4 /lib/ld-linux.so.2
[007] Sun Jul 31 16:24:00 2005 D ID: 3 ./ibc.so.6
[008] Sun Jul 31 16:24:00 2005 D ID: 2 /lib/tls/libc.so.6
[009] Sun Jul 31 16:24:00 2005 *D ID: 1 ./a.out_e2dbg
.::. ELFsh modules .::.
[*] No loaded module
(e2dbg-0.65) source ./etrelmem.esh
~load myputs.o
[*] Sun Jul 31 16:13:32 2005 - New object myputs.o loaded
[!!] Loaded file is not the linkmap, switching to STATIC mode
~switch 1
[*] Switched on object 1 (./a.out_e2dbg)
~mode dynamic
[*] e2dbg is now in DYNAMIC mode
~reladd 1 10
[*] ET_REL myputs.o injected succesfully in ET_EXEC ./a.out_e2dbg
~profile
.:: Profiling enable
+ <vm_print_actual@loop.c:38>
~redir puts myputs
+ <vm_implicit@implicit.c:91>
+ <cmd_hijack@fcthijack.c:19>
+ <elfsh_get_metasym_by_name@sym_common.c:283>
+ <elfsh_get_dynsymbol_by_name@dynsym.c:255>
+ <elfsh_get_dynsymtab@dynsym.c:87>
+ <elfsh_get_raw@section.c:691>
[P] --[ <elfsh_get_raw@section.c:691>
[P] --- Last 1 function(s) recalled 1 time(s) ---
+ <elfsh_get_dynsymbol_name@dynsym.c:17>
[W] <elfsh_get_dynsymbol_by_name@dynsym.c:274> Symbol not found
[P] --[ <elfsh_get_raw@section.c:691>
[P] --[ <elfsh_get_dynsymbol_name@dynsym.c:17>
[P] --- Last 2 function(s) recalled 12 time(s) ---
+ <elfsh_get_symbol_by_name@symbol.c:236>
+ <elfsh_get_symtab@symbol.c:110>
+ <elfsh_get_symbol_name@symbol.c:20>
[P] --[ <elfsh_get_symbol_name@symbol.c:20>
[P] --- Last 1 function(s) recalled 114 time(s) ---
+ <elfsh_hijack_function_by_name@hijack.c:25>
+ <elfsh_setup_hooks@hooks.c:199>
+ <elfsh_get_pagesize@hooks.c:783>
+ <elfsh_get_archtype@hooks.c:624>
+ <elfsh_get_arch@elf.c:179>
+ <elfsh_copy_plt@altplt.c:525>
+ <elfsh_static_file@elf.c:491>
+ <elfsh_get_segment_by_type@pht.c:215>
+ <elfsh_get_pht@pht.c:364>
+ <elfsh_get_segment_type@pht.c:174>
[P] --[ <elfsh_get_segment_type@pht.c:174>
[P] --- Last 1 function(s) recalled 4 time(s) ---
+ <elfsh_get_arch@elf.c:179>
[P] --[ <elfsh_get_arch@elf.c:179>
[P] --- Last 1 function(s) recalled 1 time(s) ---
+ <elfsh_relink_plt@altplt.c:121>
+ <elfsh_get_archtype@hooks.c:624>
[P] --[ <elfsh_get_arch@elf.c:179>
[P] --[ <elfsh_relink_plt@altplt.c:121>
[P] --[ <elfsh_get_archtype@hooks.c:624>
[P] --- Last 3 function(s) recalled 1 time(s) ---
+ <elfsh_get_elftype@hooks.c:662>
+ <elfsh_get_objtype@elf.c:204>
+ <elfsh_get_ostype@hooks.c:709>
+ <elfsh_get_real_ostype@hooks.c:679>
+ <elfsh_get_interp@interp.c:41>
+ <elfsh_get_raw@section.c:691>
[P] --[ <elfsh_get_raw@section.c:691>
[P] --- Last 1 function(s) recalled 1 time(s) ---
+ <elfsh_get_section_by_name@section.c:168>
+ <elfsh_get_section_name@sht.c:474>
[P] --[ <elfsh_get_section_name@sht.c:474>
[P] --- Last 1 function(s) recalled 1 time(s) ---
+ <elfsh_get_symbol_by_name@symbol.c:236>
+ <elfsh_get_symtab@symbol.c:110>
+ <elfsh_get_symbol_name@symbol.c:20>
[W] <elfsh_get_symbol_by_name@symbol.c:253> Symbol not found
[P] --[ <elfsh_get_symbol_name@symbol.c:20>
[P] --- Last 1 function(s) recalled 114 time(s) ---
+ <elfsh_is_pltentry@plt.c:73>
[W] <elfsh_is_pltentry@plt.c:77> Invalid NULL parameter
+ <elfsh_get_dynsymbol_by_name@dynsym.c:255>
+ <elfsh_get_dynsymtab@dynsym.c:87>
+ <elfsh_get_raw@section.c:691>
[P] --[ <elfsh_get_raw@section.c:691>
[P] --- Last 1 function(s) recalled 1 time(s) ---
+ <elfsh_get_dynsymbol_name@dynsym.c:17>
[P] --[ <elfsh_is_pltentry@plt.c:73>
[P] --[ <elfsh_get_dynsymbol_by_name@dynsym.c:255>
[P] --[ <elfsh_get_dynsymtab@dynsym.c:87>
[P] --[ <elfsh_get_raw@section.c:691>
[P] --[ <elfsh_get_dynsymbol_name@dynsym.c:17>
[P] --- Last 5 function(s) recalled 1 time(s) ---
+ <elfsh_get_plt@plt.c:16>
+ <elfsh_is_plt@plt.c:49>
+ <elfsh_get_section_name@sht.c:474>
+ <elfsh_is_altplt@plt.c:62>
[P] --[ <elfsh_is_plt@plt.c:49>
[P] --[ <elfsh_get_section_name@sht.c:474>
[P] --[ <elfsh_is_altplt@plt.c:62>
[P] --- Last 3 function(s) recalled 3 time(s) ---
+ <elfsh_get_anonymous_section@section.c:334>
+ <elfsh_get_raw@section.c:691>
[P] --[ <elfsh_is_plt@plt.c:49>
[P] --[ <elfsh_get_section_name@sht.c:474>
[P] --[ <elfsh_is_altplt@plt.c:62>
[P] --[ <elfsh_get_anonymous_section@section.c:334>
[P] --[ <elfsh_get_raw@section.c:691>
[P] --- Last 5 function(s) recalled 44 time(s) ---
+ <elfsh_get_arch@elf.c:179>
[P] --[ <elfsh_get_arch@elf.c:179>
[P] --- Last 1 function(s) recalled 1 time(s) ---
+ <elfsh_hijack_plt_ia32@ia32.c:258>
+ <elfsh_get_foffset_from_vaddr@raw.c:85>
+ <elfsh_get_pltentsz@plt.c:94>
[P] --[ <elfsh_get_arch@elf.c:179>
[P] --[ <elfsh_hijack_plt_ia32@ia32.c:258>
[P] --[ <elfsh_get_foffset_from_vaddr@raw.c:85>
[P] --[ <elfsh_get_pltentsz@plt.c:94>
[P] --- Last 4 function(s) recalled 1 time(s) ---
+ <elfsh_munprotect@runtime.c:97>
+ <elfsh_get_parent_section@section.c:380>
+ <elfsh_get_parent_segment@pht.c:304>
+ <elfsh_segment_is_readable@pht.c:14>
+ <elfsh_segment_is_writable@pht.c:21>
+ <elfsh_segment_is_executable@pht.c:28>
+ <elfsh_raw_write@raw.c:22>
+ <elfsh_get_parent_section_by_foffset@section.c:416>
+ <elfsh_get_sht@sht.c:159>
+ <elfsh_get_section_type@sht.c:887>
+ <elfsh_get_anonymous_section@section.c:334>
+ <elfsh_get_raw@section.c:691>
+ <elfsh_raw_write@raw.c:22>
+ <elfsh_get_parent_section_by_foffset@section.c:416>
+ <elfsh_get_sht@sht.c:159>
+ <elfsh_get_section_type@sht.c:887>
+ <elfsh_get_anonymous_section@section.c:334>
+ <elfsh_get_raw@section.c:691>
+ <elfsh_get_pltentsz@plt.c:94>
+ <elfsh_get_arch@elf.c:179>
+ <elfsh_mprotect@runtime.c:135>
[*] Function puts redirected to addr 0xB7FB6000 <myputs>
+ <vm_print_actual@loop.c:38>
~profile
+ <vm_implicit@implicit.c:91>
.:: Profiling disable
[*] ./etrelmem.esh sourcing -OK-
(e2dbg-0.65) continue
[..: Embedded ELF Debugger returns to the grave :...]
[e2dbg_run] returning to 0x08045139
[host] main argc 1
[host] argv[0] is : ./a.out_e2dbg
First_printf test
Hijacked puts !!! arg = First_puts
First_puts
Second_printf test
Hijacked puts !!! arg = Second_puts
Second_puts
Hijacked puts !!! arg = LEGIT FUNC
LEGIT FUNC
legit func (test) !
elfsh@WTH $
========= END DUMP 29 =========
Vraiment cool. Nous avons détourner 2 fonctions (puts et
legit_func) en utilisant 2 techniques différentes (ALTPLT et
CFLOW). Pour ceci, nous n'avons pas eu besoin d'injecter un
fichier ET_REL additionnel dans l'hôte ET_REL, mais nous avons
directement injecté le module de détournement dans la mémoire en
utilisant mmap.
Nous aurions pu afficher la SHT et la PHT juste après l'injection
ET_REL en mémoire. Nous gardons trace de tous les mappages quand
nous injectons ce genre d'objet relogeable, pour pouvoir les
démapper et les remapper plus tard :
========= BEGIN DUMP 30 =========
(e2dbg-0.65) s
[SECTION HEADER TABLE .::. SHT is not stripped]
[Object ./a.out_e2dbg]
[000] 0x00000000 ------- foff:00000 size:00308
[001] 0x08045134 a-x---- .elfsh.hooks foff:00308 size:00015
[002] 0x08046134 a-x---- .elfsh.extplt foff:04404 size:00032
[003] 0x08047134 a-x---- .elfsh.altplt foff:08500 size:04096
[004] 0x08048134 a------ .interp foff:12596 size:00019
[005] 0x08048148 a------ .note.ABI-tag foff:12616 size:00032
[006] 0x08048168 a------ .hash foff:12648 size:00064
[007] 0x080481A8 a------ .dynsym foff:12712 size:00176
[008] 0x08048258 a------ .dynstr foff:12888 size:00112
[009] 0x080482C8 a------ .gnu.version foff:13000 size:00022
[010] 0x080482E0 a------ .gnu.version_r foff:13024 size:00032
[011] 0x08048300 a------ .rel.dyn foff:13056 size:00016
[012] 0x08048310 a------ .rel.plt foff:13072 size:00056
[013] 0x08048348 a-x---- .init foff:13128 size:00023
[014] 0x08048360 a-x---- .plt foff:13152 size:00128
[015] 0x08048400 a-x---- .text foff:13312 size:00800
[016] 0x08048720 a-x---- .fini foff:14112 size:00027
[017] 0x0804873C a------ .rodata foff:14140 size:00185
[018] 0x080487F8 a------ .eh_frame foff:14328 size:00004
[019] 0x080497FC aw----- .ctors foff:14332 size:00008
[020] 0x08049804 aw----- .dtors foff:14340 size:00008
[021] 0x0804980C aw----- .jcr foff:14348 size:00004
[022] 0x08049810 aw----- .dynamic foff:14352 size:00200
[023] 0x080498D8 aw----- .got foff:14552 size:00004
[024] 0x080498DC aw----- .got.plt foff:14556 size:00040
[025] 0x08049904 aw----- .data foff:14596 size:00012
[026] 0x08049910 aw----- .bss foff:14608 size:00008
[027] 0x08049918 aw----- .elfsh.altgot foff:14616 size:00044
[028] 0x08049968 aw----- .elfsh.dynsym foff:14696 size:00192
[029] 0x08049AC8 aw----- .elfsh.dynstr foff:15048 size:00122
[030] 0x08049BA8 aw----- .elfsh.reldyn foff:15272 size:00016
[031] 0x08049BB8 aw----- .elfsh.relplt foff:15288 size:00064
[032] 0x00000000 ------- .comment foff:15400 size:00665
[033] 0x00000000 ------- .debug_aranges foff:16072 size:00120
[034] 0x00000000 ------- .debug_pubnames foff:16192 size:00042
[035] 0x00000000 ------- .debug_info foff:16234 size:06904
[036] 0x00000000 ------- .debug_abbrev foff:23138 size:00503
[037] 0x00000000 ------- .debug_line foff:23641 size:00967
[038] 0x00000000 ------- .debug_frame foff:24608 size:00076
[039] 0x00000000 ---ms-- .debug_str foff:24684 size:08075
[040] 0x00000000 ------- .debug_macinfo foff:32759 size:29295
[041] 0x00000000 ------- .shstrtab foff:62054 size:00496
[042] 0x00000000 ------- .symtab foff:64473 size:02256
[043] 0x00000000 ------- .strtab foff:66729 size:01665
[044] 0x40019000 aw----- myputs.o.bss foff:68394 size:04096
[045] 0x00000000 ------- .elfsh.rpht foff:72493 size:04096
[046] 0x4001A000 a-x---- myputs.o.text foff:76589 size:04096
[047] 0x4001B000 a--ms-- myputs.o.rodata.str1.1 foff:80685 size:04096
(e2dbg-0.65) p
[Program Header Table .::. PHT]
[Object ./a.out_e2dbg]
[00] 0x08045034 -> 0x08045134 r-x memsz(00256) filesz(00256)
[01] 0x08048134 -> 0x08048147 r-- memsz(00019) filesz(00019)
[02] 0x08045000 -> 0x080487FC r-x memsz(14332) filesz(14332)
[03] 0x080497FC -> 0x08049C30 rw- memsz(01076) filesz(01068)
[04] 0x08049810 -> 0x080498D8 rw- memsz(00200) filesz(00200)
[05] 0x08048148 -> 0x08048168 r-- memsz(00032) filesz(00032)
[06] 0x00000000 -> 0x00000000 rw- memsz(00000) filesz(00000)
[07] 0x00000000 -> 0x00000000 --- memsz(00000) filesz(00000)
[SHT correlation]
[Object ./a.out_e2dbg]
[*] SHT is not stripped
[00] PT_PHDR
[01] PT_INTERP .interp
[02] PT_LOAD .elfsh.hooks .elfsh.extplt .elfsh.altplt .interp
.note.ABI-tag .hash .dynsym .dynstr .gnu.version
.gnu.version_r .rel.dyn .rel.plt .init .plt
.text .fini .rodata .eh_frame
[03] PT_LOAD .ctors .dtors .jcr .dynamic .got .got.plt .data
.bss .elfsh.altgot .elfsh.dynsym .elfsh.dynstr
.elfsh.reldyn .elfsh.relplt
[04] PT_DYNAMIC .dynamic
[05] PT_NOTE .note.ABI-tag
[06] PT_GNU_STACK
[07] PT_PAX_FLAGS
[Runtime Program Header Table .::. RPHT]
[Object ./a.out_e2dbg]
[00] 0x40019000 -> 0x4001A000 rw- memsz(4096) filesz(4096)
[01] 0x4001A000 -> 0x4001B000 r-x memsz(4096) filesz(4096)
[02] 0x4001B000 -> 0x4001C000 r-x memsz(4096) filesz(4096)
[SHT correlation]
[Object ./a.out_e2dbg]
[*] SHT is not stripped
[00] PT_LOAD myputs.o.bss
[01] PT_LOAD myputs.o.text
[02] PT_LOAD myputs.o.rodata.str1.1
(e2dbg-0.65)
========= BEGIN DUMP 30 =========
Notre algorithme n'est pas vraiment optimisé puisqu'il alloue un
nouveau PT_LOAD par section. Ici, nous avons créer une table RPHT
(Runtime PHT) qui gère la liste de toutes les pages injectées à
l'exécution. Cette table n'a pas d'existance légale dans le
fichier ELF, mais elle nous évite d'étendre la PHT réelle avec
des zones mémoires additionnelles. La technique ne casse pas PaX
puisque toutes les zones sont allouées en utilisant les droits
strictement nécessaires. Cependant, si vous voulez rediriger une
fonction existante vers une fonction nouvellement ajoutée de
myputs.o, vous aurez besoin de changer un peu de code à
l'exécution, et donc il devient nécessaire de désactiver
l'options mrprotect pour éviter de casser PaX.
---[ B. Relocation ET_REL dans ET_DYN
Nous avons porté l'injection ET_REL et la technique EXTPLT pour
les fichiers ET_DYN. La plus grosse différence est que les
fichiers ET_DYN ont un espace d'adressage relatif sur le disque.
Bien sur, les binaires strippes n'ont pas d'effet nefaste sur nos
algorithmes et nous n'avons pas besoin d'informations
optionnelles comme la section debug ou autre chose (ça peut
sembler évident, mais des gens nous posent vraiment la question).
Voyons ce qu'il se passe sur ce fichier ET_DYN hôte :
========= BEGIN DUMP 31 =========
elfsh@WTH $ file main
main: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV),
stripped
elfsh@WTH $ ./main
0x800008c8 main(argc=0xbfa238d0, argv=0xbfa2387c, envp=0xbfa23878,
auxv=0xbfa23874) __guard=0xb7ef4148
ssp-all (Stack) Triggering an overflow by copying [20] of data into [10]
of space
main: stack smashing attack in function main()
Aborted
elfsh@WTH $ ./main AAAAA
0x800008c8 main(argc=0xbf898e40, argv=0xbf898dec, envp=0xbf898de8,
auxv=0xbf898de4) __guard=0xb7f6a148
ssp-all (Stack) Copying [5] of data into [10] of space
elfsh@WTH $ ./main AAAAAAAAAAAAAAAAAAAAAAAAAAA
0x800008c8 main(argc=0xbfd3c8e0, argv=0xbfd3c88c, envp=0xbfd3c888,
auxv=0xbfd3c884) __guard=0xb7f0b148
ssp-all (Stack) Copying [27] of data into [10] of space
main: stack smashing attack in function main()
Aborted
========= END DUMP 31 =========
Juste pour le fun, nous avons décidé d'étudier en priorité les
binaires endurcis de gentoo [11]. Ceux-ci sont fournis avec PIE
(Position independant Executable) et SSP (Stack Smashing
Protection) incorporés. Ça ne change pas une ligne de notre
algorithme. Voici quelques tests fait sur un binaire protégé
contre les débordement dans la pile avec un overflow dans le
premier paramètre, déclanchant le gestionnaire de débordement de
pile. Nous allons rediriger ce gestionnaire pour montrer qu'il
s'agit d'une fonction habituelle qui utilise les méchanismes PLT
classiques.
Voici le code que nous allons injecter :
========= BEGIN DUMP 32 =========
elfsh@WTH $ cat simple.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int fake_main(int argc, char **argv)
{
old_printf("I am the main function, I have %d argc and my "
"argv is %08X yupeelala \n",
argc, argv);
write(1, "fake_main is calling write ! \n", 30);
old_main(argc, argv);
return (0);
}
char* fake_strcpy(char *dst, char *src)
{
printf("The fucker wants to copy %s at address %08X \n", src, dst);
return ((char *) old_strcpy(dst, src));
}
void fake_stack_smash_handler(char func[], int damaged)
{
static int i = 0;
printf("calling printf from stack smashing handler %u\n", i++);
if (i>3)
old___stack_smash_handler(func, damaged);
else
printf("Same player play again [damaged = %08X] \n", damaged);
printf("A second (%d) printf from the handler \n", 2);
}
int fake_libc_start_main(void *one, void *two, void *three, void *four,
void *five, void *six, void *seven)
{
static int i = 0;
old_printf("fake_libc_start_main \n");
printf("start_main has been run %u \n", i++);
return (old___libc_start_main(one, two, three, four,
five, six, seven));
}
========= END DUMP 32 =========
Le script elfsh qui permet les modifications est le suivant :
========= BEGIN DUMP 33 =========
elfsh@WTH $ cat relinject.esh
#!../../../vm/elfsh
load main
load simple.o
reladd 1 2
redir main fake_main
redir __stack_smash_handler fake_stack_smash_handler
redir __libc_start_main fake_libc_start_main
redir strcpy fake_strcpy
save fake_main
quit
========= END DUMP 33 =========
Regardons le en actions !
========= BEGIN DUMP 34 =========
elfsh@WTH $ ./relinject.esh
The ELF shell 0.65 (32 bits built) .::.
.::. This software is under the General Public License V.2
.::. Please visit http://www.gnu.org
~load main
[*] Sun Jul 31 17:24:20 2005 - New object main loaded
~load simple.o
[*] Sun Jul 31 17:24:20 2005 - New object simple.o loaded
~reladd 1 2
[*] ET_REL simple.o injected succesfully in ET_DYN main
~redir main fake_main
[*] Function main redirected to addr 0x00005154 <fake_main>
~redir __stack_smash_handler fake_stack_smash_handler
[*] Function __stack_smash_handler redirected to addr
0x00005203 <fake_stack_smash_handler>
~redir __libc_start_main fake_libc_start_main
[*] Function __libc_start_main redirected to addr
0x00005281 <fake_libc_start_main>
~redir strcpy fake_strcpy
[*] Function strcpy redirected to addr 0x000051BD <fake_strcpy>
~save fake_main
[*] Object fake_main saved successfully
~quit
[*] Unloading object 1 (simple.o)
[*] Unloading object 2 (main) *
.:: Bye -:: The ELF shell 0.65
========= END DUMP 34 =========
Et quel est le résultat ?
========= BEGIN DUMP 35 =========
elfsh@WTH $ ./fake_main
fake_libc_start_main
start_main has been run 0
I am the main function, I have 1 argc and my argv is BF9A6F54 yupeelala
fake_main is calling write !
0x800068c8 main(argc=0xbf9a6e80, argv=0xbf9a6e2c, envp=0xbf9a6e28,
auxv=0xbf9a6e24) __guard=0xb7f78148
ssp-all (Stack) Triggering an overflow by copying [20] of data into [10]
of space
The fucker wants to copy 01234567890123456789 at address BF9A6E50
calling printf from stack smashing handler 0
Same player play again [damaged = 39383736]
A second (2) printf from the handler
elfsh@WTH $ ./fake_main AAAA
fake_libc_start_main
start_main has been run 0
I am the main function, I have 2 argc and my argv is BF83A164 yupeelala
fake_main is calling write !
0x800068c8 main(argc=0xbf83a090, argv=0xbf83a03c, envp=0xbf83a038,
auxv=0xbf83a034) __guard=0xb7f09148
ssp-all (Stack) Copying [4] of data into [10] of space
The fucker wants to copy AAAA at address BF83A060
elfsh@WTH $ ./fake_main AAAAAAAAAAAAAAA
fake_libc_start_main
start_main has been run 0
I am the main function, I have 2 argc and my argv is BF8C7F24 yupeelala
fake_main is calling write !
0x800068c8 main(argc=0xbf8c7e50, argv=0xbf8c7dfc, envp=0xbf8c7df8,
auxv=0xbf8c7df4) __guard=0xb7f97148
ssp-all (Stack) Copying [15] of data into [10] of space
The fucker wants to copy AAAAAAAAAAAAAAA at address BF8C7E20
========= END DUMP 35 =========
Aucun problème ici : nous detournons strcpy, main, libc_start_main et
__stack_smash_handler vers nos propres procédures comme le montre
la sortie. Nous appelons aussi write qui n'était pas disponible
dans le binaire original, ce qui montre que EXTPLT fonctionne sur
les objets ET_DYN, la bonne chose étant que ça marche sans
modifications.
Dans la version courante (0.65rc1), il y a cependant une
limitation sur ET_DYN. Nous devons éviter les variables
non-initialisées parce que ça pourrait ajouter des entrées dans
la table de relocation. Ce n'est pas un problème d'en ajouter
puisque nous copions aussi .rel.got (rel.dyn) dans EXTPLT sous
ET_DYN, mais ce n'est pas encore implémenté.
---[ C. Extentions d'exécutables statics
Maintenant, nous aimerions pouvoir déboguer des binaires statics
de la même manière que pour les dynamiques. Puisque nous ne
pouvons pas injecter e2dbg en utilisant les dépendances DT_NEEDED
sur les binaires statiques, l'idée est d'injecter e2dbg comme
ET_REL dans ET_EXEC puisque c'est possible sur les binaires
statiques. E2dbg a beaucoup plus de dépendances qu'un simple
programme rel.c. L'idée étendue est d'injecter la partie
manquante des librairies statiques quand c'est nécessaire.
Nous devons résoudre les dépendances à la volée pendant que
l'injection ET_REL est faite. Pour cela, nous utiliserons un
simple algorithme récursif sur le code relogeable existant :
quand un symbole n'est pas trouvé au moment du relogeage, soit
c'est un symbole old_* et c'est mis en attente de la deuxième
étape de relogeage (en fait, les symboles old_* apparaissent au
moment de la redirection, qui est fait après l'injection du
fichier ET_REL, nous ignorons donc ces symboles dans un premier
temps), ou le symbole de fonction est vraiment inconnu et nous
devons ajouter des informations pour que le rtld puisse le
résoudre quand même.
Pour permettre de trouver le ET_REL adéquat à injecter, ELFsh
extrait tous les objets ET_REL à partir d'une librairie statique (.a)
et ensuite, la résolution est faite en utilisant cet ensemble de
binaires. La fonctionnalité de workspace d'elfsh est assez utile
pour ça, quand des sessions sont faites sur plus d'un milier de
fichier ELF ET_EXEC en même temps (après extraction des modules
de libc.a et d'autres binaires statique, par exemple).
Les dépendances circulaires sont résolues en utilisant une
deuxième étape de relocalisation quand le symbole requis se
trouve dans un fichier qui sera injecté après le fichier courant.
La même seconde phase du mechanisme de relocation est utilisée
quand on doit reloger les objets ET_REL qui utilisent les symboles
old_*. Puisque les symboles OLD sont injectés au moment de la
redirection et que les fichiers ET_REL doivent être injectés avant
(pour que nous puissions utiliser les fonctions de l'objet ET_REL
comme fonctions de détournement), nous n'avons pas de symboles OLD
au moment de la relocation. La seconde phase de relocation est alors
déclanchée au moment de la sauvegarde (pour des modifications sur
le disque) ou récursivement résolu quand on injecte plusieurs ET_REL
avec des dépendances circulaires.
Un problème reste, jusqu'a présent, nous avions un PT_LOAD par
section injectée, nous atteignons vite plus de 500 PT_LOAD. Ceci
semble un peu trop pour un ficheir ELF static habituel. Nous
devons améliorer le méchanisme d'allocation PT_LOAD pour pouvoir
ajouter de plus grosses extensions à ce genre de binaire hôte.
Cette technique fournis les mêmes fonctionnalités que EXTPLT mais
pour les binaires statiques : nous pouvons injecter ce que nous
voulons (sans se soucier de ce que le fichier hôte contient).
Donc, voici un plus petit exemple fonctionnel :
========= BEGIN DUMP 36 =========
elfsh@WTH $ cat host.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int legit_func(char *str)
{
puts("legit func !");
return (0);
}
int main()
{
char *str;
char buff[BUFSIZ];
read(0, buff, BUFSIZ-1);
puts("First_puts");
puts("Second_puts");
fflush(stdout);
legit_func("test");
return (0);
}
elfsh@WTH $ file a.out
a.out: ELF 32-bit LSB executable, Intel 80386, statically linked,
not stripped
elfsh@WTH $ ./a.out
First_puts
Second_puts
legit func !
========= END DUMP 36 =========
Le code du fichier source injecté est le suivant :
========= BEGIN DUMP 37 =========
elfsh@WTH $ cat rel2.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
int glvar_testreloc = 42;
int glvar_testreloc_bss;
char glvar_testreloc_bss2;
short glvar_testreloc_bss3;
int hook_func(char *str)
{
int sd;
printf("hook func %s !\n", str);
return (old_legit_func(str));
}
int puts_troj(char *str)
{
int local = 1;
char *str2;
int fd;
char name[16];
void *a;
str2 = malloc(10);
*str2 = 'Z';
*(str2 + 1) = 0x00;
glvar_testreloc_bss = 43;
glvar_testreloc_bss2 = 44;
glvar_testreloc_bss3 = 45;
memset(name, 0, 16);
printf("Trojan injected ET_REL takes control now "
"[%s:%s:%u:%u:%hhu:%hu:%u] \n",
str2, str,
glvar_testreloc,
glvar_testreloc_bss,
glvar_testreloc_bss2,
glvar_testreloc_bss3,
local);
free(str2);
gethostname(name, 15);
printf("hostname : %s\n", name);
printf("printf called from puts_troj [%s] \n", str);
fd = open("/etc/services", 0, O_RDONLY);
if (fd)
{
if ((a = mmap(0, 100, PROT_READ, MAP_PRIVATE, fd, 0)) == (void *) -1)
{
perror("mmap");
close(fd);
printf("mmap failed : fd: %d\n", fd);
return (-1);
}
printf("-=-=-=-=-=- BEGIN /etc/services %d -=-=-=-=-=\n", fd);
printf("host : %.60s\n", (char *) a);
printf("-=-=-=-=-=- END /etc/services %d -=-=-=-=-=\n", fd);
printf("mmap succeed fd : %d\n", fd);
close(fd);
}
old_puts(str);
fflush(stdout);
return (0);
}
========= END DUMP 37 =========
Le script load_lib.esh, généré en utilisant un petit script bash,
ressemble à ça :
========= BEGIN DUMP 38 =========
elfsh@WTH $ head -n 10 load_lib.esh
#!../../../vm/elfsh
load libc/init-first.o
load libc/libc-start.o
load libc/sysdep.o
load libc/version.o
load libc/check_fds.o
load libc/libc-tls.o
load libc/elf-init.o
load libc/dso_handle.o
load libc/errno.o
========= END DUMP 38 =========
Voici le script ELFsh de l'injection :
========= BEGIN DUMP 39 =========
elfsh@WTH $ cat relinject.esh
#!../../../vm/elfsh
exec gcc -g3 -static host.c
exec gcc -g3 -static rel2.c -c
load a.out
load rel2.o
source ./load_lib.esh
reladd 1 2
redir puts puts_troj
redir legit_func hook_func
save fake_aout
quit
========= END DUMP 39 =========
Sortie épurée de l'injection :
========= BEGIN DUMP 40 =========
elfsh@WTH $ ./relinject.esh
The ELF shell 0.65 (32 bits built) .::.
.::. This software is under the General Public License V.2
.::. Please visit http://www.gnu.org
~exec gcc -g3 -static host.c
[*] Command executed successfully
~exec gcc -g3 -static rel2.c -c
[*] Command executed successfully
~load a.out
[*] Sun Jul 31 16:37:32 2005 - New object a.out loaded
~load rel2.o
[*] Sun Jul 31 16:37:32 2005 - New object rel2.o loaded
~source ./load_lib.esh
~load libc/init-first.o
[*] Sun Jul 31 16:37:33 2005 - New object libc/init-first.o loaded
~load libc/libc-start.o
[*] Sun Jul 31 16:37:33 2005 - New object libc/libc-start.o loaded
~load libc/sysdep.o
[*] Sun Jul 31 16:37:33 2005 - New object libc/sysdep.o loaded
~load libc/version.o
[*] Sun Jul 31 16:37:33 2005 - New object libc/version.o loaded
[[... 1414 files later ...]]
[*] ./load_lib.esh sourcing -OK-
~reladd 1 2
[*] ET_REL rel2.o injected succesfully in ET_EXEC a.out
~redir puts puts_troj
[*] Function puts redirected to addr 0x080B7026 <puts_troj>
~redir legit_func hook_func
[*] Function legit_func redirected to addr 0x080B7000 <hook_func>
~save fake_aout
[*] Object fake_aout saved successfully
~quit
[*] Unloading object 1 (libpthreadnonshared/pthread_atfork.oS)
[*] Unloading object 2 (libpthread/ptcleanup.o)
[*] Unloading object 3 (libpthread/pthread_atfork.o)
[*] Unloading object 4 (libpthread/old_pthread_atfork.o)
[[... 1416 files later ...]]
.:: Bye -:: The ELF shell 0.65
========= END DUMP 40 =========
Cela fonctionne-t-il ?
========= BEGIN DUMP 41 =========
elfsh@WTH $ ./fake_aout
Trojan injected ET_REL takes control now [Z:First_puts:42:43:44:45:1]
hostname : WTH
printf called from puts_troj [First_puts]
-=-=-=-=-=- BEGIN /etc/services 3 -=-=-=-=-=
host : # /etc/services
#
# Network services, Internet style
#
# Not
-=-=-=-=-=- END /etc/services 3 -=-=-=-=-=
mmap succeed fd : 3
First_puts
Trojan injected ET_REL takes control now [Z:Second_puts:42:43:44:45:1]
hostname : WTH
printf called from puts_troj [Second_puts]
-=-=-=-=-=- BEGIN /etc/services 3 -=-=-=-=-=
host : # /etc/services
#
# Network services, Internet style
#
# Not
-=-=-=-=-=- END /etc/services 3 -=-=-=-=-=
mmap succeed fd : 3
Second_puts
hook func test !
Trojan injected ET_REL takes control now [Z:legit func !:42:43:44:45:1]
hostname : WTH
printf called from puts_troj [legit func !]
-=-=-=-=-=- BEGIN /etc/services 3 -=-=-=-=-=
host : # /etc/services
#
# Network services, Internet style
#
# Not
-=-=-=-=-=- END /etc/services 3 -=-=-=-=-=
mmap succeed fd : 3
legit func !
========= END DUMP 41 =========
Oui, ça fonctionne. Maintenant, jetons un coup d'oeil au fichier
static fake_aout :
========= BEGIN DUMP 42 =========
elfsh@WTH $ ../../../vm/elfsh -f ./fake_aout -s
[*] Object ./fake_aout has been loaded (O_RDONLY)
[SECTION HEADER TABLE .::. SHT is not stripped]
[Object ./fake_aout]
[000] 0x00000000 ------- foff:000000 sz:00000
[001] 0x080480D4 a------ .note.ABI-tag foff:069844 sz:00032
[002] 0x08048100 a-x---- .init foff:069888 sz:00023
[003] 0x08048120 a-x---- .text foff:69920 sz:347364
[004] 0x0809CE10 a-x---- __libc_freeres_fn foff:417296 sz:02222
[005] 0x0809D6C0 a-x---- .fini foff:419520 sz:00029
[006] 0x0809D6E0 a------ .rodata foff:419552 sz:88238
[007] 0x080B2F90 a------ __libc_atexit foff:507792 sz:00004
[008] 0x080B2F94 a------ __libc_subfreeres foff:507796 sz:00036
[009] 0x080B2FB8 a------ .eh_frame foff:507832 sz:03556
[010] 0x080B4000 aw----- .ctors foff:512000 sz:00012
[011] 0x080B400C aw----- .dtors foff:512012 sz:00012
[012] 0x080B4018 aw----- .jcr foff:512024 sz:00004
[013] 0x080B401C aw----- .data.rel.ro foff:512028 sz:00044
[014] 0x080B4048 aw----- .got foff:512072 sz:00004
[015] 0x080B404C aw----- .got.plt foff:512076 sz:00012
[016] 0x080B4060 aw----- .data foff:512096 sz:03284
[017] 0x080B4D40 aw----- .bss foff:515380 sz:04736
[018] 0x080B5FC0 aw----- __libc_freeres_ptrs foff:520116 sz:00024
[019] 0x080B6000 aw----- rel2.o.bss foff:520192 sz:04096
[020] 0x080B7000 a-x---- rel2.o.text foff:524288 sz:04096
[021] 0x080B8000 aw----- rel2.o.data foff:528384 sz:00004
[022] 0x080B9000 a------ rel2.o.rodata foff:532480 sz:04096
[023] 0x080BA000 a-x---- .elfsh.hooks foff:536576 sz:00032
[024] 0x080BB000 aw----- libc/printf.o.bss foff:540672 sz:04096
[025] 0x080BC000 a-x---- libc/printf.o.text foff:544768 sz:04096
[026] 0x080BD000 aw----- libc/gethostname.o.bss foff:548864 sz:04096
[027] 0x080BE000 a-x---- libc/gethostname.o.text foff:552960 sz:04096
[028] 0x080BF000 aw----- libc/perror.o.bss foff:557056 sz:04096
[029] 0x080C0000 a-x---- libc/perror.o.text foff:561152 sz:04096
[030] 0x080C1000 a--ms-- libc/perror.o.rodata.str1.1 foff:565248 sz:04096
[031] 0x080C2000 a--ms-- libc/perror.o.rodata.str4.4 foff:569344 sz:04096
[032] 0x080C3000 aw----- libc/dup.o.bss foff:573440 sz:04096
[033] 0x080C4000 a-x---- libc/dup.o.text foff:577536 sz:04096
[034] 0x080C5000 aw----- libc/iofdopen.o.bss foff:581632 sz:04096
[035] 0x00000000 ------- .comment foff:585680 sz:20400
[036] 0x080C6000 a-x---- libc/iofdopen.o.text foff:585728 sz:04096
[037] 0x00000000 ------- .debug_aranges foff:606084 sz:00136
[038] 0x00000000 ------- .debug_pubnames foff:606220 sz:00042
[039] 0x00000000 ------- .debug_info foff:606262 sz:01600
[040] 0x00000000 ------- .debug_abbrev foff:607862 sz:00298
[041] 0x00000000 ------- .debug_line foff:608160 sz:00965
[042] 0x00000000 ------- .debug_frame foff:609128 sz:00068
[043] 0x00000000 ------- .debug_str foff:609196 sz:00022
[044] 0x00000000 ------- .debug_macinfo foff:609218 sz:28414
[045] 0x00000000 ------- .shstrtab foff:637632 sz:00632
[046] 0x00000000 ------- .symtab foff:640187 sz:30192
[047] 0x00000000 ------- .strtab foff:670379 sz:25442
[*] Object ./fake_aout unloaded
elfsh@WTH $ ../../../vm/elfsh -f ./fake_aout -p
[*] Object ./fake_aout has been loaded (O_RDONLY)
[Program Header Table .::. PHT]
[Object ./fake_aout]
[00] 0x8037000 -> 0x80B3D9C r-x memsz(511388) foff(000000) =>Loadable seg
[01] 0x80B4000 -> 0x80B7258 rw- memsz(012888) foff(512000) =>Loadable seg
[02] 0x80480D4 -> 0x80480F4 r-- memsz(000032) foff(069844) =>Aux. info.
[03] 0x0000000 -> 0x0000000 rw- memsz(000000) foff(000000) =>Stackflags
[04] 0x0000000 -> 0x0000000 --- memsz(000000) foff(000000) =>New PaXflags
[05] 0x80B6000 -> 0x80B7000 rwx memsz(004096) foff(520192) =>Loadable seg
[06] 0x80B7000 -> 0x80B8000 rwx memsz(004096) foff(524288) =>Loadable seg
[07] 0x80B8000 -> 0x80B8004 rwx memsz(000004) foff(528384) =>Loadable seg
[08] 0x80B9000 -> 0x80BA000 rwx memsz(004096) foff(532480) =>Loadable seg
[09] 0x80BA000 -> 0x80BB000 rwx memsz(004096) foff(536576) =>Loadable seg
[10] 0x80BB000 -> 0x80BC000 rwx memsz(004096) foff(540672) =>Loadable seg
[11] 0x80BC000 -> 0x80BD000 rwx memsz(004096) foff(544768) =>Loadable seg
[12] 0x80BD000 -> 0x80BE000 rwx memsz(004096) foff(548864) =>Loadable seg
[13] 0x80BE000 -> 0x80BF000 rwx memsz(004096) foff(552960) =>Loadable seg
[14] 0x80BF000 -> 0x80C0000 rwx memsz(004096) foff(557056) =>Loadable seg
[15] 0x80C0000 -> 0x80C1000 rwx memsz(004096) foff(561152) =>Loadable seg
[16] 0x80C1000 -> 0x80C2000 rwx memsz(004096) foff(565248) =>Loadable seg
[17] 0x80C2000 -> 0x80C3000 rwx memsz(004096) foff(569344) =>Loadable seg
[18] 0x80C3000 -> 0x80C4000 rwx memsz(004096) foff(573440) =>Loadable seg
[19] 0x80C4000 -> 0x80C5000 rwx memsz(004096) foff(577536) =>Loadable seg
[20] 0x80C5000 -> 0x80C6000 rwx memsz(004096) foff(581632) =>Loadable seg
[21] 0x80C6000 -> 0x80C7000 rwx memsz(004096) foff(585728) =>Loadable seg
[SHT correlation]
[Object ./fake_aout]
[*] SHT is not stripped
[00] PT_LOAD .note.ABI-tag .init .text __libc_freeres_fn .fini
.rodata __libc_atexit __libc_subfreeres .eh_frame
[01] PT_LOAD .ctors .dtors .jcr .data.rel.ro .got .got.plt
.data
.bss __libc_freeres_ptrs
[02] PT_NOTE .note.ABI-tag
[03] PT_GNU_STACK
[04] PT_PAX_FLAGS
[05] PT_LOAD rel2.o.bss
[06] PT_LOAD rel2.o.text
[07] PT_LOAD rel2.o.data
[08] PT_LOAD rel2.o.rodata
[09] PT_LOAD .elfsh.hooks
[10] PT_LOAD libc/printf.o.bss
[11] PT_LOAD libc/printf.o.text
[12] PT_LOAD libc/gethostname.o.bss
[13] PT_LOAD libc/gethostname.o.text
[14] PT_LOAD libc/perror.o.bss
[15] PT_LOAD libc/perror.o.text
[16] PT_LOAD libc/perror.o.rodata.str1.1
[17] PT_LOAD libc/perror.o.rodata.str4.4
[18] PT_LOAD libc/dup.o.bss
[19] PT_LOAD libc/dup.o.text
[20] PT_LOAD libc/iofdopen.o.bss |.comment
[21] PT_LOAD libc/iofdopen.o.text
[*] Object ./fake_aout unloaded
========= END DUMP 42 =========
On peut noter le ET_REL réellement injecté : printf.o@libc,
dup.o@libc, gethostname.o@libc, perror.o@libc et iofdopen.o@libc.
Chaque fichier injecté crée quelques segments PT_LOAD. Pour cet
exemple, c'est bon, mais pour injecter E2dbg, c'est vraiment trop.
Cette techniuque sera améliorée aussi vite que possible en
réutilisant les entrées PT_LOAD quand c'est possible.
----[ D. Algorithmes indépendants de l'architecture
Dans cette partie, nous donnons tous les algorithmes indépendants
des architectures que nous avons développés pour la nouvelle
technique de résidence en mémoire, librairies ET_DYN, ou
exécutables statiques.
Le nouvel algorithme générique d'injection ET_REL n'est pas si
différent de celui présenté dans l'article précédant dans
l'article Cerberus ELF Interface [0], c'est pourquoi nous ne
montrons encore que sa version courte. Cependant, le nouvel
algorithme a amélioré sa portabilité et modularité. Nous
détaillerons certaines parties qui n'ont pas été expliquée dans
les articles précédents. L'implémentation est principalement dans
elfsh_inject_etrel() du fichier relinject.c :
Nouvel Algorithme de relocalisation générique
+---------------------------------------------+
1/ Injecter le BSS du ET_REL après le BSS du programme hote
dans une nouvelle section dédiée
2/ FOREACH section dans l'objet ET_REL
[
IF [ Section est mappee et Section n'est pas BSS ]
[
- Injecter la section dans le fichier hôte ou la
mémoire
]
]
3/ Fusionner les tables de symboles du ET_REL et du fichier hôte
4/ Reloger l'objet ET_REL (Phase 1)
5/ Quand on sauvegarde, reloger l'objet ET_REL
(Phase 2 pour les symboles old)
Nous n'avions qu'une seule phase de relocalisation dans le passé.
Nous devons maintenant en avoir une deuxième puisque tous les
symboles requis ne sont pas disponibles (comme les symboles old
issus de la redirections CFLOW qui peut apparaitre après
l'injection ET_REL). Pour les modifications sur le disque, la
deuxième phase est faite au moment de la sauvegarde.
Certaines étapes dans cet algorithme sont assez évidentes, comme
les étapes 1 et 3. Elles ont été expliquées dans le premier
article [0], cependant l'algorithme BSS a changé par
compatibilité avec les fichiers ET_DYN et les injections ET_REL
multiples. Maintenant, le BSS est injecté comme une autre
section, au lieu d'ajouter un algorithme complexe de zone BSS
pour garder toujours un BSS dans le programme.
Algorithme d'injection de sections ET_DYN / ET_EXEC
+---------------------------------------------------+
L'algorithme d'injection pour les sections de DONNÉES ne change
pas entre les fichiers ET_EXEC et ET_DYN. Cependant, l'injection
de section de code change un peu pour supporter à la fois les
hôte sous forme de binaire et de librairie. Voici le nouvel
algorithme pour cette opération :
* Trouver le PT_LOAD exécutable
* Corriger la taille des sections injectées par congruence de
taille des pages
IF [ Hostfile est ET_EXEC ]
[
* Mettre la vaddr de la section injectée à la vaddr la plus
basse de la section mappée
* Soustraire la nouvelle taille de section à la nouvelle
adresse virtuelle de la section
]
ELSE IF [ Hostfile est ET_DYN ]
[
* Mettre la vaddr de la section injectée à la vadr la plus
basse de la section mappée
]
* Étendre la taille du segment de code par la taille de la section
injectée.
IF [ Hostfile est ET_EXEC ]
[
* Soustraire la vadr de la section injectée à la vaddr du
PT_LOAD de l'exécutable
]
FOREACH [ Entry dans PHT ]
[
IF [ Segment est PT_PHDR et Hostfile est ET_EXEC ]
[
* Soustraire la taille de la section injectée au segment
p_vaddr / p_paddr
]
ELSE IF [ Segment est après le PT_LOAD étendu ]
[
* Ajouter la taille de la section injectée au
segment p_offset
IF [ Hostfile est ET_DYN ]
[
* Ajouter la taille de la section injectée au
segment p_vaddr et p_paddr
]
]
]
IF [ Hostfile est ET_DYN ]
[
FOREACH [ entrée de relocation dans chaque table
de relocation ]
[
IF [ offset de relocation pointe après
la section injectée ]
[
* Changer l'offset de relocation à partir de
la taille de la section injectée
]
]
En utilisant la taille de la section injectée :
* Changer les symboles quand ils pointent après elle
* Changer les symboles dynamiques (même condition)
* Changer les entrées dynamiques du D_PTR
* Changer les entrées GOT
* Si elles existent, changer les entrées ALTGOT
* Changer DTORS et CTORS
* Changer le point d'entrée de l'en-tête ELF
]
* Injecter le nouveau symbole SECTION dans le code injecté
Algorithme d'injection statique de section ET_EXEC
+--------------------------------------------------+
Cet algorithme est utilisé pour insérer des sections dans les
binaires statiques. On le trouve dans libelfsh/inject.c, dans la
fonction elfsh_insert_static_section() :
* Changer la taille de la section injectée pour rester congru à la
taille de page.
* Créer un nouvel en-tête de programme PT_LOAD dont les bornes
correspondent à celles de la nouvelle section
* Insèrer la nouvelle sections en utilisant les algorithmes
classiques
* Insèrer le nouvel en-tête de programme dans la PHT
Algorithme d'injection de section à l'exécution en mémoire
+----------------------------------------------------------+
Cet algorithme se trouve dans libelfsh/inject.c, dans la fonction
elfsh_insert_runtime_section() :
* Créer un nouvel en-tête de programme PT_LOAD
* Insèrer une entrée SHT pour la nouvelle section à l'exécution
(pour garder une table statique à jours)
* Insèrer la nouvelle section en utilisant les algorithmes
classiques
* Insèrer le nouveau PT_LOAD dans la table runtime PHT (RPHT) avec
les mêmes bornes.
La Runtime PHT est une nouvelle table que nous avons introduite
pour nous permettre de séparer les segments habituellements
mappés par l'éditeur de lien dynamique (le segment PHT original)
des segments injectés à l'exécution. Cette manière nous conduira
dans le furut à un algorithme plus facile pour la reconstruction
du binaire à partir de son image mémoire.
Nous allons maintenant détailler l'algorithme principal (à haut
niveau) de relocation comme il est implémenté dans les fonctions
elfsh_relocate_object() et elfsh_relocate_etrel_section() du fichier
libelfsh/relinject.c . Ce code est commun pour tous les types de fichier
hôtes et pour toutes les étapes de relocalisation. Il est utilisé à
l'étape 4 de l'algorithme général :
Algorithme portable principal de relocation
+-----------------------------------------------+
Cet algorithme n'a pas encore été expliqué dans aucun article,
le voici :
FOREACH section ET_REL injectée dans le fichier hôte
[
FOREACH entrée de relocation dans le fichier ET_REL
[
* trouver les symboles nécessaires dans ET_REL pour
cette relocalisation
IF [ Symbol est COMMON ou NOTYPE ]
[
* Trouver le symbole correspondant le fichier hôte
IF [ Symbol est NOT FOUND ]
[
IF [ symbol est OLD et RELOCSTAGE == 1 ]
[
* Retarder sa relocalisation
]
ELSE
[
IF [ le type du symbole ET_REL
est NOTYPE ]
[
* Demander une nouvelle entrée PLT et
utilise son adresse pour faire la
relocation (Algorithme EXTPLT)
]
ELSE IF [ Fichier hôte est STATIC ]
[
* Utiliser la technique EXTSTATIC
(Algorithme suivant)
]
ELSE
[
* L'algorithme a échoué, retourne ERROR
]
]
]
ELSE
[
* Utilise la valeur du symbole du fichier hôte
]
]
ELSE
[
* Utilise l'adresse de base de la section
injectée comme valeur du symbole
]
- Entrée de relocalisation (switch/case entre les
gestionnaires dédiés aux architectures)
]
]
Extension EXTSTATIC de l'algorithme de relocalisation
+-----------------------------------------------------+
Dans ce cas, le fichier hôte est statique, nous pouvons essayer
de récupérer les symboles inconnus à partir des fichiers
relogeables des librairies statiques qui sont disponibles sur le
disque. Un exemple d'utilisation de la technique EXTSTATIC se
trouve dans le répertoire testsuite/etrel_inject/.
Voici l'algorithme EXTSTATIC qui se trouve à l'endroit spécifié
dans l'algorithme précédent pour fournir les mêmes
fonctionnalités que EXTPLT mais pour les binaires statics :
FOREACH objet ET_REL chargé dans ELFSH
[
IF [ Symbol est trouvé n'importe où dans l'ET_REL analysé
pour l'instant ]
[
IF [ Le symbole trouvé est plus fort que le résultat
courant ]
[
* Met à jours le meilleurs symbole et le fichier
ET_REL associé
]
ELSE
[
* Ignore le résultat de l'itération courante
]
]
]
* Injècter les dépendances ET_REL dans le fichier hôte
* Utiliser le nouveau symbole injecté dans le fichier hôte comme
symbole de relocation dans l'algorithme principal de
relocation.
Algorithme du symbole le plus fort
+----------------------------------+
Quand nous devons choisir entre plusieurs symboles qui ont le
même nom dans différents objets (autant pendant les injections
statiques qu'à l'exécution), nous utilisons ce simple algorithme
pour déterminer lequel utiliser :
IF [ Le symbole choisi pour l'instant a STT_NOTYPE ]
[
* Le symbole est un choix temporaire
]
ELSE IF [ Le symbole candidat a STT_NOTYPE ]
[
* Le symbole est un choix temporaire
]
ELSE IF [ le champ bind du symbole candidat > symbole courant ]
[
* Le symbole candidat devient le choix definitif
]
-------[ VI. Passé et présent
Dans le passé, nous avons montré que l'injection ET_REL dans les
objets ET_EXEC non-relogeable était possible. Ce papier vous a
montré des extentions multiples et des portages de cette technique
de résidence (pour des cibles ET_DYN et des exécutables
statiques). Couplée à la technique EXTPLT qui permet une post
liaison complète du fichier hôte, nous pouvons ajouter des
définitions de fonctions et utiliser des fonctions inconnues dans
le logiciel étendu. Toutes ces techniques d'injections statiques
marchent quand toutes les options de PaX sont activées sur
le binaire modifié. Bien sûr, les fonctionnalités de position
indépendante, et de protection de débordement de pile de Gentoo
ne protègent rien quand c'est appliqué à de la manipulation de
binaire, qu'elle ai lieu sur le disque où à l'exécution.
Nous avons aussi montré qu'il est possible de débogué sans
utiliser l'appel système ptrace, ce qui ouvre une porte pour le
reverse engineering et les méthodes de déboguage embarquées qui
contournent des techniques anti-débogue connues. Le débogueur
embarqué n'est pas complètement Pax-Proof et il est encore
nécessaire de désactiver le flag mrprotect. Même si ça ne semble
pas un réel problème, nous continuons de chercher une méthode
pour placer des breakpoints (e.g. des redirections) sans le
désactiver.
Nos techniques principales sont portables à beaucoup
d'architectures (x86, alpha, mips, sparc) à la fois sur des
fichiers 32bits et 64bits. Cependant, notre débogueur "preuve de
concept" n'a été implémenté que pour x86. Nous pensons que nos
techniques sont suffisament portable pour pouvoir fournir un
débogueur pour d'autres architectures sans trop de problèmes.
Distribuez et appréciez cette suite, les contributions sont les
bienvenues.
-------[ VII. Remerciements
Nous remercions tous les gens à WhatTheHack party 2005 aux
Pays-Bas. Nous nous sommes tant amusés avec vous, et nous
reviendrons la prochaine fois.
Des remerciements spéciaux à andrewg pour nous avoir expliquer la
technique sigaction, dvorak pour son intéret dans l'optimisation
de la technique ALTPLTv2 pour SPARC, sk pour libasm, et solar
pour nous avoir fournis les tests pour ET_DYN pie/ssp.
Nos respects à Devhell Labs, l'équipe PaX, le Phrackstaff,
GOBBLES, MMHS, ADM et Synnergy Networks. Un clin d'oeil final à
s/ash de RTC pour nous avoir conduit à WTH et le Coconut Crew
pour tout le reste, vous vous reconnaitrez.
-------[ VIII. Références
[0] The Cerberus ELF Interface mayhem
http://www.phrack.org/show.php?p=61&a=8
[1] The GNU debugger GNU project
http://www.gnu.org/software/gdb/
[2] PaX / grsecurity The PaX team
http://pax.grsecurity.net/
[3] binary reconstruction from a core image Silvio Cesare
http://vx.netlux.org/lib/vsc03.html
[4] Antiforensic evolution: Self Ripe & Pluf
http://www.phrack.org/show.php?p=63&a=11
[5] Next-Gen. Runtime binary encryption Zeljko Vbra
http://www.phrack.org/show.php?p=63&a=13
[6] Fenris Michal Zalewski
http://lcamtuf.coredump.cx/fenris/
[7] Ltrace Ltrace team
http://freshmeat.net/projects/ltrace/
[8] The dude (replacement to ptrace) Mammon
http://www.eccentrix.com/members/mammon/Text/d\
ude_paper.txt
[9] Binary protection schemes Andrewg
http://www.codebreakers-journal.com/viewar\
ticle.php?id=51&layout=abstract
[10] ET_REL injection in memory JP
http://www.whatever.org.ar/~cuco/MERCANO.TXT
[11] Hardened Gentoo project Hardened team
http://www.gentoo.org/proj/en/hardened/
[12] Unpacking by Code Injection Eduardo Labir
http://www.codebreakers-journal.com/viewart\
icle.php?id=36&layout=abstract