Vous ne trouvez pas de réponse à votre problème ? Alors posez la question dans le forum. Souvenez-vous qu'il n'y a jamais de question bête, mais rester dans l'ignorance parce que l'on n'ose pas poser une question, ça c'est une erreur !

HOOKING SOUS NT AVEC CREATEREMOTETHREAD (VC++7, COMPILABLE AC LE 6 AUSSI)


Information sur la source

Catégorie :.Net Niveau : Débutant Date de création : 09/05/2003 Date de mise à jour : 09/05/2003 20:38:04 Vu / téléchargé: 4 217 / 200

Note :
9 / 10 - par 3 personnes
9,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

Commentaire sur cette source (18)
Ajouter un commentaire et/ou une note

Description

bin ... ya pas grand chose a dire ...
l'executable copie une partie de son code dans la mémoire du process 'cible', et l'execute
 

Source

  • // code compilé avec vc++7
  • // c compilable aussi sur le 6, mais a vous de jouer avec les options du compilo pour que ca marche
  • #include <windows.h>
  • typedef HMODULE (WINAPI* fnLoadLibrary)(LPCTSTR LibName);
  • typedef FARPROC (WINAPI* fnGetProcAddress)(HMODULE hLib, LPCTSTR FuncName);
  • typedef int (WINAPI* fnMessageBox)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
  • typedef BOOL (WINAPI* fnFreeLibrary)(HMODULE hLib);
  • typedef void (WINAPI* fnExitThread)(DWORD dwxitCode);
  • typedef struct InJack
  • {
  • fnLoadLibrary pLoadLibrary;
  • fnGetProcAddress pGetProcAddress;
  • } InJack, *pInJack;
  • static DWORD WINAPI RemoteEntry(pInJack Data)
  • {
  • // vc++ (6 et 7) alloue la mémoire ds le code avec cette méthode, ainsi on a pas de lien vers le segment de données (qui n'est pas copié)
  • char szUser[] = {'u','s','e','r','3','2',0};
  • char szMsgBox[] = {'M','e','s','s','a','g','e','B','o','x','A',0};
  • char szKernel[] = {'k','e','r','n','e','l','3','2',0};
  • char szFreeLib[] = {'F','r','e','e','L','i','b','r','a','r','y',0};
  • char szExitThread[] = {'E','x','i','t','T','h','r','e','a','d',0};
  • HMODULE hKern = Data->pLoadLibrary(szKernel);
  • fnFreeLibrary pFreeLibrary = (fnFreeLibrary)Data->pGetProcAddress(hKern, szFreeLib);
  • fnExitThread pExitThread = (fnExitThread)Data->pGetProcAddress(hKern, szExitThread);
  • pFreeLibrary(hKern);
  • HMODULE hUser = Data->pLoadLibrary(szUser);
  • fnMessageBox pMessageBox = (fnMessageBox)Data->pGetProcAddress(hUser, szMsgBox);
  • pMessageBox(NULL, szUser, szMsgBox, NULL);
  • pFreeLibrary(hUser);
  • pExitThread(0);
  • // apparement sinon il aime pas le return 0, il fait une violation mémoire
  • // il me semble que le compilo ne l'ait pas pris en compte ...
  • return 0;
  • }
  • static void EndRemoteEntry() {}
  • void Main()
  • {
  • BOOL k;
  • // on ouvre le processus
  • HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 1356); // pid du process ... (ds le gestionnaire des tâches)
  • int cbCodeSize = ((LPBYTE) EndRemoteEntry - (LPBYTE) RemoteEntry);
  • // on copie le code
  • LPVOID CodeMem = VirtualAllocEx(hProc, NULL, cbCodeSize, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  • k = WriteProcessMemory(hProc, CodeMem, &RemoteEntry, cbCodeSize, 0);
  • // on remplit les données
  • InJack Data;
  • HMODULE hKern = LoadLibrary("kernel32");
  • Data.pGetProcAddress = (fnGetProcAddress)GetProcAddress(hKern, "GetProcAddress");
  • Data.pLoadLibrary = (fnLoadLibrary)GetProcAddress(hKern, "LoadLibraryA");
  • FreeLibrary(hKern);
  • LPVOID DataMem = VirtualAllocEx(hProc, NULL, sizeof(Data), MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
  • k = WriteProcessMemory(hProc, DataMem, &Data, sizeof(Data), 0);
  • DWORD ThreadID;
  • HANDLE hThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)CodeMem, DataMem, 0, &ThreadID);
  • CloseHandle(hThread);
  • }
