鱼C论坛

 找回密码
 立即注册
查看: 156|回复: 1

[技术交流] 私有句柄表降权保护进程不被ce附加

[复制链接]
发表于 2024-4-3 16:24:26 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
之前的帖子讨论过hook sddt,VEH保护进程不被ce附加,今天尝试句柄表降权的方式保护进程不被ce附加。

原理:在windows操作系统上有私有句柄表和全局句柄表,用openprocess打开某个进程的时候会在私有句柄表里创建一个句柄,并且指向被打开进程的EPROCESS结构体。
私有句柄表一共64位,其中31~47这个位置是私有句柄表的属性,可以设置一些权限,所以通过查找ce的私有句柄表,把它的句柄表权限改为不可读,就无法读出内存了。

第一步,根据进程名遍历进程,之前的帖子讨论过原理,不在赘述:
ULONG FindTargetPid(char* processname)
{
        //遍历进程
        ULONG process = (ULONG)PsGetCurrentProcess();
        PLIST_ENTRY processlist = (PLIST_ENTRY)(process + 0xb8);
        PLIST_ENTRY pnext = processlist->Flink;
        while (pnext != processlist)
        {
                char* name = (char*)((ULONG)pnext - 0xb8 + 0x16c);
                if (strcmp(name, processname) == 0)
                {
                        PCLIENT_ID client_id = (PCLIENT_ID)((ULONG)pnext - 0xb8 + 0xB4);//
                        return  (ULONG)client_id->UniqueProcess;//返回目标进程pid
                }
                pnext = pnext->Flink;
        }
        return 0;
}
第二部:
遍历私有句柄表:
因为这个事情要一直进程,知道被保护的进程退出,所以考虑写一个死循环,这里涉及内核中创建进程的操作,
使用系统的api:PsCreateSystemThread(&hthread, THREAD_ALL_ACCESS, NULL, NULL, NULL, threadproc,peprocess);
回调函数是threadproc,同时把它的引用计数器-1避免检测
        KeInitializeEvent(&kevent,SynchronizationEvent, FALSE);
        ULONG pid = FindTargetPid("abc.exe");//查找目标进程的pid
        if (pid == 0)
        {
                return STATUS_SUCCESS;
        }

        PEPROCESS peprocess;
        NTSTATUS status=PsLookupProcessByProcessId((HANDLE)pid, &peprocess);
        HANDLE hthread = 0;
        if (NT_SUCCESS(status))
        {
                ObReferenceObject(peprocess);
                PsCreateSystemThread(&hthread, THREAD_ALL_ACCESS, NULL, NULL, NULL, threadproc,peprocess);
        }

