Detours x64

来源:互联网 发布:linux服务器重启原因 编辑:程序博客网 时间:2024/06/11 01:34
发信人: flier (小海 [渴望并不存在的完美]), 信区: MSDN
标  题: Detours x64 [Draft]
发信站: 水木社区 (Fri Aug  3 01:01:07 2007), 站内

该死的路径依赖,为了折腾另外一个问题,不得不先把这个搞定 :S

Detours我就不介绍了,MSR放出的2.1 Express版本不支持64位
而据某人发邮件问,要10000刀才给授权,基本也不用想了 -_-b

于是乎在此基础上修修补补,基本能在x64下跑起来
因为还有些bug,暂时不放完整版本出来,大概说说流程

API Hook 基本已经是大白菜级技术,无非就是找到函数入口点
替换前面几个字节为jmp,重定向到一个跳板,完成工作再跳回去
详细的流程请参考detours的代码,下面捡x64下不一样的说

首先是在DetourAttachEx时找到原始函数和Hook函数的真实代码入口点
可能需要跳过import表,或者调试用间接跳转指令啥的,
detours.cpp里detour_skip_jmp函数就是干这个的,
不过在x64下需要做一些调整,因为某些指令的数据长度跟x86不同
这块代码我基本照抄mhook的实现,比detours增加了嵌套检查



inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)

{

  if (pbCode == NULL) {

    return NULL;

  }

  if (ppGlobals != NULL) {

    *ppGlobals = NULL;

  }

  if (pbCode[0] == 0xff && pbCode[1] == 0x25) {   // jmp [+imm32]

    // Looks like an import alias jump, then get the code it points to.

    // on x64 we have a 32-bit offset...

    PBYTE pbTarget = *(PBYTE*)(pbCode + 6 + *(INT32 *)&pbCode[2]);

    if (detour_is_imported(pbCode, pbTarget)) {

      PBYTE pbNew = *(PBYTE *)pbTarget;

      DETOUR_TRACE(("%p->%p: skipped over import table./n", pbCode, pbNew));


      return pbNew;

    }

    return detour_skip_jmp(pbTarget, ppGlobals);

  }

  else if (pbCode[0] == 0xe9) {   // jmp + rel32, Near jump with the target

    /*

    SEH64!ILT+15(?CallThatFunctionYAHXZ):

    00000000`00401014 e957000000      jmp     SEH64!CallThatFunction (000000
00`00401070)

    SEH64!CallThatFunction [d:/study/win64/seh64/seh64.cpp @ 11]:

    00000000`00401070 4057            push    rdi

    00000000`00401072 4883ec40        sub     rsp,40h

    00000000`00401076 488bfc          mov     rdi,rsp

    */

    PBYTE pbNew = pbCode + 5 + *(INT32 *)&pbCode[1];

    return detour_skip_jmp(pbNew, ppGlobals);

  }

  else if (pbCode[0] == 0xeb) {   // jmp +imm8

    // These just started appearing with CL13.

    PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];

    return detour_skip_jmp(pbNew, ppGlobals);

  }

  return pbCode;

}

知道入口后,detours会调用DetourCopyInstructionEx复制入口处代码
其根据字节可能代表的指令,把相关信息放到s_rceCopyTable表中
x64下需要修改的是从0x40到0x4f这几个入口,x64指令集中用他们
作为REX前缀,详细说明参见AMD architecture programmer manual volume 3
1.2.7 REX Prefixes 小节中有详细讨论,这儿全部当成单字节前缀即可

#ifdef DETOURS_X64 // For Rex Prefix
    { 0x40, ENTRY_CopyBytesPrefix },                    // REX Prefixes
    { 0x41, ENTRY_CopyBytesPrefix },                    // REX Prefixes
    { 0x42, ENTRY_CopyBytesPrefix },                    // REX Prefixes

    { 0x43, ENTRY_CopyBytesPrefix },                    // REX Prefixes
    { 0x44, ENTRY_CopyBytesPrefix },                    // REX Prefixes
    { 0x45, ENTRY_CopyBytesPrefix },                    // REX Prefixes
    { 0x46, ENTRY_CopyBytesPrefix },                    // REX Prefixes
    { 0x47, ENTRY_CopyBytesPrefix },                    // REX Prefixes
    { 0x48, ENTRY_CopyBytesPrefix },                    // REX Prefixes
    { 0x49, ENTRY_CopyBytesPrefix },                    // REX Prefixes
    { 0x4A, ENTRY_CopyBytesPrefix },                    // REX Prefixes
    { 0x4B, ENTRY_CopyBytesPrefix },                    // REX Prefixes
    { 0x4C, ENTRY_CopyBytesPrefix },                    // REX Prefixes
    { 0x4D, ENTRY_CopyBytesPrefix },                    // REX Prefixes
    { 0x4E, ENTRY_CopyBytesPrefix },                    // REX Prefixes
    { 0x4F, ENTRY_CopyBytesPrefix },                    // REX Prefixes
#else

而对FF开始的far indirect jmp/call 指令,x64也跟x86不同

PBYTE CDetourDis::CopyFF(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)

{   // CALL /2

    // CALL /3

    // INC /0

    // JMP /4

    // JMP /5

    // PUSH /6

    (void)pEntry;

    if (0x15 == pbSrc[1] || 0x25 == pbSrc[1]) {         // CALL [], JMP []

#ifdef DETOURS_X64

        DWORD dwOffset = *((PDWORD)&pbSrc[2]);

        *m_ppbTarget = (PBYTE)*((PDWORD_PTR)(pbSrc + 6 + dwOffset));

#else

        PBYTE *ppbTarget = *(PBYTE**)&pbSrc[2];

        *m_ppbTarget = *ppbTarget;

#endif

    }

    else if (0x10 == (0x38 & pbSrc[1]) || // CALL /2 --> reg(bits 543) of Mo
dR/M == 010

             0x18 == (0x38 & pbSrc[1]) || // CALL /3 --> reg(bits 543) of Mo
dR/M == 011

             0x20 == (0x38 & pbSrc[1]) || // JMP /4 --> reg(bits 543) of Mod
R/M == 100

             0x28 == (0x38 & pbSrc[1])    // JMP /5 --> reg(bits 543) of Mod
R/M == 101

            ) {

        *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;

    }

    const COPYENTRY ce = { 0xff, ENTRY_CopyBytes2Mod };

    return (this->*ce.pfCopy)(&ce, pbDst, pbSrc);

}

拷贝了原始指令到跳板后,需要在后面增加跳回剩余指令的jmp
x86下面detours直接用detour_gen_jmp_immediate函数生成指令
但在x64下面需要修改,因为x64下面指针是64位的,
如果目标地址在当前地址前后2G范围内,可以用32位偏移跳转
否则必须把地址放到内存中,然后在jmp指令指定保存地址的位置

detours在通过detour_alloc_trampoline函数分配跳板内存时
会尽量尝试把内存分配在目标函数2G范围内,避免长跳转指令
这样可以确保目标函数大于5字节(near jmp) 时都能工作
但这样一来就无法确保跳板跟hook函数的位置在2G内,
所以x64下面必须处理这些情况,同理照抄mhook代码如下

inline PBYTE detour_gen_jmp(PBYTE pbCode, PBYTE pbJumpTo)

{

  PBYTE pbJumpFrom = pbCode + 5;

  SIZE_T cbDiff = pbJumpFrom > pbJumpTo ? pbJumpFrom - pbJumpTo : pbJumpTo -
 pbJumpFrom;

  if (cbDiff <= 0x7fff0000) {

    *pbCode++ = 0xe9;

    *((PDWORD)pbCode) = (DWORD)(DWORD_PTR)(pbJumpTo - pbJumpFrom);

    pbCode += sizeof(DWORD);

  } else {

    *pbCode++ = 0xff;

    *pbCode++ = 0x25;

    // on x64 we write the relative address of the same location

    *((PDWORD)pbCode) = (DWORD)0;

    pbCode += sizeof(DWORD);

    

    *((PDWORD_PTR)pbCode) = (DWORD_PTR)(pbJumpTo);

    pbCode += sizeof(DWORD_PTR);

  }

  return pbCode;

}

除此之外,还有一些异常情况的处理,例如暂停线程时,
其RIP刚好在跳板或者目标函数入口处,理论上可以使用
与x86相同的机制,不过暂时没法构造环境测试

#ifdef DETOURS_X64

#define DETOURS_EIP         Rip
#define DETOURS_EIP_TYPE    DWORD64

#endif // DETOURS_X64

最后还有一些跟细节的处理,例如对ModRm下RIP的处理
回头加上再一起放出完整可用的代码吧

Reference

1. Microsoft Research Detours Package
   http://research.microsoft.com/research/downloads/Details/d36340fb-4d3c-4ddd-bf5b-1db25d03713d/Details.aspx

2. Mhook, an API hooking library, V2.0
   http://codefromthe70s.org/mhook2.asp

3. AMD64 Architecture Programmer’s Manual Volume 3: General-Purpose and System Instructions

4. Everything You Need To Know To Start Programming 64-Bit Windows Systems, Matt Pietrek
   http://msdn.microsoft.com/msdnmag/issues/06/05/x64/default.aspx?print=true&loc=null
 .    生命的意义在于   //   ____/ //_ /   //_/     http://flier_lu.blogcn.com .                           
 .        希望         / /  /___/_/// /   //_/__     __    _ _★              . 
 .        工作          / /   ____// / /    //  /  /'__`/ //`'_/              . 
 .      爱你的人         / /  /___/ / / /___/ /  /// __// / / //              . 
 .     和你爱的人         / /___/    / /_____/ /__/ /____/ / /_/              . 
 .        ……             //___/     //_____///__///____/  //_/.lu@gmail.com . 


※ 修改:·flier 于 Aug  3 01:01:35 修改本文·[FROM: 221.226.232.*]
※ 来源:·水木社区 newsmth.net·[FROM: 221.226.232.*]

 

[本篇全文] [本篇作者:FreeWizard] [进入讨论区] [返回顶部]
2
发信人: FreeWizard (  ), 信区: MSDN
标  题: Re: Detours x64 [Draft]
发信站: 水木社区 (Fri Aug  3 01:02:58 2007), 站内

orz
先re再看

【 在 flier (小海 [渴望并不存在的完美]) 的大作中提到: 】
: 该死的路径依赖,为了折腾另外一个问题,不得不先把这个搞定 :S
: Detours我就不介绍了,MSR放出的2.1 Express版本不支持64位
: 而据某人发邮件问,要10000刀才给授权,基本也不用想了 -_-b
: ...................

--

※ 来源:·水木社区 newsmth.net·[FROM: 221.221.18.159]

 

[本篇全文] [本篇作者:flier] [进入讨论区] [返回顶部]
3
发信人: flier (小海 [渴望并不存在的完美]), 信区: MSDN
标  题: Re: Detours x64 [Draft]
发信站: 水木社区 (Fri Aug  3 01:11:16 2007), 站内

编译了一下 detours 那个 dtest 例子,代码完全不受影响,设置一下库就行
这个是vs2005编译后的调试版本,可以用windbg看看,呵呵,xp pro x64 下测试通过 

【 在 flier (小海 [渴望并不存在的完美]) 的大作中提到: 】
: 该死的路径依赖,为了折腾另外一个问题,不得不先把这个搞定 :S
: Detours我就不介绍了,MSR放出的2.1 Express版本不支持64位
: 而据某人发邮件问,要10000刀才给授权,基本也不用想了 -_-b
: ...................



--
 .    生命的意义在于   //   ____/ //_ /   //_/     http://flier_lu.blogcn.com .                           
 .        希望         / /  /___/_/// /   //_/__     __    _ _★              . 
 .        工作          / /   ____// / /    //  /  /'__`/ //`'_/              . 
 .      爱你的人         / /  /___/ / / /___/ /  /// __// / / //              . 
 .     和你爱的人         / /___/    / /_____/ /__/ /____/ / /_/              . 
 .        ……             //___/     //_____///__///____/  //_/.lu@gmail.com . 


※ 来源:·水木社区 http://newsmth.net·[FROM: 221.226.232.*]


附件: Debug64.rar (237KB)

附件: pdb.rar (634KB)

 

[本篇全文] [本篇作者:peach] [进入讨论区] [返回顶部]
4
发信人: peach (换台机器挂站), 信区: MSDN
标  题: Re: Detours x64 [Draft]
发信站: 水木社区 (Fri Aug  3 08:32:21 2007), 站内

最近也折腾了一些这方面的东西,但不是64位的

有些想法,正好拿出来

API HOOK 还可以用另外一种方法,虽然也会用到 detours 相关的技术,不过性能上有不少折扣

这种方法的核心思想就是使用 SEH,在需要 HOOK 的 API 入口处,改成一个  user break point 即可,这时候只要接管 SEH 处理函数即可,在 SEH 处理函数中,你可以修改 CONTEXT::Eip 以及其他寄存器来达到改变程序的流程的目的。

这时候有两个问题,
1、API入口的第一条指令。
   a) 绝对跳转之类的指令(jmp, call)。将 user break point 移至跳转到的地址处。
   b) 条件跳转。这种情况似乎很少见
   c) 其他指令。移走,在其他地方执行。

2、如何接管 SEH 处理函数。
   Detours 中有 DetourFirstChanceExceptionFilter,可以让你在第一时间处理所发生的异常。

如果需要 HOOK 的 API 不是很多,user break point 都不用加,用硬件调试寄存器就可以完成了。


【 在 flier (小海 [渴望并不存在的完美]) 的大作中提到: 】
: 标  题: Detours x64 [Draft]
: 发信站: 水木社区 (Fri Aug  3 01:01:07 2007), 站内

: 该死的路径依赖,为了折腾另外一个问题,不得不先把这个搞定 :S

: Detours我就不介绍了,MSR放出的2.1 Express版本不支持64位
: 而据某人发邮件问,要10000刀才给授权,基本也不用想了 -_-b

: 于是乎在此基础上修修补补,基本能在x64下跑起来
: 因为还有些bug,暂时不放完整版本出来,大概说说流程

: API Hook 基本已经是大白菜级技术,无非就是找到函数入口点
: 替换前面几个字节为jmp,重定向到一个跳板,完成工作再跳回去
: 详细的流程请参考detours的代码,下面捡x64下不一样的说

: 首先是在DetourAttachEx时找到原始函数和Hook函数的真实代码入口点
: 可能需要跳过import表,或者调试用间接跳转指令啥的,
: detours.cpp里detour_skip_jmp函数就是干这个的,
: 不过在x64下需要做一些调整,因为某些指令的数据长度跟x86不同
: 这块代码我基本照抄mhook的实现,比detours增加了嵌套检查



: inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)

: {

:   if (pbCode == NULL) {

:     return NULL;

:   }

:   if (ppGlobals != NULL) {

:     *ppGlobals = NULL;

:   }

:   if (pbCode[0] == 0xff && pbCode[1] == 0x25) {   // jmp [+imm32]

:     // Looks like an import alias jump, then get the code it points to.

:     // on x64 we have a 32-bit offset...

:     PBYTE pbTarget = *(PBYTE*)(pbCode + 6 + *(INT32 *)&pbCode[2]);

:     if (detour_is_imported(pbCode, pbTarget)) {

:       PBYTE pbNew = *(PBYTE *)pbTarget;

:       DETOUR_TRACE(("%p->%p: skipped over import table./n", pbCode, pbNew));


:       return pbNew;

:     }

:     return detour_skip_jmp(pbTarget, ppGlobals);

:   }

:   else if (pbCode[0] == 0xe9) {   // jmp + rel32, Near jump with the target

:     /*

:     SEH64!ILT+15(?CallThatFunctionYAHXZ):

:     00000000`00401014 e957000000      jmp     SEH64!CallThatFunction (000000
: 00`00401070)

:     SEH64!CallThatFunction [d:/study/win64/seh64/seh64.cpp @ 11]:

:     00000000`00401070 4057            push    rdi

:     00000000`00401072 4883ec40        sub     rsp,40h

:     00000000`00401076 488bfc          mov     rdi,rsp

:     */

:     PBYTE pbNew = pbCode + 5 + *(INT32 *)&pbCode[1];

:     return detour_skip_jmp(pbNew, ppGlobals);

:   }

:   else if (pbCode[0] == 0xeb) {   // jmp +imm8

:     // These just started appearing with CL13.

:     PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];

:     return detour_skip_jmp(pbNew, ppGlobals);

:   }

:   return pbCode;

: }

: 知道入口后,detours会调用DetourCopyInstructionEx复制入口处代码
: 其根据字节可能代表的指令,把相关信息放到s_rceCopyTable表中
: x64下需要修改的是从0x40到0x4f这几个入口,x64指令集中用他们
: 作为REX前缀,详细说明参见AMD architecture programmer manual volume 3
: 1.2.7 REX Prefixes 小节中有详细讨论,这儿全部当成单字节前缀即可

: #ifdef DETOURS_X64 // For Rex Prefix
:     { 0x40, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x41, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x42, ENTRY_CopyBytesPrefix },                    // REX Prefixes

:     { 0x43, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x44, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x45, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x46, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x47, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x48, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x49, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x4A, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x4B, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x4C, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x4D, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x4E, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x4F, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: #else

: 而对FF开始的far indirect jmp/call 指令,x64也跟x86不同

: PBYTE CDetourDis::CopyFF(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)

: {   // CALL /2

:     // CALL /3

:     // INC /0

:     // JMP /4

:     // JMP /5

:     // PUSH /6

:     (void)pEntry;

:     if (0x15 == pbSrc[1] || 0x25 == pbSrc[1]) {         // CALL [], JMP []

: #ifdef DETOURS_X64

:         DWORD dwOffset = *((PDWORD)&pbSrc[2]);

:         *m_ppbTarget = (PBYTE)*((PDWORD_PTR)(pbSrc + 6 + dwOffset));

: #else

:         PBYTE *ppbTarget = *(PBYTE**)&pbSrc[2];

:         *m_ppbTarget = *ppbTarget;

: #endif

:     }

:     else if (0x10 == (0x38 & pbSrc[1]) || // CALL /2 --> reg(bits 543) of Mo
: dR/M == 010

:              0x18 == (0x38 & pbSrc[1]) || // CALL /3 --> reg(bits 543) of Mo
: dR/M == 011

:              0x20 == (0x38 & pbSrc[1]) || // JMP /4 --> reg(bits 543) of Mod
: R/M == 100

:              0x28 == (0x38 & pbSrc[1])    // JMP /5 --> reg(bits 543) of Mod
: R/M == 101

:             ) {

:         *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;

:     }

:     const COPYENTRY ce = { 0xff, ENTRY_CopyBytes2Mod };

:     return (this->*ce.pfCopy)(&ce, pbDst, pbSrc);

: }

: 拷贝了原始指令到跳板后,需要在后面增加跳回剩余指令的jmp
: x86下面detours直接用detour_gen_jmp_immediate函数生成指令
: 但在x64下面需要修改,因为x64下面指针是64位的,
: 如果目标地址在当前地址前后2G范围内,可以用32位偏移跳转
: 否则必须把地址放到内存中,然后在jmp指令指定保存地址的位置

: detours在通过detour_alloc_trampoline函数分配跳板内存时
: 会尽量尝试把内存分配在目标函数2G范围内,避免长跳转指令
: 这样可以确保目标函数大于5字节(near jmp) 时都能工作
: 但这样一来就无法确保跳板跟hook函数的位置在2G内,
: 所以x64下面必须处理这些情况,同理照抄mhook代码如下

: inline PBYTE detour_gen_jmp(PBYTE pbCode, PBYTE pbJumpTo)

: {

:   PBYTE pbJumpFrom = pbCode + 5;

:   SIZE_T cbDiff = pbJumpFrom > pbJumpTo ? pbJumpFrom - pbJumpTo : pbJumpTo -
:  pbJumpFrom;

:   if (cbDiff <= 0x7fff0000) {

:     *pbCode++ = 0xe9;

:     *((PDWORD)pbCode) = (DWORD)(DWORD_PTR)(pbJumpTo - pbJumpFrom);

:     pbCode += sizeof(DWORD);

:   } else {

:     *pbCode++ = 0xff;

:     *pbCode++ = 0x25;

:     // on x64 we write the relative address of the same location

:     *((PDWORD)pbCode) = (DWORD)0;

:     pbCode += sizeof(DWORD);

:     

:     *((PDWORD_PTR)pbCode) = (DWORD_PTR)(pbJumpTo);

:     pbCode += sizeof(DWORD_PTR);

:   }

:   return pbCode;

: }

: 除此之外,还有一些异常情况的处理,例如暂停线程时,
: 其RIP刚好在跳板或者目标函数入口处,理论上可以使用
: 与x86相同的机制,不过暂时没法构造环境测试

: #ifdef DETOURS_X64

: #define DETOURS_EIP         Rip
: #define DETOURS_EIP_TYPE    DWORD64

: #endif // DETOURS_X64

: 最后还有一些跟细节的处理,例如对ModRm下RIP的处理
: 回头加上再一起放出完整可用的代码吧

: Reference

: 1. Microsoft Research Detours Package
:    http://research.microsoft.com/research/downloads/Details/d36340fb-4d3c-4ddd-bf5b-1db25d03713d/Details.aspx

: 2. Mhook, an API hooking library, V2.0
:    http://codefromthe70s.org/mhook2.asp

: 3. AMD64 Architecture Programmer’s Manual Volume 3: General-Purpose and System Instructions

: 4. Everything You Need To Know To Start Programming 64-Bit Windows Systems, Matt Pietrek
:    http://msdn.microsoft.com/msdnmag/issues/06/05/x64/default.aspx?print=true&loc=null
:  .    生命的意义在于   //   ____/ //_ /   //_/     http://flier_lu.blogcn.com .                           
:  .        希望         / /  /___/_/// /   //_/__     __    _ _★              . 
:  .        工作          / /   ____// / /    //  /  /'__`/ //`'_/              . 
:  .      爱你的人         / /  /___/ / / /___/ /  /// __// / / //              . 
:  .     和你爱的人         / /___/    / /_____/ /__/ /____/ / /_/              . 
:  .        ……             //___/     //_____///__///____/  //_/.lu@gmail.com . 


: ※ 修改:·flier 于 Aug  3 01:01:35 修改本文·[FROM: 221.226.232.*]
: ※ 来源:·水木社区 newsmth.net·[FROM: 221.226.232.*]


--

※ 来源:·水木社区 newsmth.net·[FROM: 166.111.8.*]

 

[本篇全文] [本篇作者:bbinn] [进入讨论区] [返回顶部]
5
发信人: bbinn (明天的明天), 信区: MSDN
标  题: Re: Detours x64 [Draft]
发信站: 水木社区 (Fri Aug  3 08:55:39 2007), 站内




【 在 flier (小海 [渴望并不存在的完美]) 的大作中提到: 】
: 标  题: Detours x64 [Draft]
: 发信站: 水木社区 (Fri Aug  3 01:01:07 2007), 站内

: 该死的路径依赖,为了折腾另外一个问题,不得不先把这个搞定 :S

: Detours我就不介绍了,MSR放出的2.1 Express版本不支持64位
: 而据某人发邮件问,要10000刀才给授权,基本也不用想了 -_-b

: 于是乎在此基础上修修补补,基本能在x64下跑起来
: 因为还有些bug,暂时不放完整版本出来,大概说说流程

: API Hook 基本已经是大白菜级技术,无非就是找到函数入口点
: 替换前面几个字节为jmp,重定向到一个跳板,完成工作再跳回去
: 详细的流程请参考detours的代码,下面捡x64下不一样的说

: 首先是在DetourAttachEx时找到原始函数和Hook函数的真实代码入口点
: 可能需要跳过import表,或者调试用间接跳转指令啥的,
: detours.cpp里detour_skip_jmp函数就是干这个的,
: 不过在x64下需要做一些调整,因为某些指令的数据长度跟x86不同
: 这块代码我基本照抄mhook的实现,比detours增加了嵌套检查



: inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)

: {

:   if (pbCode == NULL) {

:     return NULL;

:   }

:   if (ppGlobals != NULL) {

:     *ppGlobals = NULL;

:   }

:   if (pbCode[0] == 0xff && pbCode[1] == 0x25) {   // jmp [+imm32]

:     // Looks like an import alias jump, then get the code it points to.

:     // on x64 we have a 32-bit offset...

:     PBYTE pbTarget = *(PBYTE*)(pbCode + 6 + *(INT32 *)&pbCode[2]);

:     if (detour_is_imported(pbCode, pbTarget)) {

:       PBYTE pbNew = *(PBYTE *)pbTarget;

:       DETOUR_TRACE(("%p->%p: skipped over import table./n", pbCode, pbNew));


:       return pbNew;

:     }

:     return detour_skip_jmp(pbTarget, ppGlobals);

:   }

:   else if (pbCode[0] == 0xe9) {   // jmp + rel32, Near jump with the target

:     /*

:     SEH64!ILT+15(?CallThatFunctionYAHXZ):

:     00000000`00401014 e957000000      jmp     SEH64!CallThatFunction (000000
: 00`00401070)

:     SEH64!CallThatFunction [d:/study/win64/seh64/seh64.cpp @ 11]:

:     00000000`00401070 4057            push    rdi

:     00000000`00401072 4883ec40        sub     rsp,40h

:     00000000`00401076 488bfc          mov     rdi,rsp

:     */

:     PBYTE pbNew = pbCode + 5 + *(INT32 *)&pbCode[1];

:     return detour_skip_jmp(pbNew, ppGlobals);

:   }

:   else if (pbCode[0] == 0xeb) {   // jmp +imm8

:     // These just started appearing with CL13.

:     PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];

:     return detour_skip_jmp(pbNew, ppGlobals);

:   }

:   return pbCode;

: }

: 知道入口后,detours会调用DetourCopyInstructionEx复制入口处代码
: 其根据字节可能代表的指令,把相关信息放到s_rceCopyTable表中
: x64下需要修改的是从0x40到0x4f这几个入口,x64指令集中用他们
: 作为REX前缀,详细说明参见AMD architecture programmer manual volume 3
: 1.2.7 REX Prefixes 小节中有详细讨论,这儿全部当成单字节前缀即可

: #ifdef DETOURS_X64 // For Rex Prefix
:     { 0x40, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x41, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x42, ENTRY_CopyBytesPrefix },                    // REX Prefixes

:     { 0x43, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x44, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x45, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x46, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x47, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x48, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x49, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x4A, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x4B, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x4C, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x4D, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x4E, ENTRY_CopyBytesPrefix },                    // REX Prefixes
:     { 0x4F, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: #else

: 而对FF开始的far indirect jmp/call 指令,x64也跟x86不同

: PBYTE CDetourDis::CopyFF(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)

: {   // CALL /2

:     // CALL /3

:     // INC /0

:     // JMP /4

:     // JMP /5

:     // PUSH /6

:     (void)pEntry;

:     if (0x15 == pbSrc[1] || 0x25 == pbSrc[1]) {         // CALL [], JMP []

: #ifdef DETOURS_X64

:         DWORD dwOffset = *((PDWORD)&pbSrc[2]);

:         *m_ppbTarget = (PBYTE)*((PDWORD_PTR)(pbSrc + 6 + dwOffset));

: #else

:         PBYTE *ppbTarget = *(PBYTE**)&pbSrc[2];

:         *m_ppbTarget = *ppbTarget;

: #endif

:     }

:     else if (0x10 == (0x38 & pbSrc[1]) || // CALL /2 --> reg(bits 543) of Mo
: dR/M == 010

:              0x18 == (0x38 & pbSrc[1]) || // CALL /3 --> reg(bits 543) of Mo
: dR/M == 011

:              0x20 == (0x38 & pbSrc[1]) || // JMP /4 --> reg(bits 543) of Mod
: R/M == 100

:              0x28 == (0x38 & pbSrc[1])    // JMP /5 --> reg(bits 543) of Mod
: R/M == 101

:             ) {

:         *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;

:     }

:     const COPYENTRY ce = { 0xff, ENTRY_CopyBytes2Mod };

:     return (this->*ce.pfCopy)(&ce, pbDst, pbSrc);

: }

: 拷贝了原始指令到跳板后,需要在后面增加跳回剩余指令的jmp
: x86下面detours直接用detour_gen_jmp_immediate函数生成指令
: 但在x64下面需要修改,因为x64下面指针是64位的,
: 如果目标地址在当前地址前后2G范围内,可以用32位偏移跳转
: 否则必须把地址放到内存中,然后在jmp指令指定保存地址的位置

: detours在通过detour_alloc_trampoline函数分配跳板内存时
: 会尽量尝试把内存分配在目标函数2G范围内,避免长跳转指令
: 这样可以确保目标函数大于5字节(near jmp) 时都能工作
: 但这样一来就无法确保跳板跟hook函数的位置在2G内,
: 所以x64下面必须处理这些情况,同理照抄mhook代码如下

: inline PBYTE detour_gen_jmp(PBYTE pbCode, PBYTE pbJumpTo)

: {

:   PBYTE pbJumpFrom = pbCode + 5;

:   SIZE_T cbDiff = pbJumpFrom > pbJumpTo ? pbJumpFrom - pbJumpTo : pbJumpTo -
:  pbJumpFrom;

:   if (cbDiff <= 0x7fff0000) {

:     *pbCode++ = 0xe9;

:     *((PDWORD)pbCode) = (DWORD)(DWORD_PTR)(pbJumpTo - pbJumpFrom);

:     pbCode += sizeof(DWORD);

:   } else {

:     *pbCode++ = 0xff;

:     *pbCode++ = 0x25;

:     // on x64 we write the relative address of the same location

:     *((PDWORD)pbCode) = (DWORD)0;

:     pbCode += sizeof(DWORD);

:     

:     *((PDWORD_PTR)pbCode) = (DWORD_PTR)(pbJumpTo);

:     pbCode += sizeof(DWORD_PTR);

:   }

:   return pbCode;

: }

: 除此之外,还有一些异常情况的处理,例如暂停线程时,
: 其RIP刚好在跳板或者目标函数入口处,理论上可以使用
: 与x86相同的机制,不过暂时没法构造环境测试

: #ifdef DETOURS_X64

: #define DETOURS_EIP         Rip
: #define DETOURS_EIP_TYPE    DWORD64

: #endif // DETOURS_X64

: 最后还有一些跟细节的处理,例如对ModRm下RIP的处理
: 回头加上再一起放出完整可用的代码吧

: Reference

: 1. Microsoft Research Detours Package
:    http://research.microsoft.com/research/downloads/Details/d36340fb-4d3c-4ddd-bf5b-1db25d03713d/Details.aspx

: 2. Mhook, an API hooking library, V2.0
:    http://codefromthe70s.org/mhook2.asp

: 3. AMD64 Architecture Programmer’s Manual Volume 3: General-Purpose and System Instructions

: 4. Everything You Need To Know To Start Programming 64-Bit Windows Systems, Matt Pietrek
:    http://msdn.microsoft.com/msdnmag/issues/06/05/x64/default.aspx?print=true&loc=null
:  .    生命的意义在于   //   ____/ //_ /   //_/     http://flier_lu.blogcn.com .                           
:  .        希望         / /  /___/_/// /   //_/__     __    _ _★              . 
:  .        工作          / /   ____// / /    //  /  /'__`/ //`'_/              . 
:  .      爱你的人         / /  /___/ / / /___/ /  /// __// / / //              . 
:  .     和你爱的人         / /___/    / /_____/ /__/ /____/ / /_/              . 
:  .        ……             //___/     //_____///__///____/  //_/.lu@gmail.com . 


: ※ 修改:·flier 于 Aug  3 01:01:35 修改本文·[FROM: 221.226.232.*]
: ※ 来源:·水木社区 newsmth.net·[FROM: 221.226.232.*]


--
-                     不谈金钱,不谈妇女,不谈琐事


※ 来源:·水木社区 newsmth.net·[FROM: 219.232.52.*]

 

[本篇全文] [本篇作者:bbinn] [进入讨论区] [返回顶部]
6
发信人: bbinn (明天的明天), 信区: MSDN
标  题: Re: Detours x64 [Draft]
发信站: 水木社区 (Fri Aug  3 08:58:20 2007), 站内

太赞了,我现在就在折腾这个。
不过OLE嵌入的时候你这招就不管用了,debug break会先被csrss.exe捕获到,
然后就直接认为嵌入失败,然后进程就suspend了。


【 在 peach (换台机器挂站) 的大作中提到: 】
: 标  题: Re: Detours x64 [Draft]
: 发信站: 水木社区 (Fri Aug  3 08:32:21 2007), 站内

: 最近也折腾了一些这方面的东西,但不是64位的

: 有些想法,正好拿出来

: API HOOK 还可以用另外一种方法,虽然也会用到 detours 相关的技术,不过性能上有不少折扣

: 这种方法的核心思想就是使用 SEH,在需要 HOOK 的 API 入口处,改成一个  user break point 即可,这时候只要接管 SEH 处理函数即可,在 SEH 处理函数中,你可以修改 CONTEXT::Eip 以及其他寄存器来达到改变程序的流程的目的。

: 这时候有两个问题,
: 1、API入口的第一条指令。
:    a) 绝对跳转之类的指令(jmp, call)。将 user break point 移至跳转到的地址处。
:    b) 条件跳转。这种情况似乎很少见
:    c) 其他指令。移走,在其他地方执行。

: 2、如何接管 SEH 处理函数。
:    Detours 中有 DetourFirstChanceExceptionFilter,可以让你在第一时间处理所发生的异常。

: 如果需要 HOOK 的 API 不是很多,user break point 都不用加,用硬件调试寄存器就可以完成了。


