• LdrpHashTable

 #370043  por cLn
 27 May 2012, 21:18
Hola...

Otro texto más de Ivan Lefou...traducido obviamente...

Después de pasar algún tiempo con en el procesador ARM y Linux PPC vuelvo otra vez a Windows, ya que es la vida real. No hay nada muy innovador en esta ocasión, sólo un truco para encontrar los archivos DLL cargados por un proceso. Por supuesto que estoy hablando de archivos DLL que tratan de esconderse de determinadas API y herramientas. Veremos que el cargador de Windows mantiene las estructuras internas para simplificar su vida y que nos puede venir bien.


Una lista de las DLL que un proceso tiene cargadas en memoria es mantenido por el loader a nivel del PEB. Hablamos también de las DLL's cargadas tanto estáticamente como dinámicamente. Esta estructura de tipo PEB_LDR_DATA
se situa a nivel del campo Ldr (offset 0xC) del PEB :
Código: [ Debe registrarse para ver este enlace ]
//
// 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;
Como se puede ver hay 3 listas que contienen la misma información pero no en el mismo orden :
InLoadOrderModuleList : Lista de los módulos en el orden de carga.
InMemoryOrderModuleList : Lista de los módulos en orden ascendente de la ImageBase.
InInitializationOrderModuleList : Lista de los módulos en el orden de inicialización. Esta lista es diferente de la DLL se inicializa porque una InLoadOrderModuleList sólo cuando sus importaciones están completas. En algunos casos es necesario cargar e inicializar otros archivos DLL antes de la nuestra.
Estas listas son, de hecho, mantenidas en el tipo de estructuras LDR_DATA_TABLE_ENTRY :
Código: [ Debe registrarse para ver este enlace ]
//
// 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;
Encontramos información como el nombre de la DLL, el ImageBase y el tamaño (SizeOfImage). También hay un campo relativamente desconocido en esta estructura: HashLinks. De hecho, cuando el cargador tiene que cargar un archivo DLL en un proceso no utiliza las listas que hemos visto anteriormente por razones de rendimiento, de hecho recorrer una lista vinculada se hace en tiempo lineal y puede ser largo si son muchos los elementos. Para ir más rápido el cargador mantiene una tabla hash para reducir el tiempo de búsqueda. Campo de HashLinks es sólo una parte de esta tabla hash.

De hecho hablamos de la variable global LdrpHashTable, es una matriz de 32 LIST_ENTRY. Los archivos DLL se distribuyen de acuerdo a su nombre, la función hash toma la primera letra del nombre de la DLL en mayúsculas y lo elimina de la 'A, todo está bien guardado en un módulo 32. Al final todo se reduce a estas definiciones :
Código: [ Debe registrarse para ver este enlace ]
#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 es accedida por :
LdrpInitializeProcess : Inicialize la tabla al principio.
LdrpInsertMemoryTableEntry : Ajusta una entrada a la tabla.
LdrpCheckForLoadedDll : Vérifica si la DLL ya está cargada.
Ahora imagine un archivo DLL de un malware que elimine las listas InLoadOrderModuleList, InInitializationOrderModuleList y InMemoryOrderModuleList. La pregunta que surge es si es posible encontrarlo sin tener que escanear la memoria en busca de patrones de formato PE en un try {}, except () {}, mientras que feo..

Al igual que LdrpHashTable se olvida con frecuencia por los atacantes, la pueden utilizar para enumerar las DLL de un proceso. He programado una pequeña herramienta que se ejecuta en la memoria de un proceso que utiliza ReadProcessMemory () para volcar la tabla hash..
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>
Ahora más razones para desarrollar un código junto a un archivo DLL que elimine estas cuatro listas:
C:\ProgHack\c\LdrpHashTable>HideDll.exe
Hide ntdll from InMemoryOrderModuleList, InLoadOrderModuleList, InInitialization
OrderModuleList and LdrpHashTable
By Ivanlef0u
BE M4D !
# Aquí, el proceso está esperando
Llevamos a cabo un vaciado de este proceso con 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, podemos ver que ntdll.dll ya no aparece. Para las herramientas de la información como Process Explorer y LordPE NO ven más la DLL cuando las primeras 3 listas están doblemente enlazadas. Por lo tanto la mayor parte del malware lo usan para estar seguros. Con LdrpHashTable uno es capaz de ver esos archivos DLL malvados.

Salvo que las cosas no son tan simples. Hay herramientas como VMMap que son capaces de detectar nuestras otras DLL. ¿Cómo? De hecho, cuando un archivo DLL se carga en memoria, esta es mapeada, el sistema mantiene el interior por lo tanto, un objeto de la sección tipo del archivo que está asignado. Usando VirtualQueryEx () puede determinar el tipo de memoria y GetMappedFileName () el nombre del archivo asignado. Estas API utilizan el ZwQueryVirtualMemory syscall. Como VMMap.. suerte también.

Así que he pogramado una pequeña herramienta que funciona de esta manera.
# Se repite el proceso anterior que oculta sus archivos DLL
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 encuentra nuestra ntdll.dll! Por lo que sé, la única manera para guardar nuestro módulo es hookear la ZwQueryVirtualMemory syscall en el kernel-land ...


Al final entendemos que hay varias maneras de encontrar los archivos DLL en un proceso. Hemos visto que estar en nuestra información debe ser utilizada ZwQueryVirtualMemory, pero en el caso de esta API sea hookeada y el atacante olvide LdrpHashTable aún puede salir adelante. Por supuesto esto no es la última técnica, pero siempre es bueno tenerlo bajo el brazo, no es difícil de entender y poner en práctica y mucho más.

Descarga de la herramienta y el código:

[ Debe registrarse para ver este enlace ]

Fuente: La misma del otro post jeje

Espero que les sirvan...

Saludos !
 #370145  por linkgl
 28 May 2012, 04:25
, No conocía esta forma de obtener las DLL's, veo muy difícil meterse con los drivers a hookear ZwQueryVirtualMemory xDD, está excelente el artículo este.