LdrpHashTable

décembre 28th, 2009 at 04:57 admin

Après avoir passé un peu de temps sur du linux ARM et PPC je retourne sous Windows parce que c’est la vraie vie. Rien de très innovant cette fois, juste un trick permettant de retrouver les DLLs chargées par un processus. Bien sur je veux parler de DLLs qui tenteraient de se cacher par rapport à certaines APIs et outils. On va voir que le loader de Windows maintient des structures en internes pour se simplifier la vie et que celles ci peuvent nous servir aussi.


Une liste des DLL qu’un processus à en mémoire est maintenu par le loader au niveau du PEB. On parle aussi bien des DLLs chargées statiquement que dynamiquement. Cette structure de type PEB_LDR_DATA
se situe au niveau du champ Ldr (offset 0xC) du PEB :

//
// Loader Data stored in the PEB
//
typedef struct _PEB_LDR_DATA
{
    ULONG Length;
    BOOLEAN Initialized;
    PVOID SsHandle;
    LIST_ENTRY InLoadOrderModuleList;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
    PVOID EntryInProgress;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

Comme vous pouvez le voir il y a 3 listes qui contiennent les mêmes informations mais pas dans le même ordre :

  • InLoadOrderModuleList : Liste des modules dans leur ordre de chargement.
  • InMemoryOrderModuleList : Liste des modules dans leur ordre croissant d’ImageBase.
  • InInitializationOrderModuleList : Liste des modules dans leur ordre d’initialisation. Cette liste est différente de la InLoadOrderModuleList parcequ’une DLL est initialisé uniquement lorsque ses imports sont complets. Dans certains cas il est nécessaire de charger et d’initialiser d’autres DLLs avant la nôtre.

Ces listes sont en fait maintenues dans des structures de type LDR_DATA_TABLE_ENTRY :

//
// Loader Data Table Entry
//
typedef struct _LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;
    union
    {
        LIST_ENTRY HashLinks;
        PVOID SectionPointer;
    };
    ULONG CheckSum;
    union
    {
        ULONG TimeDateStamp;
        PVOID LoadedImports;
    };
    PVOID EntryPointActivationContext;
    PVOID PatchInformation;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

On retrouve des informations comme le nom de la DLL, son ImageBase et sa taille (SizeOfImage). Il existe aussi un champ assez peu connu dans cette structure : HashLinks. En fait lorsque le loader doit charger une DLL dans un processus il n’utilise pas les listes qu’on a vues plus haut pour des raisons de performances, en effet parcourir une liste chainée se fait en temps linéaire et peut être long s’il y a beaucoup d’éléments. Pour aller plus vite le loader maintient une hashtable afin de diminuer le temps de recherche. Le champ HashLinks fait justement partie de cette hashtable.

En fait on parle de la variable globale LdrpHashTable, c’est un tableau de 32 LIST_ENTRY. Les DLLs y sont reparties en fonction de leur nom, la fonction de hashage prend la première lettre du nom de la DLL en uppercase et le soustrait à L’A’, le tout est bien sur passé dans un modulo 32. Au final tout cela se résume par ces définitions :

#define LDRP_HASH_TABLE_SIZE 32
#define LDRP_HASH_MASK       (LDRP_HASH_TABLE_SIZE-1)
#define LDRP_COMPUTE_HASH_INDEX(wch) ( (RtlUpcaseUnicodeChar((wch)) - (WCHAR)'A') & LDRP_HASH_MASK )
LIST_ENTRY LdrpHashTable[LDRP_HASH_TABLE_SIZE];

LdrpHashTable est accédée par :

  • LdrpInitializeProcess : Initialise la table au début.
  • LdrpInsertMemoryTableEntry : Ajoute une entrée à la table.
  • LdrpCheckForLoadedDll : Vérifie si la DLL est déjà chargée.

Maintenant imaginons une DLL malveillante qui s’enlèverait des listes InLoadOrderModuleList, InMemoryOrderModuleList et InInitializationOrderModuleList. La question se pose si il est possible de la retrouver sans scanner la mémoire en cherchant des patterns du format PE dans un try{}except(){} tout moche.

Justement comme la LdrpHashTable est souvent oublié par les attaquants on peut l’utiliser pour énumérer les DLL d’un processus. J’ai codé un petit tool qui parcourt la mémoire d’un processus à l’aide de ReadProcessMemory() pour dumper cette hashtable.

C:\ProgHack\c\LdrpHashTable>GetModuleListByHashTable.exe
Process DLLs dumper using LdrpHashTable
By Ivanlef0u
BE M4D !

Usage is : GetModuleListByHashTable.exe  (0 mean this process)

C:\ProgHack\c\LdrpHashTable>GetModuleListByHashTable.exe  0
Process DLLs dumper using LdrpHashTable
By Ivanlef0u
BE M4D !

LdrpHashTable is at 7C98E260
Dumping Dlls
ListHead[0] : 0x7C98E260
ListHead[1] : 0x7C98E268
ListHead[2] : 0x7C98E270
ListHead[3] : 0x7C98E278
ListHead[4] : 0x7C98E280
ListHead[5] : 0x7C98E288
ListHead[6] : 0x7C98E290
        ListEntry : 0x00241EFC
        FullDllName : C:\ProgHack\c\LdrpHashTable\GetModuleListByHashTable.exe

ListHead[7] : 0x7C98E298
ListHead[8] : 0x7C98E2A0
ListHead[9] : 0x7C98E2A8
ListHead[10] : 0x7C98E2B0
        ListEntry : 0x00241FFC
        FullDllName : C:\WINDOWS\system32\kernel32.dll

ListHead[11] : 0x7C98E2B8
ListHead[12] : 0x7C98E2C0
        ListEntry : 0x0024209C
        FullDllName : C:\WINDOWS\system32\MSVCRT.dll

ListHead[13] : 0x7C98E2C8
        ListEntry : 0x00241F54
        FullDllName : C:\WINDOWS\system32\ntdll.dll

ListHead[14] : 0x7C98E2D0
ListHead[15] : 0x7C98E2D8
ListHead[16] : 0x7C98E2E0
ListHead[17] : 0x7C98E2E8
ListHead[18] : 0x7C98E2F0
ListHead[19] : 0x7C98E2F8
ListHead[20] : 0x7C98E300
ListHead[21] : 0x7C98E308
ListHead[22] : 0x7C98E310
ListHead[23] : 0x7C98E318
ListHead[24] : 0x7C98E320
ListHead[25] : 0x7C98E328
ListHead[26] : 0x7C98E330
ListHead[27] : 0x7C98E338
ListHead[28] : 0x7C98E340
ListHead[29] : 0x7C98E348
ListHead[30] : 0x7C98E350
ListHead[31] : 0x7C98E358

C:\ProgHack\c\LdrpHashTable>

Maintenant raison de plus de développer à coté un code qui enlève une DLL de ces 4 listes :]