: 【 在 flier (小海 [渴望并不存在的完美]) 的大作中提到: 】
: : 标  题: Detours x64 [Draft]
: : 发信站: 水木社区 (Fri Aug  3 01:01:07 2007), 站内
: : 
: : 该死的路径依赖,为了折腾另外一个问题,不得不先把这个搞定 :S
: : 
: : Detours我就不介绍了,MSR放出的2.1 Express版本不支持64位
: : 而据某人发邮件问,要10000刀才给授权,基本也不用想了 -_-b
: : 
: : 于是乎在此基础上修修补补,基本能在x64下跑起来
: : 因为还有些bug,暂时不放完整版本出来,大概说说流程
: : 
: : API Hook 基本已经是大白菜级技术,无非就是找到函数入口点
: : 替换前面几个字节为jmp,重定向到一个跳板,完成工作再跳回去
: : 详细的流程请参考detours的代码,下面捡x64下不一样的说
: : 
: : 首先是在DetourAttachEx时找到原始函数和Hook函数的真实代码入口点
: : 可能需要跳过import表,或者调试用间接跳转指令啥的,
: : detours.cpp里detour_skip_jmp函数就是干这个的,
: : 不过在x64下需要做一些调整,因为某些指令的数据长度跟x86不同
: : 这块代码我基本照抄mhook的实现,比detours增加了嵌套检查
: : 
: : 
: : 
: : inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
: : 
: : {
: : 
: :   if (pbCode == NULL) {
: : 
: :     return NULL;
: : 
: :   }
: : 
: :   if (ppGlobals != NULL) {
: : 
: :     *ppGlobals = NULL;
: : 
: :   }
: : 
: :   if (pbCode[0] == 0xff && pbCode[1] == 0x25) {   // jmp [+imm32]
: : 
: :     // Looks like an import alias jump, then get the code it points to.
: : 
: :     // on x64 we have a 32-bit offset...
: : 
: :     PBYTE pbTarget = *(PBYTE*)(pbCode + 6 + *(INT32 *)&pbCode[2]);
: : 
: :     if (detour_is_imported(pbCode, pbTarget)) {
: : 
: :       PBYTE pbNew = *(PBYTE *)pbTarget;
: : 
: :       DETOUR_TRACE(("%p->%p: skipped over import table./n", pbCode, pbNew));
: : 
: : 
: :       return pbNew;
: : 
: :     }
: : 
: :     return detour_skip_jmp(pbTarget, ppGlobals);
: : 
: :   }
: : 
: :   else if (pbCode[0] == 0xe9) {   // jmp + rel32, Near jump with the target
: : 
: :     /*
: : 
: :     SEH64!ILT+15(?CallThatFunctionYAHXZ):
: : 
: :     00000000`00401014 e957000000      jmp     SEH64!CallThatFunction (000000
: : 00`00401070)
: : 
: :     SEH64!CallThatFunction [d:/study/win64/seh64/seh64.cpp @ 11]:
: : 
: :     00000000`00401070 4057            push    rdi
: : 
: :     00000000`00401072 4883ec40        sub     rsp,40h
: : 
: :     00000000`00401076 488bfc          mov     rdi,rsp
: : 
: :     */
: : 
: :     PBYTE pbNew = pbCode + 5 + *(INT32 *)&pbCode[1];
: : 
: :     return detour_skip_jmp(pbNew, ppGlobals);
: : 
: :   }
: : 
: :   else if (pbCode[0] == 0xeb) {   // jmp +imm8
: : 
: :     // These just started appearing with CL13.
: : 
: :     PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];
: : 
: :     return detour_skip_jmp(pbNew, ppGlobals);
: : 
: :   }
: : 
: :   return pbCode;
: : 
: : }
: : 
: : 知道入口后,detours会调用DetourCopyInstructionEx复制入口处代码
: : 其根据字节可能代表的指令,把相关信息放到s_rceCopyTable表中
: : x64下需要修改的是从0x40到0x4f这几个入口,x64指令集中用他们
: : 作为REX前缀,详细说明参见AMD architecture programmer manual volume 3
: : 1.2.7 REX Prefixes 小节中有详细讨论,这儿全部当成单字节前缀即可
: : 
: : #ifdef DETOURS_X64 // For Rex Prefix
: :     { 0x40, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x41, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x42, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: : 
: :     { 0x43, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x44, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x45, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x46, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x47, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x48, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x49, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x4A, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x4B, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x4C, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x4D, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x4E, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x4F, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: : #else
: : 
: : 而对FF开始的far indirect jmp/call 指令,x64也跟x86不同
: : 
: : PBYTE CDetourDis::CopyFF(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
: : 
: : {   // CALL /2
: : 
: :     // CALL /3
: : 
: :     // INC /0
: : 
: :     // JMP /4
: : 
: :     // JMP /5
: : 
: :     // PUSH /6
: : 
: :     (void)pEntry;
: : 
: :     if (0x15 == pbSrc[1] || 0x25 == pbSrc[1]) {         // CALL [], JMP []
: : 
: : #ifdef DETOURS_X64
: : 
: :         DWORD dwOffset = *((PDWORD)&pbSrc[2]);
: : 
: :         *m_ppbTarget = (PBYTE)*((PDWORD_PTR)(pbSrc + 6 + dwOffset));
: : 
: : #else
: : 
: :         PBYTE *ppbTarget = *(PBYTE**)&pbSrc[2];
: : 
: :         *m_ppbTarget = *ppbTarget;
: : 
: : #endif
: : 
: :     }
: : 
: :     else if (0x10 == (0x38 & pbSrc[1]) || // CALL /2 --> reg(bits 543) of Mo
: : dR/M == 010
: : 
: :              0x18 == (0x38 & pbSrc[1]) || // CALL /3 --> reg(bits 543) of Mo
: : dR/M == 011
: : 
: :              0x20 == (0x38 & pbSrc[1]) || // JMP /4 --> reg(bits 543) of Mod
: : R/M == 100
: : 
: :              0x28 == (0x38 & pbSrc[1])    // JMP /5 --> reg(bits 543) of Mod
: : R/M == 101
: : 
: :             ) {
: : 
: :         *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;
: : 
: :     }
: : 
: :     const COPYENTRY ce = { 0xff, ENTRY_CopyBytes2Mod };
: : 
: :     return (this->*ce.pfCopy)(&ce, pbDst, pbSrc);
: : 
: : }
: : 
: : 拷贝了原始指令到跳板后,需要在后面增加跳回剩余指令的jmp
: : x86下面detours直接用detour_gen_jmp_immediate函数生成指令
: : 但在x64下面需要修改,因为x64下面指针是64位的,
: : 如果目标地址在当前地址前后2G范围内,可以用32位偏移跳转
: : 否则必须把地址放到内存中,然后在jmp指令指定保存地址的位置
: : 
: : detours在通过detour_alloc_trampoline函数分配跳板内存时
: : 会尽量尝试把内存分配在目标函数2G范围内,避免长跳转指令
: : 这样可以确保目标函数大于5字节(near jmp) 时都能工作
: : 但这样一来就无法确保跳板跟hook函数的位置在2G内,
: : 所以x64下面必须处理这些情况,同理照抄mhook代码如下
: : 
: : inline PBYTE detour_gen_jmp(PBYTE pbCode, PBYTE pbJumpTo)
: : 
: : {
: : 
: :   PBYTE pbJumpFrom = pbCode + 5;
: : 
: :   SIZE_T cbDiff = pbJumpFrom > pbJumpTo ? pbJumpFrom - pbJumpTo : pbJumpTo -
: :  pbJumpFrom;
: : 
: :   if (cbDiff <= 0x7fff0000) {
: : 
: :     *pbCode++ = 0xe9;
: : 
: :     *((PDWORD)pbCode) = (DWORD)(DWORD_PTR)(pbJumpTo - pbJumpFrom);
: : 
: :     pbCode += sizeof(DWORD);
: : 
: :   } else {
: : 
: :     *pbCode++ = 0xff;
: : 
: :     *pbCode++ = 0x25;
: : 
: :     // on x64 we write the relative address of the same location
: : 
: :     *((PDWORD)pbCode) = (DWORD)0;
: : 
: :     pbCode += sizeof(DWORD);
: : 
: :     
: : 
: :     *((PDWORD_PTR)pbCode) = (DWORD_PTR)(pbJumpTo);
: : 
: :     pbCode += sizeof(DWORD_PTR);
: : 
: :   }
: : 
: :   return pbCode;
: : 
: : }
: : 
: : 除此之外,还有一些异常情况的处理,例如暂停线程时,
: : 其RIP刚好在跳板或者目标函数入口处,理论上可以使用
: : 与x86相同的机制,不过暂时没法构造环境测试
: : 
: : #ifdef DETOURS_X64
: : 
: : #define DETOURS_EIP         Rip
: : #define DETOURS_EIP_TYPE    DWORD64
: : 
: : #endif // DETOURS_X64
: : 
: : 最后还有一些跟细节的处理,例如对ModRm下RIP的处理
: : 回头加上再一起放出完整可用的代码吧
: : 
: : Reference
: : 
: : 1. Microsoft Research Detours Package
: :    http://research.microsoft.com/research/downloads/Details/d36340fb-4d3c-4ddd-bf5b-1db25d03713d/Details.aspx
: : 
: : 2. Mhook, an API hooking library, V2.0
: :    http://codefromthe70s.org/mhook2.asp
: : 
: : 3. AMD64 Architecture Programmer’s Manual Volume 3: General-Purpose and System Instructions
: : 
: : 4. Everything You Need To Know To Start Programming 64-Bit Windows Systems, Matt Pietrek
: :    http://msdn.microsoft.com/msdnmag/issues/06/05/x64/default.aspx?print=true&loc=null
: :  .    生命的意义在于   //   ____/ //_ /   //_/     http://flier_lu.blogcn.com .                           
: :  .        希望         / /  /___/_/// /   //_/__     __    _ _★              . 
: :  .        工作          / /   ____// / /    //  /  /'__`/ //`'_/              . 
: :  .      爱你的人         / /  /___/ / / /___/ /  /// __// / / //              . 
: :  .     和你爱的人         / /___/    / /_____/ /__/ /____/ / /_/              . 
: :  .        ……             //___/     //_____///__///____/  //_/.lu@gmail.com . 
: : 
: : 
: : ※ 修改:·flier 于 Aug  3 01:01:35 修改本文·[FROM: 221.226.232.*]
: : ※ 来源:·水木社区 newsmth.net·[FROM: 221.226.232.*]


: --

: ※ 来源:·水木社区 newsmth.net·[FROM: 166.111.8.*]


--
-                     不谈金钱,不谈妇女,不谈琐事


※ 来源:·水木社区 newsmth.net·[FROM: 219.232.52.*]

 

[本篇全文] [本篇作者:bbinn] [进入讨论区] [返回顶部]
7
发信人: bbinn (明天的明天), 信区: MSDN
标  题: Re: Detours x64 [Draft]
发信站: 水木社区 (Fri Aug  3 09:03:59 2007), 站内

可能这样要好一点,就是把user break point改成 divide by 0, 
就不会有别的机制抢着去处理这个异常了,就是字节多一点。



【 在 peach (换台机器挂站) 的大作中提到: 】
: 标  题: Re: Detours x64 [Draft]
: 发信站: 水木社区 (Fri Aug  3 08:32:21 2007), 站内

: 最近也折腾了一些这方面的东西,但不是64位的

: 有些想法,正好拿出来

: API HOOK 还可以用另外一种方法,虽然也会用到 detours 相关的技术,不过性能上有不少折扣

: 这种方法的核心思想就是使用 SEH,在需要 HOOK 的 API 入口处,改成一个  user break point 即可,这时候只要接管 SEH 处理函数即可,在 SEH 处理函数中,你可以修改 CONTEXT::Eip 以及其他寄存器来达到改变程序的流程的目的。

: 这时候有两个问题,
: 1、API入口的第一条指令。
:    a) 绝对跳转之类的指令(jmp, call)。将 user break point 移至跳转到的地址处。
:    b) 条件跳转。这种情况似乎很少见
:    c) 其他指令。移走,在其他地方执行。

