Oyé, oyé.
C'est les grandes vacances. On décompresse de cette dure année de scolarité qu'on a endurée. D'autres attendent des résultats pour leur examen - et, de ce faits, stressent. D'autres, comme moi, se reposent.
Je serai littéralement indisponible (sur le blog) et si je me mets à écrire un article pendant cette période, c'est que j'en ai vraiment la motivation.
Enfin, trêve de bavardages. J'ai fait un tutoriel sur les stack overflow sous windows. Je tiens à remercier 0vercl0k qui m'a autorisé à reprendre certains de ses concepts. Mon tutoriel est, par ailleurs, moins avancé que le sien.
Le lien ? ici : http://venom630.free.fr/geo/tutz/securite_informatique/buffer_overflows_windows/
Passez de bonnes vacances.
Geo
dimanche 5 juillet 2009
mardi 23 juin 2009
Faire ses shellcodes sous windows
[ 0vercl0k->where = flc ] - holiday >:] dit :
mets-les sur ton blogs, ces articles
[ 0vercl0k->where = flc ] - holiday >:] dit :
certains seraient contents de lire de tels articles
D'accord. Je veux bien.
Aujourd'hui, je vous diffuse un tutoriel amateur qui traite de la conception de shellcodes sous windows. Je n'ai rien de plus à dire, puisque tout est expliqué dedans.
Le lien ? C'est ici : http://venom630.free.fr/geo/tutz/programmation/asm/shellcoding_windoz/
J'en profite pour souhaiter bonne chance à ceux qui ont encore des épreuves à passer pour le baccalauréat.
Bonne lecture !
Geo
lundi 15 juin 2009
Sysenter, ou les interruptions noyau sous windows
Hoy ! Ca faisait longtemps que je n'avais pas posté d'article ! Pour dire, y'a rien eu en mai, c'est navrant ! Mais, comme dirait Ivanlef0u, au lieu de poster pour rien dire,
Avant de commencer, je lui dois un grand merci. Sans lui, ma lanterne ne serait pas éclairée. Petit big up également à Lord 0vercl0k, qui a su se montrer très patient. Ils forment une belle paire, si vous voulez mon avis.
Bon, bon, bon. Entrons dans le vif du sujet : l'instruction sysenter en assembleur. On rencontre souvent cette instruction lorsqu'on trace dans les appels aux APIs pendant une session de debuging. Etant curieux, je m'étais posé la question suivante : Mais c'est quelle instruction qui affiche du flux à l'écran à l'intérieur de WriteFile() ?. Eh bien, j'ai été surpris de tomber sur l'instruction Sysenter qui faisait tout basculer. Mais à quoi correspond-elle ?
L'instruction sysenter correspond au passage du mode "userland" (appelé "ring 3") au kerneland ("ring 0"). Les rings, ou anneaux sont représentés par un numéro codé sur 2 bits (donc de 0 à 3). Plus le chiffre est bas, plus les privilèges sont élevés. Les APIs se contentent donc de faire exécuter tel ou tel code en ring0, et nous, on a juste à faire de simples appels sur les APIs. Ca facilite la vie, disons.
Mon objectif premier était de déclencher ma propre apparition de flux à l'écran à l'aide d'une interruption noyau. En me référant à la Windows System Call Table, j'avais essayé avec la fameuse NtWriteFile(), sans succès.
C'est là que je viens voir Ivanlef0u, et il me conseille de tenter avec NtQuerySystemInformation(). Cette fonction, très bas niveau, sert visiblement à obtenir des informations sur le système. Enfin, au lieu d'utiliser les fonctions de type Nt, il m'a plutôt fallu utiliser les fonctions en Zw ; c'est-à-dire ZwQuerySystemInformation().
Sa documentation dans la MSDN : http://msdn.microsoft.com/en-us/library/ms725506(VS.85).aspx.
Sa description est on ne peut plus claire : "Retrieves the specified system information.", ou "réquisitionne les informations systèmes spécifiées".
Sa signature n'étant pas disponible dans un .h, il faut aller récupérer son adresse dans ntdll.dll et sauter dessus, dont la plupart des fonctions ne sont nullement documentées. Pour cela, on va utiliser deux fonctions de l'API Win32 :
(Vous verrez qu'en API WIN32, beaucoup de chose sont une histoire d' "handle".)
Liens MSDN : LoadLibrary() et GetProcAddress().
Voici une solution de code C que j'ai pu pondre :
La source est disponible ici dans son intégralité : http://venom630.free.fr/geo/blog/sysenter/ZwQuerySystemInformation_c.txt.
Je n'ai pas commenté le code (et c'est très mal), mais je vais vous expliquer concrètement ce que j'ai fait. D'abord, j'ai récupéré la grosse "enum" sur un site qui regroupe un tas de structures/enums/fonctions non documentées par Microsoft (merci à 0vercl0k, d'ailleurs) : http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/System%20Information/SYSTEM_INFORMATION_CLASS.html, et la structure SYSTEM_BASIC_INFORMATION sur la MSDN.
Petite parenthèse, pour ceux que ça intéresse : la signification des préfixes Nt et Zw : Nt signifie "New technologie", et Zw n'a aucune signification. Pourquoi ? Pour éviter les collisions entre les noms de fonction.
Enfin, j'ai fourni l'exécutable en plus du code. Tout est téléchargeable ici.
Maintenant, on va debugger notre programme et tracer à l'intérieur du call à notre fonction chargée dynamiquement. On pose donc un break à l'adresse 00401320 comme ceci :

