吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 6824|回复: 28
上一主题 下一主题
收起左侧

[调试逆向] HOOK某龙游戏

  [复制链接]
跳转到指定楼层
楼主
15151841470 发表于 2023-4-25 13:32 回帖奖励
主要针对X86游戏的HOOK
ide环境准备:VS2022、MFC.dll文件创建X86、多字节版本

1.Hook初始化1.1 Hook类的封装
[C++] 纯文本查看 复制代码
#pragma once
class Hook5
{
public:
        //-这个是构造函数, 用于初始化一个Hook5对象。它接受4个参数 :
        //-hook地址 : HOOK的位置, 也就是要替换的地址。
        //- hookTargetAdress : 跳转目标位置, 也就是HOOK跳转过去执行的地址。
        //- oldAdress : 要替换的原始5字节代码, 用于后续恢复HOOK。
        //- offset : m_hook返回地址相对于hook地址的偏移, 用于计算HOOK返回后执行的地址。
        Hook5(DWORD hook地址,DWORD hookTargetAdress,BYTE oldAdress[5], DWORD offset=5);
 
        //析构函数
        ~Hook5();        
public:
        BOOL Hook();
        BOOL UnHook();
 
public:
//由于使用了 #pragma pack(1) 指令,结构体中的成员变量 jmp 和 offset 将会紧密地排列在一起,
//不会产生字节对齐的空隙。这样构造的跳转指令在执行时可以直接跳转到指定的地址。
#pragma pack(1)
        typedef struct JmpCode5
        {
                BYTE jmp;
                DWORD offset;        
                JmpCode5() {
                        jmp = 0xE9;
                        offset = 0;
                }
        }JmpCode5;
#pragma pack()
public:
        static DWORD m_hook地址;
        static DWORD m_hook返回地址;
                 JmpCode5 m_jmpCode5;//跳转指令
                 BYTE m_oldCode[5];//原始代码
                 BOOL m_init;//是否初始化
                 BOOL m_hooked;        //是否HOOK
 
};
 

此类的作用是用于实现函数钩子(function hooking)的功能。函数钩子是一种常用的技术,用于拦截并修改程序中指定函数的行为,常用于软件调试、病毒分析、游戏修改等领域。

Hook5类实现了一个对5字节函数进行HOOK的功能,通过构造函数传入要替换的地址、跳转目标地址、原始5字节代码和返回地址相对于hook地址的偏移等参数,通过调用Hook函数实现对指定函数的拦截,并将原始函数的执行流程跳转到指定的目标函数地址,从而实现对函数行为的修改。同时,Hook5类也提供了UnHook函数用于恢复原始函数。

Hook5 类的方法包括:

构造函数 Hook5:用于初始化 Hook5 类对象,传入 hook 地址、跳转目标地址、原始 5 字节代码和偏移量;
析构函数 ~Hook5:用于释放 Hook5 对象;
Hook:实现函数 hook 的方法,将跳转指令写入 hook 地址处,并保存原有代码内存属性.同时修改内存属性为 PAGE_EXECUTE_READWRITE,最后恢复内存属性;
UnHook:取消函数 hook 的方法,将原有代码复制回 hook 地址处,同时恢复内存属性。

1.2 Hook类的实现
[C++] 纯文本查看 复制代码
#include "pch.h"
#include "Hook5.h"
 
DWORD Hook5::m_hook地址 = 0;
 
DWORD Hook5::m_hook返回地址 = 0;
 
//构造函数
Hook5::Hook5(DWORD hook地址, DWORD hookTargetAdress, BYTE oldAdress[5], DWORD offset)
{
        Call_输出调试信息("hook地址:%x    hookTargetAdress:%x", hook地址, hookTargetAdress);
 
        m_hook地址 = hook地址;
        m_hook返回地址 = hook地址 + offset;
        m_jmpCode5.offset = hookTargetAdress - hook地址 - 0x5;
        memcpy_s(m_oldCode, 5, oldAdress, 5);
        m_init = TRUE;
        m_hooked = FALSE;
}
//
Hook5::~Hook5()
{
        if (m_hooked)
        {
                UnHook();
        }
}
 
