C++ Windows实现64/32位InlineHook(x86平台)

C++实现64/32位InlineHook
Windows IDE:Visual Studio 2022

引用库

#include <iostream>
#include <string>
#include <Windows.h>
#include <vector>
#include <sstream>

前置:
获取请看这里Windows内存修改库(包括HOOK,以及常用指令)
此InlineHook部分依赖已经包含在C++内存修改和进程操作库

InlineHook就是在某地址做一个跳转,跳转到我们的自定义的汇编代码,然后跳转回去

实现代码-方法一

class InlineHookRelativeMode
{
    // 这种方式至少预留要5个字节(32/64位)
private:
    struct HookInfo
    {
        HANDLE process = 0;
        uintptr_t startAddress = 0;
        uintptr_t aimAddress = 0;
        uintptr_t offset = 0;
        std::vector<uint8_t> startMachine = {};
        std::vector<uint8_t> hookMachine = {};
        int length = 5;
        int nopUnmber = 0;
    };
    MemoryOpera memoryOpera;
    HookInfo hookInfo;
 
public:
    void install(HANDLE process, uintptr_t address, std::vector<uint8_t> machineCode, int byteGroupNumber = 0, bool is64 = false)
    {
        hookInfo.process = process;
        hookInfo.startAddress = address;
        for (const auto &element : machineCode)
        {
            hookInfo.hookMachine.push_back(element); // 保存原地址汇编码
        }
        // 分配内存
        if (is64 == true)
        {
            hookInfo.aimAddress = FindAllowRemoteMemory(process, address, machineCode.size(), 0X01FFFFFF);
        }
        else
        {
            hookInfo.aimAddress = SimpleRemoteMemory(process, machineCode.size());
        }
        hookInfo.offset = hookInfo.aimAddress - address - hookInfo.length;
        hookInfo.nopUnmber = byteGroupNumber - hookInfo.length; // 获取nop数量
        memoryOpera.Prepare(process, address, hookInfo.length);
    }
    BOOL hook()
    {
        if (hookInfo.nopUnmber < 0)
        {
            return false;
        }
        bool result = true;
        memoryOpera.Chage(3);
        std::vector<uint8_t> readByteArray = ReadMemoryByteArray(hookInfo.process, hookInfo.startAddress, static_cast<SIZE_T>(hookInfo.length + hookInfo.nopUnmber));
        for (const auto &element : readByteArray)
        {
            hookInfo.startMachine.push_back(element);
        }
        std::vector<uint8_t> JmpAimCode = {0XE9}; // jmp
        std::string startMachineCode = HEXTenToSixteen(hookInfo.offset);
        if ((startMachineCode.length() % 2 == 0) == false) // 判断是否为偶数
        {
            startMachineCode = "0" + startMachineCode;
        }
        if ((startMachineCode.length() / 2) <= hookInfo.length - 1)
        { // 判断是否是指定长度
            for (size_t i = 0; i < hookInfo.length - (startMachineCode.length() / 2); i++)
            {
                startMachineCode = "00" + startMachineCode;
            }
        }
        else
        {
            startMachineCode = startMachineCode.substr(startMachineCode.length() - 2 * (hookInfo.length - 1), 2 * (hookInfo.length - 1));
        }
        for (size_t i = startMachineCode.length(); i >= 2; i -= 2)
            {
                std::string temp = startMachineCode.substr(i - 2, 2);
                uint8_t hexByte = static_cast<uint8_t>(std::stoi(temp, nullptr, 16));
                JmpAimCode.push_back(hexByte);
            }
        for (size_t i = 0; i < hookInfo.nopUnmber; i++) // 补充nop
        {
            JmpAimCode.push_back(0X90); // nop
        }
        if (WriteMenmoryByteArray(hookInfo.process, hookInfo.startAddress, JmpAimCode) == FALSE)
        {
            result = false;
        }
        if (WriteMenmoryByteArray(hookInfo.process, hookInfo.aimAddress, hookInfo.hookMachine) == FALSE)// 写入自定义汇编码
        {
            result = false;
        }
        uintptr_t JmpBackMachineCode = 0 - hookInfo.hookMachine.size() - hookInfo.offset - hookInfo.length + hookInfo.nopUnmber; // 计算回跳偏移
        std::vector<uint8_t> JmpBackCode = {0XE9};//jmp
        std::string backMachineCode = HEXTenToSixteen(JmpBackMachineCode);
        if ((backMachineCode.length() % 2 == 0) == false) // 判断是否为偶数
        {
            backMachineCode = "0" + backMachineCode;
        }
        if ((backMachineCode.length() / 2) <= hookInfo.length - 1)// 判断是否是指定长度
        {
            for (size_t i = 0; i < hookInfo.length - (backMachineCode.length() / 2); i++)
            {
                backMachineCode = "00" + backMachineCode;
            }
        }
        else
        {
            backMachineCode = backMachineCode.substr(backMachineCode.length() - 2 * (hookInfo.length - 1), 2 * (hookInfo.length - 1));
        }
        for (size_t i = backMachineCode.length(); i >= 2; i -= 2)
        {
            std::string temp = backMachineCode.substr(i - 2, 2);
            uint8_t hexByte = static_cast<uint8_t>(std::stoi(temp, nullptr, 16));
            JmpBackCode.push_back(hexByte);
        }
        if (WriteMenmoryByteArray(hookInfo.process, hookInfo.aimAddress + hookInfo.hookMachine.size(), JmpBackCode) == FALSE)// 写入回跳汇编码
        {
            result = false;
        }
        return result;
    }
 
    BOOL uninstall()
    {
        if (hookInfo.nopUnmber < 0)
        {
            return false;
        }
        bool result = true;
        memoryOpera.Restore();
        if (WriteMenmoryByteArray(hookInfo.process, hookInfo.startAddress, hookInfo.startMachine) == FALSE)// 还原原汇编代码
        {
            result = false;
        }
        if (SimpleFreeMemory(hookInfo.process, hookInfo.aimAddress) == FALSE)// 释放分配内存
        {
            result = false;
        }
        return result;
    }
};

实现代码-方法二

class InlineHookAbsoluteMode
{
    // 这种方式至少预留要9个字节(32位),14个字节(64位)
private:
    struct HookInfo
    {
        HANDLE process = 0;
        uintptr_t startAddress = 0;
        uintptr_t aimAddress = 0;
        std::vector<uint8_t> startMachine = {};
        std::vector<uint8_t> hookMachine = {};
        int length = 9;
        bool is64 = false;
        int addressLength = 4;
        int nopUnmber = 0;
    };
    MemoryOpera memoryOpera;
    HookInfo hookInfo;
 
public:
    void install(HANDLE process, uintptr_t address, std::vector<uint8_t> machineCode, int byteGroupNumber = 0, bool is64 = false)
    {
        hookInfo.process = process;
        hookInfo.startAddress = address;
        hookInfo.hookMachine.push_back({0X58}); // pop rax(64);pop eax(32)
        hookInfo.is64 = is64;
        if (is64 == true)
        {
            hookInfo.addressLength = 8;
            hookInfo.length = 14;
        }
        for (const auto &element : machineCode)
        {
            hookInfo.hookMachine.push_back(element);
        }
        hookInfo.aimAddress = SimpleRemoteMemory(process, machineCode.size()); // 分配内存
        hookInfo.nopUnmber = byteGroupNumber - hookInfo.length;
        memoryOpera.Prepare(process, address, hookInfo.length);
    }
    BOOL hook()
    {
        if (hookInfo.nopUnmber < 0)
        {
            return false;
        }
        bool result = true;
        memoryOpera.Chage(3);
        std::vector<uint8_t> readByteArray = ReadMemoryByteArray(hookInfo.process, hookInfo.startAddress, static_cast<SIZE_T>(hookInfo.length + hookInfo.nopUnmber)); // 获取原地址数据
        for (const auto &element : readByteArray)
        {
            hookInfo.startMachine.push_back(element); // 保存原地址汇编码
        }
        std::vector<uint8_t> JmpAimCode;
        if (hookInfo.is64 == false)
        {
            JmpAimCode = {0X50, 0XB8};
            // push eax
            // mov eax,..
        }
        else
        {
            JmpAimCode = {0X50, 0X48, 0XB8};
            // push rax
            // mov rax,..
        }
        std::string startMachineCode = HEXTenToSixteen(hookInfo.aimAddress);
        if ((startMachineCode.length() % 2 == 0) == false) // 判断文本长度是否是偶数
        {
            startMachineCode = "0" + startMachineCode;
        }
        if ((startMachineCode.length() / 2) < hookInfo.addressLength)
        {
            for (size_t i = 0; i <= hookInfo.addressLength - (startMachineCode.length() / 2); i++)
            {
                startMachineCode = "00" + startMachineCode;
            }
        }
        else
        {
            startMachineCode = startMachineCode.substr(startMachineCode.length() - 2 * hookInfo.addressLength, 2 * hookInfo.addressLength);
        }
        for (size_t i = startMachineCode.length(); i >= 2; i -= 2)
            {
                std::string temp = startMachineCode.substr(i - 2, 2);
                uint8_t hexByte = static_cast<uint8_t>(std::stoi(temp, nullptr, 16));
                JmpAimCode.push_back(hexByte);
            }
        JmpAimCode.push_back({0XFF});
        JmpAimCode.push_back({0XE0});
        // jmp rax(64);jmp eax(32)
        for (size_t i = 0; i < hookInfo.nopUnmber; i++) // 补充nop
        {
            JmpAimCode.push_back(0X90);
        }
        JmpAimCode.push_back({0X58});
        if (WriteMenmoryByteArray(hookInfo.process, hookInfo.startAddress, JmpAimCode) == FALSE)
        {
            result = false;
        }
        if (WriteMenmoryByteArray(hookInfo.process, hookInfo.aimAddress, hookInfo.hookMachine) == FALSE)// 写入自定义汇编码
        {
            result = false;
        }
        uintptr_t JmpBackMachineCode = hookInfo.startAddress + hookInfo.length + hookInfo.nopUnmber - 1; // 计算回跳地址
        std::vector<uint8_t> JmpBackCode;
            if (hookInfo.is64 == false)
        {
            JmpBackCode = {0X50, 0XB8};
        }
        else
        {
            JmpBackCode = {0X50, 0X48, 0XB8};
        }
        std::string backMachineCode = HEXTenToSixteen(JmpBackMachineCode);
        if ((backMachineCode.length() % 2 == 0) == false)
        {
            backMachineCode = "0" + backMachineCode;
        }
        if ((backMachineCode.length() / 2) < hookInfo.addressLength)
        {
            for (size_t i = 0; i <= hookInfo.addressLength - (backMachineCode.length() / 2); i++)
            {
                backMachineCode = "00" + backMachineCode;
            }
        }
        else
        {
            backMachineCode = backMachineCode.substr(backMachineCode.length() - 2 * hookInfo.addressLength, 2 * hookInfo.addressLength);
        }
        for (size_t i = backMachineCode.length(); i >= 2; i -= 2)
        {
            std::string temp = backMachineCode.substr(i - 2, 2);
            uint8_t hexByte = static_cast<uint8_t>(std::stoi(temp, nullptr, 16));
            JmpBackCode.push_back(hexByte);
        }
        JmpBackCode.push_back({0XFF});
        JmpBackCode.push_back({0XE0});
        if (WriteMenmoryByteArray(hookInfo.process, hookInfo.aimAddress + hookInfo.hookMachine.size(), JmpBackCode) == FALSE)// 写入回跳汇编码
        {
            result = false;
        }
        return result;
    }
    BOOL uninstall()
    {
        if (hookInfo.nopUnmber < 0)
        {
            return false;
        }
        bool result = true;
        memoryOpera.Restore();
        if (WriteMenmoryByteArray(hookInfo.process, hookInfo.startAddress, hookInfo.startMachine) == FALSE)// 还原汇编码
        {
            result = false;
        }
        if (SimpleFreeMemory(hookInfo.process, hookInfo.aimAddress) == FALSE)// 释放分配内存
        {
            result = false;
        }
        return result;
    }
};

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
本人使用易语言 也有10几年时间,至今留下来的也只是一些怀念和情节,还记得上一次在本论坛发帖是在10年前,期间也只是零星来论坛看看。平常使用也纯属兴趣爱好,并非科班出身,一些见解如若有误,忘大神包含。我们知道目前易语言是支持32位编译,后期估计也不会有所改善了,这似乎已经成为一门被放弃的失败品。既然如此面对如今64位系统的普及,易语言爱好者面对64位程序的操作层面上就显得有些无奈和悲哀。好在有着一些执着的爱好者不想放弃,依然在鼓励解决这些问题,如本论坛的一个开源模块WOW64Ext,就为易语言操作64位模块进程提供了一些基本的封装,本人也借此基础上封装了几个功能,作为进一步扩展,有兴许的朋友可以继续完善。 一:浅谈64位进程远程hook技术 关于HOOK这个话题,网络上铺天盖地并无新鲜,故在此我就不讲述什么是HOOK这些无聊话题了,本文主要阐述一些64位下远程HOOK32位的主要区别。首先我们来看看要实现一个远程HOOK的构成顺序:1:在目标进程申请内存空间,存放我们截断后的穿插代码与HOOK原代码2:修改HOOK目标位置的指令为跳转至1申请的内存空间中3:穿插代码中把我们需要的寄存器或其他通过通讯手段传达到我们程序的回调接口中去,在这个过程中如果只需要取值,穿插代码不需要等待,如果需要修改生效,穿插代码需要等待回调接口的返回,并把修改内容写回。4:穿插代码最后跳回到HOOK位置长度的下一跳指令,或指定的位置。5:完成整个HOOK过程了解了整个过程看上去似乎很简单,确实要做到这个过程是不难的,只是要做到相对完美要考虑的情况有很多。比如对跳转使用的选择情况:HOOK跳转代码肯定是越短越好,像32位JMP跳转只需要5字节即可,但是在64位进程中情况确截然不同。32位进程寻址能力为4字节,而64位进程寻址能力变成了8字节,然而64位汇编中所有的跳转直接寻址只支持4字节,这在32位中当然不是什么问题,因为32位最大寻址本来就不会超越4字节,不存在超限的说法:但64位中想要达到长转移,必须借用寄存器或地址偏移,那么一般在64位中HOOK的跳转代码在不影响寄存器的情况下一般使用如下办法FFFFFFFFFFFFFFFF作为跳转目标地址:为了不影响寄存器必须提前压入一个寄存器 --------------------------Push raxMov rax, FFFFFFFFFFFFFFFF JMP rax 或 call rax 在内部要取回rax ,这里注意JMP和call的区别,最后平栈 -------------------------- Push rax Mov rax,FFFFFFFFFFFFFFFF Push rax Ret 在内部要取回rax ,最后平栈 有的朋友看到这要问了 我不能直接 JMP FFFFFFFFFFFFFFFF或者 push FFFFFFFFFFFFFFFF 啊,您要这么问,我实在不知如何回答你,表示无语,您还是直接下个源码玩玩算了。 其他类似的列子我就不一一举例了,总结也是差不多形态,以上列子共占用13字节长度,这还是堆栈放在了内部平,否则还要+1个字节长度,如果放弃其中一个寄存器可以-1个字节长度,所以一般网上现有的64hook一般都在12字节以上,但是一个好用的hook要占用13字节的长度,对我而言无疑无法忍受,难道真的没有其他办法了吗,要保护寄存器且支持长转移,是不是还有其他办法,那么其实是有办法的,就是通过JMP [rip] 机器码形态为 FF 25 00 00 00 00 这句代码占用6字节,那么这是什么意思呢 FF 25 = jmp ,00 00 00 00为偏移长度 对一个支持2G的字节转移长度,JMP [rip]在调试器中可以解释为 jmp qword ptr ds:[0x地址],对了,也就是读取这个偏移位置中的8字节数值作为跳转地址转移过去,如果偏移为00 00 00 00 那么就代表 JMP [rip]的下一条指令处8字节数据。想到这你也许会问  那么这个意思不就是JMP [rip]6字节+8字节长度吗,对如果是连起来确实如此,但是我们可以给他个偏移啊,不就可以分开了吗,我们只需要搜索同模块的其他位置中00或CC等连续8字节无用代码位置,把跳转的地址写入其中,那么JMP [rip]就可以通过偏移读取到跳转地址了。我们也就能实现6字节的HOOK,这个方式的亮点是改写长度小,且不影响寄存器和rsp堆栈指针,也算是达到曲线救国的目的。 比如对穿插代码中数据传递的问题:我们要获得16个通用寄存器RAX—R15的每个值,这些值我们又如何传递过去。一般远程HOOK数据传递使用消息或者远线程,因为这两种方式汇编改写量小一点,相对容易实现,在这我们不讨论远线程,我们来看看消息传递,一般是两个函数的选

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

STHUDY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值