Et on lance le programme (F9) pour ensuite tracer dans le call pas à pas (F7). On tombe sur ça :
La première instruction met le numéro du syscall dans EAX. Si l'on regarde la Windows System Call Table fournie par MetaSploit, on voit bien qu'il s'agit de NtQuerySystemInformation. Ensuite, on met dans Edx une adresse qui correspond à celle de la fonction KiFastSystemCall() (merci à 0vercl0k pour ce petit rectificatif), et on l'appelle via le call. On trace dedans :
La première instruction charge dans EDX la valeur d'ESP, ou le sommet de la pile. Probablement pour permettre, lors du passage dans ring0, de pouvoir revenir en Ring3. Apparement, les arguments de la fonction se situent aussi en-dessous dans la pile. On trace dans Sysenter, et on s'aperçoit que notre espace mémoire correspondant à arg1 en C a été modifié. A l'adresse 0022FF58, on a le nombre de processeurs dans notre ordinateur. Pour ma part, j'ai "1" car j'ai un monocore qui date un peu. Mais pour les processeurs double coeur, vous devriez voir "2" (merci à Ornlu pour avoir testé).
Voici ce que j'ai en mode console, en tout cas :
Quant aux fonctions qui affichent du flux à l'écran, elles appellent ZwRequestWaitReplyPort. Ivanlef0u m'a expliqué que
Même windows utilise les syscalls, mais ceux-ci sont moins faciles d'implémentation qu'avec linux où, pour certaines fonctions, on peut se contenter de mettre les arguments dans les registres et d'appeler le service 80h en interruption noyau. Pour Windows, ça reste compliqué mais vachement intéressant.
On va se quitter avec quelques articles qui parlent de l'instruction sysenter, notamment avec des concepts mieux détaillés voire avancés :
Ivanlef0u's Blog > SYSENTER, stepping into da ring0
Plongeon dans les appels sytème windows, par Emilien Girault (big up à toi aussi, d'ailleurs)
First Steps Into Ring0, par 0vercl0k
A la prochaine !
Geo
j'ai préféré fermer ma gueule.
Avant de commencer, je lui dois un grand merci. Sans lui, ma lanterne ne serait pas éclairée. Petit big up également à Lord 0vercl0k, qui a su se montrer très patient. Ils forment une belle paire, si vous voulez mon avis.
Bon, bon, bon. Entrons dans le vif du sujet : l'instruction sysenter en assembleur. On rencontre souvent cette instruction lorsqu'on trace dans les appels aux APIs pendant une session de debuging. Etant curieux, je m'étais posé la question suivante : Mais c'est quelle instruction qui affiche du flux à l'écran à l'intérieur de WriteFile() ?. Eh bien, j'ai été surpris de tomber sur l'instruction Sysenter qui faisait tout basculer. Mais à quoi correspond-elle ?
L'instruction sysenter correspond au passage du mode "userland" (appelé "ring 3") au kerneland ("ring 0"). Les rings, ou anneaux sont représentés par un numéro codé sur 2 bits (donc de 0 à 3). Plus le chiffre est bas, plus les privilèges sont élevés. Les APIs se contentent donc de faire exécuter tel ou tel code en ring0, et nous, on a juste à faire de simples appels sur les APIs. Ca facilite la vie, disons.
Mon objectif premier était de déclencher ma propre apparition de flux à l'écran à l'aide d'une interruption noyau. En me référant à la Windows System Call Table, j'avais essayé avec la fameuse NtWriteFile(), sans succès.
C'est là que je viens voir Ivanlef0u, et il me conseille de tenter avec NtQuerySystemInformation(). Cette fonction, très bas niveau, sert visiblement à obtenir des informations sur le système. Enfin, au lieu d'utiliser les fonctions de type Nt, il m'a plutôt fallu utiliser les fonctions en Zw ; c'est-à-dire ZwQuerySystemInformation().
Sa documentation dans la MSDN : http://msdn.microsoft.com/en-us/library/ms725506(VS.85).aspx.
Sa description est on ne peut plus claire : "Retrieves the specified system information.", ou "réquisitionne les informations systèmes spécifiées".
Sa signature n'étant pas disponible dans un .h, il faut aller récupérer son adresse dans ntdll.dll et sauter dessus, dont la plupart des fonctions ne sont nullement documentées. Pour cela, on va utiliser deux fonctions de l'API Win32 :
- LoadLibrary() : elle prend exactement un paramètre : le nom de la DLL. Elle retourne un "handle" (ou poignée) sur cette DLL. Un "handle", en informatique, permet de manipuler un bloc de donnée. Imaginez par exemple une simple raquette de tennis : il vous faut une poignée pour manipuler la grande surface. Ici, c'est pareil ;
- GetProcAddress() : elle prend respectivement en argument le handle retourné par LoadLibrary, et le nom de la fonction dont on veut l'adresse : ici, ça sera ZwQuerySystemInformation. GetProcAdress retourne aussi un handle, et cette fois, sur la fonction.
(Vous verrez qu'en API WIN32, beaucoup de chose sont une histoire d' "handle".)
Liens MSDN : LoadLibrary() et GetProcAddress().
Voici une solution de code C que j'ai pu pondre :
#include <windows.h>
#include <stdio.h>
typedef enum _SYSTEM_INFORMATION_CLASS {
SystemBasicInformation,
SystemProcessorInformation,
SystemPerformanceInformation,
SystemTimeOfDayInformation,
SystemPathInformation,
SystemProcessInformation,
SystemCallCountInformation,
SystemDeviceInformation,
SystemProcessorPerformanceInformation,
SystemFlagsInformation,
SystemCallTimeInformation,
SystemModuleInformation,
SystemLocksInformation,
SystemStackTraceInformation,
SystemPagedPoolInformation,
SystemNonPagedPoolInformation,
SystemHandleInformation,
SystemObjectInformation,
SystemPageFileInformation,
SystemVdmInstemulInformation,
SystemVdmBopInformation,
SystemFileCacheInformation,
SystemPoolTagInformation,
SystemInterruptInformation,
SystemDpcBehaviorInformation,
SystemFullMemoryInformation,
SystemLoadGdiDriverInformation,
SystemUnloadGdiDriverInformation,
SystemTimeAdjustmentInformation,
SystemSummaryMemoryInformation,
SystemNextEventIdInformation,
SystemEventIdsInformation,
SystemCrashDumpInformation,
SystemExceptionInformation,
SystemCrashDumpStateInformation,
SystemKernelDebuggerInformation,
SystemContextSwitchInformation,
SystemRegistryQuotaInformation,
SystemExtendServiceTableInformation,
SystemPrioritySeperation,
SystemPlugPlayBusInformation,
SystemDockInformation,
SystemPowerInformation2,
SystemProcessorSpeedInformation,
SystemCurrentTimeZoneInformation,
SystemLookasideInformation
} SYSTEM_INFORMATION_CLASS, *PSYSTEM_INFORMATION_CLASS;
typedef struct _SYSTEM_BASIC_INFORMATION {
BYTE Reserved1[24];
PVOID Reserved2[4];
CCHAR NumberOfProcessors;
} SYSTEM_BASIC_INFORMATION;
typedef int (*MYPROC)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
int main(int argc, char **argv) {
HINSTANCE hinstLib;
MYPROC ProcAdd;
BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;
SYSTEM_BASIC_INFORMATION arg1;
hinstLib = LoadLibrary(TEXT("ntdll.dll"));
if (hinstLib != NULL) {
ProcAdd = (MYPROC)GetProcAddress(hinstLib, "ZwQuerySystemInformation");
if (NULL != ProcAdd) {
fRunTimeLinkSuccess = TRUE;
ProcAdd(SystemBasicInformation, (PVOID)&arg1, sizeof(arg1), NULL );
printf("Processeur : %d\n",arg1.NumberOfProcessors);
system("pause > nul");
}
fFreeResult = FreeLibrary(hinstLib);
}
return 0;
}
#include <stdio.h>
typedef enum _SYSTEM_INFORMATION_CLASS {
SystemBasicInformation,
SystemProcessorInformation,
SystemPerformanceInformation,
SystemTimeOfDayInformation,
SystemPathInformation,
SystemProcessInformation,
SystemCallCountInformation,
SystemDeviceInformation,
SystemProcessorPerformanceInformation,
SystemFlagsInformation,
SystemCallTimeInformation,
SystemModuleInformation,
SystemLocksInformation,
SystemStackTraceInformation,
SystemPagedPoolInformation,
SystemNonPagedPoolInformation,
SystemHandleInformation,
SystemObjectInformation,
SystemPageFileInformation,
SystemVdmInstemulInformation,
SystemVdmBopInformation,
SystemFileCacheInformation,
SystemPoolTagInformation,
SystemInterruptInformation,
SystemDpcBehaviorInformation,
SystemFullMemoryInformation,
SystemLoadGdiDriverInformation,
SystemUnloadGdiDriverInformation,
SystemTimeAdjustmentInformation,
SystemSummaryMemoryInformation,
SystemNextEventIdInformation,
SystemEventIdsInformation,
SystemCrashDumpInformation,
SystemExceptionInformation,
SystemCrashDumpStateInformation,
SystemKernelDebuggerInformation,
SystemContextSwitchInformation,
SystemRegistryQuotaInformation,
SystemExtendServiceTableInformation,
SystemPrioritySeperation,
SystemPlugPlayBusInformation,
SystemDockInformation,
SystemPowerInformation2,
SystemProcessorSpeedInformation,
SystemCurrentTimeZoneInformation,
SystemLookasideInformation
} SYSTEM_INFORMATION_CLASS, *PSYSTEM_INFORMATION_CLASS;
typedef struct _SYSTEM_BASIC_INFORMATION {
BYTE Reserved1[24];
PVOID Reserved2[4];
CCHAR NumberOfProcessors;
} SYSTEM_BASIC_INFORMATION;
typedef int (*MYPROC)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
int main(int argc, char **argv) {
HINSTANCE hinstLib;
MYPROC ProcAdd;
BOOL fFreeResult, fRunTimeLinkSuccess = FALSE;
SYSTEM_BASIC_INFORMATION arg1;
hinstLib = LoadLibrary(TEXT("ntdll.dll"));
if (hinstLib != NULL) {
ProcAdd = (MYPROC)GetProcAddress(hinstLib, "ZwQuerySystemInformation");
if (NULL != ProcAdd) {
fRunTimeLinkSuccess = TRUE;
ProcAdd(SystemBasicInformation, (PVOID)&arg1, sizeof(arg1), NULL );
printf("Processeur : %d\n",arg1.NumberOfProcessors);
system("pause > nul");
}
fFreeResult = FreeLibrary(hinstLib);
}
return 0;
}
La source est disponible ici dans son intégralité : http://venom630.free.fr/geo/blog/sysenter/ZwQuerySystemInformation_c.txt.
Je n'ai pas commenté le code (et c'est très mal), mais je vais vous expliquer concrètement ce que j'ai fait. D'abord, j'ai récupéré la grosse "enum" sur un site qui regroupe un tas de structures/enums/fonctions non documentées par Microsoft (merci à 0vercl0k, d'ailleurs) : http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/System%20Information/SYSTEM_INFORMATION_CLASS.html, et la structure SYSTEM_BASIC_INFORMATION sur la MSDN.
Petite parenthèse, pour ceux que ça intéresse : la signification des préfixes Nt et Zw : Nt signifie "New technologie", et Zw n'a aucune signification. Pourquoi ? Pour éviter les collisions entre les noms de fonction.
Enfin, j'ai fourni l'exécutable en plus du code. Tout est téléchargeable ici.
Maintenant, on va debugger notre programme et tracer à l'intérieur du call à notre fonction chargée dynamiquement. On pose donc un break à l'adresse 00401320 comme ceci :

