Thread Hijacking

janvier 14th, 2007 at 05:00 admin

Un article que j’ai trouvé au fond d’un de mes tiroirs qui devrait en intéresser pas mal, sur la manière de detourner l’exécution d’un thread dans un autre processus, enjoy

La furtivité, le maître mot pour un rootkit, celui ci doit réussir l’exploit de se cacher dans un OS tout en exécutant ses fonctions. Pour cela il détourne les fonctions des autres processus et fait croire à l’utilisateur que tout est normal. Dans un environnement Windows cela passe par de l’injection de code qui donne au rootkit toute sa puissance. Cependant il existe une méthode d’injection peu connue celle qui consiste à directement injecter dans un Thread.

Il faut savoir que sous Windows un processus n’exécute rien, il n’est la que pour représenter un espace mémoire organisé dans lequel le code est exécuter par un Thread principal, ainsi un processus a au moins un Thread primaire qui peut engendrer de nouveau Threads qui occuperont ce même espace et exécuterons leur propre code. Pour que cette opération de « multithreadind » soit possible alors que le processeur ne peut que réaliser une instruction en même temps (je caricature un peu, tant pis pour les puristes), chaque Thread à son propre CONTEXT c’est à dire que pour chaque Thread qui n’est pas en train d’être exécuter le processeur a sauvegardé dans cette structure CONTEXT tout les états des registres processeurs. Ainsi pour « switcher » de Threads en Threads il recharge à chaque fois le CONTEXT exécutant ensuite quelques instructions puis sauvegarde le CONTEXT et peut alors passer au suivant.

Par chance Windows nous fournis des APIs permettant de manipuler les Threads :]. L’injection en théorie est assez simple il suffit d’arrêter un Thread, de changer son CONTEXT pour qu’il aille exécuter notre code et puis de le relancer… en théorie.

——–[Time to code

Voici en pratique ce que va faire notre programme :
1- Prendre en argument le nom du processus a injecter et obtenir le PID de celui ci.
2- Regarder les Threads lancer par le processus et choisir le Thread primaire.
3- Stopper le Thread et récupérer la structure CONTEXT.
4- Injecter notre code dans la mémoire du processus et changer l’EIP de la structure CONTEXT vers le début de notre code.
5- Relancer le Thread.
6- Admirer le résultat :}.

1)

Pour récupérer le PID de notre processus on va utiliser la fonction CreateToolhelp32Snapshot() de prototype :

HANDLE WINAPI CreateToolhelp32Snapshot(
DWORD dwFlags,
DWORD th32ProcessID
);

Avec pour valeur a dwFlags TH32CS_SNAPPROCESS et th32ProcessId 0. Cette fonction réalise une « photo » des processus lancés on obtient une liste de structures PROCESSENTRY32 :

typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
TCHAR szExeFile[MAX_PATH];
} PROCESSENTRY32,
*PPROCESSENTRY32;

Il nous suffit de parcourir ces structures avec Process32Next() de cette manière :

int ProcessNameToPid(char *ProcessName)
{
HANDLE hProcessSnap;
PROCESSENTRY32 pe32;

hProcessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
if(hProcessSnap==INVALID_HANDLE_VALUE)
{
printf("Error with CreateToolhelp32Snapshot: 0x%xn",GetLastError());
}

pe32.dwSize=sizeof(PROCESSENTRY32);

if(!Process32First(hProcessSnap, &pe32 ))
{
printf("Error with Process32First: %dn",GetLastError());
CloseHandle(hProcessSnap);
return 0;
}

while(Process32Next(hProcessSnap,&pe32)!=0)
{
if(_stricmp(pe32.szExeFile,ProcessName)==0)
{
CloseHandle(hProcessSnap);
return pe32.th32ProcessID;
}
}

CloseHandle(hProcessSnap);
return 0;
}

2)
De la même façon pour retrouver le Thread du processus visé on utilise CreateToolhelp32Snapshot() avec dwFlags a TH32CS_SNAPTHREAD on obtient encore une série de structures THREADENTRY32 qui contient de infos sur TOUS les Threads existant:

typedef struct tagTHREADENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ThreadID;
DWORD th32OwnerProcessID;
LONG tpBasePri;
LONG tpDeltaPri;
DWORD dwFlags;
} THREADENTRY32,
*PTHREADENTRY32;

Et on récupère le TID du Thread primaire :

while(Th32.th32OwnerProcessID!=PID && Thread32Next(hThreadSnap,&Th32)){}
TID=Th32.th32ThreadID;

3)
Maintenant qu’on a le TID il va falloir ouvrir un Handle sur le Thread le stopper et récupérer la structure CONTEXT. Pour le Handle on va utiliser la fonction OpenThread(). On spécifie les différents droit que nous avoir sur lui c’est à dire qu’on puisse obtenir son CONTEXT, le redéfinir, l’arrêter et le relancer:

hThread=OpenThread(THREAD_GET_CONTEXT|THREAD_SUSPEND_RESUME|THREAD_SET_CONTEXT, FALSE, TID);

Puis on l’arrête avec SuspendThread() et on récupère le CONTEXT avec GetThreadContext()
Attention ne pas oubliez de définir les registres que l’on veut obtenir dans la structure CONTEXT, pCtx étant après l’appel un pointeur sur la structure CONTEXT du Thread:

Ctx.ContextFlags=CONTEXT_FULL;
GetThreadContext(hThread,Ctx);

4)

Il nous faut à présent injecter notre code dans la mémoire du processus, pour cela on obtient un Handle sur le processus avec OpenProcess(), on demande à VirtualAllocEx() de nous allouer un espace mémoire exécutable et on copie notre code avec WriteProcessMemory().
On change l’EIP de la structure CONTEXT pour qu’au lancement il soit sur notre code.

On a donc notre EIP qui est prêt a lancer notre code mais il ne faut oublier que nous devons après restaurer le CONTEXT original du Thread, la façon la plus simple que j’ai trouvé pour faire cela est que mon code injecté sauvegarde sur la pile tous les registres et l’EIP. Pour les registres généraux un PUSHAD suffit pour l’EFLAGS un PUSHFD mais pour l’EIP comment faire ? J’ai donc réalisé un shellcode qui push une valeur quelconque en premier et juste avant de copier ce shellcode en mémoire je change cette valeur en l’EIP du Thread stoppé, puis a la fin du shellcode un simple RET relance la machine. Voici un petit schéma :

PUSH 0FFFFFFFFh
PUSHAD
PUSHFD
[..]
notre shellcode ici.
[..]
POPFD
POPAD
RET

En fait on modifie le 0FFFFFFFFh avec un memcpy((PBYTE)code+1,&(Ctx.Eip),4);

5)
Enfin on change le CONTEXT de notre Thread avec SetThreadContext() puis on le relance avec ResumeThread().

6) On regarde le résultat :]

J’espère que ca vous avez aimé, voici le pack avec les binaires et sources (même celle du shellcode)

http://ivanlef0u.fr/repo/ThreadHijack.rar

Entry Filed under: Non classé

2 Comments

  • 1. Heyyy  |  novembre 8th, 2012 at 16:35

    Exellent ! Grâce à ça, pour faire un rootkit, on est en mesure de se passer du fatidique hook sur ZwQuerySystemInformation pour les processus. Je n’y étais même pas arriver, à cause de la complexité pour itérer la structure SYSTEM_PROCESS_INFORMATION(je ne voulais pas omettre le temps de processeur, ni simplement modier le membre qui indique l’offset de la prochaine structure…). Bonne continuation !


  • 2. [C#] ASM Injection (SetTh&hellip  |  juillet 15th, 2013 at 22:39

    [...] throw new ApplicationException("Cannot close thread handle."); Thanks: * Ivanlef0u (provide me some advice about thread suspending/resuming) Reply With Quote [...]


Trackback this post


Calendar

mars 2024
L Ma Me J V S D
« fév    
 123
45678910
11121314151617
18192021222324
25262728293031

Most Recent Posts