Estou tentando encontrar o endereço base do kernel32 assumindo diretamente que kernel32.dll é sempre o terceiro módulo carregado.
ModLoad: 00400000 024077eb image00400000
ModLoad: 77ad0000 77c7f000 ntdll.dll
ModLoad: 75c80000 75d70000 C:\WINDOWS\SysWOW64\KERNEL32.DLL
ModLoad: 77160000 773d3000 C:\WINDOWS\SysWOW64\KERNELBASE.dll
...
Eu tinha um loop for para executar os módulos, mas notei que kernel32.dll era sempre a terceira entrada.
Posso presumir que isso é confiável?
O programa C produz
Endereço base Kernel32.dll: 0x75C80000
#include <stdio.h>
#include <stdint.h>
#include <windows.h>
#include <winternl.h>
int main()
{
PPEB peb = (PPEB)__readfsdword(0x30);
uintptr_t kernel32Base = 0;
/* Skip first two entries as kernel32.dll is always the third entry */
PLIST_ENTRY ptr = peb->Ldr->InMemoryOrderModuleList.Flink->Flink->Flink;
PLDR_DATA_TABLE_ENTRY e = CONTAINING_RECORD(ptr, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
kernel32Base = (uintptr_t)e->DllBase;
printf("Kernel32.dll Base Address: 0x%p\n", (void*)kernel32Base);
return 0;
}
Por exemplo, windbg está carregando DbgView.exe
Atualizar
Com base na resposta de Josh, a sugestão foi usarInLoadOrderModuleList
Depois de hackear por uma hora, finalmente consegui funcionar
Endereço base Kernel32.dll: 0x75C80000
A solução é declarar minhas próprias estruturas typedef customizadas para LDR_DATA_TABLE_ENTRY, PEB_LDR_DATA e PEB.
#include <stdio.h>
#include <stdint.h>
#include <windows.h>
typedef struct MY_PEB_LDR_DATA {
ULONG Length;
UCHAR Initialized;
VOID* SsHandle;
LIST_ENTRY InLoadOrderModuleList;
// ... other fields
} MY_PEB_LDR_DATA, *MY_PPEB_LDR_DATA;
typedef struct MY_LDR_DATA_TABLE_ENTRY {
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
// ... other fields
} MY_LDR_DATA_TABLE_ENTRY, *MY_PLDR_DATA_TABLE_ENTRY;
typedef struct MY_PEB {
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
MY_PPEB_LDR_DATA Ldr;
// ... other fields
} MY_PEB, *MY_PPEB;
int main()
{
MY_PPEB peb = (MY_PPEB)__readfsdword(0x30);
uintptr_t kernel32Base = 0;
// Order of modules loaded into the process: [image][ntdll][kernel32]
// Skip first two entries as kernel32.dll is always the third entry.
PLIST_ENTRY ptr = peb->Ldr->InLoadOrderModuleList.Flink->Flink->Flink;
MY_PLDR_DATA_TABLE_ENTRY e = CONTAINING_RECORD(ptr, MY_LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
kernel32Base = (uintptr_t)e->DllBase;
printf("Kernel32.dll Base Address: 0x%p\n", (void*)kernel32Base);
return 0;
}
Notas
InLoadOrderModuleList
Esta lista contém os módulos na ordem em que foram carregados no processo. Normalmente, o próprio executável é a primeira entrada, seguido por DLLs importantes do sistema, como ntdll.dll e kernel32.dll.
InMemoryOrderModuleList
Esta lista contém os módulos na ordem em que estão dispostos na memória. Não é necessariamente igual à ordem de carregamento, embora muitas vezes seja semelhante.
Você não receberá um "sim" definitivo, pois está mergulhando nos componentes internos do Windows, que são explicitamente não documentados e podem mudar entre as versões do Windows. Portanto, percorrer o PEB é mais seguro do que apenas assumir uma ordem específica.
Mas... aqui estão os detalhes do Windows 10 de acordo com o Windows Internals 7th Edition (Pavel Yosifovich, Alex Ionescu, Mark E. Russinovich e David A. Solomon).
O exe da imagem será mapeado primeiro.
O Ntdll é carregado antecipadamente porque grande parte do código do carregador existe lá:
Dado que o kernel32 aparecerá mais cedo de qualquer maneira, fazer a comparação de nomes não irá atrasá-lo muito.
Além disso, conforme mencionado no comentário, certifique-se de usar
InLoadOrderModuleList
.