之前写了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 ProcessName: %s
“,
*(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
“,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 ProcessName: %s
“,
*(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;
}
//——————————————————————–
评论关闭。