• XSS.stack #1 – первый литературный журнал от юзеров форума

Возможно ли HMODULE user32/kernel32 получить по хешам ?

Alexey18

(L3) cache
Пользователь
Регистрация
11.06.2023
Сообщения
163
Реакции
30
Возможно ли HMODULE user32/kernel32(и других) получить по хешам ? То есть без использования LoadLibruary , GetProcAddress, GetModuleHandleW посредством разбора PEB ?

Что я не так делаю? Попробовал разные вариации хеширования. HashString1 работает на функциях, где уже загружена библиотека. Пробовал в нейронку загонять херню какую-то даёт. Всё равно null. Не могу получить его.


C++:
DWORD HashString1(const char* str) { // работает с функциями
    DWORD hash = 0;
    while (*str) {
        char c = *str++;
        if (c >= 'a' && c <= 'z')
            c -= 32;
        hash = _rotr(hash, 13);
        hash += c;
    }
    return hash;
}

DWORD HashString(const char* str) {
    DWORD hash = 5381;
    int c;

    while ((c = *str++)) {
        hash = ((hash << 5) + hash) + c;
    }

    return hash;
}

HMODULE GetModuleHandleByHash(DWORD moduleNameHash) {
    PPEB pPeb = (PPEB)__readfsdword(0x30);
    PPEB_LDR_DATA pLdr = pPeb->Ldr;
    PLIST_ENTRY pListEntry = pLdr->InMemoryOrderModuleList.Flink;

    while (pListEntry != &pLdr->InMemoryOrderModuleList) {
        PLDR_DATA_TABLE_ENTRY pEntry = CONTAINING_RECORD(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);

        // Конвертируем Wide String в ASCII для хеширования
        char moduleName[256];
        WideCharToMultiByte(CP_ACP, 0, pEntry->FullDllName.Buffer, -1, moduleName, sizeof(moduleName), NULL, NULL);

        if (HashString(moduleName) == moduleNameHash) {
            return (HMODULE)pEntry->DllBase;
        }

        pListEntry = pListEntry->Flink;
    }

    return NULL;
}
 
if (HashString(moduleName) == moduleNameHash) {
Поставь здесь бряк в отладчике и сравни имена модулей до хэширования. Возможно, дело в регистре.
 
С kernel32.dll должно работать потому что она как и ntdll.dll по дефолту загружается при запуске PE файла. В отличие от них, user32.dll и другие необходимо подгружать с помощью LoadLibrary, если эта библиотека отсутствует в импорте. Если не хочешь использовать LoadLibrary напрямую, то загружай через "прокси" или через собственную реализацию LoadLibrary, это уже ближе к малварь кодингу.
По кастомной реализации GetProcAddress и GetModuleHandle на форуме есть подробная статья /threads/97365/
 
Последнее редактирование:
Возможно ли HMODULE user32/kernel32(и других) получить по хешам ? То есть без использования LoadLibruary , GetProcAddress, GetModuleHandleW посредством разбора PEB ?

Что я не так делаю? Попробовал разные вариации хеширования. HashString1 работает на функциях, где уже загружена библиотека. Пробовал в нейронку загонять херню какую-то даёт. Всё равно null. Не могу получить его.


C++:
DWORD HashString1(const char* str) { // работает с функциями
    DWORD hash = 0;
    while (*str) {
        char c = *str++;
        if (c >= 'a' && c <= 'z')
            c -= 32;
        hash = _rotr(hash, 13);
        hash += c;
    }
    return hash;
}

DWORD HashString(const char* str) {
    DWORD hash = 5381;
    int c;

    while ((c = *str++)) {
        hash = ((hash << 5) + hash) + c;
    }

    return hash;
}

HMODULE GetModuleHandleByHash(DWORD moduleNameHash) {
    PPEB pPeb = (PPEB)__readfsdword(0x30);
    PPEB_LDR_DATA pLdr = pPeb->Ldr;
    PLIST_ENTRY pListEntry = pLdr->InMemoryOrderModuleList.Flink;

    while (pListEntry != &pLdr->InMemoryOrderModuleList) {
        PLDR_DATA_TABLE_ENTRY pEntry = CONTAINING_RECORD(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);

        // Конвертируем Wide String в ASCII для хеширования
        char moduleName[256];
        WideCharToMultiByte(CP_ACP, 0, pEntry->FullDllName.Buffer, -1, moduleName, sizeof(moduleName), NULL, NULL);

        if (HashString(moduleName) == moduleNameHash) {
            return (HMODULE)pEntry->DllBase;
        }

        pListEntry = pListEntry->Flink;
    }

    return NULL;
}
Никого не смутила почему-то вот эта часть:
Код:
WideCharToMultiByte(CP_ACP, 0, pEntry->FullDllName.Buffer,

FullDllName там будет полный пусть вместе с именем дллки то есть - C:\Windows\System32\kernel32.dll
В этом коде я так понимаю юзается LDR_DATA_TABLE_ENTRY из winternl.h она не полная а точнее - там некоторые нужные поля просто сделаны как BYTE Reserved[] вот из мс её описание: https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data
а вот более полное например: https://www.nirsoft.net/kernel_struct/vista/LDR_DATA_TABLE_ENTRY.html

Как видно у нирсофта там идёт после FullDllName, UNICODE_STRING BaseDllName это как раз таки то что нужно - это именно ИМЯ МОДУЛЯ БЕЗ ПОЛНОГО ПУТИ можешь переопределить вот так просто у себя в коде:

C:
typedef struct _MY_LDR_DATA_TABLE_ENTRY {
    PVOID Reserved1[2];
    LIST_ENTRY InMemoryOrderLinks;
    PVOID Reserved2[2];
    PVOID DllBase;
    PVOID Reserved3[2];
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;     // <------- вот тут шнягу BYTE Reserved4[8]; меняем на UNICODE_STRING BaseDllName;
    PVOID Reserved5[3];
#pragma warning(push)
#pragma warning(disable: 4201) // we'll always use the Microsoft compiler
    union {
        ULONG CheckSum;
        PVOID Reserved6;
    } DUMMYUNIONNAME;
#pragma warning(pop)
    ULONG TimeDateStamp;
} MY_LDR_DATA_TABLE_ENTRY, * PMY_LDR_DATA_TABLE_ENTRY;

Это первый момент НО
Есть ещё второй, имя модуля может быть в верхнем регистре например -> KERNEL32.DLL поэтому я предлагаю тебе сделать так - все хеши по которым ты ищешь модули должны быть вычислены по строке в нижнем регистре, то есть заранее у себя делаешь: HashString("kernel32.dll") , HashString("user32.dll") и тд

и потом делаешь так в коде:
C:
while (pListEntry != &pLdr->InMemoryOrderModuleList) {
     PMY_LDR_DATA_TABLE_ENTRY pEntry = CONTAINING_RECORD(pListEntry, MY_LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);


     // Конвертируем Wide String в ASCII для хеширования
     char moduleName[256];
     WideCharToMultiByte(CP_ACP, 0, pEntry->BaseDllName.Buffer, -1, moduleName, sizeof(moduleName), NULL, NULL);


     CharLowerA(moduleName);


     if (HashString(moduleName) == moduleNameHash) {
         return (HMODULE)pEntry->DllBase;
     }


     pListEntry = pListEntry->Flink;
 }

То есть у тебя из пеб имена тоже будут в нижний регистр перегоняться перед хешированием. И как написал выше secidiot юзер32длл не грузится по дефолту в процесс
 
WinDbg тоже возвращает поля валидной LDR_DATA_TABLE_ENTRY - после Full идёт BaseDllName:

Код:
0: kd> dt _LDR_DATA_TABLE_ENTRY -v

struct _LDR_DATA_TABLE_ENTRY, 24 elements, 0xe0 bytes
   +0x000 InLoadOrderLinks   : struct  _LIST_ENTRY, 2 elements, 0x10 bytes
   +0x010 InMemoryOrderLinks : struct  _LIST_ENTRY, 2 elements, 0x10 bytes
   +0x020 InInitOrderLinks   : struct  _LIST_ENTRY, 2 elements, 0x10 bytes
   +0x030 DllBase            : Ptr64 to Void
   +0x038 EntryPoint         : Ptr64 to Void
   +0x040 SizeOfImage        : Uint4B
   +0x044 Padding1           : Uint4B
   +0x048 FullDllName        : struct  _UNICODE_STRING, 3 elements, 0x10 bytes
   +0x058 BaseDllName        : struct  _UNICODE_STRING, 3 elements, 0x10 bytes
   +0x068 Flags              : Uint4B
   +0x06c LoadCount          : Uint2B
   +0x06e TlsIndex           : Uint2B
   +0x070 SectionPointer     : Ptr64 to Void
   +0x078 CheckSum           : Uint4B
   +0x07c Padding2           : Uint4B
   +0x080 TimeDateStamp      : Uint4B
   +0x084 Padding3           : Uint4B
   +0x088 ActivationContext  : Ptr64 to struct  _ACTIVATION_CONTEXT, 0 elements, 0x0 bytes
   +0x090 PatchInformation   : Ptr64 to Void
   +0x098 ForwarderLinks     : struct  _LIST_ENTRY, 2 elements, 0x10 bytes
   +0x0a8 ServiceTagLinks    : struct  _LIST_ENTRY, 2 elements, 0x10 bytes
   +0x0b8 StaticLinks        : struct  _LIST_ENTRY, 2 elements, 0x10 bytes
   +0x0c8 ContextInformation : Ptr64 to Void
   +0x0d0 OriginalBase       : Uint8B
   +0x0d8 LoadTime           : union _LARGE_INTEGER, 4 elements, 0x8 bytes
0: kd>
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх