首页
社区
课程
招聘
[原创]暴力搜索内存枚举进程的学习笔记
发表于: 2008-10-30 14:41 18644

[原创]暴力搜索内存枚举进程的学习笔记

2008-10-30 14:41
18644

在前面的一篇文章中我写了点关于用PspCidTable枚举进程的学习笔记。不过一种方法并不能满足,所以又在网上找了一点关于暴力搜索内存枚举进程的资料,然后写了一些代码。最后写这篇文章作为自己的学习笔记。

既然是搜索,就要明白以下几个问题:
1.关于进程,我们应该搜索他的什么结构?
2.从那里搜索到哪里结束?
3.如果空间很大,如何加速搜索?
4.如何防止访问无效内存空间造成BSOD?

1.关于搜索进程的什么结构,我在网上找了一下,大概搜索的进程的PEB(PEB(Process Environment Block)——

进程环境块)。主要原因是PEB地址的高4位是相同的。
PROCESS 85982da0  SessionId: none  Cid: 03f4    Peb: 7ffde000  ParentCid: 0004
    DirBase: 0b0d0020  ObjectTable: e1010d50  HandleCount:  20.
    Image: smss.exe

PROCESS 85953880  SessionId: none  Cid: 0404    Peb: 7ffdc000  ParentCid: 03f4
    DirBase: 0b0d0040  ObjectTable: 00000000  HandleCount:   0.
    Image: autochk.exe

PROCESS 8597eda0  SessionId: 0  Cid: 043c    Peb: 7ffd9000  ParentCid: 03f4
    DirBase: 0b0d0080  ObjectTable: e17eac78  HandleCount: 608.
    Image: csrss.exe

PROCESS 858cb618  SessionId: 0  Cid: 0458    Peb: 7ffd7000  ParentCid: 03f4
    DirBase: 0b0d00a0  ObjectTable: e1a5f9e8  HandleCount: 491.
    Image: winlogon.exe

没错,都是7ffdxxxx。
我们知道,在2000系统中PEB的地址是固定的7FFDF000h。可是在Xp SP2的系统后这个值就发生了变化,但是其地址的

高4位却没有变化。具体可以看看这些代码。

PVOID HighestVadAddress;
LARGE_INTEGER CurrentTime;
HighestVadAddress = (PVOID) ((PCHAR)MM_HIGHEST_VAD_ADDRESS + 1);
KeQueryTickCount (&CurrentTime);
CurrentTime.LowPart &= ((X64K >> PAGE_SHIFT) - 1);
if (CurrentTime.LowPart <= 1) {
  CurrentTime.LowPart = 2;
  }
HighestVadAddress = (PVOID) ((PCHAR)HighestVadAddress - (CurrentTime.LowPart<<PAGE_SHIFT));

可以看出,PEB地址的设置跟时间是有关系的。在WRK中可以发现这样的定义:
#define X64K (ULONG)65536
#define MM_HIGHEST_VAD_ADDRESS ((PVOID)((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (64 * 1024)))

用WinDbg可以观察到MM_HIGHEST_USER_ADDRESS
lkd> dd MmHighestUserAddress
80558e3c  7ffeffff 00000000 00000001 00000000

那么MM_HIGHEST_VAD_ADDRESS 就是7ffdffff了,那么HighestVadAddress就是7ffe0000。而我们知道

PAGE_SHIFT其实就是12,那么((X64K >> PAGE_SHIFT) - 1)就是0xF。PEB变化的情况就只剩下16种了,接下来的

一个条件判断让0到F十六种情况一下子剩下14种了。最后用CurrentTime.LowPart<<PAGE_SHIFT的范围就是在

0x2000到0xf000中,这样HighestVadAddress 的地址就在7ffd1000到7ffde000之间的14种情况了。

2.从哪里开始搜索,到哪里结束呢?
用WinDbg的!process命令观察发现所有进程的EProcess结构地址,可以发现都

在0x80000000到"system"进程的EPROCESS之间。那么我们的搜索范围就确定了下来。至于Window怎么分配这些空间

的我还没有研究,等有时间再说吧。

3、4.如果空间很大,如何加速搜索?这个问题网上的前辈们也提供的很好的方法。那就是验证PTE和PDE的有效性。这

里就要说到地址的转换问题了。同时这样还可以有效避免BSOD。
虚拟地址->虚拟地址对应的PDE地址PDE_Address=(VirtualAddress>>22)*4+0xC0300000 
虚拟地址->虚拟地址对应的PTE地址PTE_Address=(VirtualAddress>>12)*4+0xC0000000
typedef struct _HARDWARE_PTE_X86
{
     ULONG Valid:1;
     ULONG Write:1;
     ULONG Owner:1;
     ULONG WriteThrough:1;
     ULONG CacheDisable:1;
     ULONG Accessed:1;
     ULONG Dirty:1;
     ULONG LargePage:1;
     ULONG Global:1;
     ULONG CopyOnWrite:1;
     ULONG Prototype: 1;
     ULONG reserved: 1;
     ULONG PageFrameNumber:20;
} HARDWARE_PTE_X86, *PHARDWARE_PTE_X86;
看看PTE的结构可知我们主要要判断Valid,LargePage。
然后判断其有效性,达到后来加速的效果。

根据前辈的代码,我自己重写了一下判断函数。
NTSTATUS NTAPI MmIsAddressInPageValid( IN PULONG Address ,OUT PULONG pType )
{
#ifndef PAGE_VALID
  
#define PDE_VALID 0x1
#define PTE_VALID 0x2
#define PDE_INVALID 0x3
#define PTE_INVALID 0x4
#define ADDR_INVALID 0x0
  
#endif

  ULONG ulAddr;
  ULONG ulPTE;
  ULONG ulPDE;

  ulAddr = (ULONG)Address;
  
  ulPDE = 0xc0300000 + ( ulAddr >> 22 ) * 4;
  //KdPrint(( "ulPDE%x" ,ulPDE ));
  if ( !MmIsAddressValid( (PULONG)ulPDE ) )
  {
    if ( pType != NULL )
    {
      *pType = ADDR_INVALID;
    }
    return STATUS_UNSUCCESSFUL;
  }
  if ( ( (*(PULONG)ulPDE) & 0x1 ) != 0 )
  {
    if ( ( (*(PULONG)ulPDE) & 0x80 ) != 0 )
    {
      if ( pType != NULL )
      {
        *pType = PDE_VALID;
      }
      return STATUS_SUCCESS; 
    }
    ulPTE = 0xc0000000 + ( ulAddr >> 12 ) * 4;
    //KdPrint(( "ulPTE:%x" ,ulPTE ));
    if ( !MmIsAddressValid( (PULONG)ulPTE ) )
    {
      if ( pType != NULL )
      {
        *pType = ADDR_INVALID;
      }
      return STATUS_UNSUCCESSFUL;
    }
    if ( ( (*(PULONG)ulPTE) & 0x1 ) != 0 )
    {
      if ( pType != NULL )
      {
        *pType = PTE_VALID;
      }
      return STATUS_SUCCESS;
    }
    else
    {
      if ( pType != NULL )
      {
        *pType = PTE_INVALID;
      }
      return STATUS_UNSUCCESSFUL;
    }
  }
  

  if ( pType != NULL )
  {
    *pType = PDE_INVALID;
  }
  return STATUS_UNSUCCESSFUL;
}

最后要做的事情就是写几个函数,得到自己要的变量。网上这样的代码很多,我只是重写了一下,呵呵。

得到Process Type的函数。
ULONG NTAPI GetProcessType()
{
  ULONG ulHeader;
  ULONG Type;
  ulHeader = (ULONG)PsGetCurrentProcess();
  ulHeader = OBJECT_TO_OBJECT_HEADER( ulHeader );
  Type = *(PULONG)(ulHeader + OBJECT_TYPE_OFFSET);
  return Type;
}

判断是否是一个有效的EPROCESS的地址。
NTSTATUS NTAPI MmIsProcessAddr( IN PULONG Address )
{
  ULONG ulProcessType;
  ULONG ulAddr;
  ULONG ObjectTypeAddr;
  ULONG ObjectType;

  ulAddr = (ULONG)Address;
  if ( !NT_SUCCESS( MmIsAddressInPageValid( Address ,NULL ) ) )
  {
    KdPrint(( "Address is invalid" ));
    return STATUS_UNSUCCESSFUL;
  }
  
  ObjectTypeAddr = OBJECT_TO_OBJECT_HEADER( ulAddr ) + OBJECT_TYPE_OFFSET; 
  
  if ( !NT_SUCCESS( MmIsAddressInPageValid( (PULONG)ObjectTypeAddr ,NULL ) ) )
  {
    KdPrint(( "ObjectTypeAddr is invalid" ));
    return STATUS_UNSUCCESSFUL;
  }
  
  ObjectType = *(PULONG)ObjectTypeAddr;

  if ( ObjectType == GetProcessType() )
  {
    return STATUS_SUCCESS;
  }

  return STATUS_UNSUCCESSFUL;

}

显示进程的函数。
void NTAPI ShowProcessInformation( IN PULONG Address )
{
  PLARGE_INTEGER pExitTime;
  ULONG PID;
  PUCHAR pFileName;

  pExitTime = (PLARGE_INTEGER)( (ULONG)Address + EXIT_TIME_OFFSET );
  if ( pExitTime->QuadPart != 0 )
  {
    return;
  }
  PID = *(PULONG)( (ULONG)Address + PROCESS_ID_OFFSET );
  pFileName = (PUCHAR)( (ULONG)Address + FILE_NAME_OFFSET );

  KdPrint(( "ID:%d\t\tName:%s\n" ,PID ,pFileName ));
}

得到PEB地址的高4位,让然这里不能用System的PEB。因为System的PEB总是0。
PROCESS 867bc2c0  SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
    DirBase: 01038000  ObjectTable: e1000b80  HandleCount: 506.
    Image: System

ULONG NTAPI GetPebAddressType()
{
  ULONG PebAddr;
  ULONG ulProcessAddr;
  PULONG pSystem;

  pSystem = (PULONG)PsGetCurrentProcess();
  ulProcessAddr = (ULONG)(((PLIST_ENTRY)( (ULONG)pSystem + PROCESS_LINK_OFFSET ))-

>Flink)\
            - PROCESS_LINK_OFFSET;
  PebAddr = *(PULONG)( ulProcessAddr + PEB_OFFSET );

  return ( PebAddr & 0xffff0000 );
}

最后的这个就是搜索函数。
void NTAPI DetectProcessBySearchMemory()
{
  PULONG pSystem;
  ULONG ulPoint;
  ULONG ulPebAddr;
  ULONG ulPageSign;

  pSystem = (PULONG)PsGetCurrentProcess();

  for ( ulPoint = 0x80000000 ;ulPoint < (ULONG)pSystem ;ulPoint += 4 )
  {
    if ( NT_SUCCESS( MmIsAddressInPageValid( (PLONG)ulPoint ,&ulPageSign ) ) )
    {
      ulPebAddr = *(PULONG)ulPoint;

      if ( ( ulPebAddr & 0xffff0000 ) == GetPebAddressType() )
      {
        if ( NT_SUCCESS( MmIsProcessAddr( (PULONG)(ulPoint - 

PEB_OFFSET) ) ) )
        {
          ShowProcessInformation( (PULONG)( ulPoint - 

PEB_OFFSET ) );
        }
      }
    }
    else if ( ulPageSign == PTE_INVALID )
    {
      if ( ulPoint != 0x80000000 )
      {
        ulPoint -= 4;
      }
      
      ulPoint += 0x1000;
    }
    else if ( ulPageSign == PDE_INVALID )
    {
      if ( ulPoint != 0x80000000 )
      {
        ulPoint -= 4;
      }
      ulPoint += 0x400000;
    }
    else
    {
      KdPrint(( "Error!" ));
    }
  }
}


参考文档:
加密与解密(第三版)
JIURL玩玩Win2k内存篇 分页机制
ring0检测隐藏进程 
WRK1.2


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 7
支持
分享
最新回复 (19)
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
咱居然是第一个。。。记下。。。。学习ing
2008-10-30 17:07
0
雪    币: 321
活跃值: (271)
能力值: ( LV13,RANK:1050 )
在线值:
发帖
回帖
粉丝
3
写的很详细,精彩。
2008-10-30 17:41
0
雪    币: 86
活跃值: (56)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
4
哇。。。混了这么久,第一个精华~~~幸福ing~~~
2008-10-30 17:45
0
雪    币: 231
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
Good Job
2008-10-30 18:01
0
雪    币: 327
活跃值: (1242)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
学习到东西了。
2008-10-30 21:17
0
雪    币: 22
活跃值: (443)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
借个位置问大牛门一个问题呀

为什么我在VM虚拟机里用Dbgview.exe 捕获不到消息呢

需要对Dbgview有什么特殊的设置吗
2008-10-30 23:57
0
雪    币: 0
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
回ls,捕获不到因为lz大大的可能是win2000的系统,所以PEB_OFFSET,PROCESS_LINK_OFFSET,PROCESS_ID_OFFSET都要自己设。。。。
2008-10-31 00:00
0
雪    币: 234
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
再回楼上..他可能不是这个意思吧..
2008-10-31 01:07
0
雪    币: 86
活跃值: (56)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
10
我用的2003的系统,因为可以借用WRK调试。2003 + WRK是个好东西,建议和我一样的菜菜们可以这样来学习。
2008-10-31 07:25
0
雪    币: 229
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
11
不错,,,学习,,,
2008-10-31 09:51
0
雪    币: 375
活跃值: (12)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
12
Capture Kernel你勾了没~~
2008-10-31 21:13
0
雪    币: 22
活跃值: (443)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
谢谢楼上几位大大的回复 还是没搞好

我在主机里是可以用的 虚拟机器里怎么设置也是不行 真晕了

系统为xpsp2  vm 是 6.0的.

RP问题?

上传的附件:
2008-11-1 02:21
0
雪    币: 86
活跃值: (56)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
14
[QUOTE=靴子;528875]谢谢楼上几位大大的回复 还是没搞好

我在主机里是可以用的 虚拟机器里怎么设置也是不行 真晕了

系统为xpsp2  vm 是 6.0的.

RP问题?

[/QUOTE]

如果你就按照附件里的代码编译运行后当然看不到了。我测试的系统是2003的。而你用的是Xp,几个偏移都不同。

请看代码中的这个部分
#define  PEB_OFFSET          0x1a0
#define  FILE_NAME_OFFSET    0x164
#define  PROCESS_LINK_OFFSET 0x098
#define  PROCESS_ID_OFFSET   0x094
#define  EXIT_TIME_OFFSET    0x088

你可以把其中的偏移改为Xp的试试。
2008-11-1 09:29
0
雪    币: 22
活跃值: (443)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
额.. ls误会我的意思了

我是说我在虚拟机用那个Dbgview.exe 捕获不到任何消息

并不是指这个驱动 其他消息也捕获不到

正常的情况下 创建个进程他都有消息的, 可是在虚拟机什么消息也没有.
2008-11-1 13:09
0
雪    币: 100
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
你是怎么在WRK中找到下面这些代码的
我怎么找不到?检索有什么技巧啊?
PVOID HighestVadAddress;
LARGE_INTEGER CurrentTime;
HighestVadAddress = (PVOID) ((PCHAR)MM_HIGHEST_VAD_ADDRESS + 1);
KeQueryTickCount (&CurrentTime);
CurrentTime.LowPart &= ((X64K >> PAGE_SHIFT) - 1);
if (CurrentTime.LowPart <= 1) {
  CurrentTime.LowPart = 2;
  }
HighestVadAddress = (PVOID) ((PCHAR)HighestVadAddress - (CurrentTime.LowPart<<PAGE_SHIFT));

#define X64K (ULONG)65536
#define MM_HIGHEST_VAD_ADDRESS ((PVOID)((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (64 * 1024)))
2008-11-28 13:35
0
雪    币: 215
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
写的很详细,学习……
2008-11-28 13:48
0
雪    币: 215
活跃值: (19)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
同问,dbgview在vmware里什么消息都捕获不到,在实机正常情况连鼠标移动都有记录的,vmware里一片空白
2008-12-1 18:26
0
雪    币: 202
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
帖子有内容,相当不错

但是问题界定似乎不清晰,要搜索的目标不是PEB而是PEB的地址,搜索PEB地址的目的是为了搜索到EPROCESS,因为PEB地址是EPROCESS中的一个域
2008-12-25 16:09
0
雪    币: 412
活跃值: (30)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
20
Mark一记,接着学习。
2008-12-27 23:08
0
游客
登录 | 注册 方可回帖
返回
//