看雪论坛
发新帖
1

[系统底层] [原创]OD插件之重载内核(高清有码)

wgejydy 2015-5-29 21:56 28380
写这个插件其实主要是因为现在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(FileHandle);

					return pBuffer;
				}
				else
				{
					KdPrint(("CreateImageFromFile.读取文件失败 %x",Status));
					return 0;
				}
			}
			else
			{
				KdPrint(("CreateImageFromFile.申请非分页内存失败"));
			}
			
		}
		else
		{
			KdPrint(("CreateImageFromFile.获取文件尺寸失败"));
			return 0;
		}


	}
	else
	{
		KdPrint(("CreateImageFromFile.打开文件失败"));
		return 0;
	}

	return 0;
}

    先查询一下文件大小,之后申请那么大的内存,读进来,没什么好说的,之后就是重头戏了,通过内核中未展开的PE文件结构在内存中创建一个展开的PE结构,好绕口...
ULONG 
CreatePeStructFromMem(
					  IN ULONG ulFileAddress,
					  IN ULONG ntKernelAddress)
{

	__try
	{
		PIMAGE_DOS_HEADER pIMAGE_DOS_HEADER =  (PIMAGE_DOS_HEADER)ulFileAddress;
		PIMAGE_NT_HEADERS	pIMAGE_NT_HEADERS = (PIMAGE_NT_HEADERS)(ulFileAddress + pIMAGE_DOS_HEADER->e_lfanew);
		PIMAGE_FILE_HEADER	pIMAGE_FILE_HEADER = &pIMAGE_NT_HEADERS->FileHeader;
		PIMAGE_OPTIONAL_HEADER32	pIMAGE_OPTIONAL_HEADER = &pIMAGE_NT_HEADERS->OptionalHeader;
		KdPrint(("DOS 头 %x \n",pIMAGE_DOS_HEADER));
		KdPrint(("NT 头 %x \n",pIMAGE_NT_HEADERS));
		KdPrint(("文件 头 %x \n",pIMAGE_FILE_HEADER));
		KdPrint(("可选 头 %x \n",pIMAGE_OPTIONAL_HEADER));


		if(!pIMAGE_NT_HEADERS || !pIMAGE_FILE_HEADER || !pIMAGE_OPTIONAL_HEADER) return 0;


		ULONG pMy_ImageBase = (ULONG)ExAllocatePool(NonPagedPool,pIMAGE_OPTIONAL_HEADER->SizeOfImage); 

		if(pMy_ImageBase)
		{
		
			memset((PVOID)pMy_ImageBase,0,pIMAGE_OPTIONAL_HEADER->SizeOfImage);

			//复制头加段表
			RtlCopyMemory((PVOID)pMy_ImageBase,(PVOID)ulFileAddress,pIMAGE_OPTIONAL_HEADER->SizeOfHeaders );

			//节表展开
			PIMAGE_SECTION_HEADER pSectionHeadTmp = (PIMAGE_SECTION_HEADER)((ULONG)pIMAGE_OPTIONAL_HEADER + sizeof(IMAGE_OPTIONAL_HEADER32));
			for(int i = 0;i < pIMAGE_NT_HEADERS->FileHeader.NumberOfSections;i ++)
			{

				RtlCopyMemory((PVOID)(pSectionHeadTmp->VirtualAddress + (ULONG)pMy_ImageBase),//计算VA
					(PVOID)(pSectionHeadTmp->PointerToRawData + (ULONG)ulFileAddress),//文件偏移
					pSectionHeadTmp->SizeOfRawData);//计算尺寸

				pSectionHeadTmp ++;
			}

			//导入表处理


			for(
				PIMAGE_IMPORT_DESCRIPTOR pImportDescriptorTmp = (PIMAGE_IMPORT_DESCRIPTOR)(pIMAGE_OPTIONAL_HEADER->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + (ULONG)pMy_ImageBase);
				!IsStructEmpty<IMAGE_IMPORT_DESCRIPTOR>(pImportDescriptorTmp);
			pImportDescriptorTmp++
				)
			{
				KdPrint(("导入模块 %s\n",(char*)(pImportDescriptorTmp->Name + (ULONG)pMy_ImageBase)));

				PVOID pModule = KernelGetModuleBase(((char*)(pImportDescriptorTmp->Name + (ULONG)pMy_ImageBase)));

				if(pModule) 
				{
					for(
						PIMAGE_THUNK_DATA pThunkData = (PIMAGE_THUNK_DATA)(pImportDescriptorTmp->FirstThunk + (ULONG)pMy_ImageBase); 
						!IsStructEmpty<IMAGE_THUNK_DATA>(pThunkData); 
					pThunkData++
						)
					{

						PVOID pFunctionTmp = 
							pThunkData->u1.AddressOfData & IMAGE_ORDINAL_FLAG32 ?

							KernelGetProcAddress(pModule,(PCHAR)(pThunkData->u1.AddressOfData & 0xffff))
							:
						KernelGetProcAddress(pModule,(PCHAR)((PIMAGE_IMPORT_BY_NAME)(pThunkData->u1.AddressOfData + (ULONG)pMy_ImageBase))->Name);


						if(!pFunctionTmp)
						{
							if(pThunkData->u1.AddressOfData & IMAGE_ORDINAL_FLAG32)
							{
								KdPrint(("有函数没找到,序号 %d",(PCHAR)(pThunkData->u1.AddressOfData & 0xffff)));
							}

							else
							{
								KdPrint(("有函数没找到,名字%s",(PCHAR)((PIMAGE_IMPORT_BY_NAME)(pThunkData->u1.AddressOfData + (ULONG)pMy_ImageBase))->Name));
							}
						}
						else
						{
							if(pThunkData->u1.AddressOfData & IMAGE_ORDINAL_FLAG32)
							{
								KdPrint(("已经修复函数,序号 %d",(PCHAR)(pThunkData->u1.AddressOfData & 0xffff)));
							}

							else
							{
								KdPrint(("已经修复函数,名字%s",(PCHAR)((PIMAGE_IMPORT_BY_NAME)(pThunkData->u1.AddressOfData + (ULONG)pMy_ImageBase))->Name));
							}

							pThunkData->u1.Function  = (DWORD)pFunctionTmp;
						}


					}
				}
				else
				{
					KdPrint(("获取模块 %s失败\n",(char*)(pImportDescriptorTmp->Name + (ULONG)pMy_ImageBase)));
				}

			}

			int nRelocNum = 0;

			ULONG ulR = ntKernelAddress - pIMAGE_OPTIONAL_HEADER->ImageBase;

			KdPrint(("重定位偏移 ulR =  %x",ulR));
			for(
				PIMAGE_BASE_RELOCATION pLoc = (PIMAGE_BASE_RELOCATION)(pIMAGE_OPTIONAL_HEADER->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + (ULONG)pMy_ImageBase);
				!IsStructEmpty<IMAGE_BASE_RELOCATION>(pLoc); 
			pLoc = (PIMAGE_BASE_RELOCATION)((DWORD)pLoc + pLoc->SizeOfBlock)
				)
			{
				nRelocNum ++;
				for(PWORD pHandleAddress = (PWORD)((DWORD)pLoc + sizeof(IMAGE_BASE_RELOCATION));pHandleAddress < (PWORD)((DWORD)pLoc + pLoc->SizeOfBlock);pHandleAddress ++)
				{
					if((*pHandleAddress & 0xf000) == IMAGE_REL_BASED_HIGHLOW * 0X1000)//x86重定位标记
					{

						*(PDWORD((*pHandleAddress & 0xfff) + pLoc->VirtualAddress +  (ULONG)pMy_ImageBase)) += ulR;




					}
				}

			}

			KdPrint(("重定位块 =  %d个",nRelocNum));

			return pMy_ImageBase;

		}
		else
		{
				KdPrint(("CreatePeStructFromMem.申请内存失败"));
		}
	

	}

	__except(EXCEPTION_EXECUTE_HANDLER)
	{
		KdPrint(("CreatePeStructFromMem.执行异常"));
	}
	return 0;
}


首先是把节展开,展开之后定位所有的导入函数,有时候导入模块是没加载到内核的,这种情况并没有考虑,因为用处不大,你可以先激活那个模块的功能,之后在打开插件即可(比如串口通信)分别处理按名字导入的和按序号导入两种情况,导出表就不用处理了,为啥就不用我说了吧,之后处理重定位表,注意这里
       
int nRelocNum = 0;

			ULONG ulR = ntKernelAddress - pIMAGE_OPTIONAL_HEADER->ImageBase;

			KdPrint(("重定位偏移 ulR =  %x",ulR));
			for(
				PIMAGE_BASE_RELOCATION pLoc = (PIMAGE_BASE_RELOCATION)(pIMAGE_OPTIONAL_HEADER->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + (ULONG)pMy_ImageBase);
				!IsStructEmpty<IMAGE_BASE_RELOCATION>(pLoc); 
			pLoc = (PIMAGE_BASE_RELOCATION)((DWORD)pLoc + pLoc->SizeOfBlock)
				)
			{
				nRelocNum ++;
				for(PWORD pHandleAddress = (PWORD)((DWORD)pLoc + sizeof(IMAGE_BASE_RELOCATION));pHandleAddress < (PWORD)((DWORD)pLoc + pLoc->SizeOfBlock);pHandleAddress ++)
				{
					if((*pHandleAddress & 0xf000) == IMAGE_REL_BASED_HIGHLOW * 0X1000)//x86重定位标记
					{

						*(PDWORD((*pHandleAddress & 0xfff) + pLoc->VirtualAddress +  (ULONG)pMy_ImageBase)) += ulR;


					}
				}

			}

把所有需要重定位的数据都重定向到了原内核,这样可以避免一些数据上的麻烦,不初始化就用会蓝屏。上面用了个小模块,因为PE结构太多,我没有通过PE信息中的一些个数信息来判断结构体的数量,而是通过判断是否以0结尾,感觉这样靠谱一点。所以写了个如下的小模板,判断各种结构是否为空结构
template <class T>
bool IsStructEmpty(
				   IN T *Mystruct)
{
	bool bIsEmpty = true;
	__try
	{
		for(PBYTE p = (PBYTE)Mystruct;p < (PBYTE)((DWORD)Mystruct + sizeof(T)) ;p++)
		{
			if((*p) != 0 )
			{
				bIsEmpty = false;
				break;
			}
		}
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{

		KdPrint(("IsStructEmpty Exception"));
	}
	return bIsEmpty;
}


3,这里就准备开始挂钩了,挂钩之前首先要获取KifastCallEntry地址,还是老办法,你们觉得土可以用360的方法。
ULONG GetAddressOfKiFastCallEntry()
{
	ULONG dwAddress = 0;
	BYTE vgdtr[8] = {0};
	__asm
	{
	
		push eax
			push ebx
			push ecx
			push edx
			mov ecx, 0x174
			rdmsr
			mov ebx, eax		//Selector offset

			sgdt vgdtr
			//	mov edx, vgdtr
			lea edx,vgdtr
			add edx, 0x02
			mov eax, [edx]		//GDT base
		add ebx, eax		//Selector base

			mov edx, ebx
			add edx, 0x07
			mov eax, [edx]
		shl eax, 24;
		mov edx, ebx
			add edx, 0x02
			mov ecx, [edx]
		and ecx, 0x00FFFFFF
			add eax, ecx		//Address CodeSegment
			mov ebx, eax

			mov ecx, 0x176
			rdmsr
			add eax, ebx

			mov dwAddress, eax

			pop edx
			pop ecx
			pop ebx
			pop eax
	}

	return dwAddress;
}

获取之后就要找个好地方挂钩子了,最后经过分析找了一个这样的位置,既有表的地址,也有函数的索引号,(这个地址貌似百度,QQ,360都在用)因为我们只处理SSDT的,SHADOW的就不管了,如图


下面是跳转函数的实现,因为我们要支持2003,XP,WIN7,所以就要分别应对,但是幸运的是发现2003和XP的特别像,就写了2个,省了点事。首先获取系统版本,分别处理
enum SystemEnum
{
	k_WinXp = 0,
	k_Win2003,
	k_Win7,
	k_UnKonwn
}systemEnum;
SystemEnum GetSystemInfo()
{
	ULONG majorVersion, minorVersion;
	PsGetVersion(&majorVersion, &minorVersion, NULL, NULL);
	if (majorVersion == 5 && minorVersion == 2)
	{
		
		return k_Win2003;

	}
	else if (majorVersion == 5 && minorVersion == 1)
	{

		return k_WinXp;
	}
	else if (majorVersion == 6 && minorVersion == 1)
	{
		
		return k_Win7;
	}

	return k_UnKonwn;
}

获取挂钩的位置,函数如下
ULONG GetAddressOfKiFastCallEntry_Hook(ULONG KiFastCallEntry)
{
	__try
	{
		for(ULONG i = KiFastCallEntry;i < KiFastCallEntry + 0x200;i++)
		{
			if(*(PBYTE)i == 0x2b)
			{
				if(*(PBYTE)(i+1) == 0xe1)
				{
					if(*(PBYTE)(i+2) == 0xc1)
					{
						if(*(PBYTE)(i+3) == 0xe9)
						{
							if(*(PBYTE)(i+4) == 0x02)
							{
								return i;
							}
						}
					}
				}
			}
		}
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
		KdPrint(("GetAddressOfKiFastCallEntry_Hook 发生错误"));
	}	
	return 0;
}

该获取的都获取到了,下面就要开始HOOK了,没有用那种开关中断的方式,太氧化铜(CuO)了,用了个以前写的小函数专门HOOK的。
enum HookEnum
{
        jmpHook = 0,
        pushretHook,
        callHook
}hookEnum;

首先就是判断挂钩地址是否有效,下个断言必须运行在低于DISPATCH_LEVEL级别,以免缺页中断蓝了,之后先读一下内存地址,缺页的赶紧到内存中来,(KiFastCallEntry这种高频函数肯定是在内存中的,但是为了良好HOOK习惯)之后在MmIsAddressValid一下判断内存地址到底有没有效,完毕之后用MDL描述一下要挂钩的地址,重新搞一份可以写的,之后提升级别防止被切换,当然多核下这样还是不安全的,不过毕竟我们这属于小产品不用考虑那么多,(其实腾讯,百度都没考虑,记忆犹新,打把LOL直接给我蓝屏)HOOK完收工
BOOL Hook(ULONG_PTR ulHookAddress,ULONG_PTR ulDispatchAddress,int nLength,HookEnum hookKind)
{


	KIRQL          oldIrql = 0;
	PMDL               mdl = NULL;
	PVOID pWritableAddress = NULL;
	ULONG    ulTestForPage = 0;

	//确定HOOK函数运行级别小于DISPATCH_LEVEL,以免缺页
	ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);

	_try
	{
		_asm
		{
			mov eax,ulHookAddress
			mov eax,dword ptr[eax]
			mov ulTestForPage,eax

			mov eax,ulDispatchAddress
			mov eax,dword ptr[eax]
			mov ulTestForPage,eax
		}

	}
	_except(EXCEPTION_EXECUTE_HANDLER)
	{
		return false;
	}

	if(!MmIsAddressValid((PVOID)ulHookAddress)) 
	{
		return false;
	}
	if(nLength > 20) return false;

	mdl = IoAllocateMdl((PVOID)ulHookAddress, nLength, false, false, NULL);
	if(mdl != NULL)
	{
		MmBuildMdlForNonPagedPool(mdl);

		_try
		{
			MmProbeAndLockPages(mdl,KernelMode,IoWriteAccess);
		}
		_except(EXCEPTION_EXECUTE_HANDLER)
		{
			IoFreeMdl(mdl);
			return false;
		}


		pWritableAddress = MmMapLockedPages(mdl, KernelMode);
		if (pWritableAddress != NULL) 
		{
			BYTE bCode[20] = {0x90}; 
			oldIrql = KeRaiseIrqlToDpcLevel();

			__try
			{
				switch(hookKind)
				{
				case jmpHook:
					{
						
						*(BYTE *)bCode = 0xe9;
						*(ULONG *)(bCode + 1) = ulDispatchAddress - ulHookAddress - 5;
						RtlCopyMemory(pWritableAddress, bCode, nLength);
					}break;
				case pushretHook:
					{
						*(BYTE *)bCode = 0x68;
						*(ULONG *)(bCode + 1) = ulDispatchAddress;
						*(BYTE *)(bCode + 5) = 0xc3;
						RtlCopyMemory(pWritableAddress, bCode, nLength);

						
					}break;
				case callHook:
					{
						*(BYTE *)bCode = 0xe8;
						*(ULONG *)(bCode + 1) = ulDispatchAddress - ulHookAddress-5;
						RtlCopyMemory(pWritableAddress, bCode, nLength);
						
					}break;

				}
			}

			__except(EXCEPTION_EXECUTE_HANDLER)
			{
				KdPrint(("Hook 发生错误"));
			}	

			KeLowerIrql(oldIrql);

			MmUnmapLockedPages(pWritableAddress, mdl);
		}
		MmUnlockPages(mdl);
		IoFreeMdl(mdl);
	}

	return true;
}

4,过滤框架也没设计的多复杂,也不是啥大项目,判断一下要保护的PID,是的话就走新内核,不是的话就走老内核。默认可以保护5个PID,不够就自己加,我设定5个,第一个是给OD的,其余4个自定义,下面就是2003,XP,WIN7的过滤函数,先保存寄存器和标志位,之后开辟私有栈给自己的局部变量用个,当然用完就恢复成原来的,函数中判断一下函数索引号,大过系统默认的索引就不处理了,之后判断是否SSDT表,是的话判断当前进程环境,是要保护的PID就走新内核,华丽的一个push ret,这个全局变量在驱动初始化函数中进行初始化。
__declspec(naked)void Xp_2003_KiFastCallEntry_Dispatch()
{

	DWORD  dwCurPid;
	DWORD dwCurServiceTableBase;
	DWORD dwIndex;
	bool bProtect ;
	int i ;
	_asm {
			push     ebp
			mov      ebp, esp
			sub      esp, __LOCAL_SIZE
			pushfd
			pushad
			mov dwCurServiceTableBase, edi
			mov dwIndex, eax
	}
	if (dwIndex < KeServiceDescriptorTable->NumberOfServices)
	{
		if (dwCurServiceTableBase == g_dwServiceTableBase)
		{
			dwCurPid = (DWORD)PsGetCurrentProcessId();
			bProtect = FALSE;
			for ( i = 0; i < 5; i++)
			{
				if (g_dwProtectPid[i] == dwCurPid)
				{
					bProtect = TRUE;
					break;
				}
			}
			if (bProtect)
			{
				_asm
				{
					popad
						popfd
						mov      esp, ebp
						pop      ebp
						mov edi, g_pMyServiceTable
						mov ebx, dword ptr[edi + eax * 4]
					sub esp, ecx
						shr ecx, 2
						push g_ulKiFastCallEntryRetAddr
						ret
				}
			}
		}
	}

	_asm
	{
		popad
			popfd
			mov      esp, ebp
			pop      ebp
			sub esp, ecx
			shr ecx, 2
			push g_ulKiFastCallEntryRetAddr
			ret
	}


}
__declspec(naked)void Win7_KiFastCallEntry_Dispatch()
{

	DWORD  dwCurPid;
	DWORD dwCurServiceTableBase;
	DWORD dwIndex;
	bool bProtect ;
	int i;
	_asm {
		push     ebp
			mov      ebp, esp
			sub      esp, __LOCAL_SIZE
			pushfd
			pushad
			mov dwCurServiceTableBase, edi
			mov dwIndex, eax
	}
	if (dwIndex < KeServiceDescriptorTable->NumberOfServices)
	{
		if (dwCurServiceTableBase == g_dwServiceTableBase)
		{
			dwCurPid = (DWORD)PsGetCurrentProcessId();
			bProtect = FALSE;
			for ( i = 0; i < 5; i++)
			{
				if (g_dwProtectPid[i] == dwCurPid)
				{
					bProtect = TRUE;
					break;
				}
			}
			if (bProtect)
			{
				KdPrint(("保护中 %d", dwCurPid));
				_asm
				{
					popad
						popfd
						mov      esp, ebp
						pop      ebp
						mov edi, g_pMyServiceTable
						mov edx, dword ptr[edi + eax * 4]
					sub esp, ecx
						shr ecx, 2
						push g_ulKiFastCallEntryRetAddr
						ret
				}
			}
		}
	}

	_asm
	{
		popad
			popfd
			mov      esp, ebp
			pop      ebp
			sub esp, ecx
			shr ecx, 2
			push g_ulKiFastCallEntryRetAddr
			ret
	}


}

5,这里就是通信了,主要是OD发给驱动我们保护谁谁谁,简单的一个
#define Protect_Code CTL_CODE(FILE_DEVICE_UNKNOWN,         0x801,         METHOD_IN_DIRECT,FILE_ANY_ACCESS)控制码
响应函数如下
NTSTATUS RELOADPROTECT_DispatchDeviceControl(
	IN PDEVICE_OBJECT		DeviceObject,
	IN PIRP					Irp
	)
{
	NTSTATUS status = STATUS_SUCCESS;
	PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
	int nInput = irpSp->Parameters.DeviceIoControl.InputBufferLength;;
	switch (irpSp->Parameters.DeviceIoControl.IoControlCode)
	{
	case Protect_Code:
		{
			ProtectInfo *protectInfo = (ProtectInfo *)Irp->AssociatedIrp.SystemBuffer;
			KdPrint(("[%d]:要保护的进程Pid = %d", protectInfo->nIndex, protectInfo->dwPid));
			if (protectInfo->nIndex < 5)
			{
				g_dwProtectPid[protectInfo->nIndex] = protectInfo->dwPid;
			}
			Irp->IoStatus.Status = STATUS_SUCCESS;
			Irp->IoStatus.Information = 0;

		}break;

	default:
		{
			Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
			Irp->IoStatus.Information = 0;
		}
		break;
	}

	status = Irp->IoStatus.Status;
	IoCompleteRequest(Irp, IO_NO_INCREMENT);
	return status;
}

驱动入口函数中就开始调用上面的一些代码了,重载完内核文件之后,获取到新内核中的SSDT表地址,之后根据系统版本开始挂钩
	__try
		{
			systemEnum = GetSystemInfo();
			KdPrint(("当前系统为 %d\n", systemEnum));
			ULONG ulNtBaseAddress = (ULONG)GetKernelFileBase();//获得Ntoskernel.exe在内存中的基址

			if(ulNtBaseAddress)
			{
				KdPrint(("内核地址 %x\n", ulNtBaseAddress));

				UNICODE_STRING    usModuleName;
				if(GetKernelFileFullPath(&usModuleName))//获取内核文件全路径
				{
					//内核文件读入内存
					PVOID pImageFile = CreateImageFromFile(&usModuleName);

					if(pImageFile)
					{
						//创建PE结构
						ULONG ulImagePe = CreatePeStructFromMem((ULONG)pImageFile,ulNtBaseAddress);

						if(ulImagePe)
						{
							g_dwOffset = ulImagePe - ulNtBaseAddress;

							g_dwServiceTableBase = *(PDWORD)KeServiceDescriptorTable;

							g_dwMyServiceTableBase = g_dwServiceTableBase - ulNtBaseAddress + ulImagePe;
							KdPrint(("偏移为 %x\n",g_dwOffset ));

							KdPrint(("内核重载的地址为 %x\n",ulImagePe));

							KdPrint(("KiFastCallEntry 地址为 %x\n",GetAddressOfKiFastCallEntry()));

							g_ulKiFastCallEntryRetAddr = GetAddressOfKiFastCallEntry_Hook(GetAddressOfKiFastCallEntry()) + 5;

							int nNumberOfServices = KeServiceDescriptorTable->NumberOfServices;

							g_pMyServiceTable = ExAllocatePool(NonPagedPool, nNumberOfServices * 4);

							for (int i = 0; i < nNumberOfServices; i++)
							{

								DWORD dwOrgFuncAddress = *(PDWORD)(g_dwMyServiceTableBase + i * 4);
								*(PDWORD)((ULONG)g_pMyServiceTable + i * 4) = dwOrgFuncAddress - ulNtBaseAddress + ulImagePe;
								KdPrint(("[%d] 地址为 %x 新函数地址%x\n", i, (ULONG)g_pMyServiceTable + i * 4, dwOrgFuncAddress - ulNtBaseAddress + ulImagePe));
							}

							KdPrint(("g_pMyServiceTable 地址为 %x\n", g_pMyServiceTable));

							switch (systemEnum)
							{
							case k_WinXp:
								Hook(g_ulKiFastCallEntryRetAddr - 5, (ULONG)Xp_2003_KiFastCallEntry_Dispatch,5, jmpHook);
								break;
							case k_Win2003:
								Hook(g_ulKiFastCallEntryRetAddr - 5, (ULONG)Xp_2003_KiFastCallEntry_Dispatch,5, jmpHook);
								break;
							case k_Win7:
								Hook(g_ulKiFastCallEntryRetAddr - 5, (ULONG)Win7_KiFastCallEntry_Dispatch,5, jmpHook);
								break;
							default:
								DbgPrint("未知系统\n");
								break;
							}
						}
						else
						{
							KdPrint(("创建PE文件失败\n"));
						}
						ExFreePool(pImageFile);

					}
					else
					{
						KdPrint(("创建内存文件失败\n"));
					}


				}
				else
				{
					KdPrint(("获取内核模块全路径失败\n"));
				}
			}
			else
			{
				KdPrint(("获取内核模块地址失败\n"));
			}

		}
		__except(EXCEPTION_EXECUTE_HANDLER)
		{
			KdPrint(("DriverEntry异常\n"));
		}	

6,这部分就跟驱动没啥关系了,一些R3下GUI的逻辑部分,首先有5个PID,状态为2种,保护和未保护状态,第一个按钮是保护OD自身进程,另外扩展4个可用于指定PID。如图


直接输入要保护的进程的PID即可
R3过程主要是先打开设备,如果打开了证明已经加载过了,直接通信,如果没加载过,就生成个随机名字0-9,之后加载(请原谅没卸载驱动,感觉没必要)
       
hDevice = CreateFile("\\\\.\\RELOADPROTECT_DeviceName",  
		GENERIC_READ | GENERIC_WRITE,  
		0,  
		NULL,  
		CREATE_ALWAYS,  
		FILE_ATTRIBUTE_NORMAL,  
		NULL);  
	if (hDevice == INVALID_HANDLE_VALUE)  
	{  

		srand((unsigned)timeGetTime());

		int RanCheckNum = rand()  % 10 ;	
		wsprintf(szDriverName,"%d.sys",RanCheckNum);

		char szPath[256] = {0};
		GetCurrentDirectory(256,szPath);


		char szFullPath[256] = {0};
		ReleaseRes(szDriverName,IDR_SYS1,"SYS");

		wsprintf(szFullPath,"%s\\%s",szPath,szDriverName);

		if (LoadNTDriver(szDriverName,szFullPath))
		{

			DeleteFile(szDriverName);
		}
		else
		{
			AfxMessageBox("加载失败");
			DeleteFile(szDriverName);
		}
	}
	// TODO: 在此添加额外的初始化代码
	hDevice = CreateFile("\\\\.\\RELOADPROTECT_DeviceName",  
		GENERIC_READ | GENERIC_WRITE,  
		0,  
		NULL,  
		CREATE_ALWAYS,  
		FILE_ATTRIBUTE_NORMAL,  
		NULL);  
	if (hDevice == INVALID_HANDLE_VALUE)  
	{  
		CString csInfo;
		csInfo.Format("设备打开失败 %d %.8x/n", GetLastError(), hDevice);
		AfxMessageBox(csInfo);
	}  

	memset(bProtect,FALSE,sizeof(bool) * 5);

保护的通信函数如下

bool CProtectDlg::ProtectProcess(DWORD dwPid,int nIndex)
{

	ProtectInfo *protectInfoBuff=(ProtectInfo *)malloc(sizeof(ProtectInfo))	;
	ULONG dwWrite	;
	protectInfoBuff->dwPid = dwPid;
	protectInfoBuff->nIndex = nIndex;
	return DeviceIoControl(hDevice, Protect_Code , protectInfoBuff, sizeof(ProtectInfo), NULL,NULL, &dwWrite, NULL);


}


之后把驱动编译一下,文件放在OD插件的工程中编译一下,把驱动文件以资源方式导入DLL中即可

OD插件的基本逻辑就不在这里赘述了,都是公开的,大家可以自己查查

测试部分我就不发了,和谐一点

好了,上面说了那么多都是虚的,高清有码
驱动代码
ReloadProtect.rar
插件代码
ReloadKernel.part1.rar
ReloadKernel.part2.rar
照顾下没IDE的朋友,插件也上传一份吧
ReloadKernel_Dll.rar
上传的附件:
最新回复 (92)
冬哥 2015-5-29 22:23
2
把编译出来的  发上来  没安装VS
1
wgejydy 2015-5-29 22:30
3
插件也一并上传了
whyliuxing 2015-5-29 22:47
4
不错不错,楼主有心了
terrans 2015-5-29 22:49
5
好东西呀,谢谢分享~~
值得怀疑 2015-5-29 22:59
6
不懂驱动啊·!!
肉丝袜 2015-5-29 23:02
7
对第二个插件比较感兴趣
1
elianmeng 2015-5-29 23:10
8
MARK 一下说不定哪天要用到
1
fatecaster 2015-5-29 23:53
9
感谢分享
青庭 2015-5-30 00:34
10
感谢分享
Sam.com 2015-5-30 00:39
11
感谢分享~
lwthxl 2015-5-30 09:58
12
MARK一下,很好的教程。谢谢。
寧靜致遠 2015-5-30 10:05
13
致谢,火钳刘明
1
飘云 2015-5-30 10:10
14
up  好文!
呵呵sd 2015-5-30 10:19
15
设备打开失败?
xie风腾 2015-5-30 10:57
16
只能说强大哟
黑洛 2015-5-31 03:14
17
楼主6的不行
Sello 2015-5-31 08:42
18
这个插件 无敌了 赞
sinmon 2015-5-31 11:02
19
Mark
sunflover 2015-5-31 11:21
20
为啥没设精呢
ysouyno 2015-5-31 12:39
21
谢谢楼主分享
人在塔在 2015-5-31 13:00
22
谢谢分享 好东西 mark
friendanx 2015-5-31 15:52
23
不错的文章,感谢分享。。。
艾米哈柏 2015-6-1 18:25
24
马克~~
8
kanxue 2015-6-1 18:39
25
感谢分享!
wodexinren 2015-6-1 20:08
26
mark感谢分享
totest 2015-6-1 20:17
27
不错的插件还有源码
1
sunsjw 2015-6-2 09:36
28
年度重戏,很不错。。。
netczar 2015-6-2 10:13
29
Mark此贴必火
tmxfh 2015-6-2 10:25
30
不错,顶!感谢楼主开源
skysai 2015-6-2 10:50
31
虽然看不懂,但还是支持楼主!
安全裤 2015-6-2 10:52
32
感谢分享,mark
vfdn 2015-6-2 11:11
33
不错,强烈支持,谢谢!
wssbwpp 2015-6-2 11:52
34
感谢分享,非常不错!
8
Speeday 2015-6-2 13:09
35
好东西,感谢 分享。
11
FishSeeWater 2015-6-2 13:27
36
赞,,谢谢楼主..
fanx 2015-6-2 14:29
37
这真是个不错的东东,谢谢楼主。。
靴子 2015-6-2 15:49
38
mark!
回忆的 2015-6-2 16:00
39
maker下
byebing 2015-6-2 21:02
40
TKS!
laomaotx 2015-6-4 02:47
41
很厉害啊!!!!!!
simplewj 2015-6-4 12:52
42
前排留名 MARK 谢谢分享
有点恶心 2015-6-4 13:02
43
对着前人的源码学习感觉最偷懒最省事最快捷了, 谢谢楼主
我只会易 2015-6-4 20:42
44
虽然不懂,但还是支持一个,大赞楼主
nomaster 2015-6-4 21:38
45
留名,mark
yodamaster 2015-6-4 22:08
46
感谢分享。
knight爱好者 2015-6-4 22:28
47
谢谢分享
RingMENG 2015-6-4 23:28
48
感谢分享,mark
mumaren 2015-6-5 07:25
49
狠通俗易懂的教程
safedisc 2015-6-5 07:47
50
谢谢,学习了
返回



©2000-2017 看雪学院 | Based on Xiuno BBS | 知道创宇带宽支持 | 微信公众号:ikanxue
Time: 0.014, SQL: 9 / 京ICP备10040895号-17