首页
社区
课程
招聘
[旧帖] [求助]内核文件取得SSDT的疑问 0.00雪花
发表于: 2013-9-5 19:31 1938

[旧帖] [求助]内核文件取得SSDT的疑问 0.00雪花

2013-9-5 19:31
1938
最近学写了一篇《【原创】FSD HOOK与SSDT HOOK恢复简单思路》http://bbs.pediy.com/showthread.php?t=99970 跟着写了代码 但是从磁盘内核文件打开得到SSDT始终地址不正确,具体代码如下:
//得到原始SSDT地址
void GetOriginSSDTAddr()
{
	//---------------------------------------获取系统使用的内核名称---------------------------
	
	UINT nPae***;

	__asm
	{
		pushad
		_emit 0x0f
		_emit 0x20
		_emit 0xe0		//mov eax,cr4
		shr eax,4
		and eax,1
		mov nPae***,eax
	}
	PWCHAR kernelName = nPae*** ? L"\\SystemRoot\\system32\\ntkrnlpa.exe" : L"\\SystemRoot\\system32\\ntoskrnl.exe";
	KdPrint(("the kernel name is : %ws\n", kernelName));


	//-----------------------------------------内核文件映射内存----------------------------------

	HANDLE hSection=NULL, hFile=NULL;
	SIZE_T size=0;
	NTSTATUS status;
	PUCHAR krnlImgBase = NULL;

	//转换DLL名称
	UNICODE_STRING kernelFilePath;
	RtlInitUnicodeString(&kernelFilePath, kernelName);

	OBJECT_ATTRIBUTES objectAttributes={0};
	IO_STATUS_BLOCK iosb={0};

	//初始化 objectAttributes
	InitializeObjectAttributes(&objectAttributes, &kernelFilePath, OBJ_KERNEL_HANDLE, NULL, NULL);

	__try
	{
		//打开文件
		status=ZwOpenFile(&hFile,  FILE_EXECUTE | SYNCHRONIZE, &objectAttributes, &iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
		if(!NT_SUCCESS(status))
		{
			__leave;
		}
		objectAttributes.ObjectName = 0;

		//创建内存块
		status=ZwCreateSection(&hSection, SECTION_ALL_ACCESS, &objectAttributes, 0, PAGE_READONLY, SEC_IMAGE, hFile); //PAGE_READONLY页面保护属性,必须结合SEC_IMAGE属性
		if(!NT_SUCCESS(status))
		{
			__leave;
		}

		//内存映射文件
		status=ZwMapViewOfSection(hSection, 
			ZwCurrentProcess(), 
			(PVOID*)&krnlImgBase, 
			0, 
			1024, 
			0, 
			&size, 
			ViewUnmap, 
			MEM_LARGE_PAGES,		//针对DLL文件较小是可以用MEM_TOP_DOWN 文件较大比如USER32.DLL时需要用MEM_LARGE_PAGES
			PAGE_READWRITE);		
	}
	__finally
	{
		if(hFile != NULL)
		{
			//关闭文件句柄
			ZwClose(hFile);		
		}
		if(!NT_SUCCESS(status) && hSection != NULL)
		{
			//关闭内存块
			ZwClose(hSection);
		}
	}
	//如果失败 直接返回
	if(!NT_SUCCESS(status))
	{
		KdPrint(("Kernel file mapped memory failure!\n"));
		return;
	}
	KdPrint(("Successfully mapped memory kernel file!\n"));


	//----------------------------------------得到系统内核在内存中的基地址------------------------------

	ULONG ulNeedLen, krnlBase=0;
	PUCHAR pBuffer=NULL;

	//得到需要的内存大小
    status=ZwQuerySystemInformation(SystemModuleInformation, NULL, 0, &ulNeedLen);
	//用户提供的缓冲区大小不够返回STATUS_INFO_LENGTH_MISMATCH
	if(status==STATUS_INFO_LENGTH_MISMATCH) 
	{ 
		//申请空间
		pBuffer = (PUCHAR)ExAllocatePool(PagedPool, ulNeedLen);
		status = ZwQuerySystemInformation(SystemModuleInformation, (PVOID)pBuffer, ulNeedLen, NULL);   
		if (status==STATUS_SUCCESS)
		{
			DWORD Modcnt=*(PDWORD)pBuffer;
			PSYSTEM_MODULE_INFORMATION pSysModuleInfo = (PSYSTEM_MODULE_INFORMATION)(pBuffer + sizeof(DWORD)); 
			//遍历数组得到系统内核在内存中的基地址
			for(DWORD i=0; i<Modcnt; i++)
			{
				   KdPrint(("%d\t0x%08X 0x%08X %s\n",pSysModuleInfo->Index,
					   pSysModuleInfo->Base,pSysModuleInfo->Size,pSysModuleInfo->ImageName));

				   if(strstr(pSysModuleInfo->ImageName, "nt"))
				   {
						krnlBase = (ULONG)pSysModuleInfo->Base;
						break;
				   }
				   pSysModuleInfo++;
			}
		}
		ExFreePool(pBuffer);
	}
	if(krnlBase==0)
	{
		KdPrint(("Get the kernel in memory base address failed!\n"));
		return;
	}
	KdPrint(("Get the kernel in memory base address successfully!\n"));
	

	//----------------------------------------得到原始SSDT地址------------------------------
	//方法是:首先获得系统导出的SSDT表地址,减去基地址后得到SSDT的RVA
	
	//这里是得到KeServiceDescriptorTable的另一种方法
	UNICODE_STRING uniSSDT;
	RtlInitUnicodeString(&uniSSDT, L"KeServiceDescriptorTable");
	PULONG pKeServiceDescriptorTable=(PULONG)MmGetSystemRoutineAddress(&uniSSDT);

	//当前系统导出的SSDT表地址 减去 基地址后 得到 SSDT的RVA
	ULONG ssdtRva = *pKeServiceDescriptorTable - krnlBase;

	//解析PE格式 获得原始SSDT地址 
	PUCHAR imgHeader = krnlImgBase + *(PULONG)(krnlImgBase+0x3c);				//PE头
	ULONG imgBase = *(PULONG)(imgHeader +0x34);									//镜像基地址
	USHORT numOfSec = *(PUSHORT)(imgHeader + 0x06);								//节的数量
	ULONG secTable = (ULONG)(imgHeader + 0xf8);									//节表
	ULONG secRva=0, secSize=0;
	PULONG ssdtOriginAddr=NULL;
	
	//系统加载的内核文件是内存映射的,属于虚拟内存地址
	//我们自己加载的内核文件是文件映射内存,属于文件地址 所以需要转换
	for(USHORT i=0; i<numOfSec; i++)
	{
		secRva=*(PULONG)(secTable + 0x0c);
		secSize=*(PULONG)(secTable + 0x10);
		if(ssdtRva >= secRva && ssdtRva <= secRva + secSize)
		{
			KdPrint(("find ssdt in sec %s\n", secTable));

			//内存映射文件的SSDT = (SSDT RVA – 所在节 RVA) + 所在节 RawOffset + krnlImgBase
			ssdtOriginAddr = (PULONG)(krnlImgBase + (ssdtRva - secRva + *(PULONG)(secTable + 0x14)));
			break;
		}
		secTable += 0x28;
	}
	if(ssdtOriginAddr)
	{
		KdPrint(("Get SSDT origin success!\n"));
		gDevExt->pOrgSSDTAddr = ssdtOriginAddr;
	}
	else
	{
		KdPrint(("Get SSDT origin failed!\n"));
		return;
	}

	//----------------------------------------重定位修复------------------------------

	PULONG serviceTable = (PULONG)(*pKeServiceDescriptorTable);
	for(ULONG i = 0; i < *(pKeServiceDescriptorTable + 2); i++)
	{
		if(serviceTable[i] != ssdtOriginAddr[i] - imgBase + krnlBase)
		{
			PageProtect(FALSE);
			ssdtOriginAddr[i] = ssdtOriginAddr[i] - imgBase + krnlBase;
			DbgBreakPoint();
			KdPrint(("ssdt index:%u \torigin addr: %x\tcurrent addr: %x \n", i, ssdtOriginAddr[i], serviceTable[i]));
			PageProtect(TRUE);
		}
	}
}

经过测试 文件偏移量是取对了的 因为我用打开文件读取文件的方式 是可以正确取得SSDT在文件中的地址 但是通过文件映射内存 取得的地址就有偏差
文件映射内存以后 我觉得应该和文件在磁盘上的分布是一样的吧 但现在看来不一样 不知道具体哪里错了, 还麻烦各位大哥朋友帮忙看看,谢谢先

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
免费 0
支持
分享
最新回复 (2)
雪    币: 92
活跃值: (70)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
2
真心求解
2013-9-7 10:06
0
雪    币: 6890
活跃值: (8944)
能力值: ( LV17,RANK:797 )
在线值:
发帖
回帖
粉丝
3
__asm
  {
    pushad
    _emit 0x0f
    _emit 0x20
    _emit 0xe0    //mov eax,cr4
    shr eax,4
    and eax,1
    mov nPae***,eax
  }
你前面入栈,没有出栈,栈都不平衡了,不蓝屏?
2014-4-19 14:39
0
游客
登录 | 注册 方可回帖
返回
//