学逆向论坛

找回密码
立即注册

只需一步,快速开始

发新帖

2万

积分

41

好友

1202

主题
发表于 8 小时前 | 查看: 18| 回复: 0
0x0 前言
EAX,ACX等内核级AC都陆续上了CR3的加密,即想办法接管内核异常并将_KPROCESS.DirectoryTableBase的值写份错的,然后在附加等一切需要访问_KPROCESS.DirectoryTableBase的操作触发异常时,在接管的内核异常回调中修复DirectoryTableBase的值

而作为内核"安全"开发者,我们需要来获取进程真实的CR3来读取进程数据检查安不安全~

所以,想办法解密或获取真实CR3是非常有必要的。

0x1 常用方法以及弊端
市场上大多数在售的非法驱动采用的都是 学技术打豆豆 大佬的方法:

通过CR3的页帧中ListEntry.Flink会指向被加密的进程K/EPROCESS结构指针这个原理,结合遍历MmPfnDataBase来不断比对页帧的ListEntry.Flink解密后是否为需要找的进程K/EPROCESS结构指针,从而获取到真实的CR3

但是这个方法有个缺点:
即 在Win10/11部分版本(经测Win11 24H2以及群友反馈部分高版本Win10/11)中,对应的加密算法(MiSetPageTablePfnBuddy函数中可见)与其他版本不同,需要额外处理,适配起来很麻烦

(下图为除特殊版本外,默认的加密K/EPROCESS方法)

无需解密页帧中的进程结构来获取真实CR3

无需解密页帧中的进程结构来获取真实CR3
0x2 新方法无需解密
那么有没有什么办法可以不解密页帧中ListEntry.Flink,就可以得出当前页帧是不是我们需要的CR3呢?有的,我在老外开源的EFI-APSPatchAuthority项目(见附件)中找到了如何通过进程的任意线性地址,逆推出CR3!

但是老外的写法有bug!
但是老外的写法有bug!
但是老外的写法有bug!
下面的代码已修复!!!
下面的代码已修复!!!
下面的代码已修复!!!
(不要直接Copy老外的写法,可以COPY 0x3 核心代码实现部分)

修复办法(下方代码已修复):
原版是通过进程结构下的主模块地址(_EPROCESS.SectionBaseAddress)来逆推CR3!
但是在关闭随机基址后,相同程序的进程主模块地址会相同,以至于有可能获取到其他进程的CR3!
所以我们需要找进程用户层绝对会映射的地址,并且相同程序多开进程不会重复。
经测试,_EPROCESS.Peb字段不管X64还是X86都会映射,并且符合上述条件。


0x3 核心代码实现
typedef union _VirtualAddr
{
    ULONG64 ulValue;
    struct
    {
        ULONG64 ulOffset : 12;
        ULONG64 ulPteIndex : 9;
        ULONG64 ulPdeIndex : 9;
        ULONG64 ulPdpteIndex : 9;
        ULONG64 ulPml4EIndex : 9;
        ULONG64 ulReserved : 16;
    };
} VirtualAddr, *PVirtualAddr;
 
ULONG64 KApi::GetProcessCr3FromPfnByVA(PEPROCESS Process) {
    VirtualAddr szVirtualAddr[2] = { 0 };
    ULONG64 ulPfnDatabase = m_MmPfnDatabase;
    ULONG64 ulCr3Phy = NULL;
    ULONG64 ulPML4E = NULL;
    ULONG64 ulPDPTE = NULL;
    ULONG64 ulPDE = NULL;
    ULONG64 ulPTE = NULL;
    MM_COPY_ADDRESS mcaReadAddr = { 0 };
    SIZE_T uNumberOfBytes;
    int nAbleNumber = 0;
 
    /* 判断是否找到了页帧数据库 */
    if (!ulPfnDatabase)
        return NULL;
 
    /* 判断是否存在PEB地址 */
    szVirtualAddr[0].ulValue = KApi::GetProcessPeb(Process);
    if (szVirtualAddr[0].ulValue == NULL)
        return NULL;
    /* 判断是否存在主模块地址 */
    szVirtualAddr[1].ulValue = *(PULONG64)((ULONG64)Process + KStruct.EPROCESS.SectionBaseAddress);
    if (szVirtualAddr[1].ulValue == NULL)
        return NULL;
 
    Log("Cr3 -> 0x%llx \r\n", KApi::GetProcessCr3(Process));
 
    /* 暴力遍历页帧数据库 */
    for (size_t i = 0; i < 0x1000000; i++) {
 
        /* 只有PteFrame为页帧号的才是CR3(自映射) */
        if (((*(PULONG64)(ulPfnDatabase + SIZEOF_MMPFN * i + 0x28)) & 0xFFFFFFFFFull) == i) {
             
            ulCr3Phy = i << 0xC;
 
            /* 通过PEB与主模块地址来逆推CR3 */
            nAbleNumber = 0;
            for (size_t n = 0; n < 2; n++) {
                mcaReadAddr = { 0 };
                mcaReadAddr.PhysicalAddress.QuadPart = ulCr3Phy + szVirtualAddr[n].ulPml4EIndex * 8;
                if (!NT_SUCCESS(KApi::MmCopyMemory(&ulPML4E, mcaReadAddr, 8, MM_COPY_MEMORY_PHYSICAL, &uNumberOfBytes)))
                    continue;
 
                if ((ulPML4E & 1) == 0)
                    continue;
 
                mcaReadAddr = { 0 };
                mcaReadAddr.PhysicalAddress.QuadPart = (ulPML4E & 0xFFFFFFFFF000ull) + szVirtualAddr[n].ulPdpteIndex * 8;
                if (!NT_SUCCESS(KApi::MmCopyMemory(&ulPDPTE, mcaReadAddr, 8, MM_COPY_MEMORY_PHYSICAL, &uNumberOfBytes)))
                    continue;
 
                if ((ulPDPTE & 1) == 0)
                    continue;
 
                mcaReadAddr = { 0 };
                mcaReadAddr.PhysicalAddress.QuadPart = (ulPDPTE & 0xFFFFFFFFF000ull) + szVirtualAddr[n].ulPdeIndex * 8;
                if (!NT_SUCCESS(KApi::MmCopyMemory(&ulPDE, mcaReadAddr, 8, MM_COPY_MEMORY_PHYSICAL, &uNumberOfBytes)))
                    continue;
 
                if ((ulPDE & 1) == 0)
                    continue;
 
                mcaReadAddr = { 0 };
                mcaReadAddr.PhysicalAddress.QuadPart = (ulPDE & 0xFFFFFFFFF000ull) + szVirtualAddr[n].ulPteIndex * 8;
                if (!NT_SUCCESS(KApi::MmCopyMemory(&ulPTE, mcaReadAddr, 8, MM_COPY_MEMORY_PHYSICAL, &uNumberOfBytes)))
                    continue;
 
                if ((ulPTE & 1) == 0)
                    continue;
                nAbleNumber++;
            }
 
            /* 如果PEB与主模块地址都可以逆推到,即位进程CR3 */
            if (nAbleNumber == 2) {
                return ulCr3Phy;
            }
 
        }
    }
 
    return NULL;
}
老外掀桌子最新驱动读写 模拟鼠标 解密CR3 上市驱动抄袭这一款源码.zip (5.41 MB, 下载次数: 0)


温馨提示:
1.如果您喜欢这篇帖子,请给作者点赞评分,点赞会增加帖子的热度,评分会给作者加学币。(评分不会扣掉您的积分,系统每天都会重置您的评分额度)。
2.回复帖子不仅是对作者的认可,还可以获得学币奖励,请尊重他人的劳动成果,拒绝做伸手党!
3.发广告、灌水回复等违规行为一经发现直接禁言,如果本帖内容涉嫌违规,请点击论坛底部的举报反馈按钮,也可以在【投诉建议】板块发帖举报。
论坛交流群:672619046

小黑屋|手机版|站务邮箱|学逆向论坛 ( 粤ICP备2021023307号 )|网站地图

GMT+8, 2025-12-18 18:38 , Processed in 0.107818 second(s), 39 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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