FSD通常是分别处理Fastfat.sys和Ntfs.sys。Disk则是CLASSPNP.sys,同理。
以Fastfat.sys为例,DriverEntry里有如下代码
loc_3433D:
push 70h
pop edi
push edi ; size_t
mov eax, offset _FatFastIoDispatch
push ebx ; int
push eax ; void *
mov dword ptr [esi+34h], offset _FatUnload@4 ; 此处esi == DriverEntry
mov dword ptr [esi+38h], offset _FatFsdCreate@8 ; FatFsdCreate(x,x)
mov dword ptr [esi+40h], offset _FatFsdClose@8 ; FatFsdClose(x,x)
mov dword ptr [esi+44h], offset _FatFsdRead@8 ; FatFsdRead(x,x)
mov dword ptr [esi+48h], offset _FatFsdWrite@8 ; FatFsdWrite(x,x)
mov dword ptr [esi+4Ch], offset _FatFsdQueryInformation@8 ; FatFsdQueryInformation(x,x)
mov dword ptr [esi+50h], offset _FatFsdSetInformation@8 ; FatFsdSetInformation(x,x)
mov dword ptr [esi+54h], offset _FatFsdQueryEa@8 ; FatFsdQueryEa(x,x)
mov dword ptr [esi+58h], offset _FatFsdQueryEa@8 ; FatFsdQueryEa(x,x)
mov dword ptr [esi+5Ch], offset _FatFsdFlushBuffers@8 ; FatFsdFlushBuffers(x,x)
mov dword ptr [esi+60h], offset _FatFsdQueryVolumeInformation@8 ; FatFsdQueryVolumeInformation(x,x)
mov dword ptr [esi+64h], offset _FatFsdSetVolumeInformation@8 ; FatFsdSetVolumeInformation(x,x)
mov dword ptr [esi+80h], offset _FatFsdCleanup@8 ; FatFsdCleanup(x,x)
mov dword ptr [esi+68h], offset _FatFsdDirectoryControl@8 ; FatFsdDirectoryControl(x,x)
mov dword ptr [esi+6Ch], offset _FatFsdFileSystemControl@8 ; FatFsdFileSystemControl(x,x)
mov dword ptr [esi+7Ch], offset _FatFsdLockControl@8 ; FatFsdLockControl(x,x)
mov dword ptr [esi+70h], offset _FatFsdDeviceControl@8 ; FatFsdDeviceControl(x,x)
mov dword ptr [esi+78h], offset _FatFsdShutdown@8 ; FatFsdShutdown(x,x)
mov dword ptr [esi+0A4h], offset _FatFsdPnp@8 ; FatFsdPnp(x,x)
mov [esi+28h], eax
获取
当前函数地址只需要得到Fastfat.DriverEntry->MajorFunction[]即可
获取
原始函数地址则要解析磁盘上的Fastfat.sys文件,方法是根据特征码在DriverEntry函数段里暴力搜索,取得offset _FatFsdCreate等例程的RVA,然后根据Fastfat.sys内核模块基址计算出例程的原始内存地址。另外有些例程没有被驱动程序特殊指定,则使用系统默认的处理例程(对应ntoskrnl.exe模块),这些函数的原始地址没有办法通过解析Fastfat.sys取得
以前写过XPSP3的FSD HOOK检测,没有经过严格测试,代码很挫(算法完全凭感觉写的..),你可以简单参考一下思路:
#include <ntddk.h>
#include "File.h"
typedef struct _RESTORE_DISPATCH_FUNCTION {
PDRIVER_OBJECT DriverObject;
ULONG FuncId;
ULONG OrginalValue;
} RESTORE_DISPATCH_FUNCTION, *PRESTORE_DISPATCH_FUNCTION;
typedef struct _DISPATCH_FUNCTION_INFORMATION {
ULONG FuncAddr[29];
ULONG OrginalFuncAddr[29];
} DISPATCH_FUNCTION_INFORMATION, *PDISPATCH_FUNCTION_INFORMATION;
typedef struct _QUERY_DISPATCH_FUNCTION_INFORMATION {
PDRIVER_OBJECT DriverObject;
WCHAR FilePath[260];
} QUERY_DISPATCH_FUNCTION_INFORMATION, *PQUERY_DISPATCH_FUNCTION_INFORMATION;
NTSTATUS FxRestoreDispatchFunctionHook(PRESTORE_DISPATCH_FUNCTION lpReDisFuncInfo)
{
if (!MmIsAddressRangeValid(lpReDisFuncInfo->DriverObject->MajorFunction, 4 * 29)) {
return STATUS_UNSUCCESSFUL;
}
//DbgPrint("DriverObject: %X, FuncId: %d, RealAddr: %X", lpReDisFuncInfo->DriverObject, lpReDisFuncInfo->FuncId, lpReDisFuncInfo->OrginalValue);
lpReDisFuncInfo->DriverObject->MajorFunction[lpReDisFuncInfo->FuncId] = (PDRIVER_DISPATCH)lpReDisFuncInfo->OrginalValue;
return STATUS_SUCCESS;
}
NTSTATUS FxGetDispatchFunction(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING DriverPath, PDISPATCH_FUNCTION_INFORMATION lpDispatchFunction) //PDISPATCH_FUNCTION_INFORMATION *pDispatchFunctionLinkHead)
{
if (!MmIsAddressRangeValid(pDriverObject->MajorFunction, 4 * 29)) {
return STATUS_UNSUCCESSFUL;
}
DISPATCH_FUNCTION_INFORMATION DispatchFunction;
RtlZeroMemory(lpDispatchFunction, sizeof(DISPATCH_FUNCTION_INFORMATION));
RtlCopyMemory(lpDispatchFunction->FuncAddr, pDriverObject->MajorFunction, 4 * 29);
NTSTATUS Status = NULL;
HANDLE hFile = NULL;
OBJECT_ATTRIBUTES ObjAttr;
RtlZeroMemory(&ObjAttr, sizeof(OBJECT_ATTRIBUTES));
// \\??\\C:\\WINDOWS\\system32\\drivers\\Ntfs.sys
InitializeObjectAttributes(&ObjAttr, DriverPath, NULL, NULL, NULL);
//DbgPrint("(%d)%ws", DriverPath->Length, DriverPath->Buffer);
IO_STATUS_BLOCK IoFileStatus;
Status = ZwCreateFile(&hFile, GENERIC_READ | GENERIC_WRITE, &ObjAttr, &IoFileStatus, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, NULL, NULL, NULL);
if (!NT_SUCCESS(Status)) {
//DbgPrint("CreateFile faild!");
return STATUS_UNSUCCESSFUL;
}
LARGE_INTEGER FilePointer;
RtlZeroMemory(&FilePointer, sizeof(LARGE_INTEGER));
IMAGE_DOS_HEADER DosHeader;
Status = ZwReadFile(hFile, NULL, NULL, NULL, &IoFileStatus, &DosHeader, sizeof(IMAGE_DOS_HEADER), &FilePointer, NULL);
if (!NT_SUCCESS(Status)) {
ZwClose(hFile);
return STATUS_UNSUCCESSFUL;
}
FilePointer.LowPart = DosHeader.e_lfanew + 24;
FilePointer.HighPart = 0;
IMAGE_OPTIONAL_HEADER OptionalHeader;
ZwReadFile(hFile, NULL, NULL, NULL, &IoFileStatus, &OptionalHeader, sizeof(IMAGE_OPTIONAL_HEADER), &FilePointer, NULL);
if (!NT_SUCCESS(Status)){
ZwClose(hFile);
return STATUS_UNSUCCESSFUL;
}
//DbgPrint("AddressOfEntryPoint: %X, ImageBase: %X", OptionalHeader.AddressOfEntryPoint, OptionalHeader.ImageBase);
ULONG ImageBase = OptionalHeader.ImageBase;
ULONG StartAddr = (ULONG)ExAllocatePool(NonPagedPool, 0x200);
if (StartAddr == 0) {
ZwClose(hFile);
return STATUS_UNSUCCESSFUL;
}
FilePointer.LowPart = OptionalHeader.AddressOfEntryPoint;
FilePointer.HighPart = 0;
ZwReadFile(hFile, NULL, NULL, NULL, &IoFileStatus, (PVOID)StartAddr, 0x200, &FilePointer, NULL);
if (!NT_SUCCESS(Status)) {
ZwClose(hFile);
ExFreePoolWithTag((PVOID)StartAddr, 0x200);
return STATUS_UNSUCCESSFUL;
}
ZwClose(hFile);
USHORT usHardCode1 = 0x46C7;
USHORT usHardCode2 = 0x86C7;
USHORT usHardCode3 = 0x4689;
USHORT usHardCode4 = 0x8689;
UCHAR ucHardCode5 = 0xB8;
USHORT usCheckCode = NULL;
ULONG ulEax = NULL;
BOOLEAN bIsInEax = FALSE;
UCHAR ucOffset = NULL;
UCHAR ucIndex = NULL;
ULONG ulOrginalAddr = NULL;
for (ULONG CheckAddr = StartAddr; CheckAddr <= StartAddr + 0x200 - 7; CheckAddr++)
{
if (!MmIsAddressRangeValid((PUSHORT)CheckAddr, 7)) break;
//DbgPrint("Valid: %X, Data: %X", CheckAddr, *(PUSHORT)CheckAddr);
usCheckCode = *(PUSHORT)CheckAddr;
if (usCheckCode == usHardCode1 || usCheckCode == usHardCode2) {
ucOffset = *(PUCHAR)(CheckAddr + 2);
if (!(ucOffset >= 0x38 && ucOffset <= 0xB0)) continue;
ucIndex = (ucOffset - 0x38) / 4;
if (usCheckCode == usHardCode1) {
ulOrginalAddr = *(PULONG)(CheckAddr + 3);
} else {
ulOrginalAddr = *(PULONG)(CheckAddr + 6);
}
ulOrginalAddr = (ULONG)pDriverObject->DriverStart + ulOrginalAddr - ImageBase;
//DbgPrint("(%d)ulOrginalAddr: %X", ucIndex, ulOrginalAddr);
lpDispatchFunction->OrginalFuncAddr[ucIndex] = ulOrginalAddr;
} else if (usCheckCode == usHardCode3 || usCheckCode == usHardCode4) {
DbgPrint("Found HardCode3-4 At %X, bIsInEax: %d", CheckAddr, bIsInEax);
if (!bIsInEax) {
if (*(PUCHAR)(CheckAddr - 5) = ucHardCode5) {
ulEax = *(PULONG)(CheckAddr - 4);
bIsInEax = TRUE;
ucOffset = *(PUCHAR)(CheckAddr + 2);
DbgPrint("Just In Eax, ulEax: %X, ucOffset: %X", ulEax, ucOffset);
if (!(ucOffset >= 0x38 && ucOffset <= 0xB0)) continue;
ucIndex = (ucOffset - 0x38) / 4;
ulOrginalAddr = (ULONG)pDriverObject->DriverStart + ulEax - ImageBase;
lpDispatchFunction->OrginalFuncAddr[ucIndex] = ulOrginalAddr;
}
} else {
ucOffset = *(PUCHAR)(CheckAddr + 2);
DbgPrint("Already In Eax, ulEax: %X, ucOffset: %X", ulEax, ucOffset);
if (!(ucOffset >= 0x38 && ucOffset <= 0xB0)) continue;
ucIndex = (ucOffset - 0x38) / 4;
ulOrginalAddr = (ULONG)pDriverObject->DriverStart + ulEax - ImageBase;
lpDispatchFunction->OrginalFuncAddr[ucIndex] = ulOrginalAddr;
if (usCheckCode == usHardCode3) {
if (*(PUSHORT)(CheckAddr + 3) != usHardCode3 && *(PUSHORT)(CheckAddr + 3) != usHardCode4) {
bIsInEax = FALSE;
}
} else {
if (*(PUSHORT)(CheckAddr + 6) != usHardCode3 && *(PUSHORT)(CheckAddr + 6) != usHardCode4) {
bIsInEax = FALSE;
}
}
}
}
}
ExFreePoolWithTag((PVOID)StartAddr, 0x200);
return STATUS_SUCCESS;
}
另外还有一种方法我没实现,就是上面教主说的模拟执行待检测驱动的DriverEntry,这应该算是比较通用的Dispatch Hook检测方式,不过比较困难,个人认为可以设计一个轻量级的虚拟机处理mov lea add sub等简单指令,仅仅模拟一下DriverEntry,完毕后参数里的MajorFunction[]保存的就是原始地址。虽然通用但也是比较奇葩的方法,还是有针对性的