写这个插件其实主要是因为现在32位下各种内核钩子,用一些调试工具畏首畏尾的,一会挂钩这里,一会挂钩那里,遂干脆一了百了,直接写个内核重载插件,虽说是OD插件,也可以支持其他软件。此插件名字就叫ReloadKernel V1.0吧,贴切一点:
(支持2003,XP,WIN7)请避免和360,QQ管家,百度杀毒等一同使用,对于没有DebugPort清零的保护应该是可以秒的,比如HS(亲测),当然你要处理R3的反调试,对于有调试权限清零的自己恢复,端口清零的自己改下偏移也就OK了,不会改的话再不济用这个插件也可以读写内存吧!
,请别瞎想,本人写这个插件主要是为了学习交流,请大家不要用于非法用途,先来个图
首先内核重载插件我分成以下几个步骤完成
1, 获取当前系统下的内核文件
2, 按PE格式加载硬盘中的内核文件,修复重定位表,导入导出表
3, 挂钩KiFastCallEntry
4, 实现过滤框架
5, R3,R0通信
6, OD插件GUI与逻辑部分
1,因为在单核,多核处理器等不同情况下,微软会加载不同的内核文件,所以我们需要知道当前系统所加载的是哪一个内核文件,之后获取到它的全路径,并Copy到内存中,已便操作
下面这个函数是获取系统加载的内核模块基址,没什么好说的,原理就是ZwQuerySystemInformation的11号功能,查询内核模块
PVOID
GetKernelFileBase()
{
DWORD dwsize = 0;
PVOID pKernelBase = NULL;
PVOID pSysInfo = NULL;
__try
{
ZwQuerySystemInformation(SystemModuleInformation,NULL,0,&dwsize);
pSysInfo = ::ExAllocatePool(NonPagedPool,dwsize);
if(pSysInfo)
{
memset(pSysInfo,0,dwsize);
if(NT_SUCCESS(ZwQuerySystemInformation(SystemModuleInformation,pSysInfo,dwsize,&dwsize)))
{
PSYSTEM_MODULE_INFORMATION_ENTRY psmi = (PSYSTEM_MODULE_INFORMATION_ENTRY)((PBYTE)pSysInfo + 4);
pKernelBase = (PVOID)psmi->Base;
}
else
{
KdPrint(("GetKernelFileBase.ZwQuerySystemInformation error\n"));
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
KdPrint(("GetKernelFileBase异常\n"));
}
if(pSysInfo)
{
ExFreePool(pSysInfo);
pSysInfo = NULL;
}
return pKernelBase ;
}
还需要一个获取内核模块的全路径函数
bool
GetKernelFileFullPath(
OUT PUNICODE_STRING pUnicodeStr
)
{
bool bResult = FALSE;
DWORD dwsize = 0;
PVOID pSysInfo = NULL;
_try
{
ZwQuerySystemInformation(SystemModuleInformation,NULL,0,&dwsize);
pSysInfo = ::ExAllocatePool(NonPagedPool,dwsize);
if(pSysInfo)
{
memset(pSysInfo, 0, dwsize);
if(NT_SUCCESS(ZwQuerySystemInformation(SystemModuleInformation,pSysInfo,dwsize,&dwsize)))
{
PSYSTEM_MODULE_INFORMATION_ENTRY psmi = (PSYSTEM_MODULE_INFORMATION_ENTRY)((char*)pSysInfo + 4);
char *szKernelModuleName = psmi->ImageName + psmi->PathLength;
KdPrint(("模块 %s\n",szKernelModuleName));
char szKernelModuleFullPath[255];
ANSI_STRING asModuleName;
//"\\Device\\HarddiskVolume1\\Windows\\System32\\"
char szSystemPath[] = "\\??\\c:\\Windows\\System32\\";
strcpy(szKernelModuleFullPath,(char*)szSystemPath);
strcat(szKernelModuleFullPath,szKernelModuleName);
::RtlInitAnsiString(&asModuleName,szKernelModuleFullPath);
::RtlAnsiStringToUnicodeString(pUnicodeStr,&asModuleName,TRUE);
bResult = TRUE;
}
else
{
KdPrint(("GetKernelFileFullPath.ZwQuerySystemInformation error"));
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
KdPrint(("GetKernelFileFullPath.异常"));
}
if(pSysInfo)
{
ExFreePool(pSysInfo);
pSysInfo = NULL;
}
return bResult;
}
起先我是使用“\\Device\\HarddiskVolume1\\Windows\\System32\\” 来代替系统目录的,后来经过一些测试,发现在多系统环境下,系统盘符不一定就是HarddiskVolume1了,所以干脆直接就改成"\\??\\c:\\Windows\\System32\\";如果谁有特殊癖好,你就自己改好了
2,内核重载,重头戏就是这个重载,首先就是复制一下内核文件到内存,之后在内存中将它展开,处理导入导出函数,处理重定位,在重定位时我将所有的内核数据都重定位到了原内核中,这样就省得自己再初始化内核文件了,并且也会避免很多冲突,因为有些数据只能存在一份,当然这样做的坏处就是绕不过Object Hook,系统回调了,但是这种小CASE,枚举一下就干掉了,这不在本文讨论范围内,如果有需要我也可以再发个主题关于怎么处理Object Hook。
首先就是把文件弄到内存里,函数如下
PVOID
CreateImageFromFile(
IN PUNICODE_STRING ImageFile)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
HANDLE FileHandle = NULL;
OBJECT_ATTRIBUTES ObjAttributes = {0};
IO_STATUS_BLOCK IoStatusBlock = {0};
InitializeObjectAttributes(
&ObjAttributes,
ImageFile,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = ZwCreateFile(
&FileHandle,
GENERIC_READ,
&ObjAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if(NT_SUCCESS(Status))
{
FILE_STANDARD_INFORMATION fsi = {0};
IO_STATUS_BLOCK iostatus = {0};
Status = ZwQueryInformationFile(
FileHandle,
&iostatus,
&fsi,
sizeof(FILE_STANDARD_INFORMATION),
FileStandardInformation);
if(NT_SUCCESS(Status))
{
PVOID pBuffer = ExAllocatePool(PagedPool,(LONG)fsi.EndOfFile.QuadPart);
if(pBuffer)
{
memset(pBuffer,0,(LONG)fsi.EndOfFile.QuadPart);
Status = ZwReadFile(FileHandle, NULL, NULL, NULL,&iostatus, pBuffer, (LONG)fsi.EndOfFile.QuadPart, NULL, NULL);
if(NT_SUCCESS(Status))
{
KdPrint(("Read %x bytes\n", iostatus.Information));
ZwClose(Fi