相信很多人都用过SDTRestore,其功能我就不罗嗦了。
在部分机器上SDTRestore可以顺利执行(前提是得到安全软件如ZoomAlarm或卡巴的允许),在另一部分则不能顺利执行。现列出各机器环境:
××××××××××××××××××××××××××××××
可以顺利运行的:
机器A: XP Home Edition sp2(未实时更新)
装有ZoomAlarm 7.0
用冰剑查看系统采用ntoskrnl.exe(符合SDTRestore的要求)
用SDTRestore 0.2测试可以顺利还原SSDT
KeServiceDescriptorTable: 0x8055ab80
KeServiceDescriptorTable.ServiceTable: 0x804e3d20
KeServiceDescriptorTable.ServiceLimit: 284
××××××××××××××××××××××××××××××
*********************************************
不可以顺利运行的:
机器B:XP Professional sp2 (实时更新的)
装有ZoomAlarm 7.0和卡巴6.0
用冰剑查看系统采用ntkrnlpa.exe(不符合SDTRestore的要求,手动修改程序,将ntoskrnl.exe全部替换为ntkrnlpa.exe)
用SDTRestore 0.2测试,不能顺利执行还原功能
KeServiceDescriptorTable: 0x808846e0
KeServiceDescriptorTable.ServiceTable: 0x864159b8
KeServiceDescriptorTable.ServiceLimit: 297
虽然能够顺利得到上面三个数据,但是后面映射0x864159b8时,SDTRestore得到的内容是
0x0ac9004f 0x2d2d2d67 0x00000000 0x00000000
0x00000000 0x00000000 0x00000000 0x00000000
但我用windbg.exe调试内核时:
lkd> dd 864159b8
864159b8 808cc104 8091837e 8091bbcc 809183b0
864159c8 8091bc06 809183e6 8091bc4a 8091bc8e
显然,SDTRestore得到的是错误的内容。由于SDTRestore采用了2次NtMapViewOfSection,第一次映射0x808846e0是正确的(和windbg得到的结果一样),第2次映射0x864159b8是错误的(和windbg的结果不一样),所以初步判断是这种映射出错了。
为了进一步判断是否是因为内存映射出错,特从SDTRestore中剥离出映射模块(后面有源码),单独将内核空间中的内容打印输出,以便调试。
*********************************************
◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎
我的DumpMem运行结果:
输入要查询的地址:
808846e0
该物理地址的内容为:
864159b8 00000000 00000129 8649ced0 00000000
00000000 00000000 00000000 00000000 00000000
输入要查询的地址:
864159b8
该物理地址的内容为:
0ac9004f 2d2d2d67 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000
但用Windbg的结果为:
lkd> dd 808846e0
808846e0 864159b8 00000000 00000129 8649ced0
808846f0 00000000 00000000 00000000 00000000
lkd> dd 864159b8
864159b8 808cc104 8091837e 8091bbcc 809183b0
864159c8 8091bc06 809183e6 8091bc4a 8091bc8e
对比发现,dumpmem可以正确映射0x808846e0,但不能正确映射0x864159b8
反复测试dumpmem,可以发现其
能够正确映射 0x8000 0000 -0x8200 0000附近的内核空间,但是
不能够映射 0x8600 0000附近的内核空间
◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎◎
进一步思考,机器A可以顺利执行SDTRestore,机器B不行,应该就是这种内存映射的问题了。
回到机器A的环境,我发现两次映射分别为
KeServiceDescriptorTable: 0x8055ab80
KeServiceDescriptorTable.ServiceTable: 0x804e3d20
而在机器B中,两次映射分别为
KeServiceDescriptorTable: 0x808846e0
KeServiceDescriptorTable.ServiceTable: 0x864159b8
内核地址0x864159b8不能用NtMapViewOfSection映射出来 ?难道真是这样吗?
我怀疑是卡巴作怪,于是停掉机器B的卡巴(停止其设备,阻止开机运行),重起。
进入系统再次运行dumpmem
,我很失望,结果还是一样“反复测试dumpmem,可以发现其能够正确映射 0x8000 0000 -0x8200 0000附近的内核空间,但是不能够映射 0x8600 0000附近的内核空间”。
抱着侥幸心理,我运行了SDTRestore,居然能够顺利还原SSDT!!!!
仔细看其输出信息,发现:
KeServiceDescriptorTable: 0x808846e0
KeServiceDescriptorTable.ServiceTable: 0x808a15d0(这不再是0x86xxxxxx了)
KeServiceDescriptorTable.ServiceLimit: 287
结论: 看来,不是卡巴主动做了手脚,使得我们的SDTRestore不能正确映射到0x864159b8,而是加载了卡巴的内核发生了变化,KeServiceDescriptorTable.ServiceTable的地址被移动到0x86xxxxxx那里去了,从而用NtMapViewOfSection不能正确的获取到内核空间的内容,导致SDTRestore不能获取到各个服务函数的入口地址。
疑问: 为什么NtMapViewOfSection只能正确获取内核空间0x80000000-0x82000000附近的内容? 而不能正确获取0x86000000附近的内容?
建议: 想要SDTRestore能够在机器B上顺利执行,看来得换个实现方法,建议做成驱动的形势,那样可以不用NtMapViewOfSection,而是直接读取0x86000000附近的内容了。这部分工作暂时没时间做,如果哪位仁兄有精力,帮忙作出来共享下
-----------没有权限加附件,只能贴出来
-----------
dumpmem.cpp:
// SDTRestore.cpp : 定义控制台应用程序的入口点。
//
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <aclapi.h>
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
#define OBJ_CASE_INSENSITIVE 0x00000040L
#define PAGE_READONLY 0x02
#define PAGE_READWRITE 0x04
#define DEF_KERNEL_BASE 0x80400000L
#define SystemModuleInformation 11
#define PROT_MEMBASE 0x80000000
typedef LONG NTSTATUS;
typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;
typedef struct _STRING {
USHORT Length;
USHORT MaximumLength;
PCHAR Buffer;
} ANSI_STRING, *PANSI_STRING;
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES;
typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
typedef enum _SECTION_INHERIT {
ViewShare = 1,
ViewUnmap = 2
} SECTION_INHERIT;
typedef struct _SYSTEM_MODULE_INFORMATION
{
ULONG Reserved[2];
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT Index;
USHORT Unknown;
USHORT LoadCount;
USHORT ModuleNameOffset;
CHAR ImageName[256];
} SYSTEM_MODULE_INFORMATION;
//以下是自己程序中需要用到的Native API,从ntddk.h中复制过来,然后自己计算函数地址,进行调用
NTSTATUS (WINAPI * _RtlAnsiStringToUnicodeString)
(PUNICODE_STRING DestinationString,
IN PANSI_STRING SourceString,
IN BOOLEAN);
VOID (WINAPI *_RtlInitAnsiString)
(IN OUT PANSI_STRING DestinationString,
IN PCHAR SourceString);
VOID (WINAPI * _RtlFreeUnicodeString)
(IN PUNICODE_STRING UnicodeString);
NTSTATUS (WINAPI *_NtOpenSection)
(OUT PHANDLE SectionHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes);
NTSTATUS (WINAPI *_NtMapViewOfSection)
(IN HANDLE SectionHandle,
IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN ULONG ZeroBits,
IN ULONG CommitSize,
IN OUT PLARGE_INTEGER SectionOffset, /* optional */
IN OUT PULONG ViewSize,
IN SECTION_INHERIT InheritDisposition,
IN ULONG AllocationType,
IN ULONG Protect);
NTSTATUS (WINAPI *_NtUnmapViewOfSection)
(IN HANDLE ProcessHandle,
IN PVOID BaseAddress);
NTSTATUS (WINAPI * _NtQuerySystemInformation)(UINT, PVOID, ULONG, PULONG);
//*******************************************************************************************************
// Obtain a handle to \device\physicalmemory
//
//*******************************************************************************************************
HANDLE openPhyMem() //打开到物理内存的一个句柄
{
HANDLE hPhyMem;
OBJECT_ATTRIBUTES oAttr;
ANSI_STRING aStr;
_RtlInitAnsiString(&aStr, "\\device\\physicalmemory");
UNICODE_STRING uStr;
if(_RtlAnsiStringToUnicodeString(&uStr, &aStr, TRUE) != STATUS_SUCCESS)
{
return INVALID_HANDLE_VALUE;
}
oAttr.Length = sizeof(OBJECT_ATTRIBUTES);
oAttr.RootDirectory = NULL;
oAttr.Attributes = OBJ_CASE_INSENSITIVE;
oAttr.ObjectName = &uStr;
oAttr.SecurityDescriptor = NULL;
oAttr.SecurityQualityOfService = NULL;
if(_NtOpenSection(&hPhyMem, SECTION_MAP_READ | SECTION_MAP_WRITE, &oAttr ) != STATUS_SUCCESS)
{
return INVALID_HANDLE_VALUE;
}
return hPhyMem;
}
//*******************************************************************************************************
// Map in a section of physical memory into this process's virtual address space.
//
//*******************************************************************************************************
BOOL mapPhyMem(HANDLE hPhyMem, DWORD *phyAddr, DWORD *length, PVOID *virtualAddr)//将物理内存地址映射到本进程的虚拟地址
{ //phyAddr=SDT在内核的偏移量 //length=0x2000 //virtualAddr=NULL
NTSTATUS ntStatus;
PHYSICAL_ADDRESS viewBase;
*virtualAddr = 0;
viewBase.QuadPart = (ULONGLONG) (*phyAddr);
ntStatus = _NtMapViewOfSection( hPhyMem, //物理内存句柄
(HANDLE)-1, //
virtualAddr, //in,out:供返回的地址指针,将查询的物理地址映射到该处
0, //
*length, //
&viewBase, //in,out:需要映射的内存的地址
length, //in,out:要查看的总大小
ViewShare, //继承方式
0, //
PAGE_READWRITE );//
if(ntStatus != STATUS_SUCCESS)
{
printf("Failed to map physical memory view of length %X at %X!", *length, *phyAddr);
return FALSE;
}
*phyAddr = viewBase.LowPart;
return TRUE;
}
//*******************************************************************************************************
// Unmap section of physical memory
//
//*******************************************************************************************************
void unmapPhyMem(DWORD virtualAddr)//取消物理地址和虚拟地址之间的映射
{
NTSTATUS status;
status = _NtUnmapViewOfSection((HANDLE)-1, (PVOID)virtualAddr);
if(status != STATUS_SUCCESS)
{
printf("Unmapping view failed!\n");
}
}
//*******************************************************************************************************
// Assign SECTION_MAP_WRITE assess of \device\physicalmemory to current user.
//
//*******************************************************************************************************
BOOL assignACL(void)//提升权限,使得当前进程具有SECTION_MAP_WRITE权限,以便可写物理内存
{
HANDLE hPhyMem;
OBJECT_ATTRIBUTES oAttr;
BOOL result = FALSE;
ANSI_STRING aStr;
_RtlInitAnsiString(&aStr, "\\device\\physicalmemory");
UNICODE_STRING uStr;
if(_RtlAnsiStringToUnicodeString(&uStr, &aStr, TRUE) != STATUS_SUCCESS)
{
return FALSE;
}
oAttr.Length = sizeof(OBJECT_ATTRIBUTES);
oAttr.RootDirectory = NULL;
oAttr.Attributes = OBJ_CASE_INSENSITIVE;
oAttr.ObjectName = &uStr;
oAttr.SecurityDescriptor = NULL;
oAttr.SecurityQualityOfService = NULL;
if(_NtOpenSection(&hPhyMem, READ_CONTROL | WRITE_DAC, &oAttr ) != STATUS_SUCCESS)
{
return FALSE;
}
else
{
PACL dacl;
PSECURITY_DESCRIPTOR sd;
if(GetSecurityInfo(hPhyMem, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL,
&dacl, NULL, &sd) == ERROR_SUCCESS)
{
EXPLICIT_ACCESS ea;
char userName[MAX_PATH];
DWORD userNameSize = MAX_PATH-1;
GetUserName(userName, &userNameSize);
ea.grfAccessPermissions = SECTION_MAP_WRITE;
ea.grfAccessMode = GRANT_ACCESS;
ea.grfInheritance = NO_INHERITANCE;
ea.Trustee.pMultipleTrustee = NULL;
ea.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
ea.Trustee.ptstrName = userName;
PACL newDacl;
if(SetEntriesInAcl(1, &ea, dacl, &newDacl) == ERROR_SUCCESS)
{
if(SetSecurityInfo(hPhyMem, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL,
newDacl, NULL) == ERROR_SUCCESS)
{
result = TRUE;
}
LocalFree(newDacl);
}
}
}
return result;
}
struct FixupBlock //导出表数据结构
{
unsigned long pageRVA; //相对虚拟地址RVA
unsigned long blockSize; //导出表的大小
};
BOOL getNativeAPIs(void)//通过载入ntdll.dll获取我们自己要使用到的一些API
{
HMODULE hntdll;
hntdll = GetModuleHandle("ntdll.dll");
*(FARPROC *)&_RtlAnsiStringToUnicodeString =
GetProcAddress(hntdll, "RtlAnsiStringToUnicodeString");
*(FARPROC *)&_RtlInitAnsiString =
GetProcAddress(hntdll, "RtlInitAnsiString");
*(FARPROC *)&_RtlFreeUnicodeString =
GetProcAddress(hntdll, "RtlFreeUnicodeString");
*(FARPROC *)&_NtOpenSection =
GetProcAddress(hntdll, "NtOpenSection");
*(FARPROC *)&_NtMapViewOfSection =
GetProcAddress(hntdll, "NtMapViewOfSection");
*(FARPROC *)&_NtUnmapViewOfSection =
GetProcAddress(hntdll, "NtUnmapViewOfSection");
*(FARPROC *)&_NtQuerySystemInformation =
GetProcAddress(hntdll, "ZwQuerySystemInformation");
if(_RtlAnsiStringToUnicodeString && _RtlInitAnsiString && _RtlFreeUnicodeString &&
_NtOpenSection && _NtMapViewOfSection && _NtUnmapViewOfSection && _NtQuerySystemInformation)
{
return TRUE;
}
return FALSE;
}
int main(int argc, char* argv[])
{
if(!getNativeAPIs()) //通过载入ntdll.dll获取我们自己要使用到的一些API
{
printf("Failed to get addresses of Native APIs!\n");
return 1;
}
if(!assignACL()) //提升权限,使得当前进程具有SECTION_MAP_WRITE权限,以便可写物理内存
printf("\nassignACL()失败。。。。。。1。。");
HANDLE hPhyMem = openPhyMem();//打开物理内存句柄
if(hPhyMem == INVALID_HANDLE_VALUE)
if(!assignACL()) printf("\nassignACL失败。。2。");
//这里要打开2次,注意
hPhyMem = openPhyMem();
if(hPhyMem == INVALID_HANDLE_VALUE)
{
printf("Could not open physical memory device!\nMake sure you are running as Administrator.\n");
return 1;
}
DWORD sdtAddr,sdtAddr0;
while(1)
{
printf("\n输入要查询的地址:\n");
scanf("%x",&sdtAddr);
sdtAddr0=sdtAddr;
sdtAddr-=0x80000000;
unsigned char *ptr = NULL;
DWORD pAddr = sdtAddr;//pAddr保存了SDT在内核的偏移量
DWORD wantedAddr = pAddr; //wantedAddr也保存了SDT在内核的偏移量
DWORD len = 0x2000;
getchar();
////+++++++++
printf("\npAddr:%08x\tlen:%08x",pAddr,len);
////---------------
// map in page containing KeServiceDecriptorTable
if(mapPhyMem(hPhyMem, &pAddr, &len, (LPVOID *)&ptr))//将SDT在内核的偏移量映射到虚拟地址ptr,以备用户态访问
{
DWORD start = wantedAddr - pAddr;
DWORD serviceTableAddr, sdtCount;
DWORD wantedBytes = len - start;
printf("\nStart:%d\tlen:%d\twantedBytes%d\tptr:%08x\n",start,len,wantedBytes,ptr);
if(wantedBytes >= 4) //
{
serviceTableAddr = *((DWORD *)(&ptr[start]));//获取到SDT在内核中的首地址(即服务表首地址)
printf("\n&serviceTableAddr:%08x\n",&serviceTableAddr);
printf("\n该物理地址的内容为:\n");
for(int i=0;i<16;i++) printf("%08x\t",*(((DWORD *)(&ptr[start])) + i));
}
else
{
printf("\nSorry, an unexpected situation occurred!\n");
}
unmapPhyMem((DWORD)ptr);//取消物理内存映像
printf("\n");
}
}
getchar();
return 0;
}
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!