Skip to content
Anti-analyseintermediate

NtClose Invalid Handle

Le malware ferme un handle délibérément invalide ; sous un débogueur, le noyau lève STATUS_INVALID_HANDLE, et capturer cette exception révèle un débogueur attaché.

Fermer un handle invalide se comporte différemment selon qu'un débogueur est attaché ou non. Lorsqu'un processus s'exécute sans débogueur, CloseHandle/NtClose sur un handle bidon renvoie simplement une erreur. Mais lorsqu'un débogueur est présent, le noyau lève une exception STATUS_INVALID_HANDLE (0xC0000008). Le malware enveloppe la fermeture dans un bloc try/except : si l'exception se déclenche, il en conclut qu'un débogueur est attaché.

C'est une vérification par pur effet de bord — aucune API de détection de débogueur n'est impliquée, ce qui la rend plus difficile à neutraliser avec des hooks d'API seuls.

Fonctionnement

Le code passe une valeur de handle invalide à NtClose (ou CloseHandle) à l'intérieur d'un gestionnaire d'exception. L'exécution du gestionnaire est le signal.

c
#include <windows.h>

typedef NTSTATUS (NTAPI *pfnNtClose)(HANDLE);

BOOL DebuggerByInvalidHandle(void)
{
    HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
    pfnNtClose NtClose = (pfnNtClose)GetProcAddress(hNtdll, "NtClose");

    __try {
        NtClose((HANDLE)0xDEADBEEF);   // invalid handle
    }
    __except (GetExceptionCode() == 0xC0000008 /* STATUS_INVALID_HANDLE */
              ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
        return TRUE;                   // debugger present
    }
    return FALSE;
}

N'importe quelle valeur manifestement invalide fonctionne (0xDEADBEEF, un nombre impair, un handle déjà fermé). Le comportement distinctif est que l'exception n'est levée que lorsque le processus est débogué.

Détection & contournement

  • Statique — Recherchez dans IDA/Ghidra les appels à NtClose/CloseHandle avec un argument constant qui n'est pas un handle (p. ex. 0xDEADBEEF) à l'intérieur d'un cadre d'exception structurée (__try/__except). La comparaison GetExceptionCode() == 0xC0000008 (constante 0xC0000008) dans un filtre d'exception est révélatrice.
  • Dynamique — Dans x64dbg, posez un point d'arrêt sur ntdll!NtClose et inspectez l'argument handle ; une valeur fantaisiste confirme l'astuce. Surveillez l'exception de première chance 0xC0000008 — configurez le débogueur pour la transmettre à l'application afin que le gestionnaire ne l'interprète pas comme « débogueur présent ».
  • Patch / contournement — Laissez le débogueur transmettre l'exception à l'application (ne vous arrêtez pas dessus), ou patchez l'appel : NOP l'appel NtClose, ou forcez le filtre __except / le résultat pour que la branche « débogueur présent » ne soit jamais prise. Vous pouvez aussi hooker NtClose pour qu'il retourne proprement sur les handles invalides.
  • Outils — L'option « NtClose » de ScyllaHide intercepte l'appel et supprime l'exception parasite ; les paramètres d'exception de x64dbg (transmettre 0xC0000008 à l'application) ; TitanHide au niveau noyau.
Votes

Commentaires(0)