// code compilé avec vc++7
// c compilable aussi sur le 6, mais a vous de jouer avec les options du compilo pour que ca marche
#include <windows.h>

typedef HMODULE (WINAPI* fnLoadLibrary)(LPCTSTR LibName);
typedef FARPROC (WINAPI* fnGetProcAddress)(HMODULE hLib, LPCTSTR FuncName);
typedef int (WINAPI* fnMessageBox)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
typedef BOOL (WINAPI* fnFreeLibrary)(HMODULE hLib);
typedef void (WINAPI* fnExitThread)(DWORD dwxitCode);

typedef struct InJack
{
	fnLoadLibrary pLoadLibrary;
	fnGetProcAddress pGetProcAddress;
} InJack, *pInJack;

static DWORD WINAPI RemoteEntry(pInJack Data)
{
	// vc++ (6 et 7) alloue la mémoire ds le code avec cette méthode, ainsi on a pas de lien vers le segment de données (qui n'est pas copié)
	char szUser[] = {'u','s','e','r','3','2',0};
	char szMsgBox[] = {'M','e','s','s','a','g','e','B','o','x','A',0};
	char szKernel[] = {'k','e','r','n','e','l','3','2',0};
	char szFreeLib[] = {'F','r','e','e','L','i','b','r','a','r','y',0};
	char szExitThread[] = {'E','x','i','t','T','h','r','e','a','d',0};

	HMODULE hKern = Data->pLoadLibrary(szKernel);
	fnFreeLibrary pFreeLibrary = (fnFreeLibrary)Data->pGetProcAddress(hKern, szFreeLib);
	fnExitThread pExitThread = (fnExitThread)Data->pGetProcAddress(hKern, szExitThread);
	pFreeLibrary(hKern);

	HMODULE hUser = Data->pLoadLibrary(szUser);
	fnMessageBox pMessageBox = (fnMessageBox)Data->pGetProcAddress(hUser, szMsgBox);

	pMessageBox(NULL, szUser, szMsgBox, NULL);

	pFreeLibrary(hUser);
	
	pExitThread(0); 
	// apparement sinon il aime pas le return 0, il fait une violation mémoire
	// il me semble que le compilo ne l'ait pas pris en compte ...
	return 0;
}

static void EndRemoteEntry() {}

void Main()
{
	BOOL k;
	
	// on ouvre le processus
	HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 1356); // pid du process ... (ds le gestionnaire des tâches)
	int cbCodeSize = ((LPBYTE) EndRemoteEntry - (LPBYTE) RemoteEntry);
	
	// on copie le code
	LPVOID CodeMem = VirtualAllocEx(hProc, NULL, cbCodeSize, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	k = WriteProcessMemory(hProc, CodeMem, &RemoteEntry, cbCodeSize, 0);
	
	// on remplit les données
	InJack Data;
	HMODULE hKern = LoadLibrary("kernel32");
	Data.pGetProcAddress = (fnGetProcAddress)GetProcAddress(hKern, "GetProcAddress");
	Data.pLoadLibrary = (fnLoadLibrary)GetProcAddress(hKern, "LoadLibraryA");
	FreeLibrary(hKern);

	LPVOID DataMem = VirtualAllocEx(hProc, NULL, sizeof(Data), MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
	k = WriteProcessMemory(hProc, DataMem, &Data, sizeof(Data), 0);

	DWORD ThreadID;
	HANDLE hThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)CodeMem, DataMem, 0, &ThreadID);
	CloseHandle(hThread);
}

Conclusion

Dites moi si vous avez d'autres méthodes / des améliorations :)
 

Fichier Zip

Pour les "Membres Club", vous pouvez télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !
  • RemoteThread.ncbTélécharger ce fichier [Réservé aux membres club]35 840 octets
  • RemoteThread.slnTélécharger ce fichier [Réservé aux membres club]Voir ce fichier924 octets
  • RemoteThread.suoTélécharger ce fichier [Réservé aux membres club]7 680 octets
  • RemoteThread.vcprojTélécharger ce fichier [Réservé aux membres club]3 221 octets
  • test.cppTélécharger ce fichier [Réservé aux membres club]Voir ce fichier2 820 octets

Télécharger le zip

Commentaires et avis

signaler à un administrateur
Commentaire de vbnul le 26/05/2003 20:56:09

Toutes mes félicitations, ton code marche et c pas partout qu'on trouve des choses de ce genre.
Petite précision pour VC++ 6 : compiler en release sans quoi ce code devient un trés bon killer de processus ;-)
Sinon, pourrais tu commenter un peu + le code ?
Nous dire par exemple à quoi servent les fonctons et à quoi correspondent les paramètres ?
Détal bizarre : pourquoi faut il initialiser les variables d'une manière barbare {'?','?','?'} plutot que d'utiliser la manière habituelle "user32" ?
Je vois bien que sa plante mais pourquoi ?

merci d'avance et encore bravo :-D

signaler à un administrateur
Commentaire de BlackGoddess le 27/05/2003 09:45:44

alors ... j'essaye d'expliquer :)

un fichier est composé de plusieurs sections, dont le header, le code, les data (pour ce qui nous interresse). un .exe est en général chargé a l'offset 400000 (en hexa). donc, pour lui, il pourra par exemple appeler l'offset 410000, et ca pointera ds le segment de data sur une chaine.

dans le processus cible, je n'ai copié que la fonction RemoteEntry, en effet, il ne sera de toutes facons pas copié a l'offset 400000 (etant deja occupé par le processus cible), donc tout ses appels à d'autres sections seront erronés -&gt; plantage.