: 2、如何接管 SEH 处理函数。
:    Detours 中有 DetourFirstChanceExceptionFilter,可以让你在第一时间处理所发生的异常。

: 如果需要 HOOK 的 API 不是很多,user break point 都不用加,用硬件调试寄存器就可以完成了。


: 【 在 flier (小海 [渴望并不存在的完美]) 的大作中提到: 】
: : 标  题: Detours x64 [Draft]
: : 发信站: 水木社区 (Fri Aug  3 01:01:07 2007), 站内
: : 
: : 该死的路径依赖,为了折腾另外一个问题,不得不先把这个搞定 :S
: : 
: : Detours我就不介绍了,MSR放出的2.1 Express版本不支持64位
: : 而据某人发邮件问,要10000刀才给授权,基本也不用想了 -_-b
: : 
: : 于是乎在此基础上修修补补,基本能在x64下跑起来
: : 因为还有些bug,暂时不放完整版本出来,大概说说流程
: : 
: : API Hook 基本已经是大白菜级技术,无非就是找到函数入口点
: : 替换前面几个字节为jmp,重定向到一个跳板,完成工作再跳回去
: : 详细的流程请参考detours的代码,下面捡x64下不一样的说
: : 
: : 首先是在DetourAttachEx时找到原始函数和Hook函数的真实代码入口点
: : 可能需要跳过import表,或者调试用间接跳转指令啥的,
: : detours.cpp里detour_skip_jmp函数就是干这个的,
: : 不过在x64下需要做一些调整,因为某些指令的数据长度跟x86不同
: : 这块代码我基本照抄mhook的实现,比detours增加了嵌套检查
: : 
: : 
: : 
: : inline PBYTE detour_skip_jmp(PBYTE pbCode, PVOID *ppGlobals)
: : 
: : {
: : 
: :   if (pbCode == NULL) {
: : 
: :     return NULL;
: : 
: :   }
: : 
: :   if (ppGlobals != NULL) {
: : 
: :     *ppGlobals = NULL;
: : 
: :   }
: : 
: :   if (pbCode[0] == 0xff && pbCode[1] == 0x25) {   // jmp [+imm32]
: : 
: :     // Looks like an import alias jump, then get the code it points to.
: : 
: :     // on x64 we have a 32-bit offset...
: : 
: :     PBYTE pbTarget = *(PBYTE*)(pbCode + 6 + *(INT32 *)&pbCode[2]);
: : 
: :     if (detour_is_imported(pbCode, pbTarget)) {
: : 
: :       PBYTE pbNew = *(PBYTE *)pbTarget;
: : 
: :       DETOUR_TRACE(("%p->%p: skipped over import table./n", pbCode, pbNew));
: : 
: : 
: :       return pbNew;
: : 
: :     }
: : 
: :     return detour_skip_jmp(pbTarget, ppGlobals);
: : 
: :   }
: : 
: :   else if (pbCode[0] == 0xe9) {   // jmp + rel32, Near jump with the target
: : 
: :     /*
: : 
: :     SEH64!ILT+15(?CallThatFunctionYAHXZ):
: : 
: :     00000000`00401014 e957000000      jmp     SEH64!CallThatFunction (000000
: : 00`00401070)
: : 
: :     SEH64!CallThatFunction [d:/study/win64/seh64/seh64.cpp @ 11]:
: : 
: :     00000000`00401070 4057            push    rdi
: : 
: :     00000000`00401072 4883ec40        sub     rsp,40h
: : 
: :     00000000`00401076 488bfc          mov     rdi,rsp
: : 
: :     */
: : 
: :     PBYTE pbNew = pbCode + 5 + *(INT32 *)&pbCode[1];
: : 
: :     return detour_skip_jmp(pbNew, ppGlobals);
: : 
: :   }
: : 
: :   else if (pbCode[0] == 0xeb) {   // jmp +imm8
: : 
: :     // These just started appearing with CL13.
: : 
: :     PBYTE pbNew = pbCode + 2 + *(CHAR *)&pbCode[1];
: : 
: :     return detour_skip_jmp(pbNew, ppGlobals);
: : 
: :   }
: : 
: :   return pbCode;
: : 
: : }
: : 
: : 知道入口后,detours会调用DetourCopyInstructionEx复制入口处代码
: : 其根据字节可能代表的指令,把相关信息放到s_rceCopyTable表中
: : x64下需要修改的是从0x40到0x4f这几个入口,x64指令集中用他们
: : 作为REX前缀,详细说明参见AMD architecture programmer manual volume 3
: : 1.2.7 REX Prefixes 小节中有详细讨论,这儿全部当成单字节前缀即可
: : 
: : #ifdef DETOURS_X64 // For Rex Prefix
: :     { 0x40, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x41, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x42, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: : 
: :     { 0x43, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x44, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x45, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x46, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x47, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x48, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x49, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x4A, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x4B, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x4C, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x4D, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x4E, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: :     { 0x4F, ENTRY_CopyBytesPrefix },                    // REX Prefixes
: : #else
: : 
: : 而对FF开始的far indirect jmp/call 指令,x64也跟x86不同
: : 
: : PBYTE CDetourDis::CopyFF(REFCOPYENTRY pEntry, PBYTE pbDst, PBYTE pbSrc)
: : 
: : {   // CALL /2
: : 
: :     // CALL /3
: : 
: :     // INC /0
: : 
: :     // JMP /4
: : 
: :     // JMP /5
: : 
: :     // PUSH /6
: : 
: :     (void)pEntry;
: : 
: :     if (0x15 == pbSrc[1] || 0x25 == pbSrc[1]) {         // CALL [], JMP []
: : 
: : #ifdef DETOURS_X64
: : 
: :         DWORD dwOffset = *((PDWORD)&pbSrc[2]);
: : 
: :         *m_ppbTarget = (PBYTE)*((PDWORD_PTR)(pbSrc + 6 + dwOffset));
: : 
: : #else
: : 
: :         PBYTE *ppbTarget = *(PBYTE**)&pbSrc[2];
: : 
: :         *m_ppbTarget = *ppbTarget;
: : 
: : #endif
: : 
: :     }
: : 
: :     else if (0x10 == (0x38 & pbSrc[1]) || // CALL /2 --> reg(bits 543) of Mo
: : dR/M == 010
: : 
: :              0x18 == (0x38 & pbSrc[1]) || // CALL /3 --> reg(bits 543) of Mo
: : dR/M == 011
: : 
: :              0x20 == (0x38 & pbSrc[1]) || // JMP /4 --> reg(bits 543) of Mod
: : R/M == 100
: : 
: :              0x28 == (0x38 & pbSrc[1])    // JMP /5 --> reg(bits 543) of Mod
: : R/M == 101
: : 
: :             ) {
: : 
: :         *m_ppbTarget = (PBYTE)DETOUR_INSTRUCTION_TARGET_DYNAMIC;
: : 
: :     }
: : 
: :     const COPYENTRY ce = { 0xff, ENTRY_CopyBytes2Mod };
: : 
: :     return (this->*ce.pfCopy)(&ce, pbDst, pbSrc);
: : 
: : }
: : 
: : 拷贝了原始指令到跳板后,需要在后面增加跳回剩余指令的jmp
: : x86下面detours直接用detour_gen_jmp_immediate函数生成指令
: : 但在x64下面需要修改,因为x64下面指针是64位的,
: : 如果目标地址在当前地址前后2G范围内,可以用32位偏移跳转
: : 否则必须把地址放到内存中,然后在jmp指令指定保存地址的位置
: : 
: : detours在通过detour_alloc_trampoline函数分配跳板内存时
: : 会尽量尝试把内存分配在目标函数2G范围内,避免长跳转指令
: : 这样可以确保目标函数大于5字节(near jmp) 时都能工作
: : 但这样一来就无法确保跳板跟hook函数的位置在2G内,
: : 所以x64下面必须处理这些情况,同理照抄mhook代码如下
: : 
: : inline PBYTE detour_gen_jmp(PBYTE pbCode, PBYTE pbJumpTo)
: : 
: : {
: : 
: :   PBYTE pbJumpFrom = pbCode + 5;
: : 
: :   SIZE_T cbDiff = pbJumpFrom > pbJumpTo ? pbJumpFrom - pbJumpTo : pbJumpTo -
: :  pbJumpFrom;
: : 
: :   if (cbDiff <= 0x7fff0000) {
: : 
: :     *pbCode++ = 0xe9;
: : 
: :     *((PDWORD)pbCode) = (DWORD)(DWORD_PTR)(pbJumpTo - pbJumpFrom);
: : 
: :     pbCode += sizeof(DWORD);
: : 
: :   } else {
: : 
: :     *pbCode++ = 0xff;
: : 
: :     *pbCode++ = 0x25;
: : 
: :     // on x64 we write the relative address of the same location
: : 
: :     *((PDWORD)pbCode) = (DWORD)0;
: : 
: :     pbCode += sizeof(DWORD);
: : 
: :     
: : 
: :     *((PDWORD_PTR)pbCode) = (DWORD_PTR)(pbJumpTo);
: : 
: :     pbCode += sizeof(DWORD_PTR);
: : 
: :   }
: : 
: :   return pbCode;
: : 
: : }
: : 
: : 除此之外,还有一些异常情况的处理,例如暂停线程时,
: : 其RIP刚好在跳板或者目标函数入口处,理论上可以使用
: : 与x86相同的机制,不过暂时没法构造环境测试
: : 
: : #ifdef DETOURS_X64
: : 
: : #define DETOURS_EIP         Rip
: : #define DETOURS_EIP_TYPE    DWORD64
: : 
: : #endif // DETOURS_X64
: : 
: : 最后还有一些跟细节的处理,例如对ModRm下RIP的处理
: : 回头加上再一起放出完整可用的代码吧
: : 
: : Reference
: : 
: : 1. Microsoft Research Detours Package
: :    http://research.microsoft.com/research/downloads/Details/d36340fb-4d3c-4ddd-bf5b-1db25d03713d/Details.aspx
: : 
: : 2. Mhook, an API hooking library, V2.0
: :    http://codefromthe70s.org/mhook2.asp
: : 
: : 3. AMD64 Architecture Programmer’s Manual Volume 3: General-Purpose and System Instructions
: : 
: : 4. Everything You Need To Know To Start Programming 64-Bit Windows Systems, Matt Pietrek
: :    http://msdn.microsoft.com/msdnmag/issues/06/05/x64/default.aspx?print=true&loc=null
: :  .    生命的意义在于   //   ____/ //_ /   //_/     http://flier_lu.blogcn.com .                           
: :  .        希望         / /  /___/_/// /   //_/__     __    _ _★              . 
: :  .        工作          / /   ____// / /    //  /  /'__`/ //`'_/              . 
: :  .      爱你的人         / /  /___/ / / /___/ /  /// __// / / //              . 
: :  .     和你爱的人         / /___/    / /_____/ /__/ /____/ / /_/              . 
: :  .        ……             //___/     //_____///__///____/  //_/.lu@gmail.com . 
0 0