- Автор темы
- Добавить закладку
- #41
Вот что я понял про SEH в x64: в ntdll есть экспортируемая запись RtlLookupFunctionEntry. Я во время реверса KiUserExceptionDispatcher я сначала понял только то что RtlLookupFunctionEntry первым элементом содержит количество записей в массиве структур, который начинаются с адреса RtlLookupFunctionEntry+0x10. Структуры эти содержат imagebase модуля, размер модуля и адрес exception table.
Ниже скриншот из функции, которую IDA называет RtlpxLookupFunctionTable.
В этом контексте eax счётчик и тут начинается то что мне не понятно, eax делится на два в rdx записывается eax умноженное на 3, и достаётся запись по этому адресу. R8 - ImageBase из структуры, R14 адрес где произошло исключение, после чего к R8 прибавляется SizeOfImage и сравнивается с R14. Я нашёл хороший ресёрч в котором приведены определения структур которые мне так нужны https://lhc645.wordpress.com/2010/04/13/обработка-исключений-в-x64-usermode/ Я написал небольшой код, который выводит информацию о записях в KiUserInvertedFunctionTable.
В своём лоадере я попробовал добавить запись о загружаемом образе, увеличил число структур в массиве на 1, это не дало результата, RtlpxLookupFunctionTable тупо проходит мимо большинста записей в массиве, я попробовал следующий код:
И всё заработало. Но всё же хочу разобраться почему так происходит? В KiUserInvertedFunctionTable у меня показывает 0x12 - число записей в массиве, но цикл в RtlpxLookupFunctionTable сделал всего 4 итерации, вопрос: почему RtlpxLookupFunctionTable не проходится по всем структурам?
Ниже скриншот из функции, которую IDA называет RtlpxLookupFunctionTable.
В этом контексте eax счётчик и тут начинается то что мне не понятно, eax делится на два в rdx записывается eax умноженное на 3, и достаётся запись по этому адресу. R8 - ImageBase из структуры, R14 адрес где произошло исключение, после чего к R8 прибавляется SizeOfImage и сравнивается с R14. Я нашёл хороший ресёрч в котором приведены определения структур которые мне так нужны https://lhc645.wordpress.com/2010/04/13/обработка-исключений-в-x64-usermode/ Я написал небольшой код, который выводит информацию о записях в KiUserInvertedFunctionTable.
C++:
#include <iostream>
#include <Windows.h>
int filter(unsigned int code, struct _EXCEPTION_POINTERS* ep)
{
if (code == EXCEPTION_ACCESS_VIOLATION)
{
std::cout << "Memory access violation occurred!" << std::endl;
return EXCEPTION_EXECUTE_HANDLER;
}
// If it's not an access violation exception, don't handle it.
return EXCEPTION_CONTINUE_SEARCH;
}
struct InvertedEntry
{
void* ExceptionTable;
HMODULE imagebase;
DWORD sizeofimage;
};
struct _RTL_INVERTED_FUNCTION_TABLE_ENTRY
{
PIMAGE_RUNTIME_FUNCTION_ENTRY ExceptionDirectory; // виртуальный адрес .pdata (обычно)
PVOID ImageBase; // базовый адрес модуля
ULONG ImageSize; // размер образа
ULONG ExceptionDirectorySize; // размер .pdata
};
struct _RTL_INVERTED_FUNCTION_TABLE
{
ULONG Count; // число структур RTL_INVERTED_FUNCTION_TABLE_ENTRY
ULONG MaxCount; // 0xA0 — win xp x64
ULONG Pad[0x2];
_RTL_INVERTED_FUNCTION_TABLE_ENTRY Entries[ANYSIZE_ARRAY];
};
int main()
{
GetProcAddress(GetModuleHandleA("ntdll"), "RtlLookupFunctionEntry");
_RTL_INVERTED_FUNCTION_TABLE* table = (_RTL_INVERTED_FUNCTION_TABLE*)GetProcAddress(GetModuleHandleA("ntdll"), "KiUserInvertedFunctionTable");
DWORD numberofentries = table->Count;
std::cout << "this process imagebase:" << GetModuleHandleA(0) << std::endl;
while (numberofentries)
{
numberofentries--;
std::cout << "ImageBase:" << table->Entries[numberofentries].ImageBase << std::endl;
std::cout << "Image size:" << table->Entries[numberofentries].ImageSize << std::endl;
std::cout << "ExceptionDirectory:" << table->Entries[numberofentries].ExceptionDirectory << std::endl;
std::cout << "Size of exception directory:" << table->Entries[numberofentries].ExceptionDirectorySize << std::endl;
}
__try
{
int* ptr = nullptr;
*ptr = 42; // This will cause an access violation exception
}
__except (filter(GetExceptionCode(), GetExceptionInformation()))
{
std::cout << "Exception caught." << std::endl;
}
}
C++:
void Loader::PELoader::ReplaceFunctionTableEntry(HMODULE module, void* exception_section, DWORD sizeofdirectory)
{
_RTL_INVERTED_FUNCTION_TABLE* table = (_RTL_INVERTED_FUNCTION_TABLE*)WINRTL::HashGetProcAddress(obfs::CONStrHashA("KiUserInvertedFunctionTable"), WINRTL::GetNtdll());
ULONG entries_count = table->Count;
HMODULE thisimagebase = WINCALL(winimport, DLLID_KERNEL32, GetModuleHandleA)(0); //imagebase of current process
while (entries_count)
{
entries_count--;
if (table->Entries[entries_count].ImageBase == thisimagebase)
{
_RTL_INVERTED_FUNCTION_TABLE_ENTRY* insert = (_RTL_INVERTED_FUNCTION_TABLE_ENTRY*)&table->Entries[entries_count];
DWORD oldprt;
WINCALL(winimport, DLLID_KERNEL32, VirtualProtect)(table, 0x1000, PAGE_READWRITE, &oldprt);
insert->ExceptionDirectory = (PIMAGE_RUNTIME_FUNCTION_ENTRY)exception_section;
insert->ExceptionDirectorySize = sizeofdirectory;
insert->ImageBase = module;
insert->ImageSize = optheader->SizeOfImage;
break;
}
}
}