bester 发表于 2024-1-25 17:04

HOOK_API获取对应的寄存器值

本帖最后由 bester 于 2024-1-25 17:27 编辑

成熟的Detours固然好用,但是太繁琐了,像这种比较精细化的操作,直接裸hook可以解决

什么样的问题取决于什么样的解决方式,如果考虑X64点话,用库会比较方便点,这种纯X32小玩意直接不解释连招

下面的代码,有一点点仿Detours的味道,之前的帖子也分析过,不多解释,这次算补充

另外有一个点没有考虑,线程安全,这个的话,用原子操作即可,非常简单

后续看情况补充这些不足之处,最近确实有点没时间搞这些小东西

另外其实也可以加个回调函数,看你自己

获取到值以后就触发回调,这些都是很简单东西

这里提一下只是避免有人不知道而已

以前这玩意卖E模块,还卖挺贵的 自己实现以后发现也就那么回事儿

不懂的同学可以回帖问,对代码不理解的话,我觉得挺简单的

代码适用于release模式,debug模式无法使用,原因如下
https://www.cnblogs.com/uu102/p/4695026.html
https://www.cnblogs.com/iBinary/p/14148137.html

#include <iostream>
#include<windows.h>

DWORD _CalcLen(DWORD x, DWORD y)
{
      return x - y - 5;
}
void* apiaddr = MessageBoxW;
void* jmpaddr = (void*)((int)(apiaddr) + 5);
void* MyESP = nullptr;
BYTE New_Data = { 0xE9 };
void __declspec (naked) TransFun()
{
      _asm
      {
                nop //这里会被覆盖API头部的5个字节,我这里用NOP替代
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                nop
                jmp jmpaddr
      }
}
void __declspec (naked) MiddleFun()
{
      _asm
      {
                pushad //push以后增加4*8字节
                mov eax, //这里的计算也要多增加32,也可以取消pushad 随你自己
                mov MyESP, eax
                popad
                jmp TransFun //恢复函数
               
      }
}
void inhook()
{
      *(DWORD*)(New_Data + 1) = _CalcLen((DWORD)MiddleFun, (DWORD)apiaddr);
      DWORD prot;
      VirtualProtect(MiddleFun, 5, PAGE_EXECUTE_READWRITE, &prot);
      memcpy(TransFun, apiaddr, 5);
      VirtualProtect(apiaddr, 5, PAGE_EXECUTE_READWRITE, &prot);
      memcpy(apiaddr, New_Data, 5);
}

int main()
{
      MessageBoxW(NULL, L"我是HOOK前", L"测试", MB_OK);
      inhook();
      MessageBoxW(NULL, L"HOOK_API后", L"测试", MB_OK);
      return 0;
}

deice 发表于 2024-1-25 17:51

本帖最后由 deice 于 2024-1-25 17:52 编辑

DEATHTOUCH 发表于 2024-1-25 17:42
我实现了一个极简的x64 hook方法,只要几十行,如果能有一个迷你的反汇编器计算指令长度的话,我想要不了 ...
你需要的是这个吧

