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; ulIndexNumberOfModules; 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;
}
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
}
ULONG __stdcall KiFastCallEntry_Filter(ULONG ulSyscallId,
ULONG ulSyscallAddr,
PULONG pulSyscallTable)
{
// todo.
return ulRet;
}
注意:比如只针对CE,那么 CE必须先关闭,再释放驱动,不然极可能蓝屏掉(蓝屏在重载 的NTOS中)