1.ring0-内核重载详解(NTOS)

1.取得NTOS原始的地址:
这个可以通过遍历系统模块,找到第一个被加载的模块(NTOS),获得NTOS的路径,基地址,大小:
基本思路为:
1.1 ZwQuerySystemInformation查询到所有模块
1.2 获得NTOS的路径,基地址,大小
代码如下:

NTSTATUS GetNtosModuleInfo(WCHAR *pNtosPath,ULONG nSize,
						   PULONG_PTR pNtosModBase,
						   PULONG_PTR pNtosModSize
						   )
{
	NTSTATUS						ntStatus = STATUS_UNSUCCESSFUL;
	PRTL_PROCESS_MODULES   			psmi = NULL;
	ULONG							ulSize = 0;
	ULONG						   ulIndex = 0;
 
	// 转换
	PCHAR						   pszNtosName = NULL;
	ANSI_STRING						Ansi_szNtosName;
	UNICODE_STRING				  Unicode_szNtosName;
 
	__try
	{
		do
		{
			ntStatus = ZwQuerySystemInformation(SystemModuleInformation, NULL, 0, &ulSize);
			if (STATUS_INFO_LENGTH_MISMATCH != ntStatus)
			{
				break;
			}
 
			psmi = (PRTL_PROCESS_MODULES)ExAllocatePoolWithTag(NonPagedPool, ulSize, '0YGH');
			if (NULL == psmi)
			{
				break;
			}
 
			ntStatus = ZwQuerySystemInformation(SystemModuleInformation,
				psmi, ulSize, &ulSize);
			if (STATUS_SUCCESS != ntStatus)
			{
				break;
			}
 
			//遍历打印:
			//for (ulIndex = 0; ulIndex<psmi->NumberOfModules; ulIndex++)
			//{
			//	KdPrint(("[ModInfo]-nIndex:%u--base:%p--size:%p--name:%s\n", ulIndex, psmi->Modules[ulIndex].ImageBase, psmi->Modules[ulIndex].ImageSize, psmi->Modules[ulIndex].FullPathName));
			//}
 
			if (pNtosPath)
			{
				memset(pNtosPath, 0, sizeof(WCHAR)*nSize);
				wcscpy(pNtosPath, L"\\SystemRoot\\system32\\");
				pszNtosName = psmi->Modules[0].FullPathName+psmi->Modules[0].OffsetToFileName;
				RtlInitAnsiString(&Ansi_szNtosName, pszNtosName);
				RtlAnsiStringToUnicodeString( &Unicode_szNtosName, &Ansi_szNtosName, TRUE);
				wcsncpy(pNtosPath+wcslen(pNtosPath), Unicode_szNtosName.Buffer,Unicode_szNtosName.Length);
				RtlFreeUnicodeString(&Unicode_szNtosName);
			}
 
			if (pNtosModBase
				&&MmIsAddressValid(pNtosModBase))
			{
				*pNtosModBase = psmi->Modules[0].ImageBase;
			}
 
			if (pNtosModSize
				&&MmIsAddressValid(pNtosModSize))
			{
				*pNtosModSize = psmi->Modules[0].ImageSize;
			}
 
		} while (FALSE);
 
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
 
	}
 
	if (NULL != psmi)
	{
		ExFreePool(psmi);
		psmi = NULL;
	}
 
	return ntStatus;
}


2.通过路径读取文件内容

NTSTATUS ReadFileToBuf(WCHAR *pFilePath, PVOID *ppBuf, PULONG_PTR pulSize)
{
	NTSTATUS						ntStatus = STATUS_UNSUCCESSFUL;
	HANDLE						  hFile = NULL;
	OBJECT_ATTRIBUTES			   obj;
	UNICODE_STRING					Unicode_FilePath;
	IO_STATUS_BLOCK				 iosb = {0};
	IO_STATUS_BLOCK				 io_readsb = {0};
	FILE_STANDARD_INFORMATION	   fInfo = {0};
	ULONG_PTR					   ulSize = 0;
	PVOID						   pBuf = NULL;
 
	do
	{
		RtlInitUnicodeString(&Unicode_FilePath, pFilePath);
		InitializeObjectAttributes(&obj, &Unicode_FilePath, OBJ_CASE_INSENSITIVE, NULL, NULL);
 
		ntStatus = ZwOpenFile(&hFile, SYNCHRONIZE, &obj, &iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);
		if (STATUS_SUCCESS != ntStatus)
		{
			break;
		}
 
		ntStatus = ZwQueryInformationFile(hFile,
			&iosb,
			&fInfo,
			sizeof(FILE_STANDARD_INFORMATION),
			FileStandardInformation
			);
		if (STATUS_SUCCESS != ntStatus)
		{
			break;
		}
 
		ulSize = fInfo.EndOfFile.LowPart;
		pBuf = ExAllocatePoolWithTag(PagedPool, ulSize+1024, '0YHG');
		if (NULL == pBuf)
		{
			break;
		}
 
		ntStatus = ZwReadFile(hFile,
			NULL,
			NULL,
			NULL,
			&io_readsb,
			pBuf,
			ulSize,
			0,
			NULL);
		if (STATUS_SUCCESS != ntStatus)
		{
			break;
		}
 
		if (io_readsb.Information != ulSize)
		{
			ntStatus = STATUS_UNSUCCESSFUL;
			break;
		}
 
		// 赋值
		if (pulSize)
		{
			*pulSize = ulSize;
		}
		if (ppBuf)
		{
			*ppBuf = pBuf;
		}
 
	} while (FALSE);
 
	if (hFile)
	{
		ZwClose(hFile);
		hFile = NULL;
	}
 
	if (STATUS_SUCCESS != ntStatus)
	{
		if (pBuf)
		{
			ExFreePool(pBuf);
			pBuf = NULL;
		}
	}
 
	return ntStatus;
}