inline int __insn_len_x86(void *insn, enum __bits bits) {
      int len = 0, twobytes = 0, has_modrm = 0;
      enum __bits operand_bits = __b32, addr_bits = bits;
      unsigned char *c = (unsigned char*)insn, modrm, opcode;

      /* prefixes
         *
         * 0xf0, 0xf2, 0xf3, 0x2e, 0x36
         * 0x3e, 0x26, 0x64, 0x65, 0x66, 0x67
         */

      // skip prefixes
      while (*c == 0xf0 || *c == 0xf2 || *c == 0xf3 ||
               *c == 0x2e || *c == 0x36 || *c == 0x3e || *c == 0x26 ||
               (*c & 0xfc) == 0x64) {
                if (*c == 0x66) // 16bits operands
                        operand_bits = __b16;
                if (*c == 0x67) // 16bits addressing (x86-32), 32bits addressing (x86-64)
                        addr_bits = bits == __b32 ? __b16 : __b32;
                c++;
                len++;
      }

      if (bits == __b64 && (*c & 0xf0) == 0x40) { // x86-64 && REX byte
                if (*c & REX_W)
                        operand_bits = __b64;
                c++;
                len++;
      }

      /* 0x9b prefix is used only by the following 1byte opcodes
         *
         * 0xd9 Mod != 11 Reg/Op = 110 or 111
         * 0xdb ModR/M = 0xe2 or 0xe3
         * 0xdd Reg/Op = 110 or 111
         * 0xdf ModR/M = 0xe0
         */

      // check for 2bytes opcodes (0x0f prefix)
      if (*c == 0x0f) {
                twobytes = 1;
                c++;
                len++;
      } else if (*c == 0x9b && // check 0x9b prefix
                   ( (c == 0xd9 && (c & Mod_M) != Mod_M && (c & 0x30) == 0x30) ||
                     (c == 0xdb && (c == 0xe2 || c == 0xe3)) ||
                     (c == 0xdd && (c & 0x30) == 0x30) ||
                     (c == 0xdf && c == 0xe0)
                           )) {
                c++;
                len++;
      }

      opcode = *c++;
      len++;

      /* 1byte opcodes that use ModR/M byte:
         *
         * 0x00 - 0x03, 0x08 - 0x0b,
         * 0x10 - 0x13, 0x18 - 0x1b,
         * 0x20 - 0x23, 0x28 - 0x2b,
         * 0x30 - 0x33, 0x38 - 0x3b,
         * 0x62, 0x63, 0x69, 0x6b,
         * 0x80 - 0x8f, 0xc0, 0xc1,
         * 0xc4 - 0xc7,
         * 0xd0 - 0xd3, 0xd8 - 0xdf
         * 0xf6, 0xf7, 0xfe, 0xff
         */

      if (!twobytes &&
            ((opcode & 0xf4) == 0 || (opcode & 0xf4) == 0x10 ||
             (opcode & 0xf4) == 0x20 || (opcode & 0xf4) == 0x30 ||
             opcode == 0x62 || opcode == 0x63 || opcode == 0x69 || opcode == 0x6b ||
             (opcode & 0xf0) == 0x80 || opcode == 0xc0 || opcode == 0xc1 ||
             (opcode & 0xfc) == 0xc4 || (opcode & 0xfc) == 0xd0 ||
             (opcode & 0xf8) == 0xd8 || opcode == 0xf6 || opcode == 0xf7 ||
             opcode == 0xfe || opcode == 0xff))
                has_modrm = 1;

      /* 2bytes opcodes that they *don't* use ModR/M byte:
         *
         * 0x05 - 0x09, 0x0b, 0x0e,
         * 0x30 - 0x37, 0x77, 0x80 - 0x8f,
         * 0xa0 - 0xa2, 0xa8 - 0xaa, 0xb9
         * 0xc8 - 0xcf
         */

      if (twobytes) {
                if (!((opcode >= 0x05 && opcode <= 0x09) || opcode == 0x0b ||
                      opcode == 0x0e || (opcode & 0xf8) == 0x30 || opcode == 0x77 ||
                      (opcode & 0xf0) == 0x80 || (opcode >= 0xa0 && opcode <= 0xa2) ||
                      (opcode >= 0xa8 && opcode <= 0xaa) || (opcode & 0xf8) == 0xc8 ||
                      opcode == 0xb9))
                        has_modrm = 1;

                // 3bytes opcodes
                if (opcode == 0x38 || opcode == 0x3a) {
                        c++;
                        len++;
                }

                // 3DNow! opcode
                if (opcode == 0x0f)
                        len++;
      }

      if (has_modrm) {
                len++;
                modrm = *c++;
                if (addr_bits != __b16 && (modrm & (Mod_M | RM_M)) == 5) // Mod = 00 R/M = 101
                        len += 4;
                if (addr_bits == __b16 && (modrm & (Mod_M | RM_M)) == 6) // Mod = 00 R/M = 110 and 16bits addressing
                        len += 2;
                if ((modrm & Mod_M) == 0x40) // Mod = 01
                        len += 1;
                if ((modrm & Mod_M) == 0x80) // Mod = 10
                        len += addr_bits == __b16 ? 2 : 4;

                // check SIB byte
                if (addr_bits != __b16 && (modrm & Mod_M) != Mod_M && (modrm & RM_M) == 4) { // if it has SIB
                        len++;
                        if ((modrm & Mod_M) == 0 && (*c & Base_M) == 5) // Mod = 00   SIB Base = 101
                              len += 4;
                        c++;
                }
      }

      /* Immediate operands
         *
         * 1byte opcode list:
         *
         * imm8 (1 byte)
         *
         * 0x04, 0x0c, 0x14, 0x1c, 0x24, 0x2c, 0x34, 0x3c, 0x6a, 0x6b, 0x70 - 0x7f,
         * 0x80, 0x82, 0x83, 0xa8, 0xb0 - 0xb7, 0xc0, 0xc1, 0xc6, 0xcd, 0xd4,
         * 0xd5, 0xe0 - 0xe7, 0xeb, 0xf6 (Reg/Op = 000 or Reg/Op = 001)
         *
         * imm16 (2 bytes)
         *
         * 0xc2, 0xca
         *
         * imm16/32 (2 bytes if operand_bits == __b16 else 4 bytes)
         *
         * 0x05, 0x0d, 0x15, 0x1d, 0x25, 0x2d, 0x35, 0x3d, 0x68, 0x69, 0x81, 0xa9
         * 0xc7, 0xe8, 0xe9
         *
         * imm16/32/64 (2 bytes if operand_bits == __b16, 4 bytes if __b32, 8 bytes if __b64)
         *
         * 0xb8 - 0xbf, 0xf7 (Reg/Op = 000 or Reg/Op = 001)
         *
         * moffs (2 bytes if addr_bits == __b16, 4 bytes if __b32, 8 bytes if __b64)
         *
         * 0xa0, 0xa1, 0xa2, 0xa3
         *
         * others
         *
         * 0xea, 0x9a: imm16 + imm16/32
         * 0xc8: imm16 + imm8
         *
         *
         * 2bytes opcode list:
         *
         * imm8 (1 byte)
         *
         * 0x70 - 0x73, 0xa4, 0xac, 0xba, 0xc2, 0xc4 - 0xc6
         *
         * imm16/32 (2 bytes if operand_bits == __b16 else 4 bytes)
         *
         * 0x80 - 0x8f
         *
         *
         * all 3bytes opcodes with 0x3a prefix have imm8
         */
      if (!twobytes) { // 1byte opcodes
                // imm8
                if (((opcode & 7) == 4 && (opcode & 0xf0) <= 0x30) ||
                  opcode == 0x6a || opcode == 0x6b || (opcode & 0xf0) == 0x70 ||
                  opcode == 0x80 || opcode == 0x82 || opcode == 0x83 ||
                  opcode == 0xa8 || (opcode & 0xf8) == 0xb0 || opcode == 0xc0 ||
                  opcode == 0xc1 || opcode == 0xc6 || opcode == 0xcd ||
                  opcode == 0xd4 || opcode == 0xd5 || (opcode & 0xf8) == 0xe0 ||
                  opcode == 0xeb || (opcode == 0xf6 && (modrm & 0x30) == 0))
                        len += 1;

                // imm16
                if (opcode == 0xc2 || opcode == 0xca)
                        len += 2;

                // imm16/32
                if (((opcode & 7) == 5 && (opcode & 0xf0) <= 0x30) ||
                  opcode == 0x68 || opcode == 0x69 || opcode == 0x81 ||
                  opcode == 0xa9 || opcode == 0xc7 || opcode == 0xe8 ||
                  opcode == 0xe9)
                        len += operand_bits == __b16 ? 2 : 4;

                // imm16/32/64
                if ((opcode & 0xf8) == 0xb8 || (opcode == 0xf7 && (modrm & 0x30) == 0))
                        len += operand_bits == __b16 ? 2 : operand_bits == __b32 ? 4 : 8;

                // moffs
                if ((opcode & 0xfc) == 0xa0)
                        len += addr_bits == __b16 ? 2 : addr_bits == __b32 ? 4 : 8;

                // others
                if (opcode == 0xea || opcode == 0x9a)
                        len += 2 + (operand_bits == __b16 ? 2 : 4);
                if (opcode == 0xc8)
                        len += 3;
      } else { // 2bytes opcodes
                // imm8
                if ((opcode & 0xfc) == 0x70 || opcode == 0xa4 ||
                  opcode == 0xac || opcode == 0xba || opcode == 0xc2 ||
                  (opcode >= 0xc4 && opcode <= 0xc6))
                        len += 1;

                // imm16/32
                if ((opcode & 0xf0) == 0x80)
                        len += operand_bits == __b16 ? 2 : 4;

                // 3bytes opcodes with 0x3a prefix
                if (opcode == 0x3a)
                        len += 1;
      }

      // wrong length
      if (len > MAX_INSN_LEN_x86)
                len = 1;

      return len;
}

