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; }
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中)