Не съм писал от доста време - работа, занимания, чудесии. В една от предните статии стана въпрос за една структура, която съдържа адресите на някой доста често използвани структури - KdDebuggerBlog. Проблемът е, че в 64 битовата 7-ца адресът на тази структура не е експортнат, съответно няма лесен (а дали?) начин да намерим адресът. Всъщност се оказва доста лесно да се намери адресът, използвайки малко common sense.
Първото нещо, което може да се направи е да се disassemble-не някоя функция, за която знаем че използва KdDebuggerBloc. Проблемите тук са основно 2:
- Трябва да намерим функция, която използва въпросната променлива
- Дори и да намерим такава, си е играчка да използваме някои от disassm библиотеките, за да обходим функция инструкция, по инструкция докато не стигнем до статично зададената инструкция, за която сме сигурни, че използва адресът на KdDebuggerBlock като аргумент. Например - 5-тата mov инструкция от функцията PsGetSystemProcess() (това е просто пример, таква функция в windows няма :) ).
Вторият вариант е подобен на първият, но доста по лесен за имплементиране (както ще стане ясно). За какво става на въпрос - този път пак ще сканираме паметта, но поне можем малко и ли много да съкратим search space-а и да използваме common knowledge, за да си осигурим адресът. Проблемът е да намерим needle in a haystack, като иглата е KdDebuggerBlock, а купата със сеното е kernel space-а. Разбира се абсурдно е да сканираме целият kernel space, затова ще сме умни и ще използваме следните фактори, за да намалим search space:
- KdDebuggerBlock е променлива, която е част от кърнъла => ще се намира между kernel_base_address и kernel_base_address + size_of_kernel_image.
- Тъй като по дефиниция всички глобални променливи се записват в .data section-а на Windows автоматично ще ни интересува паметта между start_of_data_section и start_of_data_section + size_of_data_section.
- Тъй като знаем layout-а на структурата, виждаме че има променливи, които са глобално експортирани, тоест ако вземем някакъв блок памет, cast-нем го към типа на дадената структура и сравним дали на дадените позиции има стойностите, които имат тези глобални променливи (MmHighestUserAddres, MmSystemRangeStart и MmUserProbeAddress) то можем с голяма точно да определим дали даденият адрес представлява адресът на KdDebuggerBlock.
- Намираме къде е зареден kernel image-а.
- Parse-ваме PE image-a, за да намерим началото и краят на ".data" секцията.
- Сканираме адресите в намереният в (2) диапазон и сравняваме дали от началото на всеки на точно определените offsets разполагаме с export-натите глобални променливи.
void findKdDebuggerBlock() { DWORD bytesScanned; DWORD sectionSize = 0; PVOID kernelbase = GetNtosBaseAddr(); PVOID sectionAddress = GetDataSectionAddress(kernelbase, §ionSize); char *debugData = (char *)sectionAddress; DbgPrint("Starting search at address 0x%p\n", sectionAddress); for(bytesScanned = 0; bytesScanned < sectionSize; bytesScanned++) { if((PVOID)((PKDDEBUGGER_DATA64)debugData)->MmHighestUserAddress == &MmHighestUserAddress && (PVOID)((PKDDEBUGGER_DATA64)debugData)->MmSystemRangeStart == &MmSystemRangeStart && (PVOID)((PKDDEBUGGER_DATA64)debugData)->MmUserProbeAddress == &MmUserProbeAddress ) { DbgPrint("Found KDEVERSIOIN BLOCK AT 0x%p\n", debugData); break; } debugData++; } DbgPrint("Exhausted search space and found nothing\n"); }