最近学写了一篇《【原创】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虚拟机自动化脱壳的方法