首页
社区
课程
招聘
[原创]ring0搜索内存枚举线程.
发表于: 2009-9-3 14:33 11377

[原创]ring0搜索内存枚举线程.

2009-9-3 14:33
11377

鸟文一篇,大大飘过...
一个多月前的文章,今天没事做,完善一下了.还有,今年吃的月饼难吃啊...
原理通过搜索枚举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不可能了.直接试的话,万一蓝屏有个闪失,光驱也没的,我不想悲剧.有个思想就可以了.


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 7
支持
分享
最新回复 (12)
雪    币: 284
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
好文,做沙发 膜拜,而学
2009-9-3 17:14
0
雪    币: 7651
活跃值: (523)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
3
绕过太容易了,替换某个东西就可以了~
2009-9-3 18:28
0
雪    币: 251
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
唉,真暴力
运行这个驱动前一定要向各路神佛祷告
2009-9-3 18:56
0
雪    币: 636
活跃值: (174)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
5
MmIsAddressValid 判断的太少了
2009-9-4 08:58
0
雪    币: 231
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
#include <ntddk.h>
哥. 带这种驱动的.要怎么编译.VS 2003 .或 2005 应该怎么设置?
2009-9-4 09:18
0
雪    币: 34
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
7
我就是这地方有点不太懂~_~
看了堕落大侠的那地方判定.貌似是逆向这个函数来实现的.
pte,pde什么的,有空得谷歌下,到时候再贴个完善一点的上来
2009-9-4 13:24
0
雪    币: 251
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
其实MmIsAddressValid的判断只有参考价值
只要不上锁,虚拟地址空间随时可以改变
2009-9-4 13:27
0
雪    币: 34
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
9
期待你放点相关资料,让我们小菜学习下~_~
2009-9-4 20:12
0
雪    币: 243
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
scm
10
支持,希望能完善的更好。
2009-9-5 08:12
0
雪    币: 34
活跃值: (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
11
努力完善中...
2009-9-5 22:26
0
雪    币: 222
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
if(*(PULONG)objectaddress == ThreadType)
{
      return TRUE;
}

这个判断太脆弱了。。。
2009-9-6 13:41
0
雪    币: 636
活跃值: (174)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
13
你要按ULONG读,至少要连续判断4个字节呀。
即把i,i+1,i+2,i+3都MmIsAddressValid一下,因为你其实是要访问这4个地址!

leftup的意思是说,就算用MmIsAddressValid判断了某地址可访问,但也有可能在你真正访问它之前被换页出去。所以最好是MmIsAddressValid了过后赶紧访问,呵呵。当然,加个锁才是最保险的。

另外,大面积的MmIsAddressValid效率会比较低,也可以考虑下RKU里的方法(给个参考,我没试过,不知具体可行不):

physicalAddr = MmGetPhysicalAddress((PVOID)i);

if( physicalAddr.HighPart > g_PhysicalPage.HighPart )
        continue;
if( physicalAddr.HighPart == g_PhysicalPage.HighPart &&
    physicalAddr.LowPart >= g_PhysicalPage.LowPart   )
        continue;
if ( !(physicalAddr.HighPart | physicalAddr.LowPart) )
        continue;
if(start!=(ULONG)MmGetVirtualForPhysical(physicalAddr))
        continue;

Do your things;
2009-9-6 15:24
0
游客
登录 | 注册 方可回帖
返回
//