/*==============================================================================*/
/*                            获取32位指令长度                                  */
/*==============================================================================*/
int insn_len_x86_32(void *insn) {
      return __insn_len_x86(insn, __b32);
}

/*==============================================================================*/
/*                            获取64位指令长度                                  */
/*==============================================================================*/
int insn_len_x86_64(void *insn) {
      return __insn_len_x86(insn, __b64);
}





ULONG GetHookLen(ULONG64 HookAddress, ULONG minLen,BOOLEAN isX64)
{
        ULONG len = 0;
        ULONG offset = 0;

        if (isX64)
        {
               
                while (len < minLen)
                {
                        offset = insn_len_x86_64(HookAddress + len);

                        len += offset;
                }
               
        }
        else
        {
                while (len < minLen)
                {
                        offset = insn_len_x86_32(HookAddress + len);

                        len += offset;
                }
        }

        return len;
}

bester 发表于 2024-1-25 17:30

@朱朱你堕落了 懂的人自然懂,你愿意带个拖油瓶我也没话说

DEATHTOUCH 发表于 2024-1-25 17:34

x86这样确实简单方便,用不着重量级的库,就是x64非常麻烦,主要是函数的入口点需要反汇编判断长度。
我记得之前搞x64版本的ws2_32.dll的hook就是,不同Windows版本的函数入口不一样的,只能用minhook来解决。

