L'injection de code est une méthode fondamentale en ingénierie logicielle pour modifier le comportement d'un processus sans en posséder le code source. Bien que de nombreuses techniques existent, nous allons nous concantrer ici sur deux approches majeures : l'injection via l'API Windows standard et l'utilisation de la bibliothèque Mircosoft Detours.
Préparation d'une DLL de Hooking
Pour illustrer l'injection, nous utilisons une DLL dont l'objectif est d'intercepter la fonction Sleep. L'utilisation de Detours permet d'éviter les problèmes de récursion infinie grâce à un "trampoline", une petite section de code qui permet d'appeler l'implémentation originale de la fonction sans déclencher à nouveau le hook.
#include "pch.h"
#include <windows.h>
#include <detours.h>
#include <iostream>
#pragma comment(lib, "detours.lib")
// Pointeur vers la fonction originale
static VOID (WINAPI* OriginalSleep)(DWORD dwMilliseconds) = Sleep;
// Fonction de remplacement (Hook)
VOID WINAPI HookedSleep(DWORD dwMilliseconds)
{
ULONGLONG start = GetTickCount64();
OriginalSleep(dwMilliseconds);
ULONGLONG end = GetTickCount64();
WCHAR msg[256];
swprintf_s(msg, L"Délai d'attente réel : %llu ms", end - start);
MessageBoxW(NULL, msg, L"Notification Hook", MB_OK);
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved)
{
if (DetourIsHelperProcess()) return TRUE;
if (reason == DLL_PROCESS_ATTACH) {
DetourRestoreAfterWith();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
// Attachement de l'interception
DetourAttach(&(PVOID&)OriginalSleep, HookedSleep);
DetourTransactionCommit();
} else if (reason == DLL_PROCESS_DETACH) {
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)OriginalSleep, HookedSleep);
DetourTransactionCommit();
}
return TRUE;
}
Injection via les API natives de Windows
Cette méthode classique repose sur la manipulation de la mémoire virtuelle d'un processus cible. Le flux logique est le suivant :
- Ouvrir le processus cible avec les droits nécessaires via
OpenProcess. - Alllouer de l'espace mémoire dans le processus distant avec
VirtualAllocEx. - Écrire le chemin de la DLL dans cet espace avec
WriteProcessMemory. - Créer un thread distant qui appelle
LoadLibrarysur le chemin écrit.
BOOL InjectDllStandard(DWORD processId, const TCHAR* dllPath) {
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
if (!hProc) return FALSE;
size_t pathSize = (_tcslen(dllPath) + 1) * sizeof(TCHAR);
LPVOID remoteMem = VirtualAllocEx(hProc, NULL, pathSize, MEM_COMMIT, PAGE_READWRITE);
if (remoteMem) {
WriteProcessMemory(hProc, remoteMem, dllPath, pathSize, NULL);
HANDLE hThread = CreateRemoteThread(hProc, NULL, 0,
(LPTHREAD_START_ROUTINE)LoadLibrary, remoteMem, 0, NULL);
if (hThread) {
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}
}
VirtualFreeEx(hProc, remoteMem, 0, MEM_RELEASE);
CloseHandle(hProc);
return TRUE;
}
Pour automatiser la recherche du processus, on utilise généralement les clichés instantanés (Snapshots) du système :
DWORD FindTargetProcessId(const TCHAR* processName) {
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
DWORD pid = 0;
if (Process32First(hSnapshot, &pe32)) {
do {
if (_tcsicmp(pe32.szExeFile, processName) == 0) {
pid = pe32.th32ProcessID;
break;
}
} while (Process32Next(hSnapshot, &pe32));
}
CloseHandle(hSnapshot);
return pid;
}
Injection via Microsoft Detours
Detours propose une approche plus intégrée via la fonction DetourCreateProcessWithDllEx. Cette fonction permet de lancer un nouveau processus directement en état de suspension, d'y injecter la DLL et de reprendre l'exécution, assurant ainsi que le hook est actif dès le premier cycle d'horloge du programme cible.
BOOL StartProcessWithDetours(TCHAR* exePath, char* dllPath) {
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
return DetourCreateProcessWithDllEx(
NULL, exePath, NULL, NULL, FALSE,
CREATE_DEFAULT_ERROR_MODE, NULL, NULL,
&si, &pi, dllPath, NULL
);
}
Gestion de la binarité (32-bit vs 64-bit)
Un aspect critique de l'utilisation de Detours concerne la compatibilité entre les architectures x86 et x64. Detours possède une logique de redirection intelligente :
- Si le processus injecteur et la cible ont des architectures différentes, Detours tente de trouver une DLL correspondante en remplaçant les suffixes "32" par "64" (ou inversement) dans le nom du fichier.
- Si les architectures sont identiques, aucune redirection automatique n'est effectuée. Il est donc impératif de fournir la DLL correspondant exactement à l'architecture de la cible dans ce cas précis.
Pour supporter l'injection croisée de manière robuste, il est recommandé de maintenir deux versions de la DLL de hook (ex: Hook64.dll et Hook32.dll) dans le même répertoire afin que Detours puisse basculer de l'une à l'autre selon les besoins du processus enfant créé.