由于进程的线程结束函数要采用被动的方法,所以创建一个event事件,设定每1s修改一个句柄权限,用waitforsignlobject等待超时。
这部分不赘述
void threadproc(PVOID peprocess)
{
        PEPROCESS pTargetProcess;
        LARGE_INTEGER timeout=RtlConvertLongToLargeInteger(-10*2000*1000);//1s
        ethread=KeGetCurrentThread();
        while (1)
        {       
                NTSTATUS status=KeWaitForSingleObject(&kevent, Executive, KernelMode, FALSE, &timeout);
                if (status==STATUS_TIMEOUT)//超时,说明没等到信号,继续执行
                {
                        for (ULONG i = 0; i < 10000; i++)
                        {
                                NTSTATUS status = PsLookupProcessByProcessId((HANDLE)(i * 4), &pTargetProcess);
                                if (NT_SUCCESS(status))
                                {
                                        SetHandAccess(peprocess, pTargetProcess);
                                }
                        }
                }
                else
                {
                        DbgPrint("teminate\n");
                        PsTerminateSystemThread(STATUS_SUCCESS);//终止线程
                }
               
        }
}
第三步:遍历私有句柄表,这里采用EPROCESS+0XF4的位置获取ObjectTable的方式获得TableCode,同时判断句柄表的层级,以及抹除低位的操作,最终得到句柄值,根据ms提供的文档修改读权限。
void SetHandAccess(PVOID srcProcess, PVOID targetProcess)
{

        ULONG pObjectTable = *(ULONG*)((ULONG)targetProcess + 0xf4);
        ULONG TableCode = *(ULONG*)pObjectTable;
        ULONG TableDeeth = TableCode & 0xF;//拿到最后一位

        switch (TableDeeth)
        {
                case 0:
                {
                        for (ULONG j = 0; j < 512; j++)
                        {
                                TableCode &= 0xFFFFFFF0;//TableCode最后一位置0
                                ULONG handle = (ULONG) * (ULONG*)(TableCode + j * 8);
                                if (MmIsAddressValid((PVOID)handle))
                                {
                                        handle &= 0xFFFFFFF8;//
                                        if ((ULONG)(handle + 0x18) == (ULONG)srcProcess)
                                        {
                                                ULONG* handleaddr = (ULONG*)(TableCode + j * 8);
                                                handleaddr++;//+4
                                                if (MmIsAddressValid(handleaddr))
                                                {
                                                        DbgPrint("找到了%x", *handleaddr);//001fffff
                                                        //001fffff`88f1f361
                                                        *handleaddr = *handleaddr& 0xFFFFFFCF;//取消读权限
                                                }
                                        }
                                }
                        }
                        break;
                }
                case 1:
                {
                        for (ULONG i = 0; i < 1024; i++)
                        {
                                for (ULONG j = 0; j < 512; j++)
                                {
                                        TableCode &=0xFFFFFFF0;//TableCode最后一位置0
                                        ULONG subtable = *(ULONG*)((ULONG)TableCode + i * 4);
                                        if (subtable == 0)
                                        {
                                                break;
                                        }
                                        ULONG handle = (ULONG) * (ULONG*)(subtable + j * 8);
                                        if (MmIsAddressValid((PVOID)handle))
                                        {
                                                handle &= 0xFFFFFFF8;//
                                                if ((ULONG)(handle + 0x18) == (ULONG)srcProcess)
                                                {
                                                        ULONG* handleaddr = (ULONG*)(TableCode + j * 8);
                                                        handleaddr++;
                                                        if (MmIsAddressValid(handleaddr))
                                                        {
                                                                DbgPrint("找到了%x", *handleaddr);
                                                                *handleaddr = (*handleaddr) & 0xFFFFFFCF;//取消读权限
                                                        }
                                                }
                                        }
                                }
                        }
                        break;
                }
                default:
                        break;
        }
}
最后驱动卸载:
void unload(PDRIVER_OBJECT pDriver)
{
        //uninstallhook();
        KeSetEvent(&kevent, 0, FALSE);
        NTSTATUS status = KeWaitForSingleObject(ethread,Executive,KernelMode,FALSE,NULL);
        if (NT_SUCCESS(status))
        {
                DbgPrint("exit");
        }
}


总结:调试了好久,蓝屏无数次,太恶心了。
感谢51hook的老师,我看了d版视频,大一穷学生,实在不好意思。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2024-4-3 16:24:51 | 显示全部楼层
首先,你提到的句柄表权限改变的方式保护进程不被 Cheat Engine(CE)附加是可行的。不过,这种方法并不是百分之百的防护,因为 CE 还是可能使用其他方式绕过这种保护。

其次,你的代码中有一些问题。首先,在遍历进程时,你使用了硬编码的地址值,这样的方式并不可靠。操作系统的内部结构在不同的 Windows 版本和架构下可能会有所变化。正确的做法是使用内核函数来遍历进程并根据进程名进行匹配。

另外,在线程函数 threadproc 中,你通过 PsLookupProcessByProcessId 函数获取目标进程的 EPROCESS 结构,并且使用 ObReferenceObject 函数增加了引用计数。但是在循环中,你没有调用 ObDereferenceObject 函数来减少引用计数,这样会导致内存泄漏。正确的做法是在使用完毕后调用 ObDereferenceObject 函数来释放引用。

最后,在 SetHandAccess 函数中,你通过计算得到句柄的地址,并修改句柄的权限。但是操作句柄表需要特权,而驱动程序一般都是以普通用户权限运行的,所以你的驱动程序需要获取特权才能执行这个操作。

希望我的回答对你有所帮助,如果还有其他问题,请随时向我提问!

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-5-2 04:48

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表