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 !

[WINNT] INTERCEPTER LES APPELS AUX API WIN32


Information sur la source

Catégorie :API Source .NET ( DotNet ) Niveau : Expert Date de création : 22/05/2004 Vu / téléchargé: 21 112 / 801

Note :
9,5 / 10 - par 4 personnes
9,50 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

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

Description

Cliquez pour voir la capture en taille normale
Voilà, je suis tombé il y a pas longtemps sur une fonction qui permettait d'intercepter un appel à une fonction de Windows, mais seulement dans le même processus. Je m'expliqe:
Chaque processus (programme) en cours d'exécution a en mémoire plusieus modules (DLL) comme Kernel32.dll, User32.dll etc. Cette fonction permettait d'intercepter tous les appels à une fonction précise d'un module (par exemple CreateFile dans Kernel32.dll) et de la remplacer par une fonction personelle, mais seulement dans le même processus. C'est à dire qu'on ne pouvait pas s'en servir pour intercepter les appels d'un autre processus, juste de son propre processus.

Comme je voulais pouvoir espionner les appels à certaines fonctions (comme la création, suppression de fichiers, ou l'accès au registre) de certains programmes, j'ai créé plusieurs DLL pour y arriver:

-SharpSpyHelper.dll, en C avec une partie d'ASM inline, qui est injectée dans le processus à espionner (pour l'exemple notepad) et qui contient tout ce qu'il faut pour hooker une fonction (la remplacer par une autre)

-SharpSpy.dll, en C# (mais qui peut être fait en C/C++, c'est pas le problème) qui injecte l'autre DLL dans le processus (notepad) et qui communique avec elle quand les fonctions considérées sont appellées (là pour l'exemple, j'intercepte les appels de notepad à LoadIconW, et je lui fait charger un icône personalisé)

Le seul problème c'est qu'injecter une DLL dans un processus nécessite d'y créer un thread avec la fonction CreateRemoteThread avec pour point d'entrée la fonction LoadLibrary (Kernel32.dll), c'est à dire que dès que le thread est créé il va charger dans le processus ma DLL. Et comme cette fonction n'est disponible que sur Windows NT/2000/XP, on ne pas intercepter des fonctions d'autres processus avec Windows 95/98/ME, désolé!

Aussi, comme la mémoire de chaque processus est isolée, si on veut faire exécuter à un autre processus une fonction qui a pour arguments des pointeurs ou des chaînes, il faut allouer de la mémoire dans ce processus avec VirtualAllocEx pour y mettre le contenu des arguments, mais cette fonction n'est aussi disponible que sous NT/2000/XP...
 

Source

  • Pour utiliser SharpSpyHelper.dll et/ou SharpSpy.dll:
  • [C#]
  • //Ajouter la référence 'SharpSpy.dll'
  • //Définir un délégué pour la fonction à appeller:
  • delegate IntPtr LoadIconWHandler( IntPtr hInstance, IntPtr lpIconName );
  • //Et écrire la fonction qui doit être appellée à la place de l'API
  • IntPtr MyLoadIconW( IntPtr hInstance, IntPtr lpIconName )
  • {
  • <...>
  • }
  • //puis ajouter ce code quelque part:
  • //INITIALISATION
  • //processus dans lequel intercepter des API
  • Process processus = Process.GetProcessesByName( "notepad" )[0];
  • SystemProcess proc = new SystemProcess( processus );
  • //tas de mémoire du processus
  • MiniHeap heap = proc.Heap;
  • SharpSpyHelper helper;
  • HookTable hook;
  • uint error = 0;
  • //on injecte SharpSpyHelper.dll dans le processus
  • uint helperAddress = proc.InjectLibraryEx( @"<Chemin de la DLL SharpSpyHelper.dll>", out error );
  • if( (helperAddress != 0) && (error == 0) )
  • {
  • helper = new SharpSpyHelper( proc, @"<Chemin de la DLL SharpSpyHelper.dll>", (IntPtr)address );
  • //on récupère la table des hooks
  • table = helper.Table;
  • helper.InitMsgWindow(); //helper.InitMsgWindow( this.Handle ); si le code s'exécute dans une Form
  • }
  • //CREATION DU HOOK
  • //on récupère un handle du processus
  • uint hProcess = helper.GetProcessHandle();
  • //on crée un hook: <DLL>, <Fonction à hooker>
  • //ici HICON LoadIconW( HINSTANCE hInstance, LPCTSTR lpIconName );
  • HookParams hParams = new HookParams( "User32.dll", "LoadIconW", heap );
  • hParams.HookName = "NativeCallSinkProxy";
  • hParams.Module = hProcess;
  • hParams.HookedAddress = helper.GetFunctionAddress( "CallSinkProxy" );
  • //Taille des arguments en octets
  • hParams.ArgumentsSize = 8;
  • //Taille de la valeur de retour en octets
  • hParams.ReturnValueSize = 4;
  • //on définit la fonction a appeller à la place de l'originale
  • hParams.Callback = new LoadIconWHandler( MyLoadIconW );
  • //on ajoute le hook à la table des hooks
  • table.Add( hParams );
  • //on initialise la table
  • table.Init();
  • //on effectue le hook
  • if( helper.HookByID( hParams.HookID ) )
  • {
  • //Le hook a réussi!
  • //tous les appels à LoadIconW dans notepad seront remplacés par un appel à MyLoadIconW
  • //rafraîchit les infos de la table des hooks, comme l'adresse originale de la fonction
  • table.Resfresh();
  • }
  • [C++ Managé]
  • Pas de différence avec la version C# si ce n'est dans la syntaxe... (pointeurs __gc, etc)
  • [C/C++]
  • //inclure les headers de Windows
  • #include <windows.h>
  • #include <winnt.h>
  • //etc
  • #include "SharpSpyHelper.h"
  • //récupérer un handle du processus d'abord
  • HANDLE hProcess;
  • LPWSTR lpModule = L"<Chemin de la DLL SharpSpyHelper.dll>";
  • DWORD cbModule = (wcslen( lpModule ) + 1) * 2;
  • //on alloue de la mémoire dans le processus
  • LPVOID hMem = VirtualAllocEx( hProcess, NULL, cbModule, MEM_COMMIT, PAGE_READWRITE );
  • DWORD dwWritten;
  • //puis on écrit dans la mémoire du processus le chemin de SharpSpyHelper.dll
  • WriteProcessMemory( hProcess, hMem, lpModule, cbModule, &dwWritten );
  • //on récupère l'addresse de LoadLibraryW dans Kernel32.dll, commune à tous les procesus
  • DWORD hKernel = GetModuleHandle( _T("Kernel32.dll") );
  • DWORD hLL = GetProcAddress( _T("LoadLibraryW") );
  • //on va injecter SharpSpyHelper.dll dans le processus
  • //c'est à dire y créer un thread qui va exécuter LoadLibraryW( "SharpSpyHelper.dll" )
  • //qui contient tous les fonctions de hook.
  • HANDLE hThread = NULL;
  • DWORD dwThreadId;
  • DWORD hModuleAddress;
  • hThread = CreateRemoteThread( hProcess, NULL, hLL, hMem, 0, &dwThreadId );
  • if( hThread != NULL )
  • {
  • //On attend que le thread se termine
  • WaitForSingleObjectEx( hThread, TIMEOUT_INFINITE, TRUE );
  • //on récupère la valeur de retour du thread: soit l'addresse de la DLL chargée,
  • //soit un code d'erreur si elle n'a pas été trouvée
  • GetExitCodeThread( hThread, &hModuleAddress );
  • }
  • //on libère la mémoire allouée pour le chemin de la DLL
  • VirtualFreeEx( hProcess, hMem, cbModule, MEM_RELEASE );
  • //à partir de la SharpSpyHelper.dll est chargé dans le processus (notepad par exemple)
  • //il reste plus qu'à obtenir l'addresse de la fonction GetMyProcAddress dans SharpSpyHelper.dll
  • //avec CreateRemoteThread mais en passant l'addresse de GetProcAddress (toujours Kernel32.dll)
  • //et comme paramètre la chaîne _T("GetMyProcAddress")
  • //une fois qu'on a l'addresse de GetMyProcAddress, il suffit de l'appeller (toujours avec CreateRemoteThread)
  • //en passant un des paramètre suivant: (pour la liste complete voir SharpSpyHelper.h)
  • #define PROCADDRESS_DLLMAIN 1
  • #define PROCADDRESS_GETPROCESSHANDLE 2
  • #define PROCADDRESS_INDIRECTCALLEX 3
  • #define PROCADDRESS_HOOK 4
  • //pour avoir l'addresse d'une fonction
  • //par exemple en appelant GetMyProcAddress( PROCADDRESS_INDIRECTCALLEX )
  • //on obtient l'addresse de la fonction IndirectCallEx dans SharpSpyHelper.dll
  • hThread = CreateRemoteThread( hProcess, NULL, <addresse de GetMyProcAddress>, PROCADDRESS_INDIRECTCALLEX, 0, &dwThreadId );
  • //etc...
  • //On récupère l'addresse de la fonction IndirectCallEx que voila:
  • void __stdcall IndirectCallEx( NCallParams* lpCallParams );
  • struct NCallParams
  • {
  • DWORD address;
  • DWORD* lpStack;
  • DWORD cbStack;
  • void* lpReturn;
  • DWORD cbReturn;
  • BOOL cdeclCall;
  • };
  • //Cette fonction permet d'appeller avec CreateRemoteThread une fonction
  • //qui a plus d'un argument. Par exemple, pour appeller
  • void InitHookList( void* lpList, DWORD cbList );
  • void* lpList;
  • DWORD cbList;
  • //un pointeur 32 bits et un DWORD = 8 octets
  • BYTE* lpStack = (BYTE*)malloc( 8 );
  • //on copie lpList et cbList dans lpStack
  • memcpy( lpStack, &lpList, 4 );
  • memcpy( lpStack + 4, &cbList, 4 );
  • //il faut d'abord créer une structure NCallParams
  • NCallParams nc;
  • //à récupérer avec GetMyprocAddress (addresse de InitHookList)
  • nc.address = lpInitHookList;
  • //pile de 8 octets
  • nc.lpStack = (DWORD*)lpStack;
  • nc.cbStack = 8;
  • //pas de valeur de retour
  • nc.lpReturn = NULL;
  • nc.cbReturn = 0;
  • //convention d'appel __stdcall
  • nc.cdeclCall = FALSE;
  • //on appelle enfin la fonction avec CreateRemoteThread
  • DWORD dwWritten;
  • LPVOID hParams = VirtualAllocEx( hProcess, NULL, sizeof(NCallParams), MEM_COMMIT, PAGE_READWRITE );
  • WriteProcessMemory( hProcess, hParams, &nc, sizeof(NCallParams), &dwWritten );
  • hThread = CreateRemoteThread( hProcess, NULL, <addresse de IndirectCallEx>, hParams, 0, &dwThreadId );
  • //idem qu'avant avec WaitForSingleObjectEx, GetExitCodeThread et VirtualFreeEx
  • //on libère aussi la mémoire de la pile
  • free( lpStack );
  • //Pour faire le hook:
  • //Créer une structure HookParams, comme dans l'exemple C#
  • struct HookParams
  • {
  • DWORD dwHookID;
  • HMODULE hModule;
  • LPSTR library;
  • LPSTR function;
  • DWORD originalAddress;
  • DWORD hookedAddress;
  • DWORD thunkAddress;
  • DWORD argsSize;
  • DWORD returnSize;
  • DWORD jumpAddress;
  • BOOL hooked;
  • };
  • HookParams hook;
  • hook.dwHookID = 0;
  • //à récupérer avec GetProcessHandle( 0 ) dans SharpSPyHelper.dll
  • hook.hModule = <hModule du processus>;
  • hook.library = "User32.dll";
  • hook.function = "LoadIconW";
  • //à récupérer avec GetMyProcAddress( PROCADDRESS_CALLSINKPROXY )
  • hook.originalAddress = NULL;
  • hook.hookedAddress = <Adresse de NativeCallSinkProxy>;
  • hook.thunkAddress = NULL;
  • hook.argsSize = 8;
  • hook.returnSize = 4;
  • hook.jumpAddress = NULL;
  • hook.hooked = FALSE;
  • //on copie le contenu de la structure HookParams dans la mémoire du processus,
  • //avec VirtualAllocEx et WriteProcessMemory
  • LPVOID lpHook = <VirtualAllocEx( ... ) >
  • //On exécute InitHookList avec GetMyProcAddres, CreateRemoteThread et IndirectCallEx
  • InitHookList( lpHook, sizeof(HookParams) );
  • //Puis on active le hook (ici, 0 est l'ID du hook - voir plus haut) ici pas besoin d'IndirectCallEx (un seul argument)
  • if( HookByID( 0 ) )
  • {
  • // le hook est réussi!
  • }
  • //après ca il reste juste à gérer dans la boucle des messages de la fenêtre de l'application
  • //gérer les messages WM_COPYDATA:
  • //un message WM_COPYDATA est envoyé à chaque fois qu'une fonction hookée est appellée
  • //avec les paramètres de l'appel (ID du hook, arguments...)
  • InitMsgWindow( <hWnd de la fênetre> );
  • //Dans WndProc
  • LRESULT WndProc( HWND, UINT, WPARAM, LPARAM );
  • //dans le switch des messages de la fenêtre
  • switch( uMsg )
  • {
  • case WM_COPYDATA:
  • {
  • LPCOPYDATA lpcd = (LPCOPYDATA)LPARAM;
  • void* pData = malloc( lpcd.cbData );
  • memcpy( pData, lpcd.lpData, lpcd.cbData );
  • //traiter les données reçues: (voir GetSendCallBuffer dans SharpSpyHelper.cpp)
  • //struct
  • //{
  • // BYTE bType = COMM_CALL;
  • // DWORD dwSize; //taille du message
  • // DWORD dwCallID; //ID d'appel - ignorer
  • // DWORD dwHookID; //ID du hook appellé
  • // DWORD cbStack; //taille de la pile
  • // //si cbStack > 0
  • // BYTE stack[cbStack]; //données de la pile (paramètres de la fonction)
  • //};
  • //Comme HICON LoadIconW( HINSTANCE hInstance, LPCTSTR lpIconName );
  • HINSTANCE hInstance;
  • LPCTSTR lpIconName;
  • //on extrait les paramètres de la pile (stack)
  • memcpy( &hInstance, &stack, 4 );
  • memcpy( &lpIconName, &stack + 4, 4 );
  • //appeller la fonction qui remplace la fonction hookée
  • //elle doit retourner un HICON
  • HICON hIcon;
  • //renvoyer un pointeur vers une structure,
  • //pour la valeur de retour
  • //struct
  • //{
  • // BYTE bType = COMM_ANSWER;
  • // DWORD dwSize; //taille du message
  • // DWORD dwCallID; //ID d'appel - mettre le même
  • // DWORD dwHookID; //ID du hook - idem
  • // DWORD cbReturn; //taille de la valeur de retour
  • // //si cbReturn > 0
  • // BYTE returnVal[cbReturn]; //valeur de retour
  • //};
  • cbReturn = 4; //sizeof(HICON)
  • bType = COMM_ANSWER;
  • //on alloue une BYTE, 4 DWORDs plus de la place pour returnVal
  • dwSize = 17 + cbReturn;
  • BYTE* pAns = (BYTE*)malloc( dwSize );
  • //on remplit la 'structure'
  • memcpy( pAns, &bType, 1 );
  • memcpy( pAns + 1, &dwSize, 4 );
  • memcpy( pAns + 5, &dwCallID, 4 );
  • memcpy( pAns + 9, &dwHookID, 4 );
  • memcpy( pAns + 13, &cbReturn, 4 );
  • memcpy( pAns + 17, &hIcon, 4 ); //valeur de retour
  • //on libère la mémoire allouée pour les données reçues
  • free( pData );
  • //on retourne la 'structure' de retour
  • return pAns;
  • }
  • }
Pour utiliser SharpSpyHelper.dll et/ou SharpSpy.dll:

[C#]
//Ajouter la référence 'SharpSpy.dll'
//Définir un délégué pour la fonction à appeller:
delegate IntPtr LoadIconWHandler( IntPtr hInstance, IntPtr lpIconName );
//Et écrire la fonction qui doit être appellée à la place de l'API
IntPtr MyLoadIconW( IntPtr hInstance, IntPtr lpIconName )
{
   <...>
}

//puis ajouter ce code quelque part:

//INITIALISATION
//processus dans lequel intercepter des API
Process processus = Process.GetProcessesByName( "notepad" )[0];
SystemProcess proc = new SystemProcess( processus );
//tas de mémoire du processus
MiniHeap heap = proc.Heap;
SharpSpyHelper helper;
HookTable hook;
uint error = 0;
//on injecte SharpSpyHelper.dll dans le processus
uint helperAddress = proc.InjectLibraryEx( @"<Chemin de la DLL SharpSpyHelper.dll>", out error );

if( (helperAddress != 0) && (error == 0) )
{
    helper = new SharpSpyHelper( proc, @"<Chemin de la DLL SharpSpyHelper.dll>", (IntPtr)address );
    //on récupère la table des hooks
    table = helper.Table;
    helper.InitMsgWindow();	//helper.InitMsgWindow( this.Handle ); si le code s'exécute dans une Form    
}


//CREATION DU HOOK
//on récupère un handle du processus
uint hProcess = helper.GetProcessHandle(); 
//on crée un hook: <DLL>, <Fonction à hooker>
//ici HICON LoadIconW( HINSTANCE hInstance, LPCTSTR lpIconName );
HookParams hParams = new HookParams( "User32.dll", "LoadIconW", heap );
hParams.HookName = "NativeCallSinkProxy";
hParams.Module = hProcess;
hParams.HookedAddress = helper.GetFunctionAddress( "CallSinkProxy" );
//Taille des arguments en octets
 hParams.ArgumentsSize = 8;
//Taille de la valeur de retour en octets
hParams.ReturnValueSize = 4;
//on définit la fonction a appeller à la place de l'originale
hParams.Callback = new LoadIconWHandler( MyLoadIconW ); 
//on ajoute le hook à la table des hooks
table.Add( hParams );
//on initialise la table
table.Init();

//on effectue le hook
if( helper.HookByID( hParams.HookID ) )
{
    //Le hook a réussi!
    //tous les appels à LoadIconW dans notepad seront remplacés par un appel à MyLoadIconW
    //rafraîchit les infos de la table des hooks, comme l'adresse originale de la fonction
    table.Resfresh();
}

[C++ Managé]
Pas de différence avec la version C# si ce n'est dans la syntaxe... (pointeurs __gc, etc)

[C/C++]
//inclure les headers de Windows
#include <windows.h>
#include <winnt.h>
//etc
#include "SharpSpyHelper.h"
//récupérer un handle du processus d'abord
HANDLE hProcess; 

LPWSTR lpModule = L"<Chemin de la DLL SharpSpyHelper.dll>";
DWORD cbModule = (wcslen( lpModule ) + 1) * 2;
//on alloue de la mémoire dans le processus
LPVOID hMem = VirtualAllocEx( hProcess,  NULL,  cbModule,  MEM_COMMIT,  PAGE_READWRITE );
DWORD dwWritten;
//puis on écrit dans la mémoire du processus le chemin de SharpSpyHelper.dll
WriteProcessMemory( hProcess, hMem, lpModule, cbModule, &dwWritten );

//on récupère l'addresse de LoadLibraryW dans Kernel32.dll, commune à tous les procesus
DWORD hKernel = GetModuleHandle( _T("Kernel32.dll") );
DWORD hLL = GetProcAddress( _T("LoadLibraryW") );
		
//on va injecter SharpSpyHelper.dll dans le processus
//c'est à dire y créer un thread qui va exécuter LoadLibraryW( "SharpSpyHelper.dll" )
//qui contient tous les fonctions de hook.

HANDLE hThread = NULL;
DWORD dwThreadId;
DWORD hModuleAddress;
hThread = CreateRemoteThread( hProcess, NULL, hLL, hMem, 0, &dwThreadId );

if( hThread != NULL )
{
    //On attend que le thread se termine
    WaitForSingleObjectEx( hThread, TIMEOUT_INFINITE, TRUE );
    //on récupère la valeur de retour du thread: soit l'addresse de la DLL chargée,
    //soit un code d'erreur si elle n'a pas été trouvée
    GetExitCodeThread( hThread, &hModuleAddress );
}
//on libère la mémoire allouée pour le chemin de la DLL
VirtualFreeEx( hProcess, hMem, cbModule, MEM_RELEASE );

//à partir de la SharpSpyHelper.dll est chargé dans le processus (notepad par exemple)
//il reste plus qu'à obtenir l'addresse de la fonction GetMyProcAddress dans SharpSpyHelper.dll
//avec CreateRemoteThread mais en passant l'addresse de GetProcAddress (toujours Kernel32.dll)
//et comme paramètre la chaîne _T("GetMyProcAddress")
//une fois qu'on a l'addresse de GetMyProcAddress, il suffit de l'appeller (toujours avec CreateRemoteThread)
//en passant un des paramètre suivant: (pour la liste complete voir SharpSpyHelper.h)

#define PROCADDRESS_DLLMAIN			1
#define PROCADDRESS_GETPROCESSHANDLE	                2
#define PROCADDRESS_INDIRECTCALLEX		3
#define PROCADDRESS_HOOK			4

//pour avoir l'addresse d'une fonction
//par exemple en appelant GetMyProcAddress( PROCADDRESS_INDIRECTCALLEX )
//on obtient l'addresse de la fonction IndirectCallEx dans SharpSpyHelper.dll
hThread = CreateRemoteThread( hProcess, NULL, <addresse de GetMyProcAddress>, PROCADDRESS_INDIRECTCALLEX, 0, &dwThreadId );
//etc...

//On récupère l'addresse de la fonction IndirectCallEx que voila:
void __stdcall IndirectCallEx( NCallParams* lpCallParams );

struct NCallParams
{
	DWORD address;				
	DWORD* lpStack;				
	DWORD cbStack;				
	void* lpReturn;				
	DWORD cbReturn;				
	BOOL cdeclCall;				
};
//Cette fonction permet d'appeller avec CreateRemoteThread une fonction
//qui a plus d'un argument. Par exemple, pour appeller

void InitHookList( void* lpList, DWORD cbList );

void* lpList;
DWORD cbList;
//un pointeur 32 bits et un DWORD = 8 octets
BYTE* lpStack = (BYTE*)malloc( 8 );
//on copie lpList et cbList dans lpStack
memcpy( lpStack, &lpList, 4 );
memcpy( lpStack + 4, &cbList, 4 );

//il faut d'abord créer une structure NCallParams
NCallParams nc;
//à récupérer avec GetMyprocAddress (addresse de InitHookList)
nc.address = lpInitHookList;
//pile de 8 octets
nc.lpStack = (DWORD*)lpStack;
nc.cbStack = 8;
//pas de valeur de retour
nc.lpReturn = NULL; 		
nc.cbReturn = 0;
//convention d'appel __stdcall
nc.cdeclCall = FALSE; 		

//on appelle enfin la fonction avec CreateRemoteThread
DWORD dwWritten;
LPVOID hParams = VirtualAllocEx( hProcess, NULL, sizeof(NCallParams), MEM_COMMIT, PAGE_READWRITE );
WriteProcessMemory( hProcess, hParams, &nc, sizeof(NCallParams), &dwWritten );
hThread = CreateRemoteThread( hProcess, NULL, <addresse de IndirectCallEx>, hParams, 0, &dwThreadId );
//idem qu'avant avec WaitForSingleObjectEx, GetExitCodeThread et VirtualFreeEx
//on libère aussi la mémoire de la pile
free( lpStack );

//Pour faire le hook:
//Créer une structure HookParams, comme dans l'exemple C#
struct HookParams
{
	DWORD dwHookID;				
	HMODULE hModule;			
	LPSTR library;				
	LPSTR function;				
	DWORD originalAddress;		
	DWORD hookedAddress;		
	DWORD thunkAddress;			
	DWORD argsSize;				
	DWORD returnSize;			
	DWORD jumpAddress;			
	BOOL  hooked;				
};

HookParams hook;
hook.dwHookID = 0;
//à récupérer avec GetProcessHandle( 0 ) dans SharpSPyHelper.dll
hook.hModule = <hModule du processus>;
hook.library = "User32.dll";
hook.function = "LoadIconW";
//à récupérer avec GetMyProcAddress( PROCADDRESS_CALLSINKPROXY )
hook.originalAddress = NULL;
hook.hookedAddress = <Adresse de NativeCallSinkProxy>;
hook.thunkAddress = NULL;
hook.argsSize = 8;
hook.returnSize = 4;
hook.jumpAddress = NULL;
hook.hooked = FALSE;

//on copie le contenu de la structure HookParams dans la mémoire du processus,
//avec VirtualAllocEx et WriteProcessMemory
LPVOID lpHook = <VirtualAllocEx( ... ) >

//On exécute InitHookList avec GetMyProcAddres, CreateRemoteThread et IndirectCallEx
InitHookList( lpHook, sizeof(HookParams) );
//Puis on active le hook (ici, 0 est l'ID du hook - voir plus haut) ici pas besoin d'IndirectCallEx (un seul argument)
if( HookByID( 0 ) ) 
{
     // le hook est réussi!
}


//après ca il reste juste à gérer dans la boucle des messages de la fenêtre de l'application
//gérer les messages WM_COPYDATA:
//un message WM_COPYDATA est envoyé à chaque fois qu'une fonction hookée est appellée
//avec les paramètres de l'appel (ID du hook, arguments...)
InitMsgWindow( <hWnd de la fênetre> );

//Dans WndProc
LRESULT WndProc( HWND, UINT, WPARAM, LPARAM );
//dans le switch des messages de la fenêtre
switch( uMsg )
{
     case WM_COPYDATA:
     {
          	LPCOPYDATA lpcd = (LPCOPYDATA)LPARAM;
          	void* pData = malloc( lpcd.cbData ); 
          
          	memcpy( pData, lpcd.lpData, lpcd.cbData );
          	//traiter les données reçues: (voir GetSendCallBuffer dans SharpSpyHelper.cpp)
          	//struct
	//{
	//    BYTE bType = COMM_CALL;
	//    DWORD dwSize;		//taille du message
	//    DWORD dwCallID;		//ID d'appel - ignorer
	//    DWORD dwHookID;		//ID du hook appellé
	//    DWORD cbStack;		//taille de la pile
	//    //si cbStack > 0
	//    BYTE stack[cbStack];	//données de la pile (paramètres de la fonction)
	//};
	//Comme HICON LoadIconW( HINSTANCE hInstance, LPCTSTR lpIconName );
	HINSTANCE hInstance;
	LPCTSTR lpIconName;
	//on extrait les paramètres de la pile (stack)
	memcpy( &hInstance, &stack, 4 );
	memcpy( &lpIconName, &stack + 4, 4 );

	//appeller la fonction qui remplace la fonction hookée
	//elle doit retourner un HICON
	HICON hIcon;
	
	//renvoyer un pointeur vers une structure, 
	//pour la valeur de retour
	//struct
	//{
	//    BYTE bType = COMM_ANSWER;
	//    DWORD dwSize;		//taille du message
	//    DWORD dwCallID;		//ID d'appel - mettre le même
	//    DWORD dwHookID;		//ID du hook - idem
	//    DWORD cbReturn;		//taille de la valeur de retour
	//    //si cbReturn > 0
	//    BYTE returnVal[cbReturn];	//valeur de retour
	//};
	cbReturn = 4; 		//sizeof(HICON)
	bType = COMM_ANSWER;
	//on alloue une BYTE, 4 DWORDs plus de la place pour returnVal
	dwSize = 17 + cbReturn;
	BYTE* pAns = (BYTE*)malloc( dwSize );
	//on remplit la 'structure'
	memcpy( pAns, &bType, 1 );
	memcpy( pAns + 1, &dwSize, 4 );	
	memcpy( pAns + 5, &dwCallID, 4 );
	memcpy( pAns + 9, &dwHookID, 4 );
	memcpy( pAns + 13, &cbReturn, 4 );
	memcpy( pAns + 17, &hIcon, 4 );	//valeur de retour
	//on libère la mémoire allouée pour les données reçues
	free( pData );
	//on retourne la 'structure' de retour
	return pAns;
     }
}

Conclusion

Les DLL SharpSpy et SharpSpyHelper que j'ai écrites sont surtout axées sur du code .NET (C++ Managé, C# ou VB.NET) mais peuvent être utilisées avec du code C/C++ comme j'ai essayé de le montrer avec le pseudo-code de l'exemple en C/C++.
Ce qu'il faut surtout retenir c'est que les fonctions qu'on intercepte ne s'exécutent pas dans le processus en cours, donc que tous les handles et pointeurs (HWND, mémoire...) ne sont pas valide et qu'il faut les copier dans l'autre processus avec WriteProcessMemory ou les récupérer après un appel avec ReadProcessMemory. Pour les handles il faut les faires créer en faisant exécuter les bonnes fonctions sur l'autre processus avec CreateRemoteThread (Par exemple il faut faire exécuter CreateFile sur l'autre processus pour accéder à un fichier).

Voilà, j'espère que ça servira à quelqu'un, malgré la complexité (mais je me suis tapé le plus gros avec SharpSpyHelper et l'ASM inline...)
 

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 !

Télécharger le zip

Commentaires et avis

signaler à un administrateur
Commentaire de EBArtSoft le 22/05/2004 22:38:12 administrateur CS

Meme genre en vb6 :
http://vbfrance.com/code.aspx?ID=22687

signaler à un administrateur
Commentaire de ymca2003 le 22/05/2004 22:53:40

dans le bouquin de Richter :
http://brunews.free.fr/brunews/download/JR4.zip
http://brunews.free.fr/brunews/download/JR4Sources.zip

il y a un exemple similaire (Part IV Chap 22, API hooking) qui utilise un hook message pour injecter une Dll dans le processus cible, utilisable donc sous Win98/ME.

signaler à un administrateur
Commentaire de Xya le 22/05/2004 23:47:05

Je connaissais pas la technique des hooks messages mais d'après ce que j'en ai compris on ne peut pas spécifier un processus particulier à hooker, SetWindowsHookEx injecte la Dll à chaque processus qui utilise une fenêtre et appelle à chaque message reçu par chaque fenêtre la fonction spécifiée, ce qui surcharge le système inutilement, alors qu'on n'a besoin d'exécuter du code une seule fois (dès qu'on peut exécuter du code on peut créer des threads et injecter du code comme on veut). Si on veut retirer le hook, la Dll est déchargée et on ne peut plus exécuter de code.

Même si ça marche sous Win98/ME ça me parait pas être le meilleur moyen d'injecter du code dans un processus.


Xya

signaler à un administrateur
Commentaire de BruNews le 23/05/2004 00:03:23 administrateur CS

Je pense inversement qu'il vaut mieux employer la technique decrite par Richter. Dans les prochains services packs (je pense) et dans Longhorn assurement, la protection des zones memoires contre l'injection de code par ecriture sera implementee pour les processeurs le supportant. SetWindowsHookEx ne subira par contre pas de modifs de ce point de vue.

signaler à un administrateur
Commentaire de Xya le 23/05/2004 00:14:09

C'est vrai que pour Longhorn les processus risquent d'être encore plus cloisonnés que maintenant, mais tant que l'utilisateur a les bonnes autorisations sur ses processus je pense qu'il sera possible d'écrire dans la mémoire d'un processus, ne serait ce qu'en utilisant les APIs et les autorisations de débogage (DebugActiveProcess & co). D'ailleurs Read/WriteProcessMemory sont classés dans MSDN parmi les fonctions de débogage. D'un autre coté, comme l'architecture de Longhorn n'est encore connue que dans ses grandes lignes (du moins parmi les non-MVPs), on n'est pas en mesure de savoir si ces techniques d'injections de Dll vont toujours être envisageables.


Xya

signaler à un administrateur
Commentaire de BruNews le 23/05/2004 00:26:14 administrateur CS

Adopte Richter pour la perennite du code alors.

BruNews, Admin CS, MVP Visual C++

signaler à un administrateur
Commentaire de EBArtSoft le 23/05/2004 00:28:15 administrateur CS

J'ajouterais que tout ce qui est fait peut etre defait
il suffira d'adapter son code a la platforme comme
tout bon code aujourd'hui. Et puis  longhorn ne sera
qu'une nieme platforme avec ses probleme a resoudre
et ses facilité a exploiter.

signaler à un administrateur
Commentaire de vitoto le 03/01/2006 05:37:49

Some use in VB.Net ?

signaler à un administrateur
Commentaire de draluorg le 01/05/2006 23:46:34

Salut a tous,

Tres interessant :)

Manque plus qu'un exemple d'utilisation en Vb.net ^^

++

signaler à un administrateur
Commentaire de HeavenForsaker le 09/05/2006 13:07:58

Bonjour,

Tu dit :

"je suis tombé il y a pas longtemps sur une fonction qui permettait d'intercepter un appel à une fonction de Windows, mais seulement dans le même processus"

Puis je savoir à quelle fonction tu fais référence ?

signaler à un administrateur
Commentaire de LeChiffre le 21/07/2007 18:13:51

Sorry for using english language. Do you know how to compile it with VS2005?
Libc.lib isn't supported by VS2005 anymore. If I replace it with Libcmt.lib, I get strange errors. Also a problem if I install libc.lib from my old computer. Maybe additional compiler settings?

signaler à un administrateur
Commentaire de Xya le 23/07/2007 19:14:10

What worked for me to build it with VS2005 was to create a new Win32 project as a Win32 DLL and replace the generated files with SharpSpyHelper.cpp/h. In the 'linker settings/Input' tab, I've added 'SharpSpyHelper.def' to 'Module Definition File' and built the solution without changing any other setting.

This dynamically links SharpSpyHelper.dll to msvcr80.dll or mscvr80d.dll (depending on your project configuration (release or debug)) which may be what you're looking for or not.

Ajouter un commentaire



Nos sponsors

Sondage...

CalendriCode

Octobre 2008
LMMJVSD
  12345
678910