BOOL Hook5::Hook()
{
        if (!m_init)
        {
                return FALSE;
        }
        if (m_hooked)return TRUE;
        __try 
        {
                Call_输出调试信息("开始hook test");
                //1.修改内存页面属性
                //参数1:要修改的内存地址
                //参数2:要修改的内存大小
                //参数3:新的内存属性
                //参数4:保存原来的内存属性
                DWORD dwOldProtect;
                VirtualProtect((LPVOID)Hook5::m_hook地址, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
 
                //2.将跳转指令写入到m_hook地址处
                memcpy_s((void*)Hook5::m_hook地址, 5, &m_jmpCode5, 5);
 
                //3.恢复内存页面属性
                VirtualProtect((LPVOID)Hook5::m_hook地址, 5, dwOldProtect, &dwOldProtect);
                m_hooked = TRUE;
                return TRUE;
        }
        __except(1)
        {
                Call_输出调试信息("拦截异常");
        }
        return 0;
}
 
 
BOOL Hook5::UnHook()
{
        if (!m_init)
        {
                return FALSE;
        }
        __try
        {
                Call_输出调试信息("开始UnHook test");
                //1.修改内存页面属性
                //参数1:要修改的内存地址
                //参数2:要修改的内存大小
                //参数3:新的内存属性
                //参数4:保存原来的内存属性
                DWORD dwOldProtect = 0;
                VirtualProtect((LPVOID)Hook5::m_hook返回地址, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
 
                //2.将跳转指令写入到m_hook地址处
                memcpy_s((void*)Hook5::m_hook地址, 5, &m_oldCode, 5);
 
                //3.恢复内存页面属性
                VirtualProtect((LPVOID)Hook5::m_hook地址, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
                m_hooked = FALSE;
                return TRUE;
        }
        __except (1)
        {
                Call_输出调试信息("拦截异常");
        }
        return 0;
}

以上就是基本的Hook类封装,所有X86 5字节的Hook都可以调用这个封装,接下来我们以Hook某龙的lua为例,简单调用这层Hook类封装。

2.Hook某龙
2.1 MFC资源dialog创建

2.2窗口类创建(略过,详情请查阅 A星算法04剑网3)

2.3.窗口模态显示(略过,详情请查阅 A星算法04剑网3)

2.4 HOOK和Unhook点击事件函数创建

2.5 Hook地址的初始化
[C++] 纯文本查看 复制代码
#define HookAdressOffset 0x29C243(此为hook目标地址的偏移)
BYTE oldAdress[5] = { 0x51, 0xFF, 0x30, 0xFF, 0xD6 };//原始代码
 
Hook5 hook5((DWORD)GetModuleHandle(NULL) + HookAdressOffset, (DWORD)MyHookFunction, oldAdress, 0x5);


2.6 hook钩子函数业务的编写
[C++] 纯文本查看 复制代码
__declspec(naked) void MyHookFunction()
{
        __asm
        {
                //保存寄存器
                pushad
                mov lua_click,ebx
                mov lua_env, ecx
                mov lua_dostring, esi
                mov lua_state, eax
 
                //恢复标志寄存器
                popad
        }
 
        //调用输出调试信息函数
        Call_输出调试信息("<%s--------%s>\n",(char*)lua_click, (char*)lua_env);
        __asm
        {
                //调用原函数,Call_输出调试信息可能会改变ecx,eax,esi的值,所以要重新赋值
                //0111C243 | 51 | push ecx |
                //0111C244 | FF30 | push dword ptr ds : [eax] |
                //0111C246 | FFD6 | call esi | call lua_dostring
                mov ecx, lua_env
                push ecx
                mov eax, lua_state
                push dword ptr ds : [eax]
                mov esi, lua_dostring
                call esi
 
 
                //跳转到原来的函数
                jmp Hook5::m_hook返回地址
        }
}


2.7 Hook点击事件调用Hook5类的Hook业务函数(记得引入Hook5.h)
[C++] 纯文本查看 复制代码
void HmainDialogASM02::OnBnClickedButton1()
{
        if (!hook5.m_hooked) {  // 只有未 Hook 时才执行
                if (hook5.Hook())
                {
                        Call_输出调试信息("successful hook");
                }
        }
        
}
 
//unhook按钮
void HmainDialogASM02::OnBnClickedButton2()
{
        if (hook5.m_hooked){  // 只有已 Hook 时才执行
                if (hook5.UnHook())
                {
                        Call_输出调试信息("successful Unhook");
                }
        }
        
 
}


3.生成DLL注入游戏





我们发现成功拦截了游戏的LUA接口,获取到所有的LUA详细信息并打印输出。

实战环节略有缺失,本文主要是作为笔记的补充以及思路的提供,读者如果有不懂之处,可私信。

言归正传,本案例的核心功能是实现对Hook功能的封装,即Hook5类。开发者可根据业务需要,调用此HOOK封装,本案例主要是通过MFC DLL的形式注入,开发人员可以自行选择注入方法。当然也可以不用我这个方法,采用外部读取目标进程的形式也不是不可以。

免费评分

参与人数 11吾爱币 +16 热心值 +10 收起 理由
SCH6310 + 1 我很赞同!
Hmily + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
qa132465 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
kuangxiao + 1 + 1 热心回复!
ShuShuLaiLo + 1 + 1 我很赞同!
gunxsword + 1 + 1 我很赞同!
xiangyujayfun + 1 + 1 我很赞同!
theStyx + 2 + 1 谢谢@Thanks!
yunji + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
0106yingzi + 1 热心回复!
cntjgaowei + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
ruanrongwei 发表于 2023-4-25 16:49
哇塞,我居然不知道这是什么游戏
3#
fireandice 发表于 2023-4-25 18:07
4#
hading 发表于 2023-4-25 20:48
5#
xj1992628 发表于 2023-4-25 23:50
盲猜一手易语言出身
6#
zhy1992 发表于 2023-4-26 08:50
很厉害 修改游戏
7#
qq824356421 发表于 2023-4-26 09:31
这是什么游戏?楼主
8#
 楼主| 15151841470 发表于 2023-4-26 11:56 |楼主
qq824356421 发表于 2023-4-26 09:31
这是什么游戏?楼主

看图识别游戏
9#
jjghaa1234 发表于 2023-4-26 12:06
虽然不知道这是什么游戏,但是阻止不了我学习的步伐,大佬厉害,感谢分享
10#
kantal 发表于 2023-4-26 19:52
这代码风格,盲猜是专业出身~~
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-21 17:08

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表