C:\ProgHack\c\LdrpHashTable>HideDll.exe
Hide ntdll from InMemoryOrderModuleList, InLoadOrderModuleList, InInitialization
OrderModuleList and LdrpHashTable
By Ivanlef0u
BE M4D !
# Ici le process est en attente

On effectue un dump de ce process avec GetModuleListByHashTable :

C:\ProgHack\c\LdrpHashTable>tasklist | find "HideDll.exe"
HideDll.exe                 3436                         0       708 Ko

C:\ProgHack\c\LdrpHashTable>GetModuleListByHashTable.exe 3436
Process DLLs dumper using LdrpHashTable
By Ivanlef0u
BE M4D !

LdrpHashTable is at 7C98E260
Dumping Dlls
ListHead[0] : 0x7C98E260
ListHead[1] : 0x7C98E268
ListHead[2] : 0x7C98E270
ListHead[3] : 0x7C98E278
ListHead[4] : 0x7C98E280
ListHead[5] : 0x7C98E288
ListHead[6] : 0x7C98E290
ListHead[7] : 0x7C98E298
        ListEntry : 0x00241EFC
        FullDllName : C:\ProgHack\c\LdrpHashTable\HideDll.exe

ListHead[8] : 0x7C98E2A0
ListHead[9] : 0x7C98E2A8
ListHead[10] : 0x7C98E2B0
        ListEntry : 0x00241FFC
        FullDllName : C:\WINDOWS\system32\kernel32.dll

ListHead[11] : 0x7C98E2B8
ListHead[12] : 0x7C98E2C0
        ListEntry : 0x0024209C
        FullDllName : C:\WINDOWS\system32\MSVCRT.dll

