Привет. Много своих статей удалил я с этого форума к сожалению. Сегодня я решил написать статью на тему, которая в народе называется Infinity-hook, в РУ-сегменте она довольно таки мало документирована, соответственно найти действительно качественные материалы задача нетривиальная (собственно, в РУ-сегменте я такие статьи и не обнаружил). Поэтому данная статья описывает работу хука, через реализацию которого можно перехватывать syscall-ы. Сам же этот хук уже с года 19 работает в античите от Riot - Vanguard. Начнем.
В целом, реализация данного хука в частности заложена в агрессивные античиты вроде VGK, за весь свой опыт работы с антивирусами, подобной реализации нигде я не замечал. Все современные антивирусы до сих пор по большей части полагаются на стандартные и документированные механизмы вроде минифильтров файловой системы, да и обычные каллбеков,.
Теперь теоритически. Когда в системе активируется трассировка системных вызовов, то ядро должно фиксировать точное время каждого сискола. За это отвечает функция PerfInfoLogSysCallEntry.
В замен же этой реализации пришла более современная и удобная реализация этого же самого хука, только работает он немного иначе.
Реализация более современной версии уходит в интерфейс HAL. Кстаати, интересно то, что таблица, которую мы будем разбирать - HalPrivateDispatchTable. Поле из данной таблицы использовалась еще и для коммуникации с драйвером из юзермода, реализация же коммуникации происходила через NtConvertBetweenAuxiliaryCounterAndPerformanceCounter, разработчики читов подменяли запись в таблице HalPrivateDispatchTable, которая указывает на процедуру xKdEnumerateDebuggingDevices. Из юзермода вызывалась обертка из ntdll, а передаваемые аргументы сквозным потоком проваливались в ядро, предоставляя разработчику идеальный и очень быстрый канал для чтения или записи памяти, быстрее он относительно способа с реестром, например.
Непосредственно получать доступ к данной таблице возможно только из под драйвера даже учитывая то, что ETW доступно даже из юзермода. Поиск адреса данной таблицы выглядит следующим образом:
В случае же с более современной реализации. В системной функции EtwpLogKernelEvent есть несколько интересных мест.
При определенных условиях инициализации ETW-логгера ядро начинает выполнять цепочку проверок и резервирования буферов. Внутри этого процесса вызывается функция EtwpReserveWithPmcCounters, которая уже внутри себя вызывает HalpCollectPmcCounters:
Ядро обращается к этому полю непосредственно по той таблице. Сама же таблица лежит в секции .data, соответственно PG на нее абсолютно никак не реагирует. Да и исторически вышло так, что Microsoft довольно таки часто работали с данной таблицей, соответственно под крыло PG решили данную таблица не добавлять.
При соответствующей настройке трассировки выполнение может доходить до перехватчика, когда ядро собирает аппаратные счетчики производительности процессора для ETW-лога. Сразу добавлю, что на этапе вызова HalpCollectPmcCounters в функции фильтрации нет прямой информации о том, какой именно сискол сейчас выполняется. На руках есть только контекст счетчиков, поэтому для определения конкретного сискол уже приходится смотреть на стек вызовов.
Непосредственно Vanguard в подобном случае используют технику Stack Walking(ake раскрутка стека). Как только управление переходит в обработчик, драйвер берет текущее значение регистра RSP и начинает сканировать стек потока назад, задача же в том, чтобы найти фрейм который принадлежит системному обработчику сисколов KiSystemServiceStart или же ее аналогам. Структура стека же выглядит так:
Пример базового обработчика, который раскручивает стек, сверяет идентификаторы событий и находит фрейм нужной системной функции, выглядит так:
Проверяем конкретный фрейм стека, и если он принадлежит, например, оригинальной NtQuerySystemInformation, мы перезаписываем его адресом нашей функции-обработчика. В качестве тестового примера внутри хука можно модифицировать структуру SystemKernelDebuggerInformation, чтобы сбрасывать флаги KernelDebuggerEnabled и KernelDebuggerNotPresent, или подменять SystemCodeIntegrityInformation, имитируя включенную целостность кода и выключенный тестовый режим подписи драйверов. И так далее. Демонстрация работы хука:
Всем пока.
В целом, реализация данного хука в частности заложена в агрессивные античиты вроде VGK, за весь свой опыт работы с антивирусами, подобной реализации нигде я не замечал. Все современные антивирусы до сих пор по большей части полагаются на стандартные и документированные механизмы вроде минифильтров файловой системы, да и обычные каллбеков,.
Предыстория про infinity-hook.
Непосредственно самую первую реализацию хука обнаружил Sirocco в процессе анализа компонентов Windows, реализация хука работала через ETW функцию, а если конкретно вернутся в 19 год, то реализация работала через механизм трассировки событий Windows (ETW), а если еще точнее - через системный регистратор WMI_LOGGER_CONTEXT. Реализация хука максимально тривиальна, я реализовал диаграмму, которую вы можете изучить, после чего я объясню, как он работает в теории:- Юзермодное приложение вызывает сискол. После чего управление переходит в обработчик KiSystemServiceStart;
- Активный логгер получает управление через PerfInfoLogSysCallEntry;
- Непосредственно внутри этой функции ядро извлекает указатель на структуру WMI_LOGGER_CONTEXT которая управляет конкретной сессией трассировки;
- Чтобы писать в лог точное время вызова, ядру необходимо знать текущий такт процессора, соответственно в данной структуре присутствовало поле GetCpuClock, который являлся обычным указателем на функцию, которая возвращает текущее время. Соответственно после логирования времени управление возвращалось к диспетчеру, и выполнялся оригинальный сискол.
Что поменялось с 2019 года?
Поменялось многое. Microsoft прикрыли лавочку с этим способом тем, что перевели структуры ETW под контроль PG и изменили сам алгоритм вызова GetCpuClock внутри PerfInfoLogSysCallEntry. В современных сборках Windows 10/11 из структуры WMI_LOGGER_CONTEXT был убран прямой голый указатель на функцию замера времени. Теперь поле GetCpuClock хранит перечисление и числовой индекс. Если показать иллюстрационно в коде то выглядит примерно так:
C++:
switch (LoggerContext->GetCpuClock) {
case 1: Time = rdtsc(); break;
case 2: Time = KeQueryPerformanceCounter(); break;
// и тд
}
В замен же этой реализации пришла более современная и удобная реализация этого же самого хука, только работает он немного иначе.
Немного про таблицу.
Продолжением идеи данного хука стало исследование разработчика Эйдана Хури и Ника Патерсона. Суть нового метода заключается в том, что внутри архитектуры логгеров остались другие, альтернативные функции обратного вызова, которые Microsoft либо забыла защитить, либо не смогла перевести на индексы из-за совместимости.Реализация более современной версии уходит в интерфейс HAL. Кстаати, интересно то, что таблица, которую мы будем разбирать - HalPrivateDispatchTable. Поле из данной таблицы использовалась еще и для коммуникации с драйвером из юзермода, реализация же коммуникации происходила через NtConvertBetweenAuxiliaryCounterAndPerformanceCounter, разработчики читов подменяли запись в таблице HalPrivateDispatchTable, которая указывает на процедуру xKdEnumerateDebuggingDevices. Из юзермода вызывалась обертка из ntdll, а передаваемые аргументы сквозным потоком проваливались в ядро, предоставляя разработчику идеальный и очень быстрый канал для чтения или записи памяти, быстрее он относительно способа с реестром, например.
ETW.
Архитектура ETW включает в себя 4 элемента:- Поставщик событий - компонент который создает события ETW. Им может быть, как сама операционная система, так и отдельное приложение или драйвер. Например, компонент системы может сообщить о создании процесса, загрузке образа или другом интересующем событии, задача протокола это сформировать событие и передать его инфраструктуре ETW;
- Сеанс трассировки - механизм, через который происходит непосредственный сбор событий. Он связывает поставщиков с системой, которая эти события получает. При запуске сеанса определяется, какие поставщики будут активны и какие события от них необходимо собирать. Полученные данные могут временно находиться в буферах, а затем передаваться потребителю или записываться в журнал;
- Потребитель - компонент, который получает события из сеанса трассировки и обрабатывает их. На данном этапе собранная информация начинает использоваться по назначению, например, антивирусный процесс может получить событие о создани нового процесса, извлечь из него идентификатор процесса, путь к исполняемому файлу и другие данные, после чего передать информацию своему механизму анализа;
- Контроллер - компонент отвечающий за управление сеансом трассировки. Через него можно запустить или остановить сеанс, добавить или отключить поставщиков, а также изменить параметры сбора событий.
Современная реализация хука.
Если говорить простым языком, HAL_PRIVATE_DISPATCH (в кругах чит-дева известная как HalPrivateDispatchTable) - это системный мост, спрятанный глубоко внутри HAL. Главная задача отвечать за взаимодействие ядра с аппаратным физическим железом причем для таких специфических функций, которые Microsoft решила не выносить в стандартные, общедоступные HAL-интерфейсы. Сама структура является просто массивом голых указателей на функции, каждый элемент этой таблицы за строго определенную аппаратную фичу, которая конфигурируется под нужды конкретной платформы. Сама таблица выглядит так:
C++:
struct HAL_PRIVATE_DISPATCH
{
unsigned int Version;
BUS_HANDLER *(*HalHandlerForBus)(INTERFACE_TYPE, unsigned int);
BUS_HANDLER *(*HalHandlerForConfigSpace)(BUS_DATA_TYPE, unsigned int);
void (*HalLocateHiberRanges)(void *);
int (*HalRegisterBusHandler)(INTERFACE_TYPE, BUS_DATA_TYPE, unsigned int, INTERFACE_TYPE, unsigned int, unsigned int, int (*)(BUS_HANDLER *), BUS_HANDLER **);
void (*HalSetWakeEnable)(unsigned __int8);
int (*HalSetWakeAlarm)(unsigned __int64, unsigned __int64);
unsigned __int8 (*HalPciTranslateBusAddress)(INTERFACE_TYPE, unsigned int, LARGE_INTEGER, unsigned int *, LARGE_INTEGER *);
int (*HalPciAssignSlotResources)(UNICODE_STRING *, UNICODE_STRING *, DRIVER_OBJECT *, DEVICE_OBJECT *, INTERFACE_TYPE, unsigned int, unsigned int, CM_RESOURCE_LIST **);
void (*HalHaltSystem)();
unsigned __int8 (*HalFindBusAddressTranslation)(LARGE_INTEGER, unsigned int *, LARGE_INTEGER *, unsigned __int64 *, unsigned __int8);
unsigned __int8 (*HalResetDisplay)();
int (*HalAllocateMapRegisters)(_ADAPTER_OBJECT *, unsigned int, unsigned int, MAP_REGISTER_ENTRY *);
int (*KdSetupPciDeviceForDebugging)(void *, DEBUG_DEVICE_DESCRIPTOR *);
int (*KdReleasePciDeviceForDebugging)(DEBUG_DEVICE_DESCRIPTOR *);
void *(*KdGetAcpiTablePhase0)(LOADER_PARAMETER_BLOCK *, unsigned int);
void (*KdCheckPowerButton)();
unsigned __int8 (*HalVectorToIDTEntry)(unsigned int);
void *(*KdMapPhysicalMemory64)(LARGE_INTEGER, unsigned int, unsigned __int8);
void (*KdUnmapVirtualAddress)(void *, unsigned int, unsigned __int8);
unsigned int (*KdGetPciDataByOffset)(unsigned int, unsigned int, void *, unsigned int, unsigned int);
unsigned int (*KdSetPciDataByOffset)(unsigned int, unsigned int, void *, unsigned int, unsigned int);
unsigned int (*HalGetInterruptVectorOverride)(INTERFACE_TYPE, unsigned int, unsigned int, unsigned int, unsigned __int8 *, unsigned __int64 *);
int (*HalGetVectorInputOverride)(unsigned int, GROUP_AFFINITY *, unsigned int *, KINTERRUPT_POLARITY *, INTERRUPT_REMAPPING_INFO *);
int (*HalLoadMicrocode)(void *);
int (*HalUnloadMicrocode)();
int (*HalPostMicrocodeUpdate)();
int (*HalAllocateMessageTargetOverride)(DEVICE_OBJECT *, GROUP_AFFINITY *, unsigned int, KINTERRUPT_MODE, unsigned __int8, unsigned int *, unsigned __int8 *, unsigned int *);
void (*HalFreeMessageTargetOverride)(DEVICE_OBJECT *, unsigned int, GROUP_AFFINITY *);
int (*HalDpReplaceBegin)(HAL_DP_REPLACE_PARAMETERS *, void **);
void (*HalDpReplaceTarget)(void *);
int (*HalDpReplaceControl)(unsigned int, void *);
void (*HalDpReplaceEnd)(void *);
void (*HalPrepareForBugcheck)(unsigned int);
unsigned __int8 (*HalQueryWakeTime)(unsigned __int64 *, unsigned __int64 *);
void (*HalReportIdleStateUsage)(unsigned __int8, KAFFINITY_EX *);
void (*HalTscSynchronization)(unsigned __int8, unsigned int *);
int (*HalWheaInitProcessorGenericSection)(WHEA_ERROR_RECORD_SECTION_DESCRIPTOR *, WHEA_PROCESSOR_GENERIC_ERROR_SECTION *);
void (*HalStopLegacyUsbInterrupts)(SYSTEM_POWER_STATE);
int (*HalReadWheaPhysicalMemory)(LARGE_INTEGER, unsigned int, void *);
int (*HalWriteWheaPhysicalMemory)(LARGE_INTEGER, unsigned int, void *);
int (*HalDpMaskLevelTriggeredInterrupts)();
int (*HalDpUnmaskLevelTriggeredInterrupts)();
int (*HalDpGetInterruptReplayState)(void *, void **);
int (*HalDpReplayInterrupts)(void *);
unsigned __int8 (*HalQueryIoPortAccessSupported)();
int (*KdSetupIntegratedDeviceForDebugging)(void *, DEBUG_DEVICE_DESCRIPTOR *);
int (*KdReleaseIntegratedDeviceForDebugging)(DEBUG_DEVICE_DESCRIPTOR *);
void (*HalGetEnlightenmentInformation)(HAL_INTEL_ENLIGHTENMENT_INFORMATION *);
void *(*HalAllocateEarlyPages)(LOADER_PARAMETER_BLOCK *, unsigned int, unsigned __int64 *, unsigned int);
void *(*HalMapEarlyPages)(unsigned __int64, unsigned int, unsigned int);
void *Dummy1;
void *Dummy2;
void (*HalNotifyProcessorFreeze)(unsigned __int8, unsigned __int8);
int (*HalPrepareProcessorForIdle)(unsigned int);
void (*HalRegisterLogRoutine)(HAL_LOG_REGISTER_CONTEXT *);
void (*HalResumeProcessorFromIdle)();
void *Dummy;
unsigned int (*HalVectorToIDTEntryEx)(unsigned int);
int (*HalSecondaryInterruptQueryPrimaryInformation)(INTERRUPT_VECTOR_DATA *, unsigned int *);
int (*HalMaskInterrupt)(unsigned int, unsigned int);
int (*HalUnmaskInterrupt)(unsigned int, unsigned int);
unsigned __int8 (*HalIsInterruptTypeSecondary)(unsigned int, unsigned int);
int (*HalAllocateGsivForSecondaryInterrupt)(char *, unsigned __int16, unsigned int *);
int (*HalAddInterruptRemapping)(unsigned int, unsigned int, PCI_BUSMASTER_DESCRIPTOR *, unsigned __int8, INTERRUPT_VECTOR_DATA *, unsigned int);
void (*HalRemoveInterruptRemapping)(unsigned int, unsigned int, PCI_BUSMASTER_DESCRIPTOR *, unsigned __int8, INTERRUPT_VECTOR_DATA *, unsigned int);
void (*HalSaveAndDisableHvEnlightenment)();
void (*HalRestoreHvEnlightenment)();
void (*HalFlushIoBuffersExternalCache)(MDL *, unsigned __int8);
void (*HalFlushExternalCache)(unsigned __int8);
int (*HalPciEarlyRestore)(_SYSTEM_POWER_STATE);
int (*HalGetProcessorId)(unsigned int, unsigned int *, unsigned int *);
int (*HalAllocatePmcCounterSet)(unsigned int, _KPROFILE_SOURCE *, unsigned int, struct _HAL_PMC_COUNTERS **);
void (*HalCollectPmcCounters)(struct HAL_PMC_COUNTERS *, unsigned __int64 *);
void (*HalFreePmcCounterSet)(struct HAL_PMC_COUNTERS *);
int (*HalProcessorHalt)(unsigned int, void *, int (*)(void *));
unsigned __int64 (*HalTimerQueryCycleCounter)(unsigned __int64 *);
void *Dummy3;
void (*HalPciMarkHiberPhase)();
int (*HalQueryProcessorRestartEntryPoint)(LARGE_INTEGER *);
int (*HalRequestInterrupt)(unsigned int);
int (*HalEnumerateUnmaskedInterrupts)(unsigned __int8 (*)(void *, HAL_UNMASKED_INTERRUPT_INFORMATION *), void *, HAL_UNMASKED_INTERRUPT_INFORMATION *);
void (*HalFlushAndInvalidatePageExternalCache)(LARGE_INTEGER);
int (*KdEnumerateDebuggingDevices)(void *, DEBUG_DEVICE_DESCRIPTOR *, KD_CALLBACK_ACTION (*)(DEBUG_DEVICE_DESCRIPTOR *));
void (*HalFlushIoRectangleExternalCache)(_MDL *, unsigned int, unsigned int, unsigned int, unsigned int, unsigned __int8);
void (*HalPowerEarlyRestore)(unsigned int);
int (*HalQueryCapsuleCapabilities)(void *, unsigned int, unsigned __int64 *, unsigned int *);
int (*HalUpdateCapsule)(void *, unsigned int, LARGE_INTEGER);
unsigned __int8 (*HalPciMultiStageResumeCapable)();
void (*HalDmaFreeCrashDumpRegisters)(unsigned int);
unsigned __int8 (*HalAcpiAoacCapable)();
int (*HalInterruptSetDestination)(INTERRUPT_VECTOR_DATA *, GROUP_AFFINITY *, unsigned int *);
void (*HalGetClockConfiguration)(HAL_CLOCK_TIMER_CONFIGURATION *);
void (*HalClockTimerActivate)(unsigned __int8);
void (*HalClockTimerInitialize)();
void (*HalClockTimerStop)();
int (*HalClockTimerArm)(_HAL_CLOCK_TIMER_MODE, unsigned __int64, unsigned __int64 *);
unsigned __int8 (*HalTimerOnlyClockInterruptPending)();
void *(*HalAcpiGetMultiNode)();
void (*(*HalPowerSetRebootHandler)(void (*)(unsigned int, volatile int *)))(unsigned int, volatile int *);
void (*HalIommuRegisterDispatchTable)(HAL_IOMMU_DISPATCH *);
void (*HalTimerWatchdogStart)();
void (*HalTimerWatchdogResetCountdown)();
void (*HalTimerWatchdogStop)();
unsigned __int8 (*HalTimerWatchdogGeneratedLastReset)();
int (*HalTimerWatchdogTriggerSystemReset)(unsigned __int8);
int (*HalInterruptVectorDataToGsiv)(INTERRUPT_VECTOR_DATA *, unsigned int *);
int (*HalInterruptGetHighestPriorityInterrupt)(unsigned int *, unsigned __int8 *);
int (*HalProcessorOn)(unsigned int);
int (*HalProcessorOff)();
int (*HalProcessorFreeze)();
int (*HalDmaLinkDeviceObjectByToken)(unsigned __int64, DEVICE_OBJECT *);
int (*HalDmaCheckAdapterToken)(unsigned __int64);
void *Dummy4;
int (*HalTimerConvertPerformanceCounterToAuxiliaryCounter)(unsigned __int64, unsigned __int64 *, unsigned __int64 *);
int (*HalTimerConvertAuxiliaryCounterToPerformanceCounter)(unsigned __int64, unsigned __int64 *, unsigned __int64 *);
int (*HalTimerQueryAuxiliaryCounterFrequency)(unsigned __int64 *);
int (*HalConnectThermalInterrupt)(unsigned __int8 (*)(KINTERRUPT *, void *));
unsigned __int8 (*HalIsEFIRuntimeActive)();
unsigned __int8 (*HalTimerQueryAndResetRtcErrors)(unsigned __int8);
void (*HalAcpiLateRestore)();
int (*KdWatchdogDelayExpiration)(unsigned __int64 *);
int (*HalGetProcessorStats)(HAL_PROCESSOR_STAT_TYPE, unsigned int, unsigned int, unsigned __int64 *);
unsigned __int64 (*HalTimerWatchdogQueryDueTime)(unsigned __int8);
int (*HalConnectSyntheticInterrupt)(unsigned __int8 (*)(KINTERRUPT *, void *));
void (*HalPreprocessNmi)(unsigned int);
int (*HalEnumerateEnvironmentVariablesWithFilter)(unsigned int, unsigned __int8 (*)(const _GUID *, const wchar_t *), void *, unsigned int *);
int (*HalCaptureLastBranchRecordStack)(unsigned int, HAL_LBR_ENTRY *, unsigned int *);
unsigned __int8 (*HalClearLastBranchRecordStack)();
int (*HalConfigureLastBranchRecord)(unsigned int, unsigned int);
unsigned __int8 (*HalGetLastBranchInformation)(unsigned int *, unsigned int *);
void (*HalResumeLastBranchRecord)(unsigned __int8);
int (*HalStartLastBranchRecord)(unsigned int, unsigned int *);
int (*HalStopLastBranchRecord)(unsigned int);
int (*HalIommuBlockDevice)(void *);
int (*HalIommuUnblockDevice)(EXT_IOMMU_DEVICE_ID *, void **);
int (*HalGetIommuInterface)(unsigned int, DMA_IOMMU_INTERFACE *);
int (*HalRequestGenericErrorRecovery)(void *, unsigned int *);
int (*HalTimerQueryHostPerformanceCounter)(unsigned __int64 *);
int (*HalTopologyQueryProcessorRelationships)(unsigned int, unsigned int, unsigned __int8 *, unsigned __int8 *, unsigned __int8 *, unsigned int *, unsigned int *);
void (*HalInitPlatformDebugTriggers)();
void (*HalRunPlatformDebugTriggers)(unsigned __int8);
void *(*HalTimerGetReferencePage)();
int (*HalGetHiddenProcessorPowerInterface)(HIDDEN_PROCESSOR_POWER_INTERFACE *);
unsigned int (*HalGetHiddenProcessorPackageId)(unsigned int);
unsigned int (*HalGetHiddenPackageProcessorCount)(unsigned int);
int (*HalGetHiddenProcessorApicIdByIndex)(unsigned int, unsigned int *);
int (*HalRegisterHiddenProcessorIdleState)(unsigned int, unsigned __int64);
void (*HalIommuReportIommuFault)(unsigned __int64, FAULT_INFORMATION *);
unsigned __int8 (*HalIommuDmaRemappingCapable)(EXT_IOMMU_DEVICE_ID *, unsigned int *);
};
C++:
UNICODE_STRING target = RTL_CONSTANT_STRING( L"HalPrivateDispatchTable" );
HalPrivateDispatchTable = reinterpret_cast< PHAL_PRIVATE_DISPATCH_TABLE >( MmGetSystemRoutineAddress( &target ) );
if ( !HalPrivateDispatchTable )
return STATUS_RESOURCE_UNAVAILABLE;
C++:
if()
{
v33 = EtwpReserveTraceBuffer(v14, v15 + 0x10, &v59, &v55, a6);
//
// ...
//
goto LABEL_19;
}
if ( )
{
v34 = EtwpReserveWithPebsIndex(v14, 0x524, v15, &v59, &v55, a6);
}
else
{
//...
v34 = EtwpReserveWithPmcCounters(v14, a5, v15, &v59, &v55, a6);
}
Ядро обращается к этому полю непосредственно по той таблице. Сама же таблица лежит в секции .data, соответственно PG на нее абсолютно никак не реагирует. Да и исторически вышло так, что Microsoft довольно таки часто работали с данной таблицей, соответственно под крыло PG решили данную таблица не добавлять.
При соответствующей настройке трассировки выполнение может доходить до перехватчика, когда ядро собирает аппаратные счетчики производительности процессора для ETW-лога. Сразу добавлю, что на этапе вызова HalpCollectPmcCounters в функции фильтрации нет прямой информации о том, какой именно сискол сейчас выполняется. На руках есть только контекст счетчиков, поэтому для определения конкретного сискол уже приходится смотреть на стек вызовов.
Непосредственно Vanguard в подобном случае используют технику Stack Walking(ake раскрутка стека). Как только управление переходит в обработчик, драйвер берет текущее значение регистра RSP и начинает сканировать стек потока назад, задача же в том, чтобы найти фрейм который принадлежит системному обработчику сисколов KiSystemServiceStart или же ее аналогам. Структура стека же выглядит так:
Пример базового обработчика, который раскручивает стек, сверяет идентификаторы событий и находит фрейм нужной системной функции, выглядит так:
C++:
void process_syscall(stack<64>& sp)
{
const auto target_fn = reinterpret_cast(sp.at(9));
if (*target_fn == o__nt_query_system_information)
*target_fn = &hkd__nt_query_system_information;
}
Подробнее описание реализации.
Если разбирать пошагово реализацию, то сам хук реализуется так:- Находим адрес таблицы HalPrivateDispatchTable через MmGetSystemRoutineAddress;
- Конфигурируем сессию логгера ядра через ZwTraceControl. Нам понадобятся как минимум коды функций EtwStartLoggerCode, EtwStopLoggerCode и EtwUpdateLoggerCode для управления сессией NT Kernel Logger;
- Дополнительно настраиваем блоки информации трассировки через ZwSetSystemInformation с классом SystemPerformanceTraceInformation. Это нужно, чтобы проинициализировать списки счетчиков EventTraceProfileCounterListInformation, без которых подсистема PMC просто не начнет собирать данные;
- Подменяем указатель оригинальной функции HalpCollectPmcCounters в таблице HAL на свой обработчик.
Всем пока.
Вложения
Последнее редактирование: