首页
论坛
课程
招聘
[原创]内核所有模块导出函数inlinehook检测
2010-4-4 21:50 15093

[原创]内核所有模块导出函数inlinehook检测

2010-4-4 21:50
15093
好像此论坛还没有谁发过吧,在其他论坛见过,但是有下载限制的。无奈,只好自己写,发出来供像偶这样起步较晚的朋友们参考参考,高手就略过吧,哈哈

主要功能:检测内核已加载模块的所有导出函数是否被inlinehook。

实现方法:将内存中的模块与原始磁盘文件来做比对,若不同,则此函数被inlinehook。所以各种狡诈的inlinehook伎俩(mov eax ,xxxx add eax,xxxx,call eax)是逃不过检测的,除非它修改原始磁盘文件。不过可惜,在比较之前是可以进行md5验证的。若原始磁盘文件被修改,显然是昭然若揭了。
相对其他较复杂检测技术,这个法子最简单而且同样有效。

主体函数实现如下:
void HookCheckAllModuleExportRoutine (PVOID sysInfo)
{
	int i,j,k,len,length;
	PSYSMODULELIST List = NULL;
	char image[256], Name[256],sysDir[256];
	ANSI_STRING FullPath;
	UNICODE_STRING uFullPath;
	ANSI_STRING Temp1,Temp2,Temp3,Temp4,Temp5;
	
	HANDLE hFile = NULL, hSection = NULL;
	PVOID BaseAddress = NULL;
	IO_STATUS_BLOCK IoStatusBlock;
	CLIENT_ID ClientID;
	PVOID SysInfo;


	PIMAGE_DOS_HEADER DosHead = NULL;
	PIMAGE_NT_HEADERS NtHeads = NULL;
	PIMAGE_SECTION_HEADER Section = NULL;
	PIMAGE_EXPORT_DIRECTORY ExportDir = NULL;
	PIMAGE_BASE_RELOCATION RelocDir = NULL;
	ULONG kBase = 0, mBase = 0, kNameBase = 0, kOrdinalBase = 0, kFunctionBase = 0;
	ULONG ByteEqual, Address = 0, index, NameNumbers, FixAddr = 0, FixAddrBase = 0,RelocSize;
	ULONG DataSecVA[24], DataSecSize[24], DataSec;
	
	
	USHORT Temp = 0;
	PUCHAR SystemFunName; 
	PKPROCESS Eprocess;
	KAPC_STATE ApcState;
	HANDLE ProcessHandle;
	UNICODE_STRING ProcessName;
	PSYSTEM_PROCESSES pSysinfo;
	
	PKUSER_SHARED_DATA kShareData = USER_SHARED_DATA;

	size_t size = 0;
	NTSTATUS status;
	OBJECT_ATTRIBUTES oa, ob = {sizeof (ob), NULL, NULL,NULL, NULL};	


	RtlInitAnsiString (&Temp2, "sys");
	RtlInitAnsiString (&Temp3, "dll");
	RtlInitAnsiString (&Temp4, "exe");
	RtlInitAnsiString (&Temp5, "win32k.sys");
	RtlInitUnicodeString (&ProcessName, L"winlogon.exe");
	
	w2ascll (Name, kShareData->NtSystemRoot);
  
	strcpy (sysDir, "\\??\\");
	strcat (sysDir, Name);
  
	List = (PSYSMODULELIST)sysInfo;
	
	for (j = 0; j < List->ulCount; j++)
	{
		

		
		if (StringCompare ("\\??\\", List->smi[j].ImageName, 4)) 
		{
		   DbgPrint ("Skip %s\n", List->smi[j].ImageName);
		   continue;
		}
		
		mBase = NULL;
		hFile = NULL;
		hSection = NULL;
		BaseAddress = NULL;
				
		memset (Name, 0, sizeof (Name));
		memset (image,0, sizeof (image));
		
		strcpy (Name, sysDir);
		strcpy(image, List->smi[j].ImageName + List->smi[j].ModuleNameOffset);
				
		len = strlen (image);	
		RtlInitAnsiString (&Temp1, image + len - 3);
		

		if (!RtlCompareString (&Temp1, &Temp2, TRUE))
		{
			if (strstr (image, "watchdog"))
			{
					strcat (Name, "\\System32\\");
			}else
			{
					strcat (Name, "\\System32\\Drivers\\");
			}
			strcat (Name, image);
		}
		else if (!RtlCompareString (&Temp1, &Temp3, TRUE) || !RtlCompareString (&Temp1, &Temp4, TRUE))
		{
			strcat (Name, "\\System32\\");
			strcat (Name, image);
		}
		else
		{
			continue;
		}
		
		RtlInitAnsiString (&Temp1, image);
		if (!RtlCompareString (&Temp5, &Temp1, TRUE))
		{		//win32.sys模块的空间必须挂靠进程才能读取
		
			status = ZwQuerySystemInformation(5,NULL,0,&length);
			if (status == STATUS_INFO_LENGTH_MISMATCH)
			{
  				SysInfo = ExAllocatePool(NonPagedPool,length);
  				if (NULL == SysInfo)
  				{
  					goto Error;
  				}
			}
			pSysinfo = (PSYSTEM_PROCESSES)SysInfo;
			status = ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, pSysinfo, length, &length);
			if(!NT_SUCCESS(status))
			{
				goto Error;
			}

			//找到winlogon.exe进程,挂靠空间
			do 
			{
				if (!RtlCompareString (&(pSysinfo->ProcessName), &ProcessName, TRUE))
				{
					ClientID = pSysinfo->Threads[0].ClientId; 
					ZwOpenProcess (&ProcessHandle, PROCESS_ALL_ACCESS, &ob, &ClientID);
					ObReferenceObjectByHandle (ProcessHandle, PROCESS_ALL_ACCESS, *PsProcessType, KernelMode, &Eprocess, NULL);
					KeStackAttachProcess (Eprocess, &ApcState);
					break;
				}

				if (pSysinfo->NextEntryDelta == 0) break;

				pSysinfo = (ULONG)pSysinfo + pSysinfo->NextEntryDelta;
			}while (1);
			
			ExFreePool(SysInfo);
			memset (Name, 0, sizeof(Name));
			strcpy (Name, "\\Device\\HarddiskVolume1\\Windows\\System32\\");
			strcat (Name, image);
			
		}

		RtlInitAnsiString (&FullPath, Name);
		RtlAnsiStringToUnicodeString (&uFullPath, &FullPath, TRUE);

		//获得模块在内核中的相关参数
		kBase = List->smi[j].Base;
		if (!MmIsAddressValid (kBase)) 
		{
				DbgPrint ("%s 'base is not valid!\n", image);
				goto Error;
		}

		DosHead = (PIMAGE_DOS_HEADER)kBase;
		NtHeads = (PIMAGE_NT_HEADERS)((ULONG)DosHead + DosHead->e_lfanew);
		Section = (PIMAGE_SECTION_HEADER) ((ULONG)NtHeads + sizeof(IMAGE_NT_HEADERS));
		
		if (NULL == NtHeads->OptionalHeader.DataDirectory[0].VirtualAddress) 
		{
		    DbgPrint ("%s  doesnt has valid export table!\n", image);
		    continue;
		}
		
		DataSec = 0;
		for (i = 0; i < NtHeads->FileHeader.NumberOfSections; i++)
		{
			if (strstr (Section->Name, "text") ||strstr (Section->Name, "PAGE")||strstr (Section->Name, "INIT"))
			{
				DataSecVA[i] = Section->Misc.VirtualSize;
				DataSecSize[i] = Section->VirtualAddress;
				DataSec++;
			}
			Section ++;
		}
		if (i ==  NtHeads->FileHeader.NumberOfSections && DataSec == 0)
		{
			DbgPrint ("%wZ  doesnt find the text section!\n", &FullPath);
		}

		
		ExportDir = (PIMAGE_EXPORT_DIRECTORY)((ULONG)kBase + NtHeads->OptionalHeader.DataDirectory[0].VirtualAddress);
		NameNumbers = ExportDir->NumberOfNames;

		kNameBase = kBase + ExportDir->AddressOfNames;
		kOrdinalBase = kBase + ExportDir->AddressOfNameOrdinals;
		kFunctionBase = kBase + ExportDir->AddressOfFunctions;



		//获得模块加载后的相关参数
		InitializeObjectAttributes (
			&oa,
			&uFullPath,
			OBJ_CASE_INSENSITIVE,
			NULL,
			NULL
			);
		status = ZwOpenFile(
			&hFile,
			FILE_READ_ACCESS,
			&oa,
			&IoStatusBlock,
			FILE_SHARE_READ,
			FILE_SYNCHRONOUS_IO_NONALERT
        );
		if (!NT_SUCCESS(status))
		{
			KdPrint (("OpenFile %wZ error\n", &uFullPath));
			goto Error;

		}
		oa.ObjectName = NULL;
		status =  ZwCreateSection(
			&hSection,
			SECTION_MAP_EXECUTE,
			&oa,
			0,
			PAGE_EXECUTE,
			SEC_IMAGE,
			hFile
			);
		if (!NT_SUCCESS(status))
		{
			
			KdPrint (("CreateSection for  %wZ error", &uFullPath));
			goto Error;

		}
		size = 0;
		status =  ZwMapViewOfSection(
			hSection,
			NtCurrentProcess(),
			&BaseAddress,
			0, 
			1000, 
			0, 
			&size, 
			(SECTION_INHERIT)1, 
			MEM_TOP_DOWN, 
			PAGE_READWRITE
			);
		if (!NT_SUCCESS(status))
		{
			KdPrint (("MapViewSection for  %wZ error", &uFullPath));
			goto Error;

		}
		
		mBase = ExAllocatePool (NonPagedPool, size);
		if (NULL == mBase)
		{	
			goto Error;
		}

		if (BaseAddress != NULL)
		{
			RtlCopyMemory (mBase, BaseAddress, size);
		}
		
		DosHead = (PIMAGE_DOS_HEADER)mBase;
		NtHeads = (PIMAGE_NT_HEADERS)((ULONG)DosHead + DosHead->e_lfanew);
		RelocDir = (PIMAGE_BASE_RELOCATION)((ULONG)mBase + NtHeads->OptionalHeader.DataDirectory[5].VirtualAddress);
		if (RelocDir != NULL)
		{
			while (RelocDir->VirtualAddress != 0 || RelocDir->SizeOfBlock !=  0)
			{
				FixAddrBase = RelocDir->VirtualAddress + (ULONG)mBase;
				RelocSize = (RelocDir->SizeOfBlock - 8)/2;
				for ( i = 0; i < RelocSize; i++)
				{
					Temp = *(PUSHORT)((ULONG)RelocDir + sizeof (IMAGE_BASE_RELOCATION) + i * 2);
					if ( (Temp & 0xF000) == 0x3000)
					{
						Temp &= 0x0FFF;
						FixAddr = FixAddrBase + (ULONG)Temp;
						*(PULONG)FixAddr = *(PULONG)FixAddr + (ULONG)kBase - (ULONG)NtHeads->OptionalHeader.ImageBase;
					}
				}
				RelocDir = (ULONG)RelocDir + RelocDir->SizeOfBlock;
			}
		}
                                /*
		if (j == 0)
		{
			KernelCheck (kBase, mBase, sysInfo);
		}
*/

		for (i = 0; i < NameNumbers; i++)
		{
			SystemFunName = (PUCHAR)(*(PULONG)(kNameBase + i * 4) + (ULONG)kBase);
			index = *((PUSHORT)(kOrdinalBase + i * 2));
			Address = *(PULONG)(kFunctionBase + index * 4);
			
			for (k = 0; k < DataSec; k++)
			{
			   if ( (Address >= DataSecVA[k]) && (Address <= DataSecVA[k] + DataSecSize[k]))
			   { 
        			length = GetFunctionLength(kBase + Address);
        			if (length)
        			{
        				 ByteEqual = RtlCompareMemory (kBase + Address, mBase + Address, length);
        				 if (ByteEqual != length)
        				 {        				 		           				 		
        					  KdPrint (("%s:%s:%x has been inline-hooked!\n", image, SystemFunName, kBase + Address)); 
        				 }
        			}
        			break;
			   }			          
			}
				
		}



Error:
		RtlFreeUnicodeString (&uFullPath);
		if (!RtlCompareString (&Temp5, &Temp1, TRUE))
		{
			KeUnstackDetachProcess (&ApcState);
		}
		if (mBase != NULL)
		{
			ExFreePool (mBase);
			mBase = NULL;
		}
		if (hFile != NULL)
		{
			ZwClose(hFile);
			hFile = NULL;
		}
		if (hSection != NULL)
		{
			ZwClose(hSection);
			hSection = NULL;
		}
		if (BaseAddress != NULL)
		{
			ZwUnmapViewOfSection(NtCurrentProcess(),BaseAddress);
			BaseAddress = NULL;
		}

	}
	return;
}

ULONG GetFunctionLength(ULONG FAddress)
{
    ULONG i = 0, Flag = 0;
    PUCHAR Temp = NULL;
    
    Temp = FAddress;
    while (!Flag && MmIsAddressValid(Temp) && MmIsAddressValid (Temp + 1)
    		&& MmIsAddressValid(Temp + 2) && MmIsAddressValid (Temp + 3)&& MmIsAddressValid (Temp + 4))
		{
			
			switch (*Temp)
			{ 
			case 0xc3:
			case 0xcf:
			case 0xcb:
				if ( (*(Temp+1) == 0xcc) || (*(Temp+1) == 0x8b && *(Temp+1) == 0xFF)) 
				{
						Flag = 1;
				}
				break;
				
			case 0xc2:
			case 0xca:
				if ( (*(Temp+3) == 0xcc)  || (*(Temp+3) == 0x8b && *(Temp+4) == 0xFF))
				{
  					Flag = 1;
				}
				break;
				
			default:
				break;
			}
			
			if (!Flag)
			{
					if (*((PULONG)Temp) == 0 && *(Temp + 4) == 0) 
						return 0;
			}
			
			Temp ++;
			i++;
		}

    return i;
}

[培训] 优秀毕业生寄语:恭喜id咸鱼炒白菜拿到远超3W月薪的offer,《安卓高级研修班》火热招生!!!

收藏
点赞0
打赏
分享
最新回复 (17)
雪    币: 373
活跃值: 活跃值 (21)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
lovesuae 活跃值 1 2010-4-5 02:08
2
0
支持,蛮有用的一篇文章
雪    币: 466
活跃值: 活跃值 (22)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
fhurricane 活跃值 1 2010-4-5 09:28
3
0
支持,学习了~~~
雪    币: 2644
活跃值: 活跃值 (578)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
kagayaki 活跃值 2010-4-5 22:36
4
0
支持,学习了~~~
雪    币: 514
活跃值: 活跃值 (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
xacker 活跃值 1 2010-4-12 16:52
5
0
内存里好多函数和PE文件都不同。
雪    币: 351
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
x敏m 活跃值 2010-4-12 17:21
6
0
看看,不知道能不能用。。。
雪    币: 34
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
sippey 活跃值 2010-4-12 22:04
7
0
读文件的函数被hook了的话,md5还能有用么?
雪    币: 514
活跃值: 活跃值 (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
xacker 活跃值 1 2010-4-12 22:22
8
0
为自己的HOOK 伪造一个内核文件
把所有对内核文件的访问全部转移
雪    币: 211
活跃值: 活跃值 (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
坏坏abc 活跃值 1 2010-8-2 10:16
9
0
请教GetFunctionLength这个实现的意思?
雪    币: 211
活跃值: 活跃值 (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
坏坏abc 活跃值 1 2010-8-2 10:40
10
0
if ( (*(Temp+1) == 0xcc) || (*(Temp+1) == 0x8b && *(Temp+1) == 0xFF))
这句代码是啥意思,还是笔误?
if ( (*(Temp+1) == 0xcc) || (*(Temp+1) == 0x8b && *(Temp+2) == 0xFF))

感觉这里很牵强吧,所有函数的结束都是这样的形式吗?
ret/retn
cc/ 8bff
雪    币: 373
活跃值: 活跃值 (21)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
lovesuae 活跃值 1 2010-8-5 09:36
11
0
在这用个反汇编引擎检测一下
雪    币: 126
活跃值: 活跃值 (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
jokersky 活跃值 1 2010-10-26 17:47
12
0
路过 看过 标记 学习
雪    币: 243
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zhuzhuwen 活跃值 2010-10-26 20:37
13
0
很多东西貌似看过~~~~~
雪    币: 123
活跃值: 活跃值 (38)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kmsmxpro 活跃值 2010-10-29 09:14
14
0
好东西,顶一下,再收藏
雪    币: 465
活跃值: 活跃值 (32)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
aait 活跃值 2010-10-29 09:21
15
0
你厉害,但是楼上有人破解了,首先hook掉你检查原始pe的功能,给你一个伪造的pe文件,哈哈哈。
雪    币: 53
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jacksona 活跃值 2011-1-12 19:03
16
0
思路很不错,加 油。
雪    币: 210
活跃值: 活跃值 (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
菜可菜 活跃值 2011-3-24 00:02
17
0
膜拜,同时学习ing 顺便MARK.
雪    币: 243
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
gotiger 活跃值 2011-4-11 11:02
18
0
用处不大,直接Bypass
游客
登录 | 注册 方可回帖
返回