鸟文一篇,大大飘过...
一个多月前的文章,今天没事做,完善一下了.还有,今年吃的月饼难吃啊...
原理通过搜索枚举peb,然后判定是否是合法的ethread来枚举线程.
参考文章 1:堕落天才(ring0枚举进程,大部分的思路都是从这来的):http://bbs.pediy.com/showthread.php?t=44243
2:分页方式 combojiang(分页机制):http://bbs.pediy.com/showthread.php?t=61327.
多多感激他们的文章了.
#include <ntddk.h>
// 分页方式
#define NONPAE 0x00000001
#define PAE 0x00000002
#define NOTFOUND 0x00000000
#define OBJECT_HEADER_SIZE 0x018
#define OBJECT_HEADER_TYPE_OFFSET 0x008
#define ETHREAD_CID_OFFSET 0x1ec
#define ETHREAD_STARTADDRESS_OFFSET 0x224
#define ETHRAED_KTHREAD_OFFSET 0x000
#define KTHREAD_TEB_OFFSET 0x020
#define ETHREAD_EXITTIME_OFFSET 0x1c8
PETHREAD pEThread;
PEPROCESS pSystem;
ULONG ThreadType;
ULONG uThreadCount;
ULONG ThreadMaxAddr;
// 确定分页模式
ULONG HowPaging()
{
ULONG i;
ULONG end;
UNICODE_STRING ustr;
RtlInitUnicodeString(&ustr, L"MmIsAddressValid"); // 从MmIsAddressValid中查询分页方式
i = (ULONG)MmGetSystemRoutineAddress(&ustr);
for(end = i + 40; i < end; i++) // 只搜索前40个字节
{
if(*(PCHAR)i == 0x2d)
{
if(*(PULONG)(i + 1) == 0x3fd00000)
{
// sub eax, 3fd00000h
return NONPAE;
}
else if(*(PULONG)(i + 1) == 0x3fa00000)
{
// sub eax, 3fa00000h
return PAE;
}
}
}
return NOTFOUND;
}
// va需要调整多少
ULONG AddHowMuch(IN ULONG va, IN ULONG paging)
{
ULONG pte;
ULONG pde;
if(paging == NONPAE)
{
pde = (((va >> 22) << 2 ) & 0xffc) + 0xc0300000;
pte = (((va >> 12) << 2 ) & 0x3FFFFC) + 0xc0000000;
}
else
{
pde = (((va >> 21) << 3) & 0x3FF8) + 0xC0600000;
pte = (((va >> 12) << 3) & 0x7FFFF8) + 0xC0000000;
}
if((*(PULONG)pde & 0x1) != 0)
{
if((*(PULONG)pde & 0x80) != 0)
{
return 0;
}
if((*(PULONG)pte & 0x01) != 0)
{
return 0;
}
else
{
return (0x1000 - 4);
}
}
else
{
if(paging == NONPAE)
{
return (0x400000 - 4);
}
else
{
return (0x200000 - 4);
}
}
}
VOID Unload(IN PDRIVER_OBJECT DriverObject)
{
KdPrint(("Driver Unload!\n"));
}
ULONG GetThreadType()
{
KdPrint(("GetThreadType!\n"));
// 获得线程对象类型的值
ThreadType = *(PULONG)((ULONG)pEThread - OBJECT_HEADER_SIZE + OBJECT_HEADER_TYPE_OFFSET);
return ThreadType;
}
BOOLEAN IsThreadType(IN ULONG i)
{
ULONG objecttype;
if(!MmIsAddressValid((PVOID)i) ||
!MmIsAddressValid((PVOID)(i + 1)) ||
!MmIsAddressValid((PVOID)(i + 2)) ||
!MmIsAddressValid((PVOID)(i + 3)) ||
) // 多谢Fypher的提示.另外这里怕MmIsAddressValid被钩的话就自己实现吧
{
return FALSE;
}
objecttype = i - KTHREAD_TEB_OFFSET - ETHRAED_KTHREAD_OFFSET - OBJECT_HEADER_SIZE +OBJECT_HEADER_TYPE_OFFSET;
if(MmIsAddressValid((PVOID)objecttype) ||
MmIsAddressValid((PVOID)(objecttype + 1) ||
MmIsAddressValid((PVOID)(objecttype + 2) ||
MmIsAddressValid((PVOID)(objecttype + 3))
{
if(*(PULONG)objecttype == ThreadType)
{
return TRUE;
}
}
return FALSE;
}
VOID ShowThread(IN ULONG i, IN ULONG pid)
{
CLIENT_ID cid;
ULONG teb;
ULONG address;
// startaddress, cid, teb
address = *(PULONG)(i + ETHREAD_STARTADDRESS_OFFSET);
cid.UniqueProcess = *(PHANDLE)((ULONG)i + ETHREAD_CID_OFFSET);
cid.UniqueThread = *(PHANDLE)((ULONG)i+ ETHREAD_CID_OFFSET + 4);
teb = *(PULONG)(i + ETHRAED_KTHREAD_OFFSET + KTHREAD_TEB_OFFSET);
if((ULONG)cid.UniqueProcess != pid) // 判断是否是制定进程的线程
{
return ;
}
KdPrint(("address of ethread:0x%x\n", i));
KdPrint(("thread addr:0x%x, tid:%5d, pid:0x%x, teb:0x%x ,_eprocess addr: 0x%x\n", address, (ULONG)cid.UniqueThread, (ULONG)cid.UniqueProcess, teb, i));
uThreadCount++;
}
VOID EnumThread(IN ULONG pid, IN ULONG pagetype)
{
ULONG teb;
ULONG i, j;
for(i = 0x80000000; i < (ULONG)ThreadMaxAddr; i += 4)
{
if(0 != (j = AddHowMuch(i, pagetype)))
{
// 地址是无效的,需要调整
i += j;
continue;
}
teb = *(PULONG)i;
if((teb & 0xfff00fff) == 0x7ff00000 || teb == 0) // teb的值总是0或者前12位为0x7ff
{
if(IsThreadType(i))
{
ShowThread(i - KTHREAD_TEB_OFFSET - ETHRAED_KTHREAD_OFFSET, pid);
}
}
}
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
ULONG pagetype;
pagetype = HowPaging();
if(pagetype == NOTFOUND)
{
KdPrint(("HowPaging() failed!\n"));
return STATUS_UNSUCCESSFUL;
}
pSystem = PsGetCurrentProcess();
ThreadMaxAddr = (ULONG)pSystem + 0x1000; // 这里的不太准确.
EnumThread(4, pagetype); // 列举system进程的thread
return STATUS_SUCCESS;
}
DriverEntry中有个明显的缺点:
ThreadMaxAddr = (ULONG)pSystem + 0x1000;
我家里那台老古董(256内存,赛扬4的u),xp sp2(140多个补丁没打,放弃了)从pspcidtable来看,ethread的变化相当的大,有的竟然是0xff开头的.进程也可能是0xff开头的.
可能是系统版本问题.我笔记本xp sp3的(没带)却可以"比较"正常.
如果非要准确的列举,就只能全部搜2GB了,或者找出系统是怎么分配空间的.
代码编译通过,我没有试过,256的老爷机装vmware不可能了.直接试的话,万一蓝屏有个闪失,光驱也没的,我不想悲剧.有个思想就可以了.
- 标 题:ring0搜索内存枚举线程.
- 作 者:mszjk
- 时 间:2009-09-03 14:33
- 链 接:http://bbs.pediy.com/showthread.php?t=97050