其实对于x64我有比minhook更轻量级的方案,就是差一个反汇编器。

朱朱你堕落了 发表于 2024-1-25 17:36

bester 发表于 2024-1-25 17:30
@朱朱你堕落了 懂的人自然懂,你愿意带个拖油瓶我也没话说

哈哈,老弟你还生气了啊,用解决问题就行,用库或不用库,目的都是为了解决问题,自己怎么习惯怎么来。

bester 发表于 2024-1-25 17:38

DEATHTOUCH 发表于 2024-1-25 17:34
x86这样确实简单方便,用不着重量级的库,就是x64非常麻烦,主要是函数的入口点需要反汇编判断长度。
我记 ...

所以我才说,什么样的问题取决于什么样的解决方式,X64用库就是比裸HOOK方便,在不同情况下肯定都考虑最优解

bester 发表于 2024-1-25 17:39

朱朱你堕落了 发表于 2024-1-25 17:36
哈哈,老弟你还生气了啊,用解决问题就行,用库或不用库,目的都是为了解决问题,自己怎么习惯怎么来。

没有啊,用不用肯定是个人习惯,我觉得无所谓,对你来说库方便那就是库方便,对我来说手写简单,就是手写简单,为什么要生气?

DEATHTOUCH 发表于 2024-1-25 17:42

bester 发表于 2024-1-25 17:38
所以我才说,什么样的问题取决于什么样的解决方式,X64用库就是比裸HOOK方便,在不同情况下肯定都考虑最优解

我实现了一个极简的x64 hook方法,只要几十行,如果能有一个迷你的反汇编器计算指令长度的话,我想要不了多少代码。
不过看Intel的白皮书真容易让人睡觉,所以一直没搞反汇编器。

DEATHTOUCH 发表于 2024-1-25 17:45

DEATHTOUCH 发表于 2024-1-25 17:42
我实现了一个极简的x64 hook方法,只要几十行,如果能有一个迷你的反汇编器计算指令长度的话,我想要不了 ...

思路就是两次跳转,第一次5字节,第二次14字节,利用PE文件头找到.text区段的空白处承接第一次跳转,第二次就想跳哪跳哪了。
就是第一次的5字节可能截断了,所以补丁的长度不好判断需要识别。

DEATHTOUCH 发表于 2024-1-25 20:23

deice 发表于 2024-1-25 17:51
你需要的是这个吧

inline int __insn_len_x86(void *insn, enum __bits bits) {

请问这段代码是否存在对应的许可证,比如MIT或BSD之类,还是说public domain,或者你自己写的就提供一个版权声明这样。
我打算开一个帖子介绍一下我的x64 hook方法,顺便分发代码(应该是MIT License这类的)。
页: [1]
查看完整版本: HOOK_API获取对应的寄存器值