ListHead[13] : 0x7C98E2C8
ListHead[14] : 0x7C98E2D0
ListHead[15] : 0x7C98E2D8
ListHead[16] : 0x7C98E2E0
ListHead[17] : 0x7C98E2E8
ListHead[18] : 0x7C98E2F0
ListHead[19] : 0x7C98E2F8
ListHead[20] : 0x7C98E300
ListHead[21] : 0x7C98E308
ListHead[22] : 0x7C98E310
ListHead[23] : 0x7C98E318
ListHead[24] : 0x7C98E320
ListHead[25] : 0x7C98E328
ListHead[26] : 0x7C98E330
ListHead[27] : 0x7C98E338
ListHead[28] : 0x7C98E340
ListHead[29] : 0x7C98E348
ListHead[30] : 0x7C98E350
ListHead[31] : 0x7C98E358

C:\ProgHack\c\LdrpHashTable>

Hop, on voit bien que ntdll.dll n’apparait plus. Pour information des outils comme Process Explorer et LordPE ne ‘voient’ plus la DLL lorsqu’elle est unlink des 3 premières doubles listes chainées. De ce fait beaucoup de malwares le font pour être tranquille. Avec la LdrpHashTable on est capable de voir ces vilaines DLLs.

Sauf que les choses ne sont pas si simples. Il existe des outils comme VMMap qui sont capable de détecter nos DLLs autrement. Comment ? En fait lorsqu’une DLL est chargé en mémoire, elle est mappée, le système maintient donc en interne un objet de type Section le fichier qui est mappé. A l’aide de VirtualQueryEx() on peut connaître le type de mémoire et avec GetMappedFileName() le nom du fichier mappé. Ces APIs utilisent toutes deux le syscall ZwQueryVirtualMemory. Comme par hasard VMMap aussi :]
Je me suis donc codé un petit tool qui fonctionne pareil.

# On reprend le process vu plus haut qui cache ses DLLs
C:\ProgHack\c\LdrpHashTable>GetModuleListByVirtualQuery.exe 3436
Process DLLs dumper using VirtualQueryEx+GetMappedFileName
By Ivanlef0u
BE M4D !

[*] Dumping Dlls
MappedFile : \Device\HarddiskVolume1\ProgHack\c\LdrpHashTable\HideDll.exe (Addre
ss : 0x400000 - SizeOfImage : 0x960)
MappedFile : \Device\HarddiskVolume1\WINDOWS\system32\msvcrt.dll (Address : 0x77be0000 - SizeOfImage : 0x58000)
MappedFile : \Device\HarddiskVolume1\WINDOWS\system32\kernel32.dll (Address : 0x7c800000 - SizeOfImage : 0x106000)
MappedFile : \Device\HarddiskVolume1\WINDOWS\system32\ntdll.dll (Address : 0x7c910000 - SizeOfImage : 0xb9000)

C:\ProgHack\c\LdrpHashTable>

Cool on retrouve bien notre ntdll.dll ! D’après ce que je sais, le seul moyen pour planquer notre module c’est de hooker le syscall ZwQueryVirtualMemory en kernel-land …

Au final vous avez compris qu’il existe plusieurs manières pour retrouver les DLLs d’un processus. On a vu que pour être sur de nos infos il faut utiliser ZwQueryVirtualMemory mais dans le cas ou cette API est hookée et que l’attaquant oublie la LdrpHashTable on peut encore s’en sortir. Bien sur ce n’est pas la technique ultime mais il est toujours bon de l’avoir sous le bras, c’est n’est pas difficile à comprendre et à implémenter en plus.

Peut être qu’un jour le monstrueux DLL Hell arrêtera de nous poser des problèmes.

Vous trouverez le code est les binaires ici :
http://ivanlef0u.fr/repo/LdrpHashTable.rar

Pour finir une petite selection de son parce que vous le valez bien

An Inch Above Sand du dernier album ‘What We All Come To Need’ de Pelican.

An Inch Above Sand

Postponed. End de l’EP ‘Samsara’ de Deviniance.

Postponed. End

Hordes To War de l’album ‘All Shall Fall’ de Immortal.

Hordes To War

Enfin Bleed de l’album ‘Obzen’ de Meshuggah parce que ca rigole zéro :

Bleed

Entry Filed under: RE

12 Comments

  • 1. mxatone  |  décembre 28th, 2009 at 20:12

    Content que tu reviennes a la realite ! :)

    SInon l’autre moyen de se cacher serait de remapper son propre binaire a l’exterieur d’une section tag SEC_IMAGE et apres de s’unload. Par contre faut vraiment en vouloir !


  • 2. Baboon  |  décembre 29th, 2009 at 14:25

    #define HASHTABLE_DATA_OFFSET 0×260

    Eh ba c’est propre ça ;)
    Tu peux pas faire mieux avec les symboles ?

    Sinon tu peux aussi forcer le loader win à utiliser une autre API que ZwMapViewOfSection (hook ou exécution contrôlée) pour loader ta dll et ainsi éviter la détection par VMMap

    Après tu peux toujours loader ta dll à la main ;) ca dépend ce que tu veux faire avec …


  • 3. admin  |  décembre 29th, 2009 at 22:06

    @Baboon
    Si on peut faire plus précis avec les symboles :]

    Sinon modifier le loader je trouver ca un peu violent par contre un petit loader de DLL ferait clairement l’affaire.


  • 4. Flopik  |  janvier 2nd, 2010 at 02:30

    La meilleur facon pour retrouver les dlls, c’est avec le VAD


  • 5. admin  |  janvier 2nd, 2010 at 13:23

    @Flopik
    Wai c’est ce que utilise ZwQueryVirtualMemory en fait.


  • 6. Flopik  |  janvier 2nd, 2010 at 16:26

    On est aussi fourer, si la node est delinker du VAD, sois en modifiant le FILE_OBJECT ou en nulliant/modifiant le VadRoot ou les enfants du MMVAD

    typedef struct _MMVAD
    {
    ULONG u1;
    PMMVAD LeftChild;
    PMMVAD RightChild;
    ULONG StartingVpn;
    ULONG EndingVpn;
    ULONG u;
    EX_PUSH_LOCK PushLock;
    ULONG u5;
    ULONG u2;
    union
    {
    PSUBSECTION Subsection;
    PMSUBSECTION MappedSubsection;
    };
    PMMPTE FirstPrototypePte;
    PMMPTE LastContiguousPte;
    } MMVAD, *PMMVAD;

    typedef struct _SUBSECTION
    {
    PCONTROL_AREA ControlArea;

    typedef struct _CONTROL_AREA
    {
    PSEGMENT Segment;
    LIST_ENTRY DereferenceList;
    ULONG NumberOfSectionReferences;
    ULONG NumberOfPfnReferences;
    ULONG NumberOfMappedViews;
    ULONG NumberOfUserReferences;
    ULONG u;
    ULONG u1;
    EX_FAST_REF FilePointer;

    FilePointer va pointer sur un FILE_OBJECT(un peu différent sous Xp et Vista)

    En faisant ca, il ne reste plus grande facon de détecter des dlls injecter ou caché à part la page table , et on deviens caché usermode et kernel mode sans aucun hook.


  • 7. un_craneur  |  janvier 3rd, 2010 at 12:21

    Je me permet de repaster les structures de Flopik.

    MMVAD (Win7 x86)
    http://msdn.msuiche.net/win7rtm_x86/MMVAD.php

    CONTROL_AREA (Win7 x86)
    http://msdn.msuiche.net/win7rtm_x86/CONTROL_AREA.php

    SUBSECTION (Win7 x86)
    http://msdn.msuiche.net/win7rtm_x86/CONTROL_AREA.php


  • 8. Mp8  |  janvier 9th, 2010 at 05:10

    Merci pour ton Article Ivan ;)

    Je me demandais si on pouvait cacher des threads avec une technique semblable (en modifiant le PEB / TEB) ?

    L’autre jour, je testais le trick :

    RtlLeaveCriticalSection( NtCurrentTeb()->Peb->LoaderLock );

    Et j’ai remarqué que les threads du processus étaient invisible dans Process Explorer.
    Mais ça provoque plusieurs problèmes, surtout avec LoadLibrary et CreateThread.


  • 9. admin  |  janvier 11th, 2010 at 01:29

    @Mp8
    Yo, mail moi sur ivanlef0u@tuxfamily.org, j’ai qqch pour toi.


  • 10. sloshy  |  janvier 17th, 2010 at 18:09

    Yo,
    J’ai un 403 sur tes sources est-ce normal?


  • 11. admin  |  janvier 17th, 2010 at 21:34

    @sloshy
    Toujours des emmerdes avec le repo. J’espère le faire revenir bientôt. En attendant les sources sont la : http://download.tuxfamily.org/ivanblog/LdrpHashTable.rar


  • 12. Hidding Module from the V&hellip  |  mars 8th, 2010 at 19:18

    [...] =) Récemment j’ai lu un post d’Ivanlef0u : LdrpHashTable dans lequel il montre comment cacher une dll de cette table. En lisant ce post on peut dire que [...]


Trackback this post


Calendar

décembre 2017
L Ma Me J V S D
« fév    
 123
45678910
11121314151617
18192021222324
25262728293031

Most Recent Posts