邪恶八进制信息安全团队技术讨论组's Archiver

delphiscn 2007-12-29 11:14

[转载]搜索高端内存枚举进程

搜索高端内存枚举进程

文章作者:gz1x
信息来源:邪恶八进制信息安全团队([url]www.eviloctal.com[/url])

之前写了pspCidTable枚举进程,有朋友mail提到了直接搜索内存高地址来枚举进程。记得好像以前uty提到过这种方法,顺带也自己试试。

第一部分:分页处理
-------------------------------------------------------------------------------------

0x80000000-0xFFFFFFFF 是系统地址空间。一般kd debug的时候很容易看到EPROCESS都集中在0x80000000-0x90000000。挂驱动进去搜吧。 :-)
嗯,不过既然是XP环境下,分页是必然了(要验证的话,系统初始化的时候break下来,CR0里31位是1)。分页了,必要的检查是少不了了。因为虚拟地址是被映射过的,一旦搜索到的页是交换到硬盘上的,那就蓝的海天一色了...

检查也不复杂,如果我们访问的虚拟地址所在页在物理内存里,虚拟地址所在页相应的 PDE,PTE 就都是有效的,那么其他的就扔给CPU转换成物理地址然后访问就完了。如果页不在物理内存中(最不希望的就是在硬盘上的交换文件里),那对应的PDE,PTE 都是无效的,一访问必然是Page-Fault。

看下有效页表项PTE的结构:
kd> dt _HARDWARE_PTE
nt!_HARDWARE_PTE
  +0x000 Valid      : Pos 0, 1 Bit
  +0x000 Write      : Pos 1, 1 Bit
  +0x000 Owner      : Pos 2, 1 Bit
  +0x000 WriteThrough   : Pos 3, 1 Bit
  +0x000 CacheDisable   : Pos 4, 1 Bit
  +0x000 Accessed     : Pos 5, 1 Bit
  +0x000 Dirty      : Pos 6, 1 Bit
  +0x000 LargePage    : Pos 7, 1 Bit
  +0x000 Global      : Pos 8, 1 Bit
  +0x000 CopyOnWrite   : Pos 9, 1 Bit
  +0x000 Prototype    : Pos 10, 1 Bit
  +0x000 reserved     : Pos 11, 1 Bit
  +0x000 PageFrameNumber : Pos 12, 20 Bits

OK,这些多的信息已经足够了...其实我们只关心里面的两个位,0位和7位。当0位为1时,说明PTE有效,我们遍历的页在物理内存中,一切安全...而7位的LargePage表示当前虚拟地址大于等于0x80000000 并且小于0xa0000000(减去0x80000000就得到了物理地址),很和谐,这部分也是安全的....
完事了,转到页目录项PDE,对我们有用的信息不多,0位是1时为Valid,其他的不关心了。 :-)

大概情况是这样:
4G地址空间的1024个页表按顺序被映射到了0xC0000000~0xC03FFFFF的4M地址空间。第一个4M地址空间的页表对应0xC0000000开始的4K,以此类推。而页目录被映射到了0xC0300000开始处的4K地址空间。
也就是说,PTE是按0x1000(4KB)步进,而PDE是按0x400000(4mb)递增。

代码如下:
ULONG ValidatePage(ULONG Addr)
{
  ULONG pte;
  ULONG pde;
  
  pde = 0xc0300000 + (Addr>>22)*4;
  if((*(PULONG)pde & 0x1) != 0)      //判断0位是否为1
  {
    if((*(PULONG)pde & 0x80) != 0)   //判断7位是否为1
    {
      return VALID;
    }
   
    pte = 0xc0000000 + (Addr>>12)*4;
    if((*(PULONG)pte & 0x1) != 0)
    {
      return VALID;
    }
    else
    {
      return PTE_INVALID;
    }
  }
  
  return PDE_INVALID;
}

几个全局变量如PDE_INVALID随便定义个值,值得关注的是怎么获取虚拟地址对应的PTE和PDE。
从WINDOWS内核勾出来的,如下:
#define MiGetPteAddress(va) ((PMMPTE)(((((ULONG)(va)) >> 12) << 2) + PTE_BASE))
#define MiGetPdeAddress(va) ((PMMPTE)(((((ULONG)(va)) >> 22) << 2) + PDE_BASE))
PTE_BASE为0xc0000000,PDE_BASE为0xc0300000。


第二部分:进程搜索
----------------------------------------------------------------------------------
参考原来uty的文章,我们重写之:

VOID SearchProcess(void)
{   
  ULONG i;
  ULONG result;
  ULONG Address;  
  
  for (i = 0x80000000 ;i<0x90000000;i+=4)
  {   
    result = ValidatePage(i);      
    if (result == VALID)
    {  
      Address = *(PULONG)i;
      if ((Address & 0xffff0000) == 0x7ffd0000)
      {         
        if(ValidateProcess(i))
        {
          DbgPrint("EPROCESS: 0x%x ",i-PEB_OFFSET);
          DbgPrint("PID:%4d\tProcessName: %s\n",
            *(PULONG)(i-PEB_OFFSET+PROCESS_ID_OFFSET),
            (PCHAR)(i-PEB_OFFSET+EPROCESS_NAME_OFFSET));

          i += EPROCESS_SIZE;
        }
      }
    }
    else
      if(result == PTE_INVALID)
      {        
        i -=4;
        i += 0x1000;
      }
      else
      {        
        i-=4;
        i+= 0x400000;
      }
  }
  
  DbgPrint("Done.");
}

有几个有意思的地方:

1. if ((Address & 0xffff0000) == 0x7ffd0000)
我没花时间去想怎么在内核里得到PEB,比如EPROCESS遍历链或者ZwQueryInformationProcess,而是直接硬编码,因为获取PEB地址的方法简单的过分,交给ring3部分来处理,如下:

#include <stdio.h>

__inline __declspec(naked) unsigned int GetPEB()
{
__asm
{
  xor esi, esi
  mov esi, fs:[esi + 30H]
  mov eax, esi
  ret
}
}

void main(void)
{
  printf("located at: 0x%0.8X\n",GetPEB());
  getchar();
}

参考我写的《WIN下获取kernel基址的shellcode探讨》。
得到的值是0x7ffd9000,对不同的进程,PEB高位是一样的,我们屏蔽低位就可以了。

2. 和XP平台相关的一些值:
#define EPROCESS_SIZE 0x25C
#define PEB_OFFSET 0x1b0
#define PROCESS_ID_OFFSET 0x084
#define OBJECT_HEADER_SIZE 0x18
#define OBJECT_TYPE_OFFSET 0x8
#define EPROCESS_NAME_OFFSET 0x174
这些都可以用KD自己看到,不废话了。 :-)

3. 判断是不是进程,这个我没做什么更改,把uty牛牛的代码直接搬过来的 *。*

4. 关于改进:
你可以考虑下程序的效率,对循环做点优化,因为毕竟不可能到0x90000000;另外一个就是system进程,枚举它还是有点问题的,自己PsGetCurrentProcess吧。
其他的,暂时想不到了...刚吃饱饭,容易得胃病...



完整的代码:
=========================================================================
#include <ntddk.h>

#define PDE_INVALID 2
#define PTE_INVALID 1
#define VALID 0

#define EPROCESS_SIZE 0x25C
#define PEB_OFFSET 0x1b0
#define PROCESS_ID_OFFSET 0x084
#define OBJECT_HEADER_SIZE 0x18
#define OBJECT_TYPE_OFFSET 0x8
#define EPROCESS_NAME_OFFSET 0x174

VOID WorkThread(IN PVOID pContext);
VOID DriverUnloAd(IN PDRIVER_OBJECT Driver_object);
VOID SearchProcess(VOID);
VOID ShowProcess(ULONG Addr);
ULONG ValidatePage(ULONG Addr);
BOOLEAN ValidateProcess(ULONG i);

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
{
  NTSTATUS dwStAtus;
  HANDLE hThreAd;
   
  DriverObject->DriverUnload = DriverUnloAd;
  
  dwStAtus = PsCreateSystemThread(
    &hThreAd,
    (ACCESS_MASK)0,
    NULL,
    (HANDLE)0,
    NULL,
    WorkThread,
    NULL
    );
  
  return STATUS_SUCCESS;
}
//--------------------------------------------------------------------
VOID DriverUnloAd(IN PDRIVER_OBJECT Driver_object)
{
}
//--------------------------------------------------------------------
VOID WorkThread(IN PVOID pContext)
{
  DbgPrint("WorkThread is ready.");
  SearchProcess();
  
  PsTerminateSystemThread(STATUS_SUCCESS);
}
//--------------------------------------------------------------------
VOID SearchProcess(void)
{   
  ULONG i;
  ULONG result;
  ULONG Address;
  
  DbgPrint("WorkThread is working.");
  for (i = 0x80000000 ;i<0x90000000;i+=4)
  {   
    result = ValidatePage(i);      
    if (result == VALID)
    {  
      Address = *(PULONG)i;
      if ((Address & 0xffff0000) == 0x7ffd0000)
      {         
        if(ValidateProcess(i))
        {
          //DbgPrint("EPROCESS: 0x%x ",i-PEB_OFFSET);
          ShowProcess(i);
          i += EPROCESS_SIZE;
        }
      }
    }
    else
      if(result == PTE_INVALID)
      {        
        i -=4;
        i += 0x1000;//4k
      }
      else
      {        
        i-=4;
        i+= 0x400000;//4mb
      }
  }
  
  for (i = 0xf0000000 ;i<0xffbe0000;i+=4)
  {
    result = ValidatePage(i);   
    if (result == VALID)
    {  
      Address = *(PULONG)i;
      if ((Address & 0xffff0000) == 0x7ffd0000)
      {        
        if(ValidateProcess(i))
        {
          //DbgPrint("EPROCESS: 0x%x ",i-PEB_OFFSET);
          ShowProcess(i);
          i += EPROCESS_SIZE;
        }
      }
    }   
    else
      if(result == PTE_INVALID)
      {        
        i -=4;
        i += 0x1000;//4k
      }
      else
      {
        i-=4;
        i+= 0x400000;//4mb
      }
  }
  
  DbgPrint("Searching is finished.");
}
//--------------------------------------------------------------------
VOID ShowProcess(ULONG i)
{
  DbgPrint("PID:%4d\tProcessName: %s\n",
    *(PULONG)(i-PEB_OFFSET+PROCESS_ID_OFFSET),(PCHAR)(i-PEB_OFFSET+EPROCESS_NAME_OFFSET));
}
//--------------------------------------------------------------------
ULONG ValidatePage(ULONG Addr)
{
  ULONG pte;
  ULONG pde;
  
  pde = 0xc0300000 + (Addr>>22)*4;
  if((*(PULONG)pde & 0x1) != 0)
  {
    if((*(PULONG)pde & 0x80) != 0)
    {
      return VALID;
    }
   
    pte = 0xc0000000 + (Addr>>12)*4;
    if((*(PULONG)pte & 0x1) != 0)
    {
      return VALID;
    }
    else
    {
      return PTE_INVALID;
    }
  }
  
  return PDE_INVALID;
}
//--------------------------------------------------------------------
BOOLEAN ValidateProcess(ULONG i)
{
  NTSTATUS stAtus;
  PUNICODE_STRING pUnicode;
  UNICODE_STRING Process;
  ULONG pObjectType;
  ULONG pObjectTypeProcess;
  
  pObjectTypeProcess = *(PULONG)((ULONG)PsGetCurrentProcess()
    -OBJECT_HEADER_SIZE +OBJECT_TYPE_OFFSET);
  
  if (ValidatePage(i-PEB_OFFSET) != VALID)
  {
    return FALSE;
  }
  
  if (ValidatePage(i-PEB_OFFSET - OBJECT_HEADER_SIZE + OBJECT_TYPE_OFFSET)
      == VALID)
  {
    pObjectType = *(PULONG)(i-PEB_OFFSET - OBJECT_HEADER_SIZE + OBJECT_TYPE_OFFSET);
  }
  else
  {
    return FALSE;
  }
  
  if(pObjectTypeProcess == pObjectType)
  {
    return TRUE;
  }
  
  return FALSE;
}
//--------------------------------------------------------------------


页: [1]
© 1999-2008 EvilOctal Security Team