spécialement sou vc++ (ca marche p-e avec d'autre compilo), qd on déclare char *szUser = "user32"; il ecrit user32 dans le segment de data, et szUser devient un pointeur vers le 'u' dans le segment.

par contre, lorsqu'on déclare char szUser[] = {'u','s','e','r','3','2',0}; le compilo alloue directement les lettres une par une dans la pile par leur code ascii, donc il n'y a pas d'appel vers aucun segment.

ah, oui, qd je parle d'offset 400000, chaque processus est chargé à partir de l'offset 400000 (du moins c'est ce qu'il voit). en effet, la mémoire d'un processus n'est pas accessible aux autres (sauf cas exceptionnels).

voila, donc avec l'astuce char szUser[] = {'u','s','e','r','3','2',0}; on peut eviter les appels aux data pour les chaines. reste plus que faire pareil pour les appels de fonctions. pour cela, j'ai créé une structure dans laquel j'ai mis les adresses de GetProcAddress et LoadLibrary (cette lib etant chargée en mémoire 'partagée' .Je n'ai pas encore super bien compris le concept, mais je sais que kernel32.dll est TJS chargée au mm endroit par OS (WinMe = BFF60000 par exemple il me semble). ensuite, on ecrit donc le contenu de la structure (les 2 adresses) dans la mémoire qu'on a alloué ds le processus cible, et on envoit en paramètre de la fonction injecté un ptr vers cette structure.

ensuite, avec ces 2 api, on peut charger toutes les dll qu'on veut.

donc, ceci est juste un exemple, mais on peut faire pas mal de choses dans le processus cible. (d'apres les MSDN, CreateRemoteThread a été créé pour le débogage ... qd on cherche sur le net, on voit que bcp de code de virus l'utilise. cette api n'est pas supportée par win 9x, elle existe fait elle retourne tjs FALSE avec en GetLastError 'non pris en charge par le système').

signaler à un administrateur
Commentaire de pow le 16/06/2003 10:18:12

Code très interessant, cependant je n'utilise pas Visual .net, et avec ma version 6.0, j'obtiens ce message en projet console :

error LNK2001: unresolved external symbol _main

si je passe en application windows :

error LNK2001: unresolved external symbol _WinMain@16

Quelqu'un aurait une solution?

Merci d'avance :]

signaler à un administrateur
Commentaire de BlackGoddess le 16/06/2003 10:28:16

tu as ignoré toutes les libs par defaut comme j'avais dit pour avoir cette erreur.

il faut donc rajouter, ds la ligne de commande du linker /entry:Main

signaler à un administrateur
Commentaire de pow le 16/06/2003 14:38:36

Oui, ben en fait j'ai trouvé la solution :{
Faire un projet application windows, et renommer la fonction Main, en une fonction main.
Vala, pas plus compliqué que cela :]
(encore désolé)

signaler à un administrateur
Commentaire de BlackGoddess le 16/06/2003 23:22:14

np lol

signaler à un administrateur
Commentaire de lolman27 le 12/06/2004 01:26:40

salut j utilise vc ++ 6
Le processus cible plante systematiquement que faire?^^

signaler à un administrateur
Commentaire de vbnul le 12/06/2004 09:27:18

Compiler en release et non en debug (je l'ai dit au dessus)

signaler à un administrateur
Commentaire de lolman27 le 12/06/2004 11:14:06

Exact .J arrive a faire tourner le code,et jai peu pres compris ms je n arrive pas a coder ce que je veux faire...
C est a dire charger un socket ds le prog cible et l utilise depuis le process principale...
Le pb c que je  suis pas(encore ;)) une bete en c++
Un peu d aide ne serait pas de refus :P thx
msn: lolman27@hotmail.com

signaler à un administrateur
Commentaire de BlackGoddess le 12/06/2004 18:33:00

utilises une dll pour faire ton "application" (dont les sockets)
puis utilises ce bout de code pour charger la dll

signaler à un administrateur
Commentaire de vbnul le 12/06/2004 19:08:00

La technique est décrite ici :
http://www.freecodz.net/articles/CodeInjection.html

signaler à un administrateur
Commentaire de lolman27 le 12/06/2004 19:19:49

merci bien

signaler à un administrateur
Commentaire de cancooler le 15/11/2004 12:30:04


Tres interessant ce code: clair et efficace, bravo :)

Bon deux petites questions:

no1 -  Que devient la memoire allouee dans le process cible lorsque le remote thread se termine?

J'ai essayer d'utiliser la fonction

VirtualFreeEx(hProc, CodeMem, 0, MEM_RELEASE )
ou
VirtualFreeEx(hProc, CodeMem, cbCodeSize, MEM_DECOMMIT )

Dans les deux cas il en resulte un plantage du process cible bien que la fonction VirtualFreeEx semble executee avec succes!?!


no2- J'ai ajoute la fonction suivante au debut de ton main: comme son nom l'indique elle permet d'activer les privileges 'debug'

bool EnableDebugPrivileges( void )
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;

if ( ! OpenProcessToken( GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) )
{
//SeDebugPrivilege is not available
return 0;
}

if ( ! LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &sedebugnameValue ) )
{
//SeDebugPrivilege is not available
return 0;
}

tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

if ( ! AdjustTokenPrivileges( hToken, FALSE, &tkp, sizeof tkp, NULL, NULL ) )
{
//SeDebugPrivilege is not available
CloseHandle( hToken );
return 0;
}



CloseHandle( hToken );

return 1;
}


Interet? Avec l'utilisation de cette fonction il est possible de creer un remote thread dans un process system. Donc a priori executer des instructions privilegiees...C cool mais bon be careful le systeme plante facilement suivant le process cible :))
Cependant, lorsque je veux afficher une MsgBox par ex en passant par un process systeme, le code est execute sans plantage, le signal sonore de la MsgBox est la, mais pas de MsgBox a l'ecran!?! Explications pliz??

signaler à un administrateur
Commentaire de BlackGoddess le 15/11/2004 18:57:30

mmh c'est en effet un problème ... je ne vois aucun moyen d'y parvenir ...

si tu libères ta propre mémoire, au retour du VirtualFreeEx, le pointeur d'execution pointera vers une adresse de la mémoire libérée, donc désormais inaccessible.

