Всем добрый день. Я начинающий в программирования ядра, поэтому просьба сильно не пинать ) Есть некое приложение (драйвер), который хукает ряд функций. RKU его палит: И успешно снимает эти хуки при необходимости. У меня стоит задача самому вручную снимать эти хуки.. драйвер для чтения SST уже написал, но не могу понять где мне брать оригинальные адреса функций. То есть на что мне менять хукнутые адреса. Из прочтённой литературы понял, что настоящие адреса можно получить импортирую их из ядра. Более детальной информации не нашёл, поэтому просьба подсказать.. желательно разумеется с примерами) Заранее, спасибо!
В двух словах: 1) загружаем копию ядра с диска. (имя возьмешь в ZwQuerySystemInformation(SystemModuleInformation), первый модуль). для этого потребуется свой пе-загрузчик (ZwCreateFile + ZwCreateSection + ZwMapViewOfSection + парсинг релоков; импорты не обрабатываем - не нужно). 2) вручную парсим экспорт и находим KeServiceDescriptorTable. Там уже в первом элементе массива (таблиц 4 штуки, не забываем - ядро, свободная (win32k в шадове), и две свободные для пользователя) уже прописан адрес KiServiceTable. Вынимаем этот адрес - это и есть массив адресов SDT. (чтобы адреса были валидными, нужно в пункте 1 при парсинге релоков перебазировать загруженную копию ядра на базу оригинального ядра в памяти). 3) заменяем настоящий массив KeServiceDescriptorTable[0].ServiceTable на массив, прочитанный с копии ядра, поэлементно. 4) ??? 5) PROFIT! Как загружать образ и парсить релоки - смотри сорцы ReactOS, например. Или винды (рипнутые). Как парсить экспорт - смотри опять же реактос. Как найти базу ядра и его имя - смотри примеры ZwQuerySystemInformation(SystemModuleInformation). Как скопировать массив - memcpy =)
Да, спасибо, именно такой пример я нашёл в этой теме: http://wasm.ru/forum/viewtopic.php?id=24061 Привожу сюда: Код (Text): #include <ntddk.h> #define DEBUG #ifdef DEBUG #define DBGPRINT DbgPrint #else #define DBGPRINT #endif typedef BOOLEAN BOOL; typedef ULONG DWORD; typedef USHORT WORD; typedef UCHAR BYTE; typedef NTSTATUS (NTAPI *NTPROC)(); typedef NTPROC *PNTPROC; typedef struct _SYSTEM_SERVICE_TABLE{ PNTPROC ServiceTable; PULONG CounterTable; ULONG ServiceLimit; PUCHAR ArgumentTable; } SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE; typedef struct _SERVICE_DESCRIPTOR_TABLE{ SYSTEM_SERVICE_TABLE NtoskrnlTable; SYSTEM_SERVICE_TABLE Table2; SYSTEM_SERVICE_TABLE Table3; SYSTEM_SERVICE_TABLE Table4; } SERVICE_DESCRIPTOR_TABLE, *PSERVICE_DESCRIPTOR_TABLE; typedef enum _SYSTEM_INFORMATION_CLASS { SystemBasicInformation, SystemProcessorInformation, SystemPerformanceInformation, SystemTimeOfDayInformation, SystemNotImplemented1, SystemProcessesAndThreadsInformation, SystemCallCounts, SystemConfigurationInformation, SystemProcessorTimes, SystemGlobalFlag, SystemNotImplemented2, SystemModuleInformation, SystemLockInformation, SystemNotImplemented3, SystemNotImplemented4, SystemNotImplemented5, SystemHandleInformation, SystemObjectInformation, SystemPagefileInformation, SystemInstructionEmulationCounts, SystemInvalidInfoClass1, SystemCacheInformation, SystemPoolTagInformation, SystemProcessorStatistics, SystemDpcInformation, SystemNotImplemented6, SystemLoadImage, SystemUnloadImage, SystemTimeAdjustment, SystemNotImplemented7, SystemNotImplemented8, SystemNotImplemented9, SystemCrashDumpInformation, SystemExceptionInformation, SystemCrashDumpStateInformation, SystemKernelDebuggerInformation, SystemContextSwitchInformation, SystemRegistryQuotaInformation, SystemLoadAndCallImage, SystemPrioritySeparation, SystemNotImplemented10, SystemNotImplemented11, SystemInvalidInfoClass2, SystemInvalidInfoClass3, SystemTimeZoneInformation, SystemLookasideInformation, SystemSetTimeSlipEvent, SystemCreateSession, SystemDeleteSession, SystemInvalidInfoClass4, SystemRangeStartInformation, SystemVerifierInformation, SystemAddVerifier, SystemSessionProcessesInformation } SYSTEM_INFORMATION_CLASS; typedef struct _SYSTEM_MODULE{ ULONG Reserved[2]; PVOID Base; ULONG Size; ULONG Flags; USHORT Index; USHORT Unknown; USHORT LoadCount; USHORT ModuleNameOffset; CHAR ImageName[256]; } SYSTEM_MODULE, *PSYSTEM_MODULE; typedef struct _SYSTEM_MODULE_INFORMATION { ULONG dwCount; SYSTEM_MODULE Modules[1]; } SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION; #ifdef __cplusplus extern "C" { #endif extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable; extern NTSYSAPI NTSTATUS NTAPI ZwCreateSection( OUT PHANDLE SectionHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN PLARGE_INTEGER SectionSize OPTIONAL, IN ULONG Protect, IN ULONG Attributes, IN HANDLE FileHandle ); extern NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation ( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, OUT PVOID SystemInformation, IN ULONG Length, OUT PULONG ReturnLength ); #ifdef __cplusplus } #endif #define RESTORE_SYSCALL 0xAD void DriverUnload(PDRIVER_OBJECT DriverObject){ } NTSTATUS DrvUnhookQST(ULONG dwSysCall, ULONG dwOriginal){ ULONG CR0Reg; DBGPRINT("fun addr - %x", dwOriginal); _asm mov eax,CR0 _asm mov CR0Reg,eax _asm and eax,0xFFFEFFFF _asm mov cr0, eax KeServiceDescriptorTable->NtoskrnlTable.ServiceTable[dwSysCall] = (NTPROC)dwOriginal; _asm mov eax,CR0Reg _asm mov cr0,eax } HANDLE hKernelFile = 0, hKernelSection = 0; PVOID pKernel = NULL; void UnMapKernel(){ if (pKernel) ZwUnmapViewOfSection(pKernel, NtCurrentProcess()); if (hKernelSection) ZwClose(hKernelSection); if (hKernelFile) ZwClose(hKernelFile); } NTSTATUS MapKernel(){ UNICODE_STRING usKernelFile; OBJECT_ATTRIBUTES oaKernelFile; NTSTATUS ns = STATUS_SUCCESS; IO_STATUS_BLOCK iosb; ULONG dwViewSize = 0; // RtlInitUnicodeString(&usKernelFile, L"\\SystemRoot\\System32\\ntoskrnl.exe"); RtlInitUnicodeString(&usKernelFile, L"\\SystemRoot\\System32\\ntkrnlpa.exe"); InitializeObjectAttributes(&oaKernelFile, &usKernelFile, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); ns = ZwOpenFile(&hKernelFile, FILE_READ_DATA | SYNCHRONIZE, &oaKernelFile, &iosb, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT); if (ns != STATUS_SUCCESS) return ns; ns = ZwCreateSection(&hKernelSection, SECTION_MAP_READ, NULL, NULL, PAGE_READONLY, 0x8000000, hKernelFile); if (ns != STATUS_SUCCESS){ UnMapKernel(); return ns; } ns = ZwMapViewOfSection(hKernelSection, NtCurrentProcess(), &pKernel, 0, 0, NULL, &dwViewSize, ViewShare, 0, PAGE_READONLY); if (ns != STATUS_SUCCESS) { UnMapKernel(); } return ns; } ULONG mystrcmp(char *str1, char *str2){ while(*str1 && *str2) if(*str1 == *str2){ str1++; str2++; }else return 0; if(*str1 && !*str2) return 0; else if (*str2 && !*str1) return 0; return 1; } ULONG xGetProcAddress(ULONG hModule, char *pszName){ ULONG dwNameCount, *AddressOfNames, *AddressOfFunctions; USHORT *AddressOfOrdinals; ULONG i; char *pszExportName; [b] char *ptr = (char *)hModule; ptr += 0x3c; ptr = (char *)(*(ULONG *)ptr) + hModule + 0x78; ptr = (char *)(*(ULONG *)ptr) + hModule; dwNameCount = *(ULONG *)(ptr + 24);[/b] AddressOfNames = (ULONG *)(*(ULONG *)(ptr + 32) + hModule); AddressOfOrdinals = (USHORT *)((*(ULONG *)(ptr + 36)) + hModule); AddressOfFunctions = (ULONG *)((*(ULONG *)(ptr + 28)) + hModule); DBGPRINT("%d", dwNameCount); for(i = 0; i < dwNameCount; i++){ pszExportName = (char *)(AddressOfNames[i] + hModule); DBGPRINT("%s", pszExportName); if(mystrcmp(pszExportName, pszName) == 1) return (ULONG)(AddressOfFunctions[AddressOfOrdinals[i]]); } return 0; } typedef struct _IMAGE_BASE_RELOCATION{ ULONG VirtualAddress; ULONG SizeOfBlock; } IMAGE_BASE_RELOCATION, *PIMAGE_BASE_RELOCATION; typedef struct { USHORT offset: 12; USHORT type: 4; } IMAGE_FIXUP_ENTRY, *PIMAGE_FIXUP_ENTRY; ULONG GetKiServiceTable(ULONG SDT, ULONG hModule){ char *ptr; PIMAGE_BASE_RELOCATION pbr; PIMAGE_FIXUP_ENTRY pfe; ULONG bFirst = 1, dwPointerRVA, i; ptr = (char *)hModule; ptr += 0x3c; ptr = (char *)(*(ULONG *)ptr) + hModule + 0xA0; pbr = (PIMAGE_BASE_RELOCATION)((char *)(*(ULONG *)ptr) + hModule); while ((bFirst) || (pbr->VirtualAddress)){ bFirst = 0; pfe = (PIMAGE_FIXUP_ENTRY)((ULONG)pbr + 8); for (i = 0; i < (pbr->SizeOfBlock - 8) >> 1; i++, pfe++) if (pfe->type == 3){ dwPointerRVA = pbr->VirtualAddress + pfe->offset; if (*(PULONG)(hModule + dwPointerRVA) - 0x400000 == SDT) if (*(PUSHORT)(hModule + dwPointerRVA - 2) == 0x05c7) return (*(PULONG)(hModule + dwPointerRVA + 4) - 0x400000 + hModule); } *(PULONG)&pbr += pbr->SizeOfBlock; } return 0; } ULONG GetKernelBase(){ ULONG cb = 0, i; PSYSTEM_MODULE_INFORMATION p = NULL; NTSTATUS ns; ZwQuerySystemInformation(SystemModuleInformation, &p, 0, &cb); if (!cb) return 0; p = ExAllocatePool(PagedPool, cb); if (!p) return 0; ns = ZwQuerySystemInformation(SystemModuleInformation, p, cb, &cb); if (ns != STATUS_SUCCESS) goto exit; for (i = 0; i < p->dwCount; i++) { if (mystrcmp("ntkrnlpa.exe", &(p->Modules[i].ImageName[p->Modules[i].ModuleNameOffset])) == 1) return (ULONG)p->Modules[i].Base; } exit: ExFreePool(p); return 0; } NTSTATUS RestoreSDT(){ ULONG SDT, KernelBase; PNTPROC KiServiceTable; if (MapKernel() != STATUS_SUCCESS) return STATUS_SUCCESS; SDT = xGetProcAddress((ULONG)pKernel, "KeServiceDescriptorTable"); if (!SDT) goto exit; KiServiceTable = (PNTPROC)GetKiServiceTable(SDT, (ULONG)pKernel); if (!KiServiceTable) goto exit; KernelBase = GetKernelBase(); if (!KernelBase) goto exit; //DrvUnhookQST(RESTORE_SYSCALL, (ULONG)(KiServiceTable[RESTORE_SYSCALL]) - 0x400000 + GetKernelBase()); exit: UnMapKernel(); return STATUS_SUCCESS; } NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath){ DBGPRINT("Loading driver"); DriverObject->DriverUnload = DriverUnload; RestoreSDT(); return STATUS_SUCCESS; } И точно также как в той теме, у меня идёт неверное чтение PE заголовка, а точнее адрес секции экспорта.. Иными словами: Код (Text): dwNameCount = *(ULONG *)(ptr + 24) dwNameCount = 0
Ребят, не могу понять... Код (Text): char *ptr = (char *)hModule; ptr += 0x3c; // смещаемся до PE NT заголовка ptr = (char *)(*(ULONG *)ptr) + hModule + 0x78; // смещаемся до адреса секции экспорта. Адрес, если смотреть через winhex: 0x00199000 ptr = (char *)(*(ULONG *)ptr) + hModule; // прибавляем к нему адрес по которому загружен модуль Всё верно? Так вот что я вижу (если рассматривать бинарник с диска при помощи winhex): Выделенные байты: 0x00199000 Их же мне показывает PEID при анализе этого же бинарника: Но как видно по этому скриншоту, по этому адресу в реале ничего нет.. поэтому и код не работает, как я понимаю. Если в peid посмотреть дамп секции экспорта, там откуда-то всплывают вообще другие адреса: 0x00181200 - 0x0018c7a1 Откуда он их взял? :/
RVA это Relative Virtual Address - адрес относительно базы образа. А ты смотришь смещение. Тебе нужно узнать по таблице секций, какой секции соответствует твой RVA, добавить PointerToRawData и вычесть VirtualAddress. Получишь смещение в файле. Но все это делать не нужно будет в коде - ZwMapViewOfSection секцию с SEC_IMAGE отмапит согласно таблице секций. Прочитай про пе формат
Во всех статьях, которые я читаю, VirtualAddress'ом называют как раз RVA. Получается, что мне надо к RVA прибавить PointerToRawData и вычесть этот же RVA? не понял.. что вы подразумеваете под VirtualAddress? Секцию моего RVA (00199000) я определил - .edata Что с этой секцией делать не понял :/
Почитай "от зелёного к красному" http://www.wasm.ru/article.php?article=green2red02, там подробно объясняется что к чему. Вычисление offset'a по RVA оттуда Код (Text): //Base - файл проецируется в память, это его база //RVA - значение, которое нужно преобразовать в Offset DWORD RVAtoOffset(DWORD Base,DWORD RVA) { PIMAGE_NT_HEADERS pPE=(PIMAGE_NT_HEADERS)((long)Base+((PIMAGE_DOS_HEADER)Base)-»e_lfanew); short NumberOfSection=pPE-»FileHeader.NumberOfSections; long SectionAlign=pPE-»OptionalHeader.SectionAlignment; PIMAGE_SECTION_HEADER Section=(PIMAGE_SECTION_HEADER) (pPE-»FileHeader.SizeOfOptionalHeader+(long)& (pPE-»FileHeader)+sizeof(IMAGE_FILE_HEADER)); long VirtualAddress,PointerToRawData; bool flag=false; for (int i=0;i«NumberOfSection;i++) { if ((RVA>=(Section-»VirtualAddress))&& (RVA«Section-»VirtualAddress+ ALIGN_UP((Section-»Misc.VirtualSize),SectionAlign) )) { VirtualAddress=Section-»VirtualAddress; PointerToRawData=Section-»PointerToRawData; flag=true; break; } Section++; } if (flag) return RVA-VirtualAddress+PointerToRawData; else return RVA; }
Да, проблема в том, что поле, содержащее RVA, называется IMAGE_SECTION_HEADER::VirtualAddress. Но там лежит относительный виртуальный адрес. Определимся с терминологией: Virtual address - адрес байта в памяти после загрузки модуля. Relative Virtual Address - адрес байта в памяти относительно базы модуля. File offset (в структуре IMAGE_SECTION_HEADER: PointerToRawData) - смещение байта внутри файла. Простые правила: 1) VA = ImageBase + RVA 2) Везде, кроме таблицы секций, в PE заголовках лежат RVA. 3) Чтобы вычислить RVA по смещению Offset в файле, нужно пройтись по таблице секций, найти секцию, у которой (PointerToRawData >= Offset && (PointerToRawData + SizeOfRawData) < Offset). Как только нашли, берем поля VirtualAddress (это RVA!) и PointerToRawData секции и считаем: RVA = VirtualAddress + (Offset - PointerToRawData). (То бишь мы отнимаем от нашего смещения смещение начала секции и добавляем RVA начала секции, получаем RVA байта) 4) Чтобы вычислить смещение по RVA, нужно найти секцию, у которой (VirtualAddress >= RVA && (VirtualAddress + SizeOfRawData, VirtualSize)) < RVA). Как только нашли такую секцию, можно узнать смещение байта: Offset = (RVA - VirtualAddress) + PointerToRawData. (Вычитаем из RVA байта RVA начала секции, получаем смещение внутри секции. Добавляем смещение начала секции, получаем смещение байта в файле).
haxorart Странно кстати, авторы там пишут, цитирую а сами полагаются на VirtualSize во всех случаях в RVAToOffset() На самом деле, достаточно прибавить SizeOfRawData - все равно, если виртуальный размер больше, то оставшиеся байты не имеют эквивалента на диске (выравнивание или неинициализированные данные).