欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]drv experiment : FOLLOW_JMP parse

[原创]drv experiment : FOLLOW_JMP parse

在nt!*API中, 有些入口地址处附近有5个字节的JMP代码.
用FOLLOW_JMP宏可以算出JMP到的绝对地址.

#define FOLLOW_JMP( JMPADDR ) ((ULONG)(JMPADDR) + 5 + *(ULONG*)((ULONG)(JMPADDR) + 1))

这个宏不好理解, 且是硬编码, 用函数实现了一个FOLLOW_JMP, 通用一些.
根据给定的内核API地址, 给定JMP命令前面的字节数量, 分析从JMP命令得到JMP到的绝对地址.
/// @file           \prjGetJmpCmdAddr\main.cpp
/// @brief          FOLLOW_JMP 宏不太好理解, 换成函数来分析FOLLOW_JMP的实现
/**
#define FOLLOW_JMP( JMPADDR ) \
((ULONG)(JMPADDR) + 5 + *(ULONG*)((ULONG)(JMPADDR) + 1))
FOLLOW_JMP 适用于 nt!FsRtlIsPagingFile
*/
#include <Ntddk.h>
    /**
    nt!FsRtlIsPagingFile:
    804ed978 8bff            mov     edi,edi
    804ed97a 55              push    ebp
    804ed97b 8bec            mov     ebp,esp
    804ed97d 5d              pop     ebp
    804ed97e e907dd0100      jmp     nt!MmIsFileObjectAPagingFile (8050b68a)
    nt!MmIsFileObjectAPagingFile:
    8050b68a 8bff            mov     edi,edi
    */
/// nt!FsRtlIsPagingFile API入口附近, JMP命令前面有6个字节
#define BYTES_CNT_BEFORE_JMP_ON_FS_RTL_IS_PAGING_FILE   6
/// JMP 命令是5个字节
#define BYTES_CNT_JMP_CMD                               5
/// JMP命令字
#define ASM_CMD_JMP                                     0xE9
BOOLEAN fnGetApiJmpAddr(wchar_t * pcApiName, 
                           ULONG uAddrBeforeJmpCmd, 
                           ULONG * pJmpAddr);
PVOID GetKernelApiAddress(wchar_t * pcApiName);
/// @fn         fnGetAddrJmpTo
/// @brief      查找JMP命令要跳到的绝对地址
/// @param      ULONG ulAddrJmpCmd, JMP命令地址
/// @return     ULONG
/// @retval     0, ulApiAddr地址后面不是JMP指令或者失败
/// @retval     非0, 得到了ulApiAddr之后JMP指令要跳到的地址
ULONG fnGetAddrJmpTo(ULONG ulAddrJmpCmd);
extern "C"
NTSTATUS DriverEntry(
                     IN PDRIVER_OBJECT DriverObject,
                     IN PUNICODE_STRING RegistryPath)
{
    ULONG   pJmpAddr    =   0;
    KdPrint((">> DriverEntry\r\n"));
    if (fnGetApiJmpAddr(
                        L"FsRtlIsPagingFile", 
                        BYTES_CNT_BEFORE_JMP_ON_FS_RTL_IS_PAGING_FILE, 
                        &pJmpAddr))
    {
        DbgPrint("pJmpAddr = 0x%X\r\n", pJmpAddr);
    }
    else
        DbgPrint("Test_GetApiJmpAddr failed\r\n");
    KdPrint(("<< DriverEntry\r\n"));
    return STATUS_SUCCESS;
}
BOOLEAN fnGetApiJmpAddr(wchar_t * pcApiName, 
                           ULONG uAddrBeforeJmpCmd, 
                           ULONG * pJmpAddr)
{
    ULONG   ulApiAddr       =   0;  ///< API地址
    ULONG   ulAddrJmpCmd    =   0;  ///< JMP命令地址
    if ((NULL == pcApiName) || (NULL == pJmpAddr))
        return FALSE;
    ulApiAddr = (ULONG) GetKernelApiAddress(pcApiName);
    if (!MmIsAddressValid((PVOID)(ulApiAddr)))
    {
        return FALSE;
    }
    ulAddrJmpCmd = ulApiAddr + uAddrBeforeJmpCmd;
    /**
    kd> dv /V ulApiAddr
    f7a4cc60 @ebp-0x04       ulApiAddr = 0x804ed978
    kd> dv /V ulAddrBeforeJmpCmd
    f7a4cc5c @ebp-0x08 ulAddrBeforeJmpCmd = 0x804ed97e
    kd> uf nt!FsRtlIsPagingFile
    nt!FsRtlIsPagingFile:
    804ed978 8bff            mov     edi,edi
    804ed97a 55              push    ebp
    804ed97b 8bec            mov     ebp,esp
    804ed97d 5d              pop     ebp
    804ed97e e907dd0100      jmp     nt!MmIsFileObjectAPagingFile (8050b68a)
    nt!MmIsFileObjectAPagingFile:
    8050b68a 8bff            mov     edi,edi
    */
    /// 实验可知: ulApiAddr + uAddrBeforeJmpCmd 是JMP命令的地址
    if (MmIsAddressValid((PVOID)(ulAddrJmpCmd)))
    {
        /// *pJmpAddr is 8050b68a ~
        *pJmpAddr = fnGetAddrJmpTo(ulAddrJmpCmd);
        return TRUE;
    }
    return FALSE;
}
ULONG fnGetAddrJmpTo(ULONG ulAddrJmpCmd)
{
    BOOLEAN bRc                 =   FALSE;
    UCHAR   ucCmdByte           =   0;  ///< JMP命令的命令字
    ULONG   ulAddrJmpCmdContent =   0;  ///< JMP命令的内容所在的地址
    ULONG   ulAddrOffset        =   0;  ///< JMP命令要跳到的相对地址
    ULONG   ulAddrFollowJmp     =   0;  ///< JMP命令要跳到的绝对地址
    __try
    {
        ucCmdByte = *(UCHAR *)(ulAddrJmpCmd);
        if (ASM_CMD_JMP == ucCmdByte)
        {
            ulAddrJmpCmdContent = (ULONG)(ulAddrJmpCmd) + 1;
            ulAddrOffset = *(ULONG*)(ulAddrJmpCmdContent);
            ulAddrFollowJmp = ulAddrJmpCmd + BYTES_CNT_JMP_CMD + ulAddrOffset;
            bRc = TRUE; ///< 全部执行成功, 没有异常发生
        }
    }
    __finally
    {
    }
    return bRc ? ulAddrFollowJmp : 0;
}
PVOID GetKernelApiAddress(wchar_t * pcApiName)
{
    UNICODE_STRING str;
    if (NULL == pcApiName)
        return NULL;
    RtlInitUnicodeString(&str, pcApiName);
    /// MmGetSystemRoutineAddress
    /// If the function name can be resolved, 
    /// the routine returns a pointer to the function. 
    /// Otherwise, the routine returns NULL.
    return MmGetSystemRoutineAddress(&str);
}

上传的附件 prjGetJmpCmdAddr_2013_0619.rar

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功