Et on lance le programme (F9) pour ensuite tracer dans le call pas à pas (F7). On tombe sur ça :
7C91D92E > B8 AD000000 MOV EAX,0AD
7C91D933 BA 0003FE7F MOV EDX,7FFE0300
7C91D938 FF12 CALL DWORD PTR DS:[EDX]
7C91D93A C2 1000 RETN 10
7C91D933 BA 0003FE7F MOV EDX,7FFE0300
7C91D938 FF12 CALL DWORD PTR DS:[EDX]
7C91D93A C2 1000 RETN 10
La première instruction met le numéro du syscall dans EAX. Si l'on regarde la Windows System Call Table fournie par MetaSploit, on voit bien qu'il s'agit de NtQuerySystemInformation. Ensuite, on met dans Edx une adresse qui correspond à celle de la fonction KiFastSystemCall() (merci à 0vercl0k pour ce petit rectificatif), et on l'appelle via le call. On trace dedans :
7C91E510 > 8BD4 MOV EDX,ESP
7C91E512 0F34 SYSENTER
7C91E514 > C3 RETN
7C91E512 0F34 SYSENTER
7C91E514 > C3 RETN
La première instruction charge dans EDX la valeur d'ESP, ou le sommet de la pile. Probablement pour permettre, lors du passage dans ring0, de pouvoir revenir en Ring3. Apparement, les arguments de la fonction se situent aussi en-dessous dans la pile. On trace dans Sysenter, et on s'aperçoit que notre espace mémoire correspondant à arg1 en C a été modifié. A l'adresse 0022FF58, on a le nombre de processeurs dans notre ordinateur. Pour ma part, j'ai "1" car j'ai un monocore qui date un peu. Mais pour les processeurs double coeur, vous devriez voir "2" (merci à Ornlu pour avoir testé).
Voici ce que j'ai en mode console, en tout cas :
Processeur : 1
Quant aux fonctions qui affichent du flux à l'écran, elles appellent ZwRequestWaitReplyPort. Ivanlef0u m'a expliqué que
la console est gérée bizarrement. Ca à donc l'air compliqué d'afficher du flux soi-même avec un sysenter. Si y'a un commentaire pour me dépanner, il est le bienvenue. Mes connaissances sur le sujet ne sont que peu conséquentes, je suis débutant.
Conclusion ?
Même windows utilise les syscalls, mais ceux-ci sont moins faciles d'implémentation qu'avec linux où, pour certaines fonctions, on peut se contenter de mettre les arguments dans les registres et d'appeler le service 80h en interruption noyau. Pour Windows, ça reste compliqué mais vachement intéressant.
On va se quitter avec quelques articles qui parlent de l'instruction sysenter, notamment avec des concepts mieux détaillés voire avancés :
Ivanlef0u's Blog > SYSENTER, stepping into da ring0
Plongeon dans les appels sytème windows, par Emilien Girault (big up à toi aussi, d'ailleurs)
First Steps Into Ring0, par 0vercl0k
A la prochaine !
Geo
mercredi 29 avril 2009
Format PE - Part II
Salut, salut.
Chose promise, chose due. Je continue l'aventure sur le format PE. Tout d'abord, je vaisvous raconter ma sale journée vous résumer brièvement ce qu'évoquait le "Part I" :
- Etude de la définition du format ;
- Etude de l'en-tête DOS ;
- Etude de l'en-tête principale ;
- Afficher le timestamp de compilation - ou, pour rester français, la date à laquelle le programme a été traduit en code machine ;
- Afficher le nombre de sections.
Je m'étais arrêté sur le dernier point, et j'avais enchaîné avec un article qui récupérait le code machine de la section .code. Je parle de section, mais je ne l'ai pas vraiment définie. Alors, allons-y.
Je ne vais pas m'attarder sur google pour chercher une définition, mais plutôt essayer de vous expliquer tout cela comme si vous étiez en face de moi. Une section, dans un exécutable, c'est une sorte de paragraphe qui a ses propriétés. Chaque section a une en-tête qui lui est propre et qui définit ses caractéristiques. Voici celles que je suis en mesure de vous décrire succinctement :
- son nom, codé sur 8 octets ;
- sa taille virtuelle : la taille à réserver lors du chargement de la section en mémoire (je me suis servi de cette caractéristique pour le dump de la section .text, après avoir compilé le shellcode d'0vercl0k) ;
- son adresse virtuelle (on en parlera prochainement) ;
- l'offset du début de la section ;
- ...
Si vous ne comprenez toujours pas, je peux vous citer des exemples de section connues :
- .text : contient toutes les instructions binaires qui seront exécutées sequentiellement ;
- .rdata : contient les données nécessaires au programme (r = read). Par exemple, si vous avez fait, en C, un printf("Hello"), la chaîne de caractère sera écrite "en bazar" dans la section .rdata, et on accédera à cette chaîne via son adresse mémoire virtuelle ;
- .bss : espace mémoire réservé par le programme afin de permettre aux instructions machines de stocker des valeurs si besoin ;
- .idata : espace mémoire réservé pour la table des importations. Cette section renferme le nom des fonctions utilisées par le programme, ainsi que les bibliothèques dynamiques (ou DLL) chargées.
En résumé, si on se replonge dans le bain, un PE ressemble à ça :

Certes, pour ceux qui ont déjà lu de la documentation, ce schéma n'est pas exceptionnel et on le retrouve de partout.
Pour ceux qui ouvrent des exécutables avec OllyDbg, vous vous apercevez que les adresses mémoires sont de la forme 0040XXXX. Ce sont des adresses mémoires virtuelles. La base des adresses mémoires est définie dans l'en-tête optionnelle du PE :
00400098 0B01 DW 010B ; MagicNumber = PE32
0040009A 02 DB 02 ; MajorLinkerVersion = 2
0040009B 38 DB 38 ; MinorLinkerVersion = 38 (56.)
0040009C 000A0000 DD 00000A00 ; SizeOfCode = A00 (2560.)
004000A0 00120000 DD 00001200 ; SizeOfInitializedData = 1200 (4608.)
004000A4 00020000 DD 00000200 ; SizeOfUninitializedData = 200 (512.)
004000A8 80120000 DD 00001280 ; AddressOfEntryPoint = 1280
004000AC 00100000 DD 00001000 ; BaseOfCode = 1000
004000B0 00200000 DD 00002000 ; BaseOfData = 2000
004000B4 00004000 DD 00400000 ; ImageBase = 400000
(pour info, j'ai dumpé un bout de l'en-tête de mon fameux "Hello.exe" du part I, avec OllyDbg).
Je vous ai montré le champ important. Malgré ça, il y en a d'autres plutôt intéressants. Je n'expliciterai cependant pas là-dessus. Quoi, vous tenez quand même à savoir ?
Bon, eh bien, je dois vous avouer que j'aurais du le faire plus tôt ; je vais vous passer un pdf qui semble très bien résumer (mieux que moi, en tout cas) l'architecture du format PE. http://venom630.free.fr/geo/blog/format_pe_partii/Le_format_PE.pdf. A croire que cet article n'aurait servi à rien, si ce n'est vous retranscrire cela dans un langage peut-être plus familier !
Exceptionnellement, on ne va pas se quitter avec un code source. Il faut que je prenne le temps de me mettre à l'API WIN32, mais, en toute honnêteté, j'ai drôlement du mal à me familiariser avec la msdn et les noms de fonctions compliqués. A croire que je ne sais me servir que que Sleep() et de MessageBox(). Celui qui se marre, ... grmbl.
Là, encore, il n'y a pas grand chose à dire pour clore l'article, si ce n'est que le format PE est intéressant, et qu'une idée m'est venue à la tête concernant les sections. Je n'ai pas fait de recherche sur google, mais autant retranscrire l'idée maintenant (pour le peu de lecteurs qui suivent) : si on ne déclare pas une section avec son en-tête, se peut-il qu'on puisse allouer un espace mémoire non "détecté" dans les en-tête et y injecter du code malicieux ? A réfléchir. Ou pas.
Geo
URL de support : http://venom630.free.fr/geo/?path=blog/format_pe_partii (contient l'image et le pdf adéquats à l'article)
Chose promise, chose due. Je continue l'aventure sur le format PE. Tout d'abord, je vais
- Etude de la définition du format ;
- Etude de l'en-tête DOS ;
- Etude de l'en-tête principale ;
- Afficher le timestamp de compilation - ou, pour rester français, la date à laquelle le programme a été traduit en code machine ;
- Afficher le nombre de sections.
Je m'étais arrêté sur le dernier point, et j'avais enchaîné avec un article qui récupérait le code machine de la section .code. Je parle de section, mais je ne l'ai pas vraiment définie. Alors, allons-y.
Je ne vais pas m'attarder sur google pour chercher une définition, mais plutôt essayer de vous expliquer tout cela comme si vous étiez en face de moi. Une section, dans un exécutable, c'est une sorte de paragraphe qui a ses propriétés. Chaque section a une en-tête qui lui est propre et qui définit ses caractéristiques. Voici celles que je suis en mesure de vous décrire succinctement :
- son nom, codé sur 8 octets ;
- sa taille virtuelle : la taille à réserver lors du chargement de la section en mémoire (je me suis servi de cette caractéristique pour le dump de la section .text, après avoir compilé le shellcode d'0vercl0k) ;
- son adresse virtuelle (on en parlera prochainement) ;
- l'offset du début de la section ;
- ...
Si vous ne comprenez toujours pas, je peux vous citer des exemples de section connues :
- .text : contient toutes les instructions binaires qui seront exécutées sequentiellement ;
- .rdata : contient les données nécessaires au programme (r = read). Par exemple, si vous avez fait, en C, un printf("Hello"), la chaîne de caractère sera écrite "en bazar" dans la section .rdata, et on accédera à cette chaîne via son adresse mémoire virtuelle ;
- .bss : espace mémoire réservé par le programme afin de permettre aux instructions machines de stocker des valeurs si besoin ;
- .idata : espace mémoire réservé pour la table des importations. Cette section renferme le nom des fonctions utilisées par le programme, ainsi que les bibliothèques dynamiques (ou DLL) chargées.
En résumé, si on se replonge dans le bain, un PE ressemble à ça :

Certes, pour ceux qui ont déjà lu de la documentation, ce schéma n'est pas exceptionnel et on le retrouve de partout.
Autre "trucs" que j'ai oubliés
Pour ceux qui ouvrent des exécutables avec OllyDbg, vous vous apercevez que les adresses mémoires sont de la forme 0040XXXX. Ce sont des adresses mémoires virtuelles. La base des adresses mémoires est définie dans l'en-tête optionnelle du PE :
00400098 0B01 DW 010B ; MagicNumber = PE32
0040009A 02 DB 02 ; MajorLinkerVersion = 2
0040009B 38 DB 38 ; MinorLinkerVersion = 38 (56.)
0040009C 000A0000 DD 00000A00 ; SizeOfCode = A00 (2560.)
004000A0 00120000 DD 00001200 ; SizeOfInitializedData = 1200 (4608.)
004000A4 00020000 DD 00000200 ; SizeOfUninitializedData = 200 (512.)
004000A8 80120000 DD 00001280 ; AddressOfEntryPoint = 1280
004000AC 00100000 DD 00001000 ; BaseOfCode = 1000
004000B0 00200000 DD 00002000 ; BaseOfData = 2000
004000B4 00004000 DD 00400000 ; ImageBase = 400000
(pour info, j'ai dumpé un bout de l'en-tête de mon fameux "Hello.exe" du part I, avec OllyDbg).
Je vous ai montré le champ important. Malgré ça, il y en a d'autres plutôt intéressants. Je n'expliciterai cependant pas là-dessus. Quoi, vous tenez quand même à savoir ?
Bon, eh bien, je dois vous avouer que j'aurais du le faire plus tôt ; je vais vous passer un pdf qui semble très bien résumer (mieux que moi, en tout cas) l'architecture du format PE. http://venom630.free.fr/geo/blog/format_pe_partii/Le_format_PE.pdf. A croire que cet article n'aurait servi à rien, si ce n'est vous retranscrire cela dans un langage peut-être plus familier !
Exceptionnellement, on ne va pas se quitter avec un code source. Il faut que je prenne le temps de me mettre à l'API WIN32, mais, en toute honnêteté, j'ai drôlement du mal à me familiariser avec la msdn et les noms de fonctions compliqués. A croire que je ne sais me servir que que Sleep() et de MessageBox(). Celui qui se marre, ... grmbl.
Conclusion ?
Là, encore, il n'y a pas grand chose à dire pour clore l'article, si ce n'est que le format PE est intéressant, et qu'une idée m'est venue à la tête concernant les sections. Je n'ai pas fait de recherche sur google, mais autant retranscrire l'idée maintenant (pour le peu de lecteurs qui suivent) : si on ne déclare pas une section avec son en-tête, se peut-il qu'on puisse allouer un espace mémoire non "détecté" dans les en-tête et y injecter du code malicieux ? A réfléchir. Ou pas.
Geo
URL de support : http://venom630.free.fr/geo/?path=blog/format_pe_partii (contient l'image et le pdf adéquats à l'article)
mardi 21 avril 2009
Faire des gâteaux avec PHP (CakePHP)
Yop,
afin de ne pas laisser le blog dans l'oubli, je me permets de rafraichir les nouvelles avec ce billet.
Je n'ai pas vraiment le temps et la motivation pour faire quoi que ce soit, ces temps-ci. Je vais donc essayer (et je dis bien, essayer) de vous parler d'un framework php que j'ai découvert via un tutoriel sur le site du zéro : CakePHP. Le tutoriel se situe à cette adresse : http://www.siteduzero.com/tutoriel-3-115159-cake-php-du-gateau-pour-tous-les-zeros.html, et est entretenu par Jibriss.
Il explique brièvement les bases, à savoir la définition d'un framework ou encore la programmation orientée objet (POO) en PHP, et nous parle un peu de ce fameux CakePHP. J'ai testé tout le bazar, et j'avoue être agréablement surpris par la simplicité de prise en main.
Un exemple tout con. J'ai l'habitude de taper mes formulaires html à la main, à savoir :
Le framework, lui, propose de taper ça :
On n'a vraiment pas à se soucier de ce qui se passe derrière. Ça n'est cependant pas interdit de trifouiller les sources pour comprendre comment ça marche !
Le billet se termine ici. Court, je vous l'avoue. Mais faut bien donner des signes de vie, de temps en temps !
A la prochaine.
Geo
afin de ne pas laisser le blog dans l'oubli, je me permets de rafraichir les nouvelles avec ce billet.
Je n'ai pas vraiment le temps et la motivation pour faire quoi que ce soit, ces temps-ci. Je vais donc essayer (et je dis bien, essayer) de vous parler d'un framework php que j'ai découvert via un tutoriel sur le site du zéro : CakePHP. Le tutoriel se situe à cette adresse : http://www.siteduzero.com/tutoriel-3-115159-cake-php-du-gateau-pour-tous-les-zeros.html, et est entretenu par Jibriss.
Il explique brièvement les bases, à savoir la définition d'un framework ou encore la programmation orientée objet (POO) en PHP, et nous parle un peu de ce fameux CakePHP. J'ai testé tout le bazar, et j'avoue être agréablement surpris par la simplicité de prise en main.
Un exemple tout con. J'ai l'habitude de taper mes formulaires html à la main, à savoir :
<form method="post" action="cible.php">
<table>
<tr>
<td>Nom :</td>
...
...
<table>
<tr>
<td>Nom :</td>
...
...
Le framework, lui, propose de taper ça :
<?php
echo $form->create('Livre');
echo $form->input('Nom');
echo $form->input('Prenom');
echo $form->end('Ajouter'); ?>
echo $form->create('Livre');
echo $form->input('Nom');
echo $form->input('Prenom');
echo $form->end('Ajouter'); ?>
On n'a vraiment pas à se soucier de ce qui se passe derrière. Ça n'est cependant pas interdit de trifouiller les sources pour comprendre comment ça marche !
Le billet se termine ici. Court, je vous l'avoue. Mais faut bien donner des signes de vie, de temps en temps !
A la prochaine.
Geo
vendredi 3 avril 2009
Interlude : release de dbsave & GeoStr
Assis sur un fauteuil confortable, un homme qui semble être en costard se racle la gorge et vous présente les news du 3 Avril 2009.
Bien le bonjour ! Merci d'être avec nous en cette fin d'après midi. Aujourd'hui, Geo tient à vous annoncer deux choses :
Eh oui ! Je me suis dit qu'améliorer le script de back-up de bdd ne ferait aucun mal. En plus, j'y trouve mon compte. Ce qui change :
- Dump amélioré ;
- Gestion des clés primaires composées ;
- Possibilité d'entrées des paramètres en ligne de commande.
Le lien : http://venom630.free.fr/geo/tools/dbsave_php.txt
Pour mes lecteurs, vous souvenez-vous de GeoStr ? Petit module amateur personnel qui permet de manipuler les chaînes de caractère en C !
J'ai ajouté la fonction GeoStr_trim ; l'équivalent de trim() en PHP. Sa signature :
char *GeoStr_trim(char *str);
Cette fonction se contente de nettoyer les "blancs" - espaces, tabulations, sauts de ligne etc - en début et fin de str... Et se contente de la retourner, tout simplement.
/!\ Et encore un sujet qui fâche : j'ai mis à votre dispositions deux "destructeurs" ; en effet, j'utilise l'allocation dynamique dans mes fonctions, et chaque malloc() a son free(). Vaut mieux rester propre.
void GeoStr_destroy(char *str);
Cette fonction libère la mémoire allouée par votre pointeur char*
void GeoStr_destroytab(char **tab);
Cette fonction libère les tableaux générés par la fonction GeoStr_explode().
http://venom630.free.fr/geo/?path=tools/GeoStr
C'étaitle journal l'interlude du blog.
J'avoue que je réserve tout de même un paragraphe pour vous annoncer que je risque de retomber dans une période de flemme. Faut pas m'en vouloir, je suis en vacances.
Sur ce, bon surf, et merci pour ceux qui se tiennent au jus.
J'oubliais... J'aime les nouilles.
Geo
Bien le bonjour ! Merci d'être avec nous en cette fin d'après midi. Aujourd'hui, Geo tient à vous annoncer deux choses :
Release de dbsave
Eh oui ! Je me suis dit qu'améliorer le script de back-up de bdd ne ferait aucun mal. En plus, j'y trouve mon compte. Ce qui change :
- Dump amélioré ;
- Gestion des clés primaires composées ;
- Possibilité d'entrées des paramètres en ligne de commande.
Le lien : http://venom630.free.fr/geo/tools/dbsave_php.txt
Release de GeoStr
Pour mes lecteurs, vous souvenez-vous de GeoStr ? Petit module amateur personnel qui permet de manipuler les chaînes de caractère en C !
J'ai ajouté la fonction GeoStr_trim ; l'équivalent de trim() en PHP. Sa signature :
char *GeoStr_trim(char *str);
Cette fonction se contente de nettoyer les "blancs" - espaces, tabulations, sauts de ligne etc - en début et fin de str... Et se contente de la retourner, tout simplement.
/!\ Et encore un sujet qui fâche : j'ai mis à votre dispositions deux "destructeurs" ; en effet, j'utilise l'allocation dynamique dans mes fonctions, et chaque malloc() a son free(). Vaut mieux rester propre.
void GeoStr_destroy(char *str);
Cette fonction libère la mémoire allouée par votre pointeur char*
void GeoStr_destroytab(char **tab);
Cette fonction libère les tableaux générés par la fonction GeoStr_explode().
http://venom630.free.fr/geo/?path=tools/GeoStr
C'était
Et pour la suite ?
J'avoue que je réserve tout de même un paragraphe pour vous annoncer que je risque de retomber dans une période de flemme. Faut pas m'en vouloir, je suis en vacances.
Sur ce, bon surf, et merci pour ceux qui se tiennent au jus.
J'oubliais... J'aime les nouilles.
Geo
mardi 31 mars 2009
Disseque ton PE, et récupère ta section code.
Salut salut,
étant donné les rapides soutiens dans les commentaires des articles précédents (et j'en remercie les auteurs) ainsi que d'une motivation inouïe, je me mets à continuer ma route sur le format PE. Cet article conclura le mois de mars avec brio, je l'espère.
Je me triture la cervelle en cours. Mais j'avais déjà réfléchi à injecter du code à la place des octets nuls présents à partir de 0x240, j'en avais parlé avec UnKnOwN*DrAgOoN - il est chiant à écrire, son pseudo - ; j'y suis arrivé, et j'y consacrerai un article plus tard.
Cet article servira de support à la pratique pour trifouiller le format PE. Il vous faudra unn outil intéressant que vous possédez certainement : Olly dbg. Pour ceux qui ne connaissent pas, Olly Dbg est un outil gratuit qui permet de désassembler des programmes afin de :
- Cracker (ouuuh c'est mal !) ;
- "Reverser" le programme - c'est-à-dire comprendre son fonctionnement en lisant le code assembleur ;
- Trouver le problème en cas d'algorithme mal implémenté ;
- ...
Ici, on va s'en servir pour analyser la mémoire d'un exécutable. Voici ce dont vous aurez besoin pour la pratique : http://venom630.free.fr/geo/?path=blog/pratique_pe.
Quatre fichiers :
- sh_over_asm.txt : shellcode conçu par 0vercl0k que j'ai piqué sur son blog (il ne m'en voudra pas, puisque je l'embrasse sur la fesse droite et lui adresse mes remerciements les plus profonds du cœur et tout) ;
- sh_over.exe : version compilée du shellcode d'0vercl0k (avec MASM32) ;
- ...
Vous verrez les deux fichiers plus tard. Je sais, j'ai un peu gâté, mais tant pis.
(Cliquez sur les images pour les voir en grand)
On ouvre sh_over.exe avec ollydbg, et on va voir dans la mémoire.

On tombe sur ça :

J'ai grisé la zone intéressante, qui va nous permettre de voir les en-têtes du format PE. Double-cliquez sur cette zone. Déplacez-vous jusqu'à, ensuite, trouver ça :

Je peux maintenant expliquer où je veux en venir. Je souhaite faire un programme qui ouvre un exécutable, en extrait le contenu de la section qui contient le code, et qui affiche les opcodes - équivalents binaires des instructions en assembleur - en dur dans la console (on peut enregistrer le flux de sortie via l'opérateur > dans la ligne de commandes).
Pour ce faire, j'ai besoin d'élaborer un algorithme précis. Tout d'abord, connaître l'offset de notre en-tête PE (logique), trouver le nombre de sections présentes ainsi que la taille de l'en-tête optionnelle. Une fois que j'ai trouvé ces informations, je peux me déplacer dans le fichier à la fin de l'en-tête optionnelle pour trouver l'en-tête des options qui suivent (cf. capture d'écran).
Je peux aussi élaborer une structure C de l'en-tête d'une section (Pour info, ne maîtrisant pas l'API Win32, je ne me servirai pas de windows.h pour prendre la structure toute faite) :
La section fait 40 octets ; multiple de 4, donc pas de problème de Data Structure Alignment. Le lien pointe sur un article de 5m0k3 qui évoque ce concept, et je vous le recommande car je n'irai pas plus loin. Il ne faut pas dériver du sujet.
Dans notre structure :
- il faudra vérifier que le membre Characteristics ait le flag "Je contiens du code" actif ; si ce n'est pas le cas, on saute à la section suivante (si y'en a plusieurs) ;
- dès qu'on aura la section qui nous intéresse, on se déplacera en dur dans le fichier à l'aide du membre PointerToRawData ;
- On lira les opcodes via une boucle for() qui s'arrêtera jusqu'à SizeOfRawData.
PointerToRawData contient 200. Voyons voir...

J'en mets mon zizi à couper qu'il s'agit de notre section qui contient le code exécutable !
Vraiment bien foutue et utile, cette en-tête de section. Au fait, j'aurais pu faire un algorithme qui trouve la section ".text", mais la section de code ne s'appelle pas tout le temps comme ça ; c'est, certes, une convention, mais les compilateur Borland nomment cette section "CODE". Et puis, elles ont le nom qu'elles veulent.
Au final, j'élabore un code (que je commente au max) qui va dumper la section code de notre exécutable pour ensuite se servir des opcodes en tant que shellcode, et j'en passe. Ce fut une bonne expérience pour moi afin de comprendre davantage le PE.
Voici le code :
Source disponible ici : http://venom630.free.fr/geo/blog/pratique_pe/shellcodeme_c.txt - Exécutable disponible ici : http://venom630.free.fr/geo/blog/pratique_pe/shellcodeme.exe
Pff... Que dire ? Que ça m'a occupé deux bonnes heures pour éviter les pensées négatives ? Je sais pas. On a qu'à dire que je trace mon évolution.
Par ailleurs, j'aspire à penser qu'il existe sûrement des outils qui se chargent de faire ça ; Heurs et Squallsurf en ont peut-être codé un semblable que j'ai pas réussi à faire marcher, mais je viens de comprendre pourquoi ils disaient que le code C ne devaient pas comporter d'appels à des fonctions extérieures. Effectivement, en assembleur, cela se traduit par des CALL. Et les fonctions se trouvent peut-être ailleurs... M'enfin, je préfère m'abstenir de dire des conneries, d'où le "peut-être".
C'est tout. J'espère continuer sur cette lancée, et merci aux soutiens, ça fait véritablement plaisir.
Je vous laisse, j'ai des devoirs à faire. Merde.
étant donné les rapides soutiens dans les commentaires des articles précédents (et j'en remercie les auteurs) ainsi que d'une motivation inouïe, je me mets à continuer ma route sur le format PE. Cet article conclura le mois de mars avec brio, je l'espère.
Je me triture la cervelle en cours. Mais j'avais déjà réfléchi à injecter du code à la place des octets nuls présents à partir de 0x240, j'en avais parlé avec UnKnOwN*DrAgOoN - il est chiant à écrire, son pseudo - ; j'y suis arrivé, et j'y consacrerai un article plus tard.
Cet article servira de support à la pratique pour trifouiller le format PE. Il vous faudra unn outil intéressant que vous possédez certainement : Olly dbg. Pour ceux qui ne connaissent pas, Olly Dbg est un outil gratuit qui permet de désassembler des programmes afin de :
- Cracker (ouuuh c'est mal !) ;
- "Reverser" le programme - c'est-à-dire comprendre son fonctionnement en lisant le code assembleur ;
- Trouver le problème en cas d'algorithme mal implémenté ;
- ...
Ici, on va s'en servir pour analyser la mémoire d'un exécutable. Voici ce dont vous aurez besoin pour la pratique : http://venom630.free.fr/geo/?path=blog/pratique_pe.
Quatre fichiers :
- sh_over_asm.txt : shellcode conçu par 0vercl0k que j'ai piqué sur son blog (il ne m'en voudra pas, puisque je l'embrasse sur la fesse droite et lui adresse mes remerciements les plus profonds du cœur et tout) ;
- sh_over.exe : version compilée du shellcode d'0vercl0k (avec MASM32) ;
- ...
Vous verrez les deux fichiers plus tard. Je sais, j'ai un peu gâté, mais tant pis.
(Cliquez sur les images pour les voir en grand)
On ouvre sh_over.exe avec ollydbg, et on va voir dans la mémoire.

On tombe sur ça :

J'ai grisé la zone intéressante, qui va nous permettre de voir les en-têtes du format PE. Double-cliquez sur cette zone. Déplacez-vous jusqu'à, ensuite, trouver ça :

Je peux maintenant expliquer où je veux en venir. Je souhaite faire un programme qui ouvre un exécutable, en extrait le contenu de la section qui contient le code, et qui affiche les opcodes - équivalents binaires des instructions en assembleur - en dur dans la console (on peut enregistrer le flux de sortie via l'opérateur > dans la ligne de commandes).
Pour ce faire, j'ai besoin d'élaborer un algorithme précis. Tout d'abord, connaître l'offset de notre en-tête PE (logique), trouver le nombre de sections présentes ainsi que la taille de l'en-tête optionnelle. Une fois que j'ai trouvé ces informations, je peux me déplacer dans le fichier à la fin de l'en-tête optionnelle pour trouver l'en-tête des options qui suivent (cf. capture d'écran).
Je peux aussi élaborer une structure C de l'en-tête d'une section (Pour info, ne maîtrisant pas l'API Win32, je ne me servirai pas de windows.h pour prendre la structure toute faite) :
typedef struct {
char name[8];
long VirtualSize,
VirtualAddress,
SizeOfRawData,
PointerToRawData,
PointerToRelocations,
PointerToLineNumbers;
short NumberOfRelocations,
NumberOfLineNumbers;
long Characteristics;
} Section_Pe;La section fait 40 octets ; multiple de 4, donc pas de problème de Data Structure Alignment. Le lien pointe sur un article de 5m0k3 qui évoque ce concept, et je vous le recommande car je n'irai pas plus loin. Il ne faut pas dériver du sujet.
Dans notre structure :
- il faudra vérifier que le membre Characteristics ait le flag "Je contiens du code" actif ; si ce n'est pas le cas, on saute à la section suivante (si y'en a plusieurs) ;
- dès qu'on aura la section qui nous intéresse, on se déplacera en dur dans le fichier à l'aide du membre PointerToRawData ;
- On lira les opcodes via une boucle for() qui s'arrêtera jusqu'à SizeOfRawData.
PointerToRawData contient 200. Voyons voir...

J'en mets mon zizi à couper qu'il s'agit de notre section qui contient le code exécutable !
Vraiment bien foutue et utile, cette en-tête de section. Au fait, j'aurais pu faire un algorithme qui trouve la section ".text", mais la section de code ne s'appelle pas tout le temps comme ça ; c'est, certes, une convention, mais les compilateur Borland nomment cette section "CODE". Et puis, elles ont le nom qu'elles veulent.
Au final, j'élabore un code (que je commente au max) qui va dumper la section code de notre exécutable pour ensuite se servir des opcodes en tant que shellcode, et j'en passe. Ce fut une bonne expérience pour moi afin de comprendre davantage le PE.
Voici le code :
#include <stdio.h>
#include <stdlib.h>
#define OFFSET_E_LFANEW 0x3C // Offset absolu contenant l'offset d'en-tête PE
#define MOTMAGIQUE 0x00004550 // Mot magique de l'en-tête PE ('PE#0#0')
#define CONTAINS_CODE 0x020 // Flag qui vérifie si la section PE contient du code
// Structure représentant l'en-tête d'une section PE
typedef struct {
char name[8];
long VirtualSize,
VirtualAddress,
SizeOfRawData,
PointerToRawData,
PointerToRelocations,
PointerToLineNumbers;
short NumberOfRelocations,
NumberOfLineNumbers;
long Characteristics;
} Section_Pe;
int main(int argc, char **argv) {
// Il faut inciter à fournir les arguments en ligne de commande
if(argc < 2) {
printf("Utilisation : %s\n", argv[0]);
exit(EXIT_FAILURE);
}
long MagicWord, // Mot magique pour vérifier qu'il s'agit bien d'un PE
OffsetPeHeader; // L'offset du header PE
short NumberOfSections, // Nombre de sections
SizeOptionalHeader; // taille header optionnel
FILE *fe; // Pointeur de fichier
// On essaie d'ouvrir le fichier exécutable
if((fe = fopen(argv[1],"rb")) == NULL) {
printf("Erreur lors de l'ouverture de %s !\n",argv[1]);
exit(EXIT_FAILURE);
}
// On se déplace a l'offset (à partir du début) qui contient lui-même
// l'offset de la signature PE, puis on a lit
fseek(fe, OFFSET_E_LFANEW, SEEK_SET);
fread(&OffsetPeHeader, 1, sizeof(long), fe);
// On s'y déplace
fseek(fe, OffsetPeHeader, SEEK_SET);
// On lit le mot magique et on vérifie sa conformité
fread(&MagicWord, 1, sizeof(long), fe);
if(MagicWord != MOTMAGIQUE) {
printf("Ce fichier n'est pas un PE.\n");
exit(EXIT_FAILURE);
}
// On se déplace pour trouver le nombre de sections (toujours à partir du début)
// Le + 2 sert à sauter par dessus le mot magique & le nombre représentant l'architecture système
// qui ne nous intéresse pas.
fseek(fe, 2, SEEK_CUR);
// On lit le nombre de sections
fread(&NumberOfSections, 1, sizeof(short), fe);
// On s'occupe ensuite de savoir s'il y a une en-tête optionnelle
// en récupérant sa taille. Si != 0, alors elle existe.
// On saute le timestamp (+4), L'adresse pointant
// vers la table des symboles (+4) et le nombre des symboles (+4)
// = 12 octets
fseek(fe, 12, SEEK_CUR);
// On lit la taille de l'header optionnel
fread(&SizeOptionalHeader, 1, sizeof(short), fe);
// Et on se déplace jusqu'aux en-têtes de sections
// (ou jusqu'à l'en-tête section, s'il n'y en a qu'une)
// après avoir sauté les caractéristiques (+2)
fseek(fe, 2 + SizeOptionalHeader, SEEK_CUR);
// On lit la première section
Section_Pe SectionActuelle;
fread(&SectionActuelle, 1, sizeof(Section_Pe), fe);
// Tant que la section lue ne contient pas du code, on cherche la bonne
while(!(SectionActuelle.Characteristics & CONTAINS_CODE)) {
fread(&SectionActuelle, 1, sizeof(Section_Pe), fe);
}
// On a notre section exécutable. On affiche ses informations
printf("Name = %s\n"
"VirtualSize = 0x%x (%d.)\n"
"VirtualAddress = %d\n"
"SizeOfRawData = 0x%x (%d.)\n"
"PointerToRawData = 0x%x\n\n",
SectionActuelle.name, SectionActuelle.VirtualSize, SectionActuelle.VirtualSize,
SectionActuelle.VirtualAddress,SectionActuelle.SizeOfRawData, SectionActuelle.SizeOfRawData,
SectionActuelle.PointerToRawData);
// On s'y positionne
fseek(fe, SectionActuelle.PointerToRawData, SEEK_SET);
// On lit les octets (opcodes) et on les affiche sous la forme \xXX
int i; // compteur de boucle
unsigned char opcode; // opcode lu
for(i = 0; i < SectionActuelle.VirtualSize; i++) {
fread(&opcode, 1, sizeof(char), fe);
printf("\\x%02X", opcode);
}
printf("\n");
fclose(fe);
return EXIT_SUCCESS;
}
Source disponible ici : http://venom630.free.fr/geo/blog/pratique_pe/shellcodeme_c.txt - Exécutable disponible ici : http://venom630.free.fr/geo/blog/pratique_pe/shellcodeme.exe
C:\Pratique_format_PE>shellcodeme sh_over.exe
Name = .text
VirtualSize = 0x3b (59.)
VirtualAddress = 4096
SizeOfRawData = 0x200 (512.)
PointerToRawData = 0x200
\x33\xDB\x33\xC0\x66\xB8\x6C\x6C\x50\x68\x33\x32\x2E\x64\x68\x75\x73\x65\x72\xBF
\x77\x1D\x80\x7C\x54\xFF\xD7\x53\x68\x63\x6C\x30\x6B\x68\x30\x76\x65\x72\x8B\xCC
\x53\x51\x51\x53\xBF\x0B\x05\xD5\x77\xFF\xD7\x53\xBF\xA2\xCA\x81\x7C\xFF\xD7
Name = .text
VirtualSize = 0x3b (59.)
VirtualAddress = 4096
SizeOfRawData = 0x200 (512.)
PointerToRawData = 0x200
\x33\xDB\x33\xC0\x66\xB8\x6C\x6C\x50\x68\x33\x32\x2E\x64\x68\x75\x73\x65\x72\xBF
\x77\x1D\x80\x7C\x54\xFF\xD7\x53\x68\x63\x6C\x30\x6B\x68\x30\x76\x65\x72\x8B\xCC
\x53\x51\x51\x53\xBF\x0B\x05\xD5\x77\xFF\xD7\x53\xBF\xA2\xCA\x81\x7C\xFF\xD7
Conclusion
Pff... Que dire ? Que ça m'a occupé deux bonnes heures pour éviter les pensées négatives ? Je sais pas. On a qu'à dire que je trace mon évolution.
Par ailleurs, j'aspire à penser qu'il existe sûrement des outils qui se chargent de faire ça ; Heurs et Squallsurf en ont peut-être codé un semblable que j'ai pas réussi à faire marcher, mais je viens de comprendre pourquoi ils disaient que le code C ne devaient pas comporter d'appels à des fonctions extérieures. Effectivement, en assembleur, cela se traduit par des CALL. Et les fonctions se trouvent peut-être ailleurs... M'enfin, je préfère m'abstenir de dire des conneries, d'où le "peut-être".
C'est tout. J'espère continuer sur cette lancée, et merci aux soutiens, ça fait véritablement plaisir.
Je vous laisse, j'ai des devoirs à faire. Merde.
Inscription à :
Articles (Atom)