首页
社区
课程
招聘
SDTRestore v0.2中关于内存映射的疑惑
发表于: 2007-6-6 17:34 8317

SDTRestore v0.2中关于内存映射的疑惑

2007-6-6 17:34
8317
相信很多人都用过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;
}

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (8)
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2


有没有人试过啊?或者自己在使用SDTRestore的过程中遇到这问题?

为什么NtMapViewOfSection只能正确获取内核空间0x80000000-0x82000000附近的内容? 而不能正确获取0x86000000附近的内容?

谁能够给个合理的解释啊 ?
2007-6-7 14:14
0
雪    币: 26
活跃值: (28)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
我也被这个问题困扰了很久。
顶一个,希望有高手来解答一下。
2007-7-3 20:22
0
雪    币: 26
活跃值: (28)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
楼主怎么也不来关注一下,不知道你研究的如何了?
2007-7-4 02:13
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
做成驱动的形式,就可以直接读取0x86000000附近的内容了,无论那部分地址是否在卡巴斯基的驱动内部。

在驱动中也不需要 内存映射了,直接访问这部分地址的内容。

可能这个问题比较老,都没人回应,我也懒得贴代码了,楼上的兄弟自己在驱动中试试看,很容易解决。
2007-7-4 21:57
0
雪    币: 26
活跃值: (28)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
是啊,在驱动中肯定行啊。
可楼主兄弟的帖子说的是在ring3下,
我还指望能解决ring3下这个问题呢。
2007-7-5 10:50
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
我还是没搞懂,在ring3层用NtMapViewOfSection,为什么只能在0x80000000-0x82000000这附近的地址成功映射,而0x86000000附近映射得到错误的数据(并不会失败)。

难道是这里没人关心这个?或是也像我这样不知道

用驱动实现,代价是一样的,在ring3下依然要Administrator权限,依然要会被安全套装捕捉到。所以直接做到驱动里去吧,
2007-7-5 11:38
0
雪    币: 207
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
不是很懂的~!
2007-11-20 22:47
0
雪    币: 1852
活跃值: (504)
能力值: (RANK:1010 )
在线值:
发帖
回帖
粉丝
9
其实你都分析出来了。
既然卡巴会把数据移动到0x86xxxxxx那里去,那么他也会对这个地址的数据进行保护。
如果他不对数据进行保护,干嘛移来移去。
在ring0卡巴只需要封了NtMapViewOfSection,ring3层的就没戏
另外卡巴可能考虑到不能把NtMapViewOfSection完全封杀,于是只对外部程序操作敏感数据时才进行处理。
2007-11-21 16:03
0
游客
登录 | 注册 方可回帖
返回
//