drv experiment : FOLLOW_JMP parse

来源:互联网 发布:麒麟臂数据 编辑:程序博客网 时间:2024/06/10 23:35

在nt!*API中, 有些入口地址处附近有5个字节的JMP代码.

用FOLLOW_JMP宏可以算出JMP到的绝对地址.

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

这个宏不好理解, 且是硬编码, 用函数实现了一个FOLLOW_JMP, 通用一些.

根据给定的内核API地址, 给定JMP命令前面的字节数量, 分析从JMP命令得到JMP到的绝对地址.


工程下载: prjGetJmpCmdAddr_2013_0619.rar


/// @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                                     0xE9BOOLEAN 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);}