0x00 前言
参考文章:
https://blog.csdn.net/whatday/article/details/14160875
https://blog.csdn.net/pjz969/article/details/8615085
首先重载内核不是什么新技术,对于绕过HOOK是一种一刀切的做法,不过对于我这样初探内核的小白还是很有总结意义的。
本篇涉及的知识点:0环和3环通信,PE结构(除了基础的头外还有重定位表),用到了两种HOOK(SSDT HOOK,Inline HOOK),API函数调用(逆了一个函数KiFastCallEntry)。
流程:
1)按照字节对齐把文件中的PE结构在内存里展开
2)利用重定位表修改全局变量
3)COPY自己的SSDT表
4)函数调用时换成新的内核(HOOK KiFastCallEntry)
0x01 展开PE结构
因为我的环境是win7 32位,所以要把ntkrnlpa.exe文件在内存里展开.
1. 首先,在内核里使用RtlInitUnicodeString创建对象,打开文件使用ZwCreateFile,并且要初始化InitializeObjectAttributes。
2. 通过PE结构的知识,DOS头,PE头,节表,是紧挨在一起的无需扩展,只需确定在文件中的偏移和大小利用ZwReadFile即可获取:
1)DOS头的偏移就是0,大小为sizeof(IMAGE_DOS_HEADER);
2)PE头的偏移由DOS最后一个成员确定ImageDosHeader.e_lfanew,大小为sizeof(IMAGE_NT_HEADER);
3)节表的偏移为+= sizeof(IMAGE_NT_HEADERS),因为节表里HEADER的数量不确定,所以需要到
IMAGE_NT_HEADER->IMAGE_FILE_HEADER.NumberOfSection确定节的数目,所以大小为 sizeof(IMAGE_SECTION_HEADER) * 前者。
4)每个节的偏移由
IMAGE_SCTION_HEADER->SectionHeader[i].VirTualAddress(相对于文件开头),
节的大小需要判断:比较SectionHeader[i].Misc.VirTualSize(实际的大小)和SectionHeader[i].SizeOfRawData(按字节对齐 时文件中的大小),因为全局变量的关系,所以两者大小不一定,取大的那个。
3. 第三,就是用RtlCopyMemory一段一段COPY DOS头,PE头,节表,和每个节。
void LoadKernel()
{
NTSTATUS status;
UNICODE_STRING uFileName;
HANDLE hFile;
OBJECT_ATTRIBUTES ObjAttr;
IO_STATUS_BLOCK IoStatusBlock;
LARGE_INTEGER FileOffset;
ULONG retsize;
PVOID lpVirtualPointer;
ULONG uLoop;
ULONG SectionVirtualAddress, SectionSize;
IMAGE_DOS_HEADER ImageDosHeader;
IMAGE_NT_HEADERS ImageNtHeader;
IMAGE_SECTION_HEADER* lpImageSectionHeader;
InitializeObjectAttributes(&ObjAttr, \
& uFileName, \
OBJ_CASE_INSENSITIVE, \
NULL, \
NULL);
RtlInitUnicodeString(&uFileName, L"\\??\\C:\\WINDOWS\\system32\\ntkrnlpa.exe");
//打开文件
status = ZwCreateFile(\
& hFile, \
FILE_ALL_ACCESS, \
& ObjAttr, \
& IoStatusBlock, \
0, \
FILE_ATTRIBUTE_NORMAL, \
FILE_SHARE_READ, \
FILE_OPEN, \
FILE_NON_DIRECTORY_FILE, \
NULL, \
0);
if (!NT_SUCCESS(status))
{
KdPrint(("CreateFile Failed!"));
return;
}
//读取DOS头
FileOffset.QuadPart = 0;
status = ZwReadFile(hFile, \
NULL, \
NULL, \
NULL, \
& IoStatusBlock, \
& ImageDosHeader, \
sizeof(IMAGE_DOS_HEADER), \
& FileOffset, \
0);
if (!NT_SUCCESS(status))
{
KdPrint(("Read ImageDosHeader Failed!"));
ZwClose(hFile);
return;
}
//读取NT头
FileOffset.QuadPart = ImageDosHeader.e_lfanew;
status = ZwReadFile(hFile, \
NULL, \
NULL, \
NULL, \
& IoStatusBlock, \
& ImageNtHeader, \
sizeof(IMAGE_NT_HEADERS), \
& FileOffset, \
0);
if (!NT_SUCCESS(status))
{
KdPrint(("Read ImageNtHeaders Failed!"));
ZwClose(hFile);
return;
}
//读取节表
lpImageSectionHeader = (IMAGE_SECTION_HEADER*)ExAllocatePool(NonPagedPool, \
sizeof(IMAGE_SECTION_HEADER) * ImageNtHeader.FileHeader.NumberOfSections);
FileOffset.QuadPart += sizeof(IMAGE_NT_HEADERS);
status = ZwReadFile(hFile, \
NULL, \
NULL, \
NULL, \
& IoStatusBlock, \
lpImageSectionHeader, \
sizeof(IMAGE_SECTION_HEADER) * ImageNtHeader.FileHeader.NumberOfSections, \
& FileOffset, \
0);
if (!NT_SUCCESS(status))
{
KdPrint(("Read ImageSectionHeader Failed!"));
ExFreePool(lpImageSectionHeader);
ZwClose(hFile);
return;
}
//COPY数据到内存
lpVirtualPointer = ExAllocatePool(NonPagedPool, \
ImageNtHeader.OptionalHeader.SizeOfImage);
if (lpVirtualPointer == 0)
{
KdPrint(("lpVirtualPointer Alloc space Failed!"));
ZwClose(hFile);
return;
}
memset(lpVirtualPointer, 0, ImageNtHeader.OptionalHeader.SizeOfImage);
//COPY DOS头
RtlCopyMemory(lpVirtualPointer, \
& ImageDosHeader, \
sizeof(IMAGE_DOS_HEADER));
//COPY NT头
RtlCopyMemory((PVOID)((ULONG)lpVirtualPointer + ImageDosHeader.e_lfanew), \
& ImageNtHeader, \
sizeof(IMAGE_NT_HEADERS));
//COPY 区表
RtlCopyMemory((PVOID)((ULONG)lpVirtualPointer + ImageDosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS)), \
lpImageSectionHeader, \
sizeof(IMAGE_SECTION_HEADER) * ImageNtHeader.FileHeader.NumberOfSections);
//依次COPY 各区段数据
for (uLoop = 0;uLoop < ImageNtHeader.FileHeader.NumberOfSections;uLoop++)
{
SectionVirtualAddress = lpImageSectionHeader[uLoop].VirtualAddress;//对应区段相对偏移
if (lpImageSectionHeader[uLoop].Misc.VirtualSize > lpImageSectionHeader[uLoop].SizeOfRawData)
SectionSize = lpImageSectionHeader[uLoop].Misc.VirtualSize;//取最大的占用空间
else
SectionSize = lpImageSectionHeader[uLoop].SizeOfRawData;
FileOffset.QuadPart = lpImageSectionHeader[uLoop].PointerToRawData;//对应区段的超始地址
status