免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!
完整代码地址:下载名为: htdHook2.0.zip 的文件
链接:https://pan.baidu.com/s/17_aXbMCyexGIlP3Yz_ajIA
提取码:ilbw
--来自百度网盘超级会员V4的分享
上一个内容:49.实现调试器HOOK机制
以 49.实现调试器HOOK机制 它的代码为基础进行修改
48.破坏性更小的代码跳转(异常)(异常拦截说明在它里面)
辅助核心文件说明:
CWndMain(辅助窗口主要由OnBnClickedButton2函数开始)
调用SetHook函数就开始进行拦截了,拦截(SetHook函数)中实现了游戏代码修改、还原、拦截地址与拦截之后的处理函数关联功能。
HOOKPOINT.h文件里的内容:
// 当前文件是用来关联拦截点与拦截之后处理函数、游戏代码修改、还原的 HOOKPOINT.h 文件里的函数
#pragma once
#define _HTDHOOK2_
typedef struct CPUINFO {
unsigned EFlags;
unsigned Edi;
unsigned Esi;
unsigned Ebp;
unsigned Esp;
unsigned Ebx;
unsigned Edx;
unsigned Ecx;
unsigned Eax;
unsigned Eip;
}*PCPUINFO;
// 下面代码不删的原因是,记录一下c语言写法(玩法)
//#ifdef _HTDHOOK2_
//#define HOOKREFS PCONTEXT val
//#else
//#define HOOKREFS PCPUINFO val
//#endif // _HTDHOOK2_
#define HOOKREFS2 PCONTEXT val
#define HOOKREFS PCONTEXT val
typedef bool (*HOOKBACK)(PCPUINFO);
typedef bool (*HOOKBACK2)(PCONTEXT);
#define _ESI val->Esi
#define _EDI val->Edi
#define _EBP val->Ebp
#define _ESP val->Esp
#define _EBX val->Ebx
#define _EDX val->Edx
#define _ECX val->Ecx
#define _EAX val->Eax
#define _EIP val->Eip
typedef unsigned char uchar;
typedef class HOOKPOINT
{
private:
// Address表示在哪进入外挂代码,也就是在什么地方做hook
LPVOID Address;
HOOKPOINT* NextPoint;
HOOKPOINT* BackPoint;
// 外挂代码的地址
HOOKBACK DestCall;
public:
// 修复区代码
char* CodeFix;
LPVOID AddressRet;
public:
HOOKPOINT();
~HOOKPOINT();
HOOKPOINT(LPVOID, LPVOID, LPVOID _HOOKBACK, uchar lenth, HOOKPOINT*, HOOKPOINT* _NextPoint = NULL);
HOOKPOINT* AddPonit(LPVOID, LPVOID, LPVOID _HOOKBACK, uchar lenth);
HOOKPOINT* FindPoint(LPVOID);
HOOKBACK2 GetHookBack2();
HOOKBACK GetHookBack();
}*PHOOKPOINT;
HOOKPOINT.cpp文件里的内容:
#include "pch.h"
#include "HOOKPOINT.h"
unsigned GetJMPCode(unsigned distance, unsigned eip);
HOOKPOINT::HOOKPOINT()
{
}
HOOKPOINT::~HOOKPOINT()
{
delete CodeFix;
}
HOOKPOINT::HOOKPOINT(LPVOID _adr, LPVOID _adrRet, LPVOID _hbk, uchar lenth, HOOKPOINT* _BackP, HOOKPOINT* _NextPoint):
Address(_adr), AddressRet(_adrRet), DestCall((HOOKBACK)_hbk), BackPoint{ _BackP }, NextPoint{ _NextPoint }
{
DWORD offcount;
/**
下面是修复 call(硬编码是E8) JMP(硬编码是E9)
_adr是要做hook的地方, 假设做hook的地方是call xxxx
call 后面的地址是通过
它是有一个公式要跳转到的目标地址 - 执行jmp指令的内存地址(eip) - 5 这个公式算出来的
只单纯的对它修复,地址是原来的,但是这个代码被我们的辅助代码进行了修复也就是它的内存地址
进入了辅助代码的内存空间里,这就导致要跳转的位置不正确,所以对call或者jmp这种跳转做修复时
它们后面跟的地址也要使用 要跳转到的目标地址 - 执行jmp指令的内存地址(eip) - 5 这个公式计算
公式里的值全都要用辅助代码给它的哪个地址才可以
下面只实现了长跳的jmp与call
*/
char* code = (char*)(_adr);
unsigned* u = (unsigned*)(code + 1);
unsigned dest = u[0] + (unsigned)_adr + 5;
bool fix{};
/**
switch的case用的是int类型,然后code[0]是一个char类型然后switch会做一个隐形的类型转换
所以要把code[0]的值当做int类型来写
*/
switch (code[0])
{
case 0xFFFFFFE8: {
}
case 0xFFFFFFE9:{
fix = true;
break;
}
default:
break;
}
CodeFix = new char[lenth + 0x5];
memcpy(CodeFix, _adr, lenth);
if (fix) {
AfxMessageBox(L"非借壳是否会卡拉和监考老师");
u = (unsigned*)(CodeFix + 1);
u[0] = GetJMPCode(dest, (unsigned)CodeFix);
}
/**
下面是之前分析剑侠情缘用来无敌的代码
mov [esi + 10], edi
mov eax, [esi+10]
jmp Address+lenth // 这个是执行完剑侠情缘原有代码让它再跳回去
CodeFix长度是 lenth+0x5,lenth是要进行hook位置的指令长度,0x5是执行完指令之后让他跳回去
*/
DWORD dOld;
CodeFix[lenth] = 0xE9;
unsigned* adr = (unsigned*)(CodeFix + lenth + 1);
adr[0] = GetJMPCode((unsigned)_adr + lenth, (unsigned)&CodeFix[lenth]);
VirtualProtect(CodeFix, lenth+0x5, PAGE_EXECUTE_READWRITE, &dOld);
}
HOOKPOINT* HOOKPOINT::AddPonit(LPVOID _adr,LPVOID _adrRet, LPVOID _hbk, uchar lenth)
{
NextPoint = new HOOKPOINT(_adr, _adrRet, _hbk, lenth, this);
return NextPoint;
}
HOOKPOINT* HOOKPOINT::FindPoint(LPVOID _adr)
{
PHOOKPOINT _point = this;
do {
if (_point->Address == _adr)return _point;
_point = _point->NextPoint;
}while(_point);
return nullptr;
}
HOOKBACK2 HOOKPOINT::GetHookBack2()
{
HOOKBACK2 _rt = (HOOKBACK2)DestCall;
return _rt;
}
HOOKBACK HOOKPOINT::GetHookBack()
{
return DestCall;
}
htdHook2.cpp文件里面的内容:
#include "pch.h"
#include "htdHook2.h"
htdHook2* htdHook2Ptr;
LONG _stdcall PvectoredExceptionHandler(PEXCEPTION_POINTERS val) {
//CString wTxt;
//wTxt.Format(L"111----%X", val->ContextRecord->Eip);
//AfxMessageBox(wTxt);
//return EXCEPTION_CONTINUE_SEARCH;
/**
call指令执行时会让eip指向下一条指令的位置,
这里减去0x5是让他回到call的位置,也就是得到从哪来的
*/
unsigned _eip = val->ContextRecord->Eip;
PHOOKPOINT point = htdHook2Ptr->Points.FindPoint((LPVOID)_eip);
if (point) {
if (point->GetHookBack2()(val->ContextRecord)) {
// 继续执行原有代码
val->ContextRecord->Eip = (unsigned)point->CodeFix;
}
else {
// 调转到指定位置执行
val->ContextRecord->Eip = (unsigned)point->AddressRet;
}
// 拦截异常不让当前异常往后传递
return EXCEPTION_CONTINUE_EXECUTION;
}
// 拦截异常让当前异常往后传递(表示当前异常不是我们要拦截的异常)
else return EXCEPTION_CONTINUE_SEARCH;
}
htdHook2::htdHook2()
{
PPointLast = &Points;
htdHook2Ptr = this;
AddVectoredExceptionHandler(1, PvectoredExceptionHandler);
}
void htdHook2::SetHook(LPVOID Address, uchar len, HOOKBACK2 hookBack, LPVOID AddressRet)
{
DWORD dOld;
DWORD dNew;
// 更改内存权限让它可读可写
VirtualProtect(Address, 0x1, PAGE_EXECUTE_READWRITE, &dOld);
PPointLast = PPointLast->AddPonit(Address, AddressRet, hookBack, len);
char* code = (char*)Address;
code[0] = 0xCC;
VirtualProtect(Address, 0x1, dOld, &dNew);
}
htdHook2.h文件里面的内容:
// 当前文件是用来关联拦截点与拦截之后处理函数、游戏代码修改、还原的 HOOKPOINT.h 文件里的函数
#pragma once
#include "HOOKPOINT.h"
class htdHook2
{
private:
PHOOKPOINT PPointLast{};
public:
HOOKPOINT Points;
public:
htdHook2();
void SetHook(LPVOID Address, uchar len, HOOKBACK2 hookBack, LPVOID AddressRet=0);
};