D.Modifier le loader pour qu'il utilise NTDLL directement

1.Théorie

Depuis le début, l'ensemble de fonction utilisé faisait partie de kernerl32.dll (VirtualAlloc, VirtualProtect)

On souhaite désormais utiliser NTDLL.

La Hiérarchie des Appels Windows (Call Stack)

1. Le Concept de "Wrappers" (Enveloppes)

Sous Windows, les fonctions que nous utilisons (comme VirtualAlloc) ne sont pas celles qui font le travail final. Ce sont des wrappers conçus pour faciliter la vie du développeur.

  • API Win32 (Haut niveau) : VirtualAlloc, CreateThread. SituĂ©es dans kernel32.dll. Elles sont simples Ă  utiliser mais trĂšs surveillĂ©es.

  • API Native (Bas niveau) : NtAllocateVirtualMemory, NtCreateThreadEx. SituĂ©es dans ntdll.dll. Elles sont complexes, peu documentĂ©es, mais permettent de "parler" presque directement au noyau.


2. La Chaßne d'Exécution (Utilisation de windbg)

Mon investigation a prouvé que l'exécution suit un chemin précis pour passer du Mode Utilisateur (ton programme) au Mode Noyau (le Kernel).

Le flux observé :

  1. Mon Code appelle VirtualAlloc.

  2. kernel32.dll reçoit l'appel et le transmet à kernelbase.dll.

  3. kernelbase.dll prépare les structures de données complexes et appelle ntdll.dll (la fonction Nt...).

On a bien NtAllocateVirtualMemory qui est utilisĂ©. On fait la mĂȘme chose pour virtualProtect :

Ce qui fera : VirtualProtect -> ntProtectVirtualMemory


3. L'Instruction syscall : La FrontiĂšre

C'est l'étape ultime du monde utilisateur.

  • Le syscall dĂ©clenche une interruption CPU qui fait basculer le processeur en Mode PrivilĂ©giĂ© (Ring 0).

  • À ce moment-lĂ , le noyau Windows prend le relais pour manipuler physiquement la RAM.


4. Pourquoi est-ce crucial ?

A. Le "Hooking" (La Surveillance)

Les Antivirus et EDR placent des hooks au début des fonctions comme VirtualAlloc. Si tu passes par là, tu es "noté" dans leurs logs.

  • Mon but : Sauter par-dessus ces hooks en appelant directement la fonction dans ntdll.dll.


5. Lexique Technique Ă  retenir

  • ntdll.dll : La bibliothĂšque la plus basse de Windows. Tous les chemins y mĂšnent.

  • Native API : L'ensemble des fonctions commençant par Nt ou Zw.

Le code (Avant) :

Avant on avait ça :

Donc on doit modifier les fonctions qui font appel Ă  l'API Windows :

Lien intéressant pour saisir les fonctions GetProcAdress et GetModuleHandle :

Modification vers ntdll:

Avant ça encore une partie théorique :

Résolution Dynamique et Native API

1. La Résolution Dynamique d'API (Dynamic Resolution)

En développement offensif, on utilise le Dynamic Resolving pour masquer les intentions du binaire. Au lieu que le Linker inscrive les fonctions dans l'IAT (Import Address Table) à la compilation, on les localise manuellement au Runtime (pendant l'exécution).

Les fonctions de recherche (Kernel32.dll) :

  • GetModuleHandleA : Localise une DLL dĂ©jĂ  chargĂ©e en mĂ©moire (ex: ntdll.dll).

    • Le suffixe 'A' signifie ANSI. Cela force l'utilisation de chaĂźnes char* (8 bits), Ă©vitant les conflits avec les rĂ©glages Unicode du projet.

  • GetProcAddress : Parcourt la Export Directory Table de la DLL pour extraire l'adresse mĂ©moire d'une fonction. Elle n'accepte que des chaĂźnes ANSI.


2. Le Mécanisme du typedef (Le Blueprint)

Puisque ntdll.dll n'est pas exposée dans les headers standards, on doit créer un Blueprint (une signature) pour que le compilateur sache comment mapper les appels.

Analyse de la syntaxe : typedef NTSTATUS (NTAPI * pNtAllocateVirtualMemory)(...)

  • typedef : DĂ©finit un nouveau type de donnĂ©e (un alias).

  • NTSTATUS : Le type de retour. Le succĂšs est codĂ© par STATUS_SUCCESS (0x00000000).

  • NTAPI : C'est la Calling Convention (convention d'appel). Elle dĂ©finit comment les arguments sont empilĂ©s et comment la Stack est nettoyĂ©e. C'est un alias pour __stdcall.

  • * pNt... : Le symbole * indique un Function Pointer (pointeur de fonction). Le prĂ©fixe p est la convention pour "Pointer".

3. Le Workflow Technique

Pour porter un loader vers ntdll, on suit ce Workflow :

  1. Declaration : Définir le Blueprint avec typedef.

  2. Instantiation : Créer une variable du type défini (ex: pNtAllocateVirtualMemory MyNtAlloc;).

  3. Binding : Utiliser GetProcAddress pour lier la variable à l'adresse réelle dans ntdll.

  4. Call : Invoquer la fonction en passant les pointeurs vers vos variables de contrĂŽle.

Last updated