手游外挂基础篇之inline-hook
字数 837 2025-08-22 18:37:22
Android手游外挂开发基础:Inline Hook技术详解
1. 引言
本文档详细讲解Android平台下使用Inline Hook技术实现游戏破解的方法。Inline Hook是一种通过修改目标函数的机器码来实现函数劫持的技术,在手游安全分析和外挂开发中有着广泛应用。
2. Inline Hook基本原理
2.1 核心概念
Inline Hook主要通过以下步骤实现:
- 修改目标函数指令:在目标函数开始处插入跳转指令
- 跳转到桩函数(Stub):执行自定义的桩函数代码
- 执行原始指令:在桩函数中执行被覆盖的原始指令
- 返回原流程:跳转回目标函数的剩余部分继续执行
2.2 经典流程图
原始流程:
目标函数开始 -> 原始指令1 -> 原始指令2 -> ... -> 函数返回
Hook后流程:
目标函数开始 -> 跳转到桩函数 -> 执行自定义代码 -> 执行被覆盖的原始指令 -> 跳回原始流程 -> 原始指令3 -> ... -> 函数返回
3. 技术实现详解
3.1 核心数据结构
typedef struct tagINLINEHOOKINFO {
void *pHookAddr; // hook的地址
void *pStubShellCodeAddr; // 桩函数shellcode地址
void (*onCallBack)(struct pt_regs *); // 回调函数
void **ppOldFuncAddr; // 存放原指令函数地址的指针
BYTE szbyBackupOpcodes[OPCODEMAXLEN]; // 备份的原指令
} INLINE_HOOK_INFO;
3.2 关键功能函数
3.2.1 修改内存页属性
bool ChangePageProperty(void *pAddress, size_t size) {
unsigned long ulPageSize = sysconf(_SC_PAGESIZE);
unsigned long ulNewPageStartAddress = (unsigned long)(pAddress) & ~(ulPageSize - 1);
long lPageCount = (size / ulPageSize) + 1;
int iRet = mprotect((const void *)(ulNewPageStartAddress),
lPageCount * ulPageSize,
PROT_READ | PROT_WRITE | PROT_EXEC);
return iRet != -1;
}
3.2.2 构造跳转指令
bool BuildArmJumpCode(void *pCurAddress, void *pJumpAddress) {
// LDR PC, [PC, #-4]的机器码是0xE51FF004
BYTE szLdrPCOpcodes[8] = {0x04, 0xF0, 0x1F, 0xE5};
memcpy(szLdrPCOpcodes + 4, &pJumpAddress, 4);
memcpy(pCurAddress, szLdrPCOpcodes, 8);
cacheflush(*((uint32_t *)pCurAddress), 8, 0);
return true;
}
3.3 Hook主流程
bool HookArm(INLINE_HOOK_INFO *pstInlineHook) {
// 1. 初始化hook点信息
if (!InitArmHookInfo(pstInlineHook)) return false;
// 2. 构造桩函数
if (!BuildStub(pstInlineHook)) return false;
// 3. 构造原指令函数
if (!BuildOldFunction(pstInlineHook)) return false;
// 4. 覆盖原指令
if (!RebuildHookTarget(pstInlineHook)) return false;
return true;
}
3.4 桩函数实现(ARM汇编)
.global _shellcode_start_s
.global _shellcode_end_s
.global _hookstub_function_addr_s
.global _old_function_addr_s
.data
_shellcode_start_s:
push {r0, r1, r2, r3}
mrs r0, cpsr
str r0, [sp, #0xC]
str r14, [sp, #8]
add r14, sp, #0x10
str r14, [sp, #4]
pop {r0}
push {r0-r12}
mov r0, sp
ldr r3, _hookstub_function_addr_s
blx r3
ldr r0, [sp, #0x3C]
msr cpsr, r0
ldmfd sp!, {r0-r12}
ldr r14, [sp, #4]
ldr sp, [r13]
ldr pc, _old_function_addr_s
_hookstub_function_addr_s:
.word 0xffffffff
_old_function_addr_s:
.word 0xffffffff
_shellcode_end_s:
.end
4. 实战案例:修改游戏计时器
4.1 目标函数分析
目标函数是一个计时器判断函数,当计数器超过300时返回胜利信息:
JNIEXPORT jstring JNICALL Java_com_example_gslab_ibored_MainActivity_UpdateResult(
JNIEnv *pJniEnv, jclass Jclass) {
unsigned int uiLocalVar = 1;
uiTimeCounter += uiLocalVar;
if (uiTimeCounter > 300) {
return pJniEnv->NewStringUTF("Enough. You Win!");
} else {
return pJniEnv->NewStringUTF("Just Wait.");
}
}
4.2 实现步骤
- 定位Hook点:使用IDA找到判断指令的位置
- 计算偏移地址:获取模块基址和Hook点偏移
- 实现Hook代码:
void EvilHookStubFunctionForIBored(pt_regs *regs) {
LOGI("In Evil Hook Stub.");
regs->uregs[0] = 0x333; // 修改r0寄存器值
}
void ModifyIBored() {
void *pModuleBaseAddr = GetModuleBaseAddr(-1, "libnative-lib.so");
uint32_t uiHookAddr = (uint32_t)pModuleBaseAddr + 0x3349c;
InlineHook((void *)(uiHookAddr), EvilHookStubFunctionForIBored);
}
4.3 获取模块基址
void *GetModuleBaseAddr(pid_t pid, char *pszModuleName) {
char szMapFilePath[256] = {0};
snprintf(szMapFilePath, sizeof(szMapFilePath),
pid < 0 ? "/proc/self/maps" : "/proc/%d/maps", pid);
FILE *pFileMaps = fopen(szMapFilePath, "r");
while (fgets(szFileLineBuffer, sizeof(szFileLineBuffer), pFileMaps)) {
if (strstr(szFileLineBuffer, pszModuleName)) {
char *pszModuleAddress = strtok(szFileLineBuffer, "-");
return (void *)strtoul(pszModuleAddress, NULL, 16);
}
}
fclose(pFileMaps);
return 0;
}
5. 编译配置
5.1 Hook模块编译配置(Android.mk)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CPPFLAGS += -g -O0
LOCAL_ARM_MODE := arm
LOCAL_MODULE := IHook
LOCAL_SRC_FILES := IHook.c ihookstub.s
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
include $(BUILD_STATIC_LIBRARY)
5.2 主模块编译配置
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CPPFLAGS += -g -O0
LOCAL_ARM_MODE := arm
LOCAL_MODULE := InlineHook
LOCAL_STATIC_LIBRARIES:= IHook
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../InlineHook
LOCAL_SRC_FILES := InlineHook.cpp
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
include $(BUILD_SHARED_LIBRARY)
6. 关键注意事项
- 指令集一致性:确保Hook代码和目标程序使用相同的指令集(ARM/Thumb)
- 指令覆盖长度:ARM模式下需要覆盖8字节(两条指令)
- 执行顺序:先构造桩函数再覆盖原指令,避免竞争条件
- 寄存器保存:桩函数必须妥善保存和恢复所有使用的寄存器
- 内存属性:修改代码段内存属性为可写可执行
7. 总结
Inline Hook技术是Android平台游戏安全分析和外挂开发的基础技术之一。通过本文介绍的方法,可以实现对目标函数的劫持和修改,达到改变游戏逻辑的目的。实际应用中还需要考虑对抗检测、稳定性优化等问题。