首页
社区
课程
招聘
[学习]根据pid手动遍历进程句柄表思路
发表于: 5天前 523

[学习]根据pid手动遍历进程句柄表思路

5天前
523

大致方向

1.根据传入pid获取EPROCESS对象, GetProcessByPid是我自己实现的, 方式有点拉是硬取, 思路是获取当前进程, 找到活动进程链表, 依次遍历这个链表知道pid匹配则取出对象

1
2
3
4
5
6
7
8
//根据pid获取eprocess
UINT_PTR v_EPROCESS;
BOOLEAN isSuccess = GetProcessByPid(pid, &v_EPROCESS);
//获取EPROCESS失败则是pid不正确 直接返回
if (!isSuccess)
{
    return;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//通过pid获取EPROCESS
BOOLEAN GetProcessByPid(UINT64 pid,UINT_PTR* v_EPROCESS)
{
    //获取当前进程
    PEPROCESS CurrentProcess = PsGetCurrentProcess();
    //获取ActiveProcessLinks活动进程链表
    PLIST_ENTRY Head = (PLIST_ENTRY)((PUCHAR)CurrentProcess + 0x2f0);
    //临时链表头
    PLIST_ENTRY Entry = Head;
    //do while 循环遍历链表
    do {
        //回到EPROCESS头
        PEPROCESS Process = (PEPROCESS)((PUCHAR)Entry - 0x2f0);
        //获取当前遍历pid
        HANDLE tempPid = PsGetProcessId(Process);
        //获取ImageFileName
        CHAR imageName[16] = { 0 };
        GetImageNameByEProcess(Process,&imageName);
        //RtlCopyMemory(imageName,(PUCHAR)Process + 0x450, 15);// +0x450 ImageFileName偏移
        //如果pid相等则返回
        if (pid == tempPid)
        {
            *v_EPROCESS = Process;
            PZY_PRINT("通过pid获取EPROCESS成功! pid=%llu imageName=%s", (UINT64)tempPid, imageName);
            return TRUE;
        }
        Entry = Entry->Flink;
    } while (Entry != Head);//不等于表头则继续循环
    return FALSE;
}

2.根据EPROCESS对象获取句柄表_HANDLE_TABLE(我直接使用硬编码偏移了, 没有用结构)
图片描述

1
2
3
//获取句柄表
HANDLE_TABLE* v_HANDLE_TABLE = (HANDLE_TABLE*)poi(v_EPROCESS + 0x418);//这里+0x418直接取硬编码 比较方便 但是不同系统可能不一致
UINT_PTR TableCode = v_HANDLE_TABLE->TableCode;

3.取出句柄表中的TableCode取最后3字节获取句柄表等级
图片描述

1
2
3
//句柄表等级
UINT64 level = TableCode & 0x3;
UINT64 TableCodeBase = TableCode & ~3;

4.根据句柄表等级分别遍历句柄表, 句柄表中取出句柄项遍历, 在我的系统上entry为0x10大小, 所以0x1000/0x10=0x100, 我根据windbg取出的大小是0xc, 多了个Spare2保留字段, 但是我查看数据发现实际上只用到0x10大小, 所以还是以数据为准
图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//level == 0
for (int i = 0;i < 0x100;i++)
{
     
}
//level == 1
for (int i = 0;i < 0x100;i++)
{
    for (int j = 0;j < 0x100;j++)
    {
         
    }
}
//level == 2
for (int i = 0;i < 0x100;i++)
{
    for (int j = 0;j < 0x100;j++)
    {
        for (int k = 0;k < 0x100;k++)
        {
 
        }
    }
}

5.根据句柄项中的InfoTable来判断此句柄项是否有效(这是我测试后选用的判断方法 大家有其他方法也可以自行选择)

1
2
3
4
5
6
7
8
9
for (int i = 0;i < 0x100;i++)
{
    //如果句柄不为空则继续
    if (v_HANDLE_TABLE_ENTRY->InfoTable)
    {
        
    }
    v_HANDLE_TABLE_ENTRY += 1;
}

6.获取句柄值, 句柄值每次间隔4个单位, 用i*4即可取出, 如果是二级判断则是j * 4 + 0x100 * i, 三级以此类推

1
2
3
4
5
//句柄值 level == 0
UINT64 handleCode = i * 4;
//句柄值 level == 1
UINT64 handleCode = j * 4 + 0x100 * i;
//句柄值 level == 2 这个还没有实现 因为项目没用到3三级表

7.根据句柄项中的InfoTable解密得到Object对象, 下面的算法也是直接从系统函数逆出来的, 但是大体都是一样的, 我用的是win10, 不一样的地方可能在于0x0FFFFFFFFFFFFFFF0, win7应该是0x0FF0, 具体参数系统函数, 0x0FFFFFFFFFFFFFFF0这个偏移只有在一级以上才用到, 用来偏移句柄表的, 例如2级句柄表中的二级结构, 我这里的算法是0xe30c72eb2530ffff>>0x10=0xffffe30c72eb2530, 0xffffe30c72eb2530&0x0FFFFFFFFFFFFFFF0=0xffffe30c72eb2530, 0xffffe30c72eb2530+0x30=0xffffe30c72eb2560
图片描述

1
2
//解析句柄表的InfoTable得到句柄对象
UINT_PTR handleObject = (((INT64)poi(v_HANDLE_TABLE_ENTRY) >> 0x10) & 0x0FFFFFFFFFFFFFFF0) + 0x30;

8.根据句柄对象判断句柄类型, 我的做法是获取了句柄类型对象后取出Name成员进行判断, 进程就是Process字符
图片描述

1
2
3
4
5
6
7
8
9
//获取句柄类型
UINT_PTR v_OBJECT_TYPE = MyObGetObjectType(handleObject);
//process类型判断字符
UNICODE_STRING ProcessType;
RtlInitUnicodeString(&ProcessType, L"Process");
//得到句柄名称
UNICODE_STRING ObjectTypeName = GetObjectTypeNameByObjectType(v_OBJECT_TYPE);
//判断句柄类型 是否是Process
if (RtlEqualUnicodeString(&ObjectTypeName, &ProcessType, TRUE))

9.测试结果: 这里我用的ce来遍历测试, 刚打开ce时只有2个进程句柄分别都是自己cheatengine-x86_64-SSE4-AVX2.exe, 在附加了进程之后会多一个进程句柄, 我选择附加的是vmtoolsd.exe, 当你改变附加进程后遍历依旧是正确的说明遍历算法就是正确的, 以此验证
图片描述
图片描述

1
2
3
4
pzy: [R0][TraverseHandleTable:194] ================ 开始遍历 [cheatengine-x8][3844] 句柄表 ================
pzy: [R0][TraverseHandleTable:290] ObjectTypeName: Process PID=3844 Handle=2EC EPROCESS=FFFFE30C72A9B0C0 Attributes=1FFFFF fileName=cheatengine-x86_64-SSE4-AVX2.exe 
pzy: [R0][TraverseHandleTable:290] ObjectTypeName: Process PID=3844 Handle=36C EPROCESS=FFFFE30C72A9B0C0 Attributes=1FFFFF fileName=cheatengine-x86_64-SSE4-AVX2.exe 
pzy: [R0][TraverseHandleTable:290] ObjectTypeName: Process PID=5192 Handle=148 EPROCESS=FFFFE30C7260C340 Attributes=1FFFFF fileName=vmtoolsd.exe 

10.之后就是拓展自己的功能了, 总体难度不高 就是前期思路不清楚导致我踩了不少坑, 并且windbg似乎有些bug, 建议多重启虚拟测试机, 我遇到的bug例如: 有时断点停不下来, 值取不到等等, 以为是自己错了其实重启后又正常了


传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 5天前 被mb_binusgki编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回