3.按镜像大小分配内存,一般建议分 配多一点,并映射MDL得到虚拟地址可读可写可执行

NTSTATUS MemoryMapMdl(PVOID pBase, ULONG ulSize, PMDL* ppMdl, PVOID* ppMapBase)
{
	NTSTATUS						ntStatus = STATUS_UNSUCCESSFUL;
	PMDL							pMdl = NULL;
	PVOID						   pMdlVA = NULL;
 
	do
	{
		pMdl = IoAllocateMdl(pBase, ulSize, FALSE, FALSE, NULL);
		if (NULL == pMdl)
		{
			break;
		}
 
		__try
		{
			// 锁住,避免被page out,直接会导致MmGetSystemAddressForMdl蓝屏
			MmProbeAndLockPages(pMdl, KernelMode, IoModifyAccess);
			//MmBuildMdlForNonPagedPool(pMdl);//如果是非分页内存,可以使用这个,使用这个就不需要MmUnmapLockedPages&MmUnlockPages了
		}
		__except(EXCEPTION_EXECUTE_HANDLER)
		{
			IoFreeMdl(pMdl);
			break;
		}
 
		pMdlVA = MmMapLockedPagesSpecifyCache(pMdl, KernelMode, MmCached, NULL, FALSE, NormalPagePriority);
		if (NULL == pMdlVA)
		{
			MmUnlockPages(pMdl);
			IoFreeMdl(pMdl);
			break;
		}
 
		ntStatus = MmProtectMdlSystemAddress(pMdl, PAGE_EXECUTE_READWRITE);
		if (STATUS_SUCCESS != ntStatus)
		{
			MmUnmapLockedPages(pMdlVA, pMdl);
			MmUnlockPages(pMdl);
			IoFreeMdl(pMdl);
			break;
		}
 
		// 赋值
		if (ppMapBase && MmIsAddressValid(ppMapBase))
		{
			*ppMapBase = pMdlVA;
		}
 
		if (ppMdl && MmIsAddressValid(ppMdl))
		{
			*ppMdl = pMdl;
		}
 
	} while (FALSE);
 
	return ntStatus;
}


4.对MDL映射出来的内存清0,再复制Header,复制section

NTSTATUS CopySections(PVOID pBase, PVOID pData, PIMAGE_NT_HEADERS pNtHdr)
{
	ULONG						   ulIndex = 0;
	PIMAGE_SECTION_HEADER			pSectionHdr = IMAGE_FIRST_SECTION(pNtHdr);
 
	// todo.
 
	return STATUS_SUCCESS;
}

5.修复IAT
5.1 像查找NTOS一样查找到依赖的dll的基地址
5.2 根据函数名搜索依赖的dll的导出表,找到函数地址,填充
5.3 在校验NTOS时,注意原始NTOS的导入表为init的,所以不存在了,需查看IAT表(12序号)

NTSTATUS BuildNtosImportTable(PVOID pBase)
{
	PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pBase;
	PIMAGE_NT_HEADERS pNt = NULL;
	PIMAGE_IMPORT_DESCRIPTOR pImport = NULL;
	PIMAGE_THUNK_DATA pOrigThunk = NULL;
	PIMAGE_THUNK_DATA pFirstThunk = NULL;
	PIMAGE_IMPORT_BY_NAME pName = NULL;
	ULONG_PTR FunctionAddr = 0;
	ULONG_PTR ModBase = 0;
 
	if (NULL == pDos
		|| IMAGE_DOS_SIGNATURE != pDos->e_magic)
	{
		return STATUS_INVALID_IMAGE_FORMAT;
	}
 
	pNt = (PIMAGE_NT_HEADERS)((PUCHAR)pBase+pDos->e_lfanew);
	if (IMAGE_NT_SIGNATURE != pNt->Signature)
	{
		return STATUS_INVALID_IMAGE_FORMAT;
	}
 
	if (0 == pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size)
	{
		return STATUS_SUCCESS;
	}
 
	pImport = (PIMAGE_IMPORT_DESCRIPTOR)((PUCHAR)pBase+pNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
 
	// todo
 
	return STATUS_SUCCESS;
}


6.修复重定位表,其目的是使用同一份全局变量,所以重定位差值一定要是原始NTOS-0X400000(OptionalHeader.ImageBase)

NTSTATUS BuildNtosRelocTable(PVOID pBase,PVOID pSysBase)
{
	//todo
	return STATUS_SUCCESS;
}


7.修复SSDT表(仅X86下适用)

7.1 这时可以用.reload /i 来再加载一份NT的pdb,可以看到,新的NT的SSDT对象内容全为0,所以需要重建:

// 仅在X86下使用
void SetNewSSDT(PVOID pNewBase,PVOID pSysBase)
{
	// todo
}

8.hook管家的函数,实现SSDT分发

ULONG __stdcall KiFastCallEntry_Filter(ULONG ulSyscallId,
									   ULONG ulSyscallAddr,
									   PULONG pulSyscallTable)
{
	// todo.
	return ulRet;
}
注意:比如只针对CE,那么 CE必须先关闭,再释放驱动,不然极可能蓝屏掉(蓝屏在重载 的NTOS中)
以下代 码 在装了QQ管家+CE下,可以查看被保护的内存(如NP)













  • 5
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值