在>=win10 17134枚举驱动的另一种方法

首先枚举驱动的方法很多,这里不做过多介绍,此文仅简单说明x64系统,x86结构和偏移需重新收集.

在17134版本中,MI_SYSTEM_IMAGE_STATE结构新增了一个成员 即以下的最后一个成员ImageTree,此树保存驱动LdrSection

题外话:MI_SYSTEM_IMAGE_STATE是_MI_SYSTEM_INFORMATION的子结构,_MI_SYSTEM_INFORMATION是nt!MiState

nt!_MI_SYSTEM_IMAGE_STATE
   +0x000 FixupList        : _LIST_ENTRY
   +0x010 LoadLock         : _EX_PUSH_LOCK
   +0x018 LoadLockOwner    : Ptr64 _ETHREAD
   +0x020 LoadLockCount    : Uint4B
   +0x024 FixupLock        : Int4B
   +0x028 FirstLoadEver    : UChar
   +0x029 LargePageAll     : UChar
   +0x030 LastPage         : Uint8B
   +0x038 LargePageList    : _LIST_ENTRY
   +0x048 StrongCodeLoadFailureList : _LIST_ENTRY
   +0x058 SystemBase       : [1] Ptr64 _KLDR_DATA_TABLE_ENTRY
   +0x060 BeingDeleted     : Ptr64 _KLDR_DATA_TABLE_ENTRY
   +0x068 MappingRangesPushLock : _EX_PUSH_LOCK
   +0x070 MappingRanges    : [2] Ptr64 _MI_DRIVER_VA
   +0x080 PageCount        : Uint8B
   +0x088 PageCounts       : _MM_SYSTEM_PAGE_COUNTS
   +0x098 CollidedLock     : _EX_PUSH_LOCK
   +0x0a0 ImageTree        : _RTL_AVL_TREE

然后观察LDR_DATA_TABLE_ENTRY结构,最后几个成员也是变化了很多,在17134之后的版本基本固定

+0x0b8 ParentDllBase    : Ptr64 Void
   +0x0c0 SwitchBackContext : Ptr64 Void
   +0x0c8 BaseAddressIndexNode : _RTL_BALANCED_NODE
   +0x0e0 MappingInfoIndexNode : _RTL_BALANCED_NODE
   +0x0f8 OriginalBase     : Uint8B
   +0x100 LoadTime         : _LARGE_INTEGER

在e0处有一个Node,此Node在17134之前乱码? 17134及之后用于插入ImageTree

遍历ImageTree,得到Node,Node-0xe8即等于ldrSection(pDriverObject->Section),为什么是e8,可了解下BALANCED_NODE.

插入的行为在nt!MiProcessLoaderEntry中(是不是很熟悉???),哈哈.

首先要定位到ImageTree

有个取巧的办法,

MI_SYSTEM_IMAGE_STATE结构和MmSession的结构是相连的,MI_SYSTEM_IMAGE_STATE最后一个成员之后跟着的就是MmSession,那么找MmSeeion-8即可定位到ImageTree

MmSeeion可以通过MmMapViewInSystemSpace下的第一个lea得到.

code:

PVOID GetUndocumentFunctionAddress(
        IN PCWSTR pFunName,
        IN PUCHAR pStartAddress,
        IN UCHAR* pFeatureCode,
        IN ULONG FeatureCodeNum,
        ULONG SerSize,
        UCHAR SegCode,
        LONG AddNum,
        BOOLEAN ByName)
    {
        ULONG dwIndex = 0;
        PUCHAR pFunAddress = NULL;
        ULONG dwCodeNum = 0;

        if (pFeatureCode == NULL)
            return NULL;

        if (FeatureCodeNum >= 50)
            return NULL;

        if (ByName)
        {
            if (pFunName == NULL || !MmIsAddressValid((PVOID)pFunName))
                return NULL;

            UNICODE_STRING usRoutine = { 0 };

            RtlInitUnicodeString(&usRoutine, pFunName);

            pFunAddress = (PUCHAR)MmGetSystemRoutineAddress(&usRoutine);
            if (pFunAddress == NULL)
                return NULL;
        }
        else
        {
            if (pStartAddress == NULL || !MmIsAddressValid(pStartAddress))
            {
                DPRINT("invalid start address:%p\n", pStartAddress);
                return NULL;
            }

            pFunAddress = pStartAddress;
        }

        for (dwIndex = 0; dwIndex < SerSize; dwIndex++)
        {
            __try
            {
                if (!MmIsAddressValid(pFunAddress + dwIndex))
                {
                    //DPRINT("invalid 2:%p\n", pFunAddress + dwIndex);
                    return 0;
                }

                if (pFunAddress[dwIndex] == pFeatureCode[dwCodeNum] || pFeatureCode[dwCodeNum] == SegCode)
                {
                    dwCodeNum++;

                    if (dwCodeNum == FeatureCodeNum)
                        return pFunAddress + dwIndex - dwCodeNum + 1 + AddNum;

                    continue;
                }

                if (dwCodeNum)
                    dwIndex = dwIndex - dwCodeNum + 1;

                dwCodeNum = 0;
            }
            __except (EXCEPTION_EXECUTE_HANDLER)
            {
                DPRINT("exp %p\n", pFunAddress + dwIndex);
                return 0;
            }
        }

        return 0;
    }

    PVOID GetOpCodePoint(PVOID pCallPoint, ULONG offsetToImm, ULONG dwCodeLen)
    {
        if (pCallPoint == NULL || !MmIsAddressValid(pCallPoint))
            return NULL;

#ifndef AMD64
        return (PVOID)((ULONG)pCallPoint + *(ULONG*)((PUCHAR)pCallPoint + offsetToImm) + dwCodeLen);
#else
        LARGE_INTEGER tm;
        LARGE_INTEGER FuncAddress;

        tm.QuadPart = (LONGLONG)pCallPoint;

        ULONG dwOffset = *(ULONG*)((PUCHAR)pCallPoint + offsetToImm);

        FuncAddress.QuadPart = (ULONGLONG)pCallPoint + dwOffset + dwCodeLen;

        tm.LowPart = FuncAddress.LowPart;

        if (!MmIsAddressValid((PVOID)tm.QuadPart))
            return nullptr;

        return (PVOID)tm.QuadPart;

#endif // AMD64

    }

    PVOID GetMmSession()
    {
        PVOID p = nullptr;

        do
        {
            auto leaRdx = GetUndocumentFunctionAddress(L"MmMapViewInSystemSpace", nullptr, (UCHAR*)"\x48\x8d\x15", 3, 0x300, 0x90, 0, true);

            if (leaRdx == nullptr || !MmIsAddressValid(leaRdx))
                break;

            p = GetOpCodePoint(leaRdx, 3, 7);

            if (p == nullptr || !MmIsAddressValid(p))
                p = nullptr;

        } while (false);

        return p;
    }

    PVOID GetSystemImageTree()
    {
        if (*NtBuildNumber < 17134)
            return nullptr;

        auto MmSession = (PULONG_PTR)GetMmSession();

        if (MmSession == nullptr)
            return nullptr;

        auto MmSystemImageTree = MmSession - 1;

        
        /*
        * 见MiState _MI_SYSTEM_INFORMATION
        * MmSession的上一个成员是MI_SYSTEM_IMAGE_STATE结构的最后一个成员 Only >= win10 17134新增
        * 
        * windows将ldr插入此成员进行管理,具体见MI_SYSTEM_IMAGE_STATE结构,entry- ldr对应偏移(0xe8) = ldr头部
        */
        return MmSystemImageTree;
    }

    void EnumDrvSectionByImageTree()
    {
        PRTL_AVL_TREE info = (PRTL_AVL_TREE)GetSystemImageTree();

        if (info == nullptr)
            return;

        PRTL_BALANCED_NODE* stack = (PRTL_BALANCED_NODE*)ExAllocatePoolWithTag(NonPagedPool, 0x5000 * sizeof(PRTL_BALANCED_NODE), 'xxxx');

        if (stack == nullptr)
        {
            DPRINT("%s stack is null.\n");
            return;
        }

        RtlZeroMemory(stack, 0x5000 * sizeof(PRTL_BALANCED_NODE));

        int top = 0;

        RTL_BALANCED_NODE* p = (PRTL_BALANCED_NODE)info->Root;

        auto dwCount = 0;

        do
        {
            while (p != nullptr)
            {
                // _RTL_BALANCED_NODE MappingInfoIndexNode+8 x64only
                PLDR_DATA_TABLE_ENTRY entry = (PLDR_DATA_TABLE_ENTRY)((ULONG_PTR)p - 0xe8);

                DPRINT("ldrSection[%p]:base:0x%p - size:0x%x drvBaseName:%wZ\n", entry, entry->DllBase, entry->SizeOfImage, &entry->BaseDllName);

                /*
                * 如果要扫描 需注意session 驱动
                */
                dwCount++;


                top = top + 1;
                stack[top] = p;
                p = p->Left;
            }

            if (top != 0)
            {
                p = stack[top];
                top = top - 1;
                p = p->Right;
            }

        } while (((top != 0) || (p != nullptr)));

        ExFreePoolWithTag(stack, 'xxxx');

        DPRINT("section count:%d\n", dwCount);
    }

    void DriverUnload(PDRIVER_OBJECT pDriverObject)
    {

    }

    NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegisterPath)
    {
        pDriverObject->DriverUnload = DriverUnload;

        if (*NtBuildNumber < 17134)
            return STATUS_NOT_SUPPORTED;

        EnumDrvSectionByImageTree();

        return STATUS_SUCCESS;
    }

效果图:

在>=win10 17134枚举驱动的另一种方法_第1张图片

最后:在win10启用了影子页之后,很多可以hook的函数都无法hook了,虽然可以把hook函数加入影子页,但这种操作只能在ntos刚启动没多久的情况下执行.

个人感觉以后的内核木马,都以efi,boot起步了.这甚至可以使PatchGuard来保护已经篡改的数据.

 

再再再最后:

几年前我的一篇博客,最后留言可谓是迷茫至极,那也是我黑暗的时光,前段时间,那篇博客收到了一个回复,有关太阳,有关阳光,虽然现在自身已无当时那样的迷茫,但多少还是有一些负能量的东西.

前几天看到一个新闻,有关信仰。让自己不得不重新审视自己。终究是太敏感,太脆弱了。是啊,只有自己转动,才能看到光。谢谢那位陌生人。

真正能让你走远的 都是自律积极和勤奋。共勉。

你可能感兴趣的:(驱动学习,调试,内核,二叉树)