Skip to content
KitploitKITPLOIT
OutilsBlog
Log in
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

FluxContactConfidentialité© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
hwbp4mw — Moteur de hooking par points d'arrêt matériels pour Windows qui utilise les registres de débogage pour hooker des fonctions, contourner ETW/AMSI et échapper à la surveillance EDR en espace utilisateur. | Kitploit
Outils/GitHubGitHub/rad9800/hwbp4mw
Évasion IDS/IPSDébogueursPost-ExploitationRed TeamingAttaque Adversariale
GitHubrad9800/hwbp4mw

hwbp4mw

Moteur de hooking par points d'arrêt matériels pour Windows qui utilise les registres de débogage pour hooker des fonctions, contourner ETW/AMSI et échapper à la surveillance EDR en espace utilisateur.

Voir le dépôt
2735419il y a 3 ansVérifié par Kitploit

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

Cet article était à l’origine destiné au VX-Underground Black Mass Halloween Edition 2022.

Moteurs de hooking :

  • Moteur de hooking hwbp x86/x64 multi-thread sécurisé en c
  • Bibliothèque de breakpoints PAGE_GUARD/hwbp en c++20
  • Bibliothèque hwbp (exemple de DLL) en c++20

Technique générique d’évasion en user-land x64 utilisant les registres de débogage :

  • TamperingSyscalls2 en c
  • TamperingSyscalls2 en c++20

Exemples de hooks ETW/AMSI disponibles

  • rad9800/misc

Breakpoints matériels pour logiciels malveillants v 1.0

Notre tâche consiste à hooker trivialement des fonctions et à détourner le flux du code selon les besoins, puis à supprimer le hook une fois qu’il n’est plus nécessaire.

Nous ne pouvons pas envisager d’appliquer des hooks IAT car ils ne sont pas toujours appelés et donc peu fiables. Le hooking inline est une technique puissante ; cependant, elle nécessite de patcher la mémoire là où se trouve le code. C’est une technique puissante, mais des outils tels que PE-Sieve et Moneta peuvent distinguer la différence entre la copie résidente en mémoire et celle sur disque d’un module et signaler cela. Cela nous laisse avec l’outil parfait pour le travail : les registres de débogage, bien qu’ils soient assez sous-estimés par les auteurs de malwares !

Sur Windows, en résumé, un processus est essentiellement une encapsulation de threads, et chacun de ces threads maintient un contexte qui est l’état du thread : les registres et la pile, etc. Les registres de débogage sont une ressource privilégiée, tout comme leur configuration ; cependant, Windows expose divers syscalls qui nous permettent de demander au noyau d’effectuer une action privilégiée en notre nom ; cela inclut la configuration des registres de débogage qui sont parfaits pour nous. NtSetThreadContext et NtGetThreadContext exposent des fonctionnalités permettant de modifier tout contexte de thread pour lequel nous pouvons ouvrir un handle avec le privilège nécessaire. Nous pouvons voir comment définir les registres de débogage avec l’API Win32.```c CONTEXT context = { .ContextFlags = CONTEXT_DEBUG_REGISTERS }; GetThreadContext(thd, &context);

// set our debug information in the Dr registers

SetThreadContext(thd, &context);
Il y a 8 registres de débogage, de Dr0 à Dr7. Ceux qui nous intéressent sont uniquement
Dr0-3, dans lesquels nous stockons les adresses sur lesquelles nous souhaitons placer des points d'arrêt, et Dr6 n'est que le statut de débogage.
Le plus important est Dr7, qui décrit les conditions des points d'arrêt dans lesquelles le processeur lèvera une exception. L'utilisation des registres de débogage comporte diverses limitations, comme un nombre limité (4) et le fait qu'ils ne s'appliquent pas à tous les threads/nouveaux threads créés. Nous allons chercher à remédier à certaines de ces limitations !

Lorsque l'exception est levée, le processeur recherche un gestionnaire d'exceptions que nous pouvons définir et enregistrer dans notre programme [1]. Dans notre gestionnaire d'exceptions défini, nous voulons que notre code associé (différents flux de code) s'exécute lorsque le point d'arrêt correspondant est déclenché.```c
LONG WINAPI ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo)
{
	if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP)
	{
		// Look for our associated code flow relative to our RIP 
		if (HWBP_ADDRESS_MAP.contains(ExceptionInfo->ContextRecord->Rip)) {
			HWBP_ADDRESS_MAP.at(ExceptionInfo->ContextRecord->Rip).func(ExceptionInfo);
			return EXCEPTION_CONTINUE_EXECUTION;
		}
	}
	return EXCEPTION_CONTINUE_SEARCH;
}

Ceci est réalisé par une fonction constructeur qui définit la correspondance entre une fonction lambda "callback" et une adresse. using EXCEPTION_FUNC = std::function <void(PEXCEPTION_POINTERS)>;```c typedef struct { UINT pos; EXCEPTION_FUNC func; } HWBP_CALLBACK;

// Global std::unordered_map<uintptr_t, HWBP_CALLBACK> HWBP_ADDRESS_MAP{ 0 };

// Create our mapping HWBP_ADDRESS_MAP[address].func = function; HWBP_ADDRESS_MAP[address].pos = pos;

Nous devons parcourir tous les threads de nos processus et définir les ajustements correspondants dans le contexte pour chacun d'eux. Cela peut être réalisé à l'aide des fonctions d'assistance ToolHelp32 : CreateToolhelp32Snapshot et Thread32Next. Ce n'est rien d'extraordinaire, mais cela répond à l'une de nos limites : ne pas nous attacher à tous les threads.```c
VOID SetHWBPS(const uintptr_t address, const UINT pos, const bool init = true)
{
	DWORD pid{ GetCurrentProcessId() };
	HANDLE h{ CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0) };
	if (h != INVALID_HANDLE_VALUE) {
		THREADENTRY32 te{ .dwSize = sizeof(THREADENTRY32) };
		if (Thread32First(h, &te)) {
			do {
				if ((te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) +
					sizeof(te.th32OwnerProcessID)) && te.th32OwnerProcessID == pid) {

					HANDLE thd = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
					if (thd != INVALID_HANDLE_VALUE) {
						SetHWBP(thd, address, pos, init);
						CloseHandle(thd);
					}
				}
				te.dwSize = sizeof(te);
			} while (Thread32Next(h, &te));
		}
		CloseHandle(h);
	}
}

Le fait d'avoir des points d'arrêt matériels définis est sans doute suspect, car cela peut indiquer une activité malveillante (bien qu'aucun EDR, à ma connaissance, ne les recherche activement). Ils peuvent être utilisés contre nous comme un IoC potentiel, nous devons donc supprimer leurs traces une fois que nous avons fini de les utiliser.

Nous pouvons implémenter cela dans notre fonction de destructeur !! Cela itérera sur tous les threads et vérifiera si le registre (&context.Dr0)[pos] pointe vers l'adresse à laquelle nous avons initialement défini le point d'arrêt matériel (pos est simplement un index % 4 nous donnant accès aux context.Dr0-Dr3). Nous pouvons également supprimer les conditions nécessaires dans le registre Dr7. Nous devons aussi penser à supprimer notre entrée de mappage. Par conséquent, notre point d'arrêt matériel ne sera présent que pendant la durée requise !```c SetHWBPS(address, pos, false); HWBP_ADDRESS_MAP.erase(address);

Un exemple de point d'arrêt matériel serait Sleep, où l'on remplace simplement la durée du sommeil
par 0.```c
HWBP HWBPSleep{ (uintptr_t)&Sleep, 0,	// Set Dr 0 
	([&](PEXCEPTION_POINTERS ExceptionInfo) {
		ExceptionInfo->ContextRecord->Rcx = 0;
		ExceptionInfo->ContextRecord->EFlags |= (1 << 16);	// continue execution
}) };

Nous savons qu'il faut définir RCX en raison de la convention d'appel rapide à quatre registres de Windows x64[1]. Le premier argument du constructeur est l'adresse sur laquelle placer le point d'arrêt, le second est le registre Dr0- 3 dans lequel stocker (notez que nous ne pouvons avoir que 4 adresses de point d'arrêt à la fois), et le troisième est une fonction lambda qui capturera par référence PEXCEPTION_POINTERS, qui est l'information qu'un gestionnaire d'exceptions recevra. Cela nous permettra en fin de compte de contrôler le déroulement d'un programme différemment selon le point d'arrêt qui a été déclenché.

Télécharger l’outil