
Chargeur de shellcode dynamique doté de capacités d'évasion sophistiquées
AsmLdr est un chargeur de shellcode avancé implémenté en langage assembleur x64, conçu pour les environnements Windows x64. Son objectif principal est d'exécuter des charges utiles chiffrées tout en minimisant la détection par les antivirus avancés, les systèmes de détection et de réponse aux points de terminaison (EDR), les sandboxes et les débogueurs. Pour ce faire, il résout les modules, les API et les appels système dynamiquement au moment de l'exécution, éliminant ainsi les dépendances statiques comme les tables d'importation ou les adresses codées en dur. Cette approche crée un binaire compact avec un faible profil de détection.
Le chargeur utilise le module stomping pour placer le shellcode dans la section exécutable d'une DLL existante, déchiffre la charge utile en mémoire et l'exécute via des appels système indirects avec usurpation de pile. Il inclut des fonctions d'évasion telles que des mesures anti-débogage, des ajustements temporels pour imiter les schémas d'application normaux et le contournement du suivi d'événements Windows (ETW). Ces capacités en font un outil pour les opérations de red team et la recherche en sécurité. Avertissement éthique : Ce projet est uniquement à des fins éducatives et de recherche. Il démontre des mécanismes internes bas niveau de Windows, notamment la traversée du PEB, l'analyse des tables d'exportation, le dé-hooking des appels système et la manipulation des protections mémoire. À utiliser dans des environnements contrôlés (par ex., machines virtuelles). Toute utilisation malveillante est illégale et contraire à l'éthique. L'auteur décline toute responsabilité en cas d'utilisation abusive.
AsmLdr offre une gamme de fonctionnalités pour l'exécution furtive de charges utiles et l'évasion. Chacune est décrite ci-dessous, en mettant l'accent sur sa fonction et ses avantages :
Résolution de modules via la traversée du PEB : Le chargeur énumère les modules en cours d'exécution en suivant la chaîne de modules du PEB. Il fait correspondre les noms de modules par des comparaisons Unicode pour trouver et renvoyer l'adresse de base de la DLL souhaitée. Cette méthode contourne les fonctions de chargement standard, réduisant ainsi le risque d'interception.
Résolution d'API à partir des tables d'exportation : Le chargeur examine le répertoire d'exportation d'une DLL pour localiser les fonctions. Il fait correspondre les API à l'aide de hachages ou, éventuellement, de vérifications directes de noms avec contrôle de longueur afin d'éviter toute confusion entre des noms de fonctions similaires. Cela fournit l'adresse de la fonction sans dépendre d'importations pré-liées.
Protections anti-débogage : Le chargeur détecte le débogage via plusieurs contrôles et se termine proprement si l'un d'eux indique une analyse.
Génération de code factice pour résister à l'analyse : Le chargeur exécute des séquences d'opérations non essentielles pour créer des schémas déroutants en désassemblage, rendant l'analyse statique plus difficile sans impact sur l'efficacité à l'exécution.
Normalisation temporelle et comportementale : Le chargeur mesure la vitesse du système pour exécuter des charges de travail simulées correspondant aux schémas d'application normaux, ce qui l'aide à se fondre parmi les processus légitimes. Il marque également une brève pause avant les actions clés pour normaliser le timing d'exécution.
Contournement de la journalisation ETW : Le chargeur configure des points d'arrêt matériels sur les API de journalisation ETW afin d'intercepter et d'ignorer les appels de journalisation d'événements, empêchant ainsi toute trace dans les journaux système.
Dé-hooking multicouche : Le chargeur récupère dynamiquement les numéros d'appel système valides (SSN) en vérifiant directement les fonctions, en analysant le code voisin si elles sont hookées, ou en comptant les motifs dans ntdll pour estimer le nombre, avec ajustement pour les exceptions connues.
Techniques utilisées :
Appel système indirect et usurpation de pile : Le chargeur trouve des chemins de code propres dans ntdll pour les appels système et enchaîne jusqu'à 32 opérations, masquant la pile d'appels pour échapper au traçage. Il restaure l'état d'origine après l'achèvement.
DLL hollowing : Le chargeur charge une DLL cible, autorise temporairement l'écriture dans sa section de code, insère le shellcode chiffré, le déchiffre et rétablit les protections en lecture seule/exécution.
Déchiffrement en mémoire : Le chargeur déchiffre le shellcode en appliquant un motif de clé répétitif directement dans la mémoire injectée.
Fonctions utilitaires : Le chargeur inclut des outils pour mesurer la longueur des chaînes, comparer des chaînes (caractères standard ou larges), hacher les noms pour les recherches, valider les pointeurs mémoire et lire les horodatages pour les contrôles temporels.
Le script Python generate.py chiffre le fichier shellcode brut. Il ajoute un remplissage pour l'alignement, crée ou utilise une clé, et applique un XOR à chaque octet avec la clé selon un cycle répétitif. Le fichier de sortie shellcode.asm définit les tailles, les tableaux pour les données chiffrées et la clé, ainsi qu'une routine pour charger les données au moment de l'exécution. Le déchiffrement du chargeur correspond à ce processus en mémoire. Notez que des clés plus longues augmentent le temps d'attente.
Préparez un binaire shellcode brut (par ex., payload.bin). Exécutez python generate.py payload.bin [optional_key] pour produire shellcode.asm.
Modifiez szDllName (mots hexadécimaux Unicode dans evader.asm) pour d'autres DLL comme urlmon.dll. Recalculez les hachages DJB2 pour les nouvelles API en utilisant la logique str_hash. Commentez les macros (par ex., KILL_DEBUGGERS) pour le débogage.
AsmLdr est optimisé pour Windows 10 et 11 x64 ; les mises à jour du noyau peuvent modifier les décalages (par ex., PEB+0x18) ou les motifs NTDLL (par ex., 0xb8d18b4c), ce qui nécessite une validation. La gestion des erreurs repose sur les valeurs de retour NTSTATUS (par ex., <0 en cas d'échec), sans journalisation intégrée. Les opérations factices et simulées consomment des cycles CPU, ce qui peut alerter les moniteurs de ressources sur les systèmes contraints. À utiliser uniquement dans des environnements contrôlés.