charger une dll puis appeler une fonction exportée qui libérerait la mémoire et ferait un ExitThread pour etre dur que le pointeur ne revienne jamais ?
mais la dll resterait chargée pour le processus hôte, on tourne en rond ...

signaler à un administrateur
Commentaire de pow le 16/11/2004 11:14:48

"lorsque je veux afficher une MsgBox par ex en passant par un process systeme, le code est execute sans plantage, le signal sonore de la MsgBox est la, mais pas de MsgBox a l'ecran!?! Explications pliz??"

Imo, ca c'est parsque le process cible n'a pas d'handle parent (genre pas de gestion des events/fenetres).

int MessageBox(
    HWND hWnd,
    LPCTSTR lpText,
    LPCTSTR lpCaption,
    UINT uType
);

le premier parametre doit être un handle vers une fenetre parent, j'ai eu des cas où en mettant null, cela ne fonctionnait pas... (probleme que tu décris)

signaler à un administrateur
Commentaire de BlackGoddess le 18/11/2004 19:16:16

le problème est qu'un process systeme de dépend pas d'un utilisateur, aussi il ne sait pas dans quel contexte afficher la fenetre (sur quelle session utilisateur)

signaler à un administrateur
Commentaire de cancooler le 22/11/2004 12:40:35

Bon maintenant que j'en sais un peu plus, quelques petites modifs  sont a apporter a ton code pour le rendre 'clean'- j'espere que tu m'en voudras pas ;) -

D'abord, le flag "MEM_COMMIT" est suffisant pour l'appel a  VirtualAllocEx: la memoire sera reservee uniquement dans l'espace memoire virtuel du process cible.

Ensuite pour ne pas laisser de trace, il faut faire appel a VirtualFreeEx de cette facon:

VirtualFreeEx(hProc, CodeMem, 0, MEM_RELEASE);
VirtualFreeEx(hProc, DataMem, 0, MEM_RELEASE);

Mais attention, il faut etre sur que le remote thread ait bien termine son execution: pour cela on place un

WaitForSingleObject(hThread, INFINITE);

avant l'appel a VirtualFreeEx.

Enfin un CloseHandle(hProc); pour finir.


L'injection dans un process systeme fonctionne egalement - pas pour tous a priori et certains aiment pas du tout!.
Pour l'affichage de la MsgBox par ex, je recupere le hWnd de la TaskBar ( avec un FindWindow(TEXT("Shell_TrayWnd"),NULL); ) et je rajoute un champ HWND phWnd dans la struct InJack, par ex...

Pour finir je conseille a quiconque voulant *tout* savoir sur l'injection de code l'excellent article de  Robert Kuster : "Three Ways to Inject Your Code into Another Process" a l'@: http://www.codeproject.com/threads/winspy.asp


signaler à un administrateur
Commentaire de taye78 le 12/05/2006 02:31:10

salut, bon qqs années après... hehe
"
int cbCodeSize = ((LPBYTE) EndRemoteEntry - (LPBYTE) RemoteEntry);
"
Içi tu supposes l'ordre des fonctions en mémoire(l'une apres lautre). Cependant, le linker peut changer l'ordre des fonctions, il peut par exemple mettre RemoteEntry avant EndRemoteEntry. Ce qui planterait le process cible!
Malgrés l'option /ORDER cela reste considere "risque".

Bon code, 9/10, a+

Ajouter un commentaire



Nos sponsors

Sondage...

CalendriCode

Juillet 2009
LMMJVSD
  12345
6789101112
13141516171819
20212223242526
2728293031  

Consulter la suite du CalendriCode

Comparez les prix Nouvelle version

Photothèque Nouveau !



Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés
Temps d'éxécution de la page : 0,437 sec

Google Coop CodeS-SourceS Google Coop CodeS-SourceS


Certaines images présentes sur le site (notament certains avatars) sont issues des collections IconShock, donc si vous souhaitez utiliser ces icons vous devez les acheter, ne les copiez pas et ne utilisez pas dans vos sites et applications sans les avoir commandé.