介绍:
前段时间写了SSDT Hook,没来得及写SSDT Hook 的ANTI策略,今天来写一下。
其实,ANTI SSDT Hook 也很简单,无非就是对比一下内存中加载的SSDT 表中的函数地址和原文件(ntkrnlpa.exe)中的SSDT表中函数地址是否一致即可。
(一)
⚪ 获取ntkrnlpa.exe模块的完整路径
⚪将文件读取到当前内存空间中
⚪将RVA转化为FOA,找到原文件读取到内存中的SSDT表位置。
⚪判断内存加载的SSDT表中的函数地址和原文件中的SSDT表中函数地址是否一致
⚪关闭写保护,覆盖FakeFunction函数地址。
⚪开启写保护,ANTI完成。
这些步骤当中其实还涉及到一些小细节去要介绍一下,比如说SSDT表中的存放的函数地址,都是Nt系列函数,那是如何找到的目标函数地址呢?这就要从Zw函数调用开始说起了,当你调用Zw函数的时候,Zw函数代码中会传入一个序列号,这个序列号就是Zw对应的Nt系列函数在SSDT表中的下标,所以我们在Zw函数中可以找到对应的序列号。
2: kd> u 0x8485acd8
nt!ZwOpenProcess:
8485acd8 b8 be000000 mov eax,0BEh
8485acdd 8d542404 lea edx,[esp+4]
8485ace1 9c pushfd
8485ace2 6a08 push 8
8485ace4 e8d5190000 call nt!KiSystemService (8485c6be)
8485ace9 c21000 ret 10h
nt!ZwOpenProcessToken:
8485acec b8bf000000 mov eax,0BFh
8485acf1 8d542404 lea edx,[esp+4]
注意红色部分!!!!
在我通过Windbg中找出ZwOpenProcess函数的反汇编的时候,有一个0BEh的序列号传入,这就是我们要找的东西。具体为什么是这样,可以看我之前些的Zw和Nt系列函数的区别。
(二)
#pragma once
#include<fltKernel.h>
#include"Common.h"
#include"SystemHelper.h"
#include<ntimage.h>
NTSTATUS SSDTHookANTI();
ULONG GetRawOffset(IN PVOID ImageBase, IN ULONG TableRVA, OUT PULONG FileImageBase);
VOID OnEnableWrite(); //关闭写保护
VOID OnDisableWrite(); //开启写保护
#include"SSDTHook-ANTI.h"
extern PVOID __KernelModuleBase ;
extern ULONG __KernelModuleSize;
extern UNICODE_STRING __KernelModulePath ;
extern PSYSTEM_SERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;
#define SSDT_INDEX(ZwFunctionAddress) * (PULONG)((PUCHAR)ZwFunctionAddress +1)
NTSTATUS SSDTHookANTI()
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
HANDLE FileHandle = NULL;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
FILE_STANDARD_INFORMATION FileStandardInfo;
char * BufferData = NULL;
ULONG RawOffset = 0;
UNICODE_STRING v1;
LARGE_INTEGER Offset;
ULONG FileImageBase = 0; //优先加载地址
ULONG SSDTRva = 0;
PUCHAR ZwOpenProcessAddress = NULL;
Status = MaGetKernelModuleInfo();
if (!NT_SUCCESS(Status))
{
return Status;
}
RtlInitUnicodeString(&v1, L"ZwOpenProcess");
ZwOpenProcessAddress = (PUCHAR)MmGetSystemRoutineAddress(&v1);
InitializeObjectAttributes(&ObjectAttributes, &__KernelModulePath, OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = ZwCreateFile(&FileHandle,
SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if (!NT_SUCCESS(Status))
{
return Status;
}
Status = ZwQueryInformationFile(FileHandle,
&IoStatusBlock,
&FileStandardInfo,
sizeof(FILE_STANDARD_INFORMATION),
FileStandardInformation);
if (!NT_SUCCESS(Status))
{
ZwClose(FileHandle);
return Status;
}
BufferData = ExAllocatePool(PagedPool, FileStandardInfo.EndOfFile.LowPart);
if (BufferData == NULL)
{
ZwClose(FileHandle);
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = ZwReadFile(FileHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
BufferData,
FileStandardInfo.EndOfFile.LowPart,
&Offset,
NULL);
if (!NT_SUCCESS(Status))
{
ExFreePool(BufferData);
ZwClose(FileHandle);
return STATUS_UNSUCCESSFUL;
}
//SSDT表在内存的偏移 内存粒度0x1000对齐的
SSDTRva = (ULONG_PTR)(KeServiceDescriptorTable->ServiceTableBase) - (ULONG)__KernelModuleBase;
//SSDT表在文件中的偏移 内存粒度0x200对齐的
RawOffset = GetRawOffset(BufferData, SSDTRva, &FileImageBase);
//判断是否HOOK
if (((PULONG_PTR)(KeServiceDescriptorTable->ServiceTableBase))[SSDT_INDEX(ZwOpenProcessAddress)] !=
(((PULONG_PTR)(RawOffset + (ULONG_PTR)BufferData))[SSDT_INDEX(ZwOpenProcessAddress)]
- (ULONG_PTR)FileImageBase + (ULONG_PTR)__KernelModuleBase))
{
OnEnableWrite();
((PULONG_PTR)(KeServiceDescriptorTable->ServiceTableBase))[SSDT_INDEX(ZwOpenProcessAddress)] =
((PULONG_PTR)(RawOffset+(ULONG_PTR)BufferData))[SSDT_INDEX(ZwOpenProcessAddress)] -
(ULONG_PTR)FileImageBase + (ULONG_PTR)__KernelModuleBase;
OnDisableWrite();
}
if (FileHandle != NULL)
{
ZwClose(FileHandle);
FileHandle = NULL;
}
if (BufferData != NULL)
{
ExFreePool(BufferData);
BufferData = NULL;
}
RtlFreeUnicodeString(&__KernelModulePath);
return Status;
}
ULONG GetRawOffset(IN PVOID ImageBase, IN ULONG TableRVA, OUT PULONG FileImageBase)
{
PIMAGE_DOS_HEADER ImageDosHeader = NULL;
PIMAGE_NT_HEADERS ImageNtHeader = NULL;
PIMAGE_FILE_HEADER ImageFileHeader = NULL;
PIMAGE_SECTION_HEADER ImageSectionHeader = NULL;
PIMAGE_OPTIONAL_HEADER ImageOptionHeader = NULL;
ULONG SectionNumber = 0;
ULONG RawOffset = 0;
ULONG i;
ImageDosHeader = (PIMAGE_DOS_HEADER)ImageBase;
ImageNtHeader = ImageDosHeader->e_lfanew + (ULONG_PTR)ImageDosHeader;
ImageFileHeader = &ImageNtHeader->FileHeader;
ImageOptionHeader = &ImageNtHeader->OptionalHeader;
ImageSectionHeader = (PIMAGE_SECTION_HEADER)((PUCHAR)ImageNtHeader+sizeof(ULONG)+sizeof(IMAGE_FILE_HEADER)+
ImageFileHeader->SizeOfOptionalHeader);
//预先加载的位置
*FileImageBase = ImageOptionHeader->ImageBase;
SectionNumber = ImageFileHeader->NumberOfSections;
for (i = 0;i < SectionNumber;i++, ImageSectionHeader++)
{
if (TableRVA > ImageSectionHeader->VirtualAddress &&
TableRVA < ImageSectionHeader->VirtualAddress + ImageSectionHeader->SizeOfRawData)
{
RawOffset = TableRVA - ImageSectionHeader->VirtualAddress + ImageSectionHeader->PointerToRawData;
//RawOffset = (ULONG)ImageBase + RawOffset;
return (ULONG)RawOffset;
}
}
return 0;
}
VOID OnEnableWrite()
{
__try
{
_asm
{
cli //禁止中断发生
mov eax,cr0
and eax,not 10000h //cr0寄存器中第17位 WP位
mov cr0,eax
}
}
__except (1)
{
}
}
VOID OnDisableWrite()
{
__try
{
_asm
{
mov eax, cr0
or eax, 10000h //cr0寄存器中第17位 WP位
mov cr0, eax
sti //恢复中断屏蔽
}
}
__except (1)
{
}
}