├── README.md └── jni ├── Android.mk ├── Application.mk ├── Interface ├── Android.mk └── InlineHook.cpp └── hook ├── Android.mk ├── InlineHook.c ├── InlineHook.h ├── armshellcode.s └── thumb2shellcode.s /README.md: -------------------------------------------------------------------------------- 1 | [TOC] 2 | 3 | # 概述 4 | 5 | 这是一个完整的InlineHook的使用模块,直接将jni目录clone下来,使用ndk-build编译即可 6 | 7 | 8 | 9 | # 使用 10 | 11 | ## 共享库 12 | 13 | 1. clone该demo 14 | 15 | 2. 修改ModifyIBored函数里GetModuleBaseAddr的参数,选择需要hook的so模块 16 | 17 | 3. 找到需要hook的指令地址,计算偏移(指令的内存地址-指令所在模块的基址),然后写入 18 | 19 | ```uint32_t uiHookAddr = (uint32_t)pModuleBaseAddr + 偏移;```中的偏移 20 | 21 | 4. 完善替换函数EvilHookStubFunctionForIBored,执行自己需要的操作 22 | 23 | 5. ndk-build编译出共享库so文件 24 | 25 | 6. 在代码中使用`System.loadLibrary`加载共享库,加载的时候就会调用用hook函数 26 | 27 | `void ModifyIBored() __attribute__((constructor));` 28 | 29 | 7. (可选)使用ptrace注入共享库到指令进程中 30 | 31 | 32 | 33 | # 模块 34 | 35 | * hook文件夹:这个文件夹里存放hook功能源码 36 | * interface文件夹:使用hook功能的地方,可以自行修改InlineHook.cpp中的ModifyIBored函数来hook指定模块的指定指令,也可以按照上面结构自己写 37 | 38 | 39 | 40 | ## HookArm 41 | 42 | InlineHook.c文件中的函数。 43 | 44 | 针对32位ARM指令进行hook 45 | 46 | 47 | 48 | ## HookThumb 49 | 50 | InlineHook.c文件中的函数。 51 | 52 | 针对Thumb-2指令集进行hook 53 | 54 | 55 | 56 | # 不足 57 | 58 | 这个项目是比较简单的demo,并不涉及指令修复等内容 -------------------------------------------------------------------------------- /jni/Android.mk: -------------------------------------------------------------------------------- 1 | include $(call all-subdir-makefiles) -------------------------------------------------------------------------------- /jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi-v7a 2 | APP_STL := c++_static 3 | APP_CPPFLAGS += -fexceptions -------------------------------------------------------------------------------- /jni/Interface/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | 4 | include $(CLEAR_VARS) 5 | 6 | LOCAL_CXXFLAGS += -g -O0 7 | LOCAL_ARM_MODE := arm 8 | LOCAL_MODULE := IHook 9 | LOCAL_STATIC_LIBRARIES:= InlineHook 10 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/../hook 11 | LOCAL_SRC_FILES := InlineHook.cpp 12 | LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog 13 | 14 | include $(BUILD_SHARED_LIBRARY) -------------------------------------------------------------------------------- /jni/Interface/InlineHook.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern "C" 4 | { 5 | #include "InlineHook.h" 6 | } 7 | 8 | //声明函数在加载库时被调用,也是hook的主函数 9 | void ModifyIBored() __attribute__((constructor)); 10 | 11 | typedef std::vector InlineHookInfoPVec; 12 | static InlineHookInfoPVec gs_vecInlineHookInfo; //管理HOOK点 13 | 14 | /** 15 | * 对外inline hook接口,负责管理inline hook信息 16 | * @param pHookAddr 要hook的地址 17 | * @param onCallBack 要插入的回调函数 18 | * @return inlinehook是否设置成功(已经设置过,重复设置返回false) 19 | */ 20 | bool InlineHook(void *pHookAddr, void (*onCallBack)(struct pt_regs *)) 21 | { 22 | bool bRet = false; 23 | 24 | if(pHookAddr == NULL || onCallBack == NULL) 25 | { 26 | return bRet; 27 | } 28 | 29 | //填写hook点位置和用户自定义回调函数 30 | THUMB_INLINE_HOOK_INFO* pstInlineHook = new THUMB_INLINE_HOOK_INFO(); 31 | pstInlineHook->pHookAddr = pHookAddr; 32 | pstInlineHook->onCallBack = onCallBack; 33 | 34 | if(HookThumb(pstInlineHook) == false) 35 | { 36 | LOGI("HookArm fail."); 37 | delete pstInlineHook; 38 | return bRet; 39 | } 40 | 41 | gs_vecInlineHookInfo.push_back(pstInlineHook); 42 | LOGI("HookArm completed."); 43 | return true; 44 | } 45 | 46 | /** 47 | * 用户自定义的回调函数,修改r0寄存器大于300 48 | */ 49 | void EvilHookStubFunctionForIBored(pt_regs *regs) 50 | { 51 | LOGI("In Evil Hook Stub."); 52 | regs->uregs[0] = 0x333; 53 | } 54 | 55 | /** 56 | * 1.Hook入口 57 | */ 58 | void ModifyIBored() 59 | { 60 | LOGI("In IHook's ModifyIBored."); 61 | void* pModuleBaseAddr = GetModuleBaseAddr(-1, "libnative-lib.so"); 62 | LOGI("libnative-lib.so base addr is 0x%X.", pModuleBaseAddr); 63 | if(pModuleBaseAddr == 0) 64 | { 65 | LOGI("get module base error."); 66 | return; 67 | } 68 | 69 | //模块基址加上HOOK点的偏移地址就是HOOK点在内存中的位置 70 | uint32_t uiHookAddr = (uint32_t)pModuleBaseAddr + 0x3299a; 71 | LOGI("uiHookAddr is %X", uiHookAddr); 72 | LOGI("uiHookAddr instructions is %X", *(long *)(uiHookAddr)); 73 | LOGI("uiHookAddr instructions is %X", *(long *)(uiHookAddr+4)); 74 | 75 | //HOOK函数 76 | InlineHook((void*)(uiHookAddr), EvilHookStubFunctionForIBored); 77 | } 78 | 79 | -------------------------------------------------------------------------------- /jni/hook/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH := $(call my-dir) 2 | 3 | 4 | include $(CLEAR_VARS) 5 | 6 | LOCAL_CPPFLAGS += -g -O0 7 | LOCAL_ARM_MODE := arm 8 | LOCAL_MODULE := InlineHook 9 | LOCAL_SRC_FILES := InlineHook.c armshellcode.s thumb2shellcode.s 10 | LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog 11 | 12 | include $(BUILD_STATIC_LIBRARY) 13 | -------------------------------------------------------------------------------- /jni/hook/InlineHook.c: -------------------------------------------------------------------------------- 1 | #include "InlineHook.h" 2 | 3 | /** 4 | * 通用函数:获取so模块加载进内存的基地址,通过查看/proc/$pid/maps文件 5 | * 6 | * @param pid 模块所在进程pid,如果访问自身进程,可填小余0的值,如-1 7 | * @param pszModuleName 模块名字 8 | * @return void* 模块的基地址,错误返回0 9 | */ 10 | void * GetModuleBaseAddr(pid_t pid, char* pszModuleName) 11 | { 12 | FILE *pFileMaps = NULL; 13 | unsigned long ulBaseValue = 0; 14 | char szMapFilePath[256] = {0}; 15 | char szFileLineBuffer[1024] = {0}; 16 | 17 | /* 判断是否为自身maps文件*/ 18 | if(pid < 0) 19 | { 20 | snprintf(szMapFilePath, sizeof(szMapFilePath), "/proc/self/maps"); 21 | } 22 | else 23 | { 24 | snprintf(szMapFilePath, sizeof(szMapFilePath), "/proc/%d/maps", pid); 25 | } 26 | 27 | pFileMaps = fopen(szMapFilePath, "r"); 28 | if (NULL == pFileMaps) 29 | { 30 | return (void *)ulBaseValue; 31 | } 32 | /* 循环遍历maps文件,找到对应模块名,截取字符串中的基地址*/ 33 | while (fgets(szFileLineBuffer, sizeof(szFileLineBuffer), pFileMaps) != NULL) 34 | { 35 | if(strstr(szFileLineBuffer, pszModuleName)) 36 | { 37 | char *pszModuleAddress = strtok(szFileLineBuffer, "-"); 38 | ulBaseValue = strtoul(pszModuleAddress, NULL, 16); 39 | 40 | if (ulBaseValue == 0x8000) 41 | { 42 | ulBaseValue = 0; 43 | } 44 | break; 45 | } 46 | } 47 | 48 | return ulBaseValue; 49 | } 50 | 51 | /** 52 | * 通用函数,修改页属性,让内存块内的代码可执行 53 | * 54 | * @param pAddress 需要修改属性起始地址 55 | * @param size 需要修改页属性的长度 56 | * @return bool 是否修改成功 57 | */ 58 | bool ChangePageProperty(void *pAddress, size_t size) 59 | { 60 | bool bRet = false; 61 | 62 | while(1) 63 | { 64 | if(pAddress == NULL) 65 | { 66 | LOGI("change page property error."); 67 | break; 68 | } 69 | 70 | unsigned long ulPageSize = sysconf(_SC_PAGESIZE); 71 | int iProtect = PROT_READ | PROT_WRITE | PROT_EXEC; 72 | /*页对齐,以4096的倍数为起始位置*/ 73 | unsigned long ulNewPageStartAddress = (unsigned long)(pAddress) & ~(ulPageSize - 1); 74 | /* 计算至少需要多少内存页(0x1000byte)可以包含size大小*/ 75 | long lPageCount = (size / ulPageSize) + 1; 76 | int iRet = mprotect((const void *)(ulNewPageStartAddress), lPageCount*ulPageSize , iProtect); 77 | 78 | if (iRet == -1) 79 | { 80 | LOGI("mprotect error:%s", strerror(errno)); 81 | break; 82 | } 83 | 84 | bRet = true; 85 | break; 86 | } 87 | 88 | return bRet; 89 | } 90 | 91 | /** 92 | * ARM32:初始化hook点信息,保存原指令的opcode 93 | * 94 | * @param pstInlineHook 保存hook点信息的结构体 95 | * @return bool 是否初始化成功 96 | */ 97 | bool InitArmHookInfo(ARM_INLINE_HOOK_INFO* pstInlineHook) 98 | { 99 | bool bRet = false; 100 | 101 | while(1) 102 | { 103 | if(pstInlineHook == NULL) 104 | { 105 | LOGI("arm pstInlineHook is null"); 106 | break; 107 | } 108 | 109 | memcpy(pstInlineHook->szbyBackupOpcodes, pstInlineHook->pHookAddr, 8); 110 | bRet = true; 111 | break; 112 | } 113 | 114 | return bRet; 115 | } 116 | 117 | /** 118 | * ARM32:构造桩函数 119 | * 120 | * @param pstInlineHook 保存hook点信息的结构体 121 | * @return bool 是否构造成功 122 | */ 123 | bool BuildArmStub(ARM_INLINE_HOOK_INFO* pstInlineHook) 124 | { 125 | bool bRet = false; 126 | 127 | while(1) 128 | { 129 | if(pstInlineHook == NULL) 130 | { 131 | LOGI("arm pstInlineHook is null"); 132 | break; 133 | } 134 | 135 | /* 需要在shellcode中定义的四个全局变量。*/ 136 | void *p_shellcode_start_s = &_shellcode_start_s; 137 | void *p_shellcode_end_s = &_shellcode_end_s; 138 | void *p_hookstub_function_addr_s = &_hookstub_function_addr_s; 139 | void *p_old_function_addr_s = &_old_function_addr_s; 140 | /* 申请一块内存,放入桩函数的shellcode*/ 141 | size_t sShellCodeLength = p_shellcode_end_s - p_shellcode_start_s; 142 | void *pNewShellCode = malloc(sShellCodeLength); 143 | 144 | if(pNewShellCode == NULL) 145 | { 146 | LOGI("arm shellcode malloc fail."); 147 | break; 148 | } 149 | memcpy(pNewShellCode, p_shellcode_start_s, sShellCodeLength); 150 | if(ChangePageProperty(pNewShellCode, sShellCodeLength) == false) 151 | { 152 | LOGI("change shell code page property fail."); 153 | break; 154 | } 155 | 156 | /* ppHookStubFunctionAddr的值是一个变量值的地址。这个变量值是shellcode中用户自定义函数地址(在新申请的空间中)*/ 157 | void **ppHookStubFunctionAddr = pNewShellCode + (p_hookstub_function_addr_s - p_shellcode_start_s); 158 | *ppHookStubFunctionAddr = pstInlineHook->onCallBack; 159 | /* 桩函数地址*/ 160 | pstInlineHook->pStubShellCodeAddr = pNewShellCode; 161 | /* _old_function_addr_s变量的地址,这个变量值就是原指令函数的函数指针值*/ 162 | pstInlineHook->ppOldFuncAddr = pNewShellCode + (p_old_function_addr_s - p_shellcode_start_s); 163 | 164 | bRet = true; 165 | break; 166 | } 167 | 168 | return bRet; 169 | } 170 | 171 | /** 172 | * ARM32:构造跳转指令。 173 | * 174 | * @param pCurAddress 当前地址,要构造跳转指令的位置 175 | * @param pJumpAddress 目的地址,要从当前位置跳过去的地址 176 | * @return bool 跳转指令是否构造成功 177 | */ 178 | bool BuildArmJumpCode(void *pCurAddress , void *pJumpAddress) 179 | { 180 | bool bRet = false; 181 | 182 | while(1) 183 | { 184 | if(pCurAddress == NULL || pJumpAddress == NULL) 185 | { 186 | LOGI("arm jump address null."); 187 | break; 188 | } 189 | 190 | /* LDR PC, [PC, #-4]的机器码是0xE51FF004 */ 191 | BYTE szLdrPCOpcodes[8] = {0x04, 0xF0, 0x1F, 0xE5}; 192 | memcpy(szLdrPCOpcodes + 4, &pJumpAddress, 4); 193 | memcpy(pCurAddress, szLdrPCOpcodes, 8); 194 | /* 刷新缓存中的指令,防止缓存中指令未进行修改引起的错误*/ 195 | cacheflush(*((uint32_t*)pCurAddress), 8, 0); 196 | 197 | bRet = true; 198 | break; 199 | } 200 | 201 | return bRet; 202 | } 203 | 204 | /** 205 | * ARM:构造原指令函数。申请一块内存,写入原指令和跳转指令 206 | * * 执行原指令 207 | * * 跳转到原始指令流程中,即原指令的下一条指令处 208 | * 出了上面两个功能我们还需要将shellcode中的原指令函数地址进行填充,补全桩函数中原指令函数地址 209 | * 210 | * @param pstInlineHook hook点相关信息的结构体 211 | * @return bool 原指令函数是否构造成功 212 | */ 213 | bool BuildArmOldFunction(ARM_INLINE_HOOK_INFO* pstInlineHook) 214 | { 215 | bool bRet = false; 216 | 217 | while(1) 218 | { 219 | if(pstInlineHook == NULL) 220 | { 221 | LOGI("build old function , arm pstInlineHook is null"); 222 | break; 223 | } 224 | 225 | /* 8字节原指令,8字节原指令的下一条指令*/ 226 | void * pNewEntryForOldFunction = malloc(16); 227 | if(pNewEntryForOldFunction == NULL) 228 | { 229 | LOGI("arm new entry for old function malloc fail."); 230 | break; 231 | } 232 | 233 | if(ChangePageProperty(pNewEntryForOldFunction, 16) == false) 234 | { 235 | LOGI("arm change new entry page property fail."); 236 | break; 237 | } 238 | 239 | /* 拷贝原指令到内存块中*/ 240 | memcpy(pNewEntryForOldFunction, pstInlineHook->szbyBackupOpcodes, 8); 241 | /* 拷贝跳转指令到内存块中*/ 242 | if(BuildArmJumpCode(pNewEntryForOldFunction + 8, pstInlineHook->pHookAddr + 8) == false) 243 | { 244 | LOGI("arm build jump opcodes for new entry fail."); 245 | break; 246 | } 247 | 248 | /* 填充shellcode里stub的回调地址*/ 249 | *(pstInlineHook->ppOldFuncAddr) = pNewEntryForOldFunction; 250 | 251 | bRet = true; 252 | break; 253 | } 254 | 255 | return bRet; 256 | } 257 | 258 | /** 259 | * ARM:覆盖HOOK点的指令,跳转到桩函数的位置 260 | * 261 | * @param pstInlineHook inlinehook信息 262 | * @return bool 原地跳转指令是否构造成功 263 | */ 264 | bool RebuildArmHookTarget(ARM_INLINE_HOOK_INFO* pstInlineHook) 265 | { 266 | bool bRet = false; 267 | 268 | while(1) 269 | { 270 | if(pstInlineHook == NULL) 271 | { 272 | LOGI("arm cover old instructions, pstInlineHook is null"); 273 | break; 274 | } 275 | 276 | /* 修改原位置的页属性,保证可写*/ 277 | if(ChangePageProperty(pstInlineHook->pHookAddr, 8) == false) 278 | { 279 | LOGI("arm change page property error."); 280 | break; 281 | } 282 | 283 | /* 覆盖原指令为跳转指令*/ 284 | if(BuildArmJumpCode(pstInlineHook->pHookAddr, pstInlineHook->pStubShellCodeAddr) == false) 285 | { 286 | LOGI("arm build jump opcodes for new entry fail."); 287 | break; 288 | } 289 | 290 | bRet = true; 291 | break; 292 | } 293 | 294 | return bRet; 295 | } 296 | 297 | /** 298 | * ARM:恢复原指令,删除hook点 299 | * 300 | * @param pstInlineHook inlinehook信息 301 | * @return bool 删除hook点是否成功 302 | */ 303 | bool RestroeArmHookTarget(ARM_INLINE_HOOK_INFO* pstInlineHook) 304 | { 305 | bool bRet = false; 306 | 307 | while(1) 308 | { 309 | if(pstInlineHook == NULL) 310 | { 311 | LOGI("arm cover old instructions, pstInlineHook is null"); 312 | break; 313 | } 314 | 315 | /* 修改原位置的页属性,保证可写*/ 316 | if(ChangePageProperty(pstInlineHook->pHookAddr, 8) == false) 317 | { 318 | LOGI("arm change page property error."); 319 | break; 320 | } 321 | 322 | if(InitArmHookInfo(pstInlineHook) == false) 323 | { 324 | LOGI("arm pstInlineHook is null."); 325 | break; 326 | } 327 | /* 恢复原指令*/ 328 | memcpy(pstInlineHook->pHookAddr, pstInlineHook->szbyBackupOpcodes, 8); 329 | cacheflush(*((uint32_t*)pstInlineHook->pHookAddr), 8, 0); 330 | 331 | bRet = true; 332 | break; 333 | } 334 | 335 | return bRet; 336 | } 337 | 338 | /** 339 | * ARM:对外提供Hook函数的调用接口。 340 | * 341 | * @param pstInlineHook inlinehook信息 342 | * @return bool 是否hook成功 343 | */ 344 | bool HookArm(ARM_INLINE_HOOK_INFO* pstInlineHook) 345 | { 346 | bool bRet = false; 347 | 348 | while(1) 349 | { 350 | if(pstInlineHook == NULL) 351 | { 352 | LOGI("arm pstInlineHook is null."); 353 | break; 354 | } 355 | 356 | /* 初始化hook点的信息,将原指令地址处的指令内容存放到hook点结构体中*/ 357 | if(InitArmHookInfo(pstInlineHook) == false) 358 | { 359 | LOGI("Init Arm HookInfo fail."); 360 | break; 361 | } 362 | 363 | /* 1. 构造桩函数*/ 364 | if(BuildArmStub(pstInlineHook) == false) 365 | { 366 | LOGI("Arm BuildStub fail."); 367 | break; 368 | } 369 | LOGI("ARM BuildStub completed."); 370 | 371 | /* 2. 构造原指令函数,执行被覆盖指令并跳转回原始指令流程*/ 372 | if(BuildArmOldFunction(pstInlineHook) == false) 373 | { 374 | LOGI("BuildArmOldFunction fail."); 375 | break; 376 | } 377 | LOGI("BuildArmOldFunction completed."); 378 | 379 | /* 3. 改写原指令为跳转指令,跳转到桩函数处*/ 380 | if(RebuildArmHookTarget(pstInlineHook) == false) 381 | { 382 | LOGI("RebuildHookAddress fail."); 383 | break; 384 | } 385 | LOGI("RebuildArmHookAddress completed."); 386 | 387 | bRet = true; 388 | break; 389 | } 390 | 391 | return bRet; 392 | } 393 | 394 | /** 395 | * Thumb-2:初始化Hook点信息,根据用户指定位置,将该处的指令存进hook点结构体中 396 | * 397 | * @param pstInlineHook hook点信息的结构体 398 | * @return bool 是否初始化成功 399 | */ 400 | bool InitThumbHookInfo(THUMB_INLINE_HOOK_INFO* pstInlineHook) 401 | { 402 | bool bRet = false; 403 | char caddr[10] = {0}; 404 | unsigned long addr = 0; 405 | 406 | while(1) 407 | { 408 | if(pstInlineHook == NULL) 409 | { 410 | LOGI("Thumb init THUMB_INLINE_HOOK_INFO failed."); 411 | break; 412 | } 413 | /* 计算需要覆盖的opcode长度*/ 414 | sprintf(caddr, "%p", pstInlineHook->pHookAddr); 415 | addr = strtoul(caddr, 0, 16); 416 | if(SET_BIT0(addr) % 4 != 0) 417 | { 418 | pstInlineHook->thumb2OpcodeLen = 10; 419 | } 420 | else 421 | { 422 | pstInlineHook->thumb2OpcodeLen = 8; 423 | } 424 | 425 | memcpy(pstInlineHook->szbyBackupOpcodes, pstInlineHook->pHookAddr, pstInlineHook->thumb2OpcodeLen); 426 | bRet = true; 427 | break; 428 | } 429 | 430 | return bRet; 431 | } 432 | 433 | /** 434 | * Thumb-2:构造桩函数 435 | * 436 | * @param pstInlineHook hook点信息的结构体 437 | * @return bool 是否构造成功 438 | */ 439 | bool BuildThumbStub(THUMB_INLINE_HOOK_INFO* pstInlineHook) 440 | { 441 | bool bRet = false; 442 | 443 | while(1) 444 | { 445 | if(pstInlineHook == NULL) 446 | { 447 | LOGI("Thumb pstInlineHook is null"); 448 | break; 449 | } 450 | 451 | void *p_shellcode_start_s_thumb = &_shellcode_start_s_thumb; 452 | void *p_shellcode_end_s_thumb = &_shellcode_end_s_thumb; 453 | void *p_hookstub_function_addr_s_thumb = &_hookstub_function_addr_s_thumb; 454 | void *p_old_function_addr_s_thumb = &_old_function_addr_s_thumb; 455 | /* 申请一块内存,放入桩函数的shellcode*/ 456 | size_t sShellCodeLength = p_shellcode_end_s_thumb - p_shellcode_start_s_thumb; 457 | void *pNewShellCode = malloc(sShellCodeLength); 458 | 459 | if(pNewShellCode == NULL) 460 | { 461 | LOGI("Thumb shellcode malloc fail."); 462 | break; 463 | } 464 | memcpy(pNewShellCode, p_shellcode_start_s_thumb, sShellCodeLength); 465 | 466 | if(ChangePageProperty(pNewShellCode, sShellCodeLength) == false) 467 | { 468 | LOGI("Thumb change shell code page property fail."); 469 | break; 470 | } 471 | 472 | /* 用户自定义函数地址*/ 473 | void **ppHookStubFunctionAddr = pNewShellCode + (p_hookstub_function_addr_s_thumb - p_shellcode_start_s_thumb); 474 | *ppHookStubFunctionAddr = pstInlineHook->onCallBack; 475 | /* 桩函数地址*/ 476 | pstInlineHook->pStubShellCodeAddr = pNewShellCode; 477 | /* 保留地址:原指令函数指针的存放地址*/ 478 | pstInlineHook->ppOldFuncAddr = pNewShellCode + (p_old_function_addr_s_thumb - p_shellcode_start_s_thumb); 479 | 480 | bRet = true; 481 | break; 482 | } 483 | 484 | return bRet; 485 | } 486 | 487 | /** 488 | * Thumb-2:构造Thumb指令集的函数跳转 489 | * 490 | * @param pCurAddress 当前地址,要构造跳转指令的位置 491 | * @param pJumpAddress 目的地址,要从当前位置跳过去的地址 492 | * @return bool 跳转指令是否构造成功 493 | */ 494 | bool BuildThumbJumpCode(void *pCurAddress , void *pJumpAddress) 495 | { 496 | bool bRet = false; 497 | char caddr[10] = {0}; 498 | unsigned long addr = 0; 499 | 500 | while(1) 501 | { 502 | if(pCurAddress == NULL || pJumpAddress == NULL) 503 | { 504 | LOGI("Thumb jump address null."); 505 | break; 506 | } 507 | 508 | sprintf(caddr, "%p", pCurAddress); 509 | addr = strtoul(caddr, 0, 16); 510 | /* 如果原指令地址不能被4整除就用NOP填充1条thumb16指令的长度,让跳转指令被4整除*/ 511 | /* LDR PC, [PC, #0]的thumb指令是0xF000F8DF*/ 512 | if(SET_BIT0(addr) % 4 != 0) 513 | { 514 | BYTE szLdrPCOpcodes[10] = {0x00, 0xBF, 0xDF, 0xF8, 0x00, 0xF0}; 515 | memcpy(szLdrPCOpcodes + 6, &pJumpAddress, 4); 516 | memcpy(pCurAddress, szLdrPCOpcodes, 10); 517 | cacheflush(*((uint32_t*)pCurAddress), 10, 0); 518 | } 519 | else 520 | { 521 | BYTE szLdrPCOpcodes[8] = {0xDF, 0xF8, 0x00, 0xF0}; 522 | memcpy(szLdrPCOpcodes + 4, &pJumpAddress, 4); 523 | memcpy(pCurAddress, szLdrPCOpcodes, 8); 524 | cacheflush(*((uint32_t*)pCurAddress), 8, 0); 525 | } 526 | 527 | bRet = true; 528 | break; 529 | } 530 | 531 | return bRet; 532 | } 533 | 534 | /** 535 | * Thumb-2:构造原指令函数 536 | * 537 | * @param pstInlineHook hook点相关信息的结构体 538 | * @return bool 原指令函数是否构造成功 539 | */ 540 | bool BuildThumbOldFunction(THUMB_INLINE_HOOK_INFO* pstInlineHook) 541 | { 542 | bool bRet = false; 543 | 544 | while(1) 545 | { 546 | if(pstInlineHook == NULL) 547 | { 548 | LOGI("build old function , thumb pstInlineHook is null"); 549 | break; 550 | } 551 | 552 | /* 申请空间将原指令拷进去并赋可执行权限*/ 553 | void * pNewEntryForOldFunction = malloc(20); 554 | if(pNewEntryForOldFunction == NULL) 555 | { 556 | LOGI("Thumb new entry for old function malloc fail."); 557 | break; 558 | } 559 | 560 | if(ChangePageProperty(pNewEntryForOldFunction, 20) == false) 561 | { 562 | LOGI("thumb change new entry page property fail."); 563 | break; 564 | } 565 | 566 | memcpy(pNewEntryForOldFunction, pstInlineHook->szbyBackupOpcodes, pstInlineHook->thumb2OpcodeLen); 567 | if(BuildThumbJumpCode(pNewEntryForOldFunction + pstInlineHook->thumb2OpcodeLen, pstInlineHook->pHookAddr + pstInlineHook->thumb2OpcodeLen+1) == false) 568 | { 569 | LOGI("Thumb build jump opcodes for new entry fail."); 570 | break; 571 | } 572 | 573 | *(pstInlineHook->ppOldFuncAddr) = pNewEntryForOldFunction; 574 | 575 | bRet = true; 576 | break; 577 | } 578 | 579 | return bRet; 580 | } 581 | 582 | /** 583 | * Thumb:覆盖原指令 584 | * 585 | * @param pstInlineHook inlinehook信息 586 | * @return bool 原地跳转指令是否构造成功 587 | */ 588 | bool RebuildThumbHookTarget(THUMB_INLINE_HOOK_INFO* pstInlineHook) 589 | { 590 | bool bRet = false; 591 | 592 | while(1) 593 | { 594 | if(pstInlineHook == NULL) 595 | { 596 | LOGI("Thumb cover old instructions, pstInlineHook is null"); 597 | break; 598 | } 599 | 600 | if(ChangePageProperty(pstInlineHook->pHookAddr, pstInlineHook->thumb2OpcodeLen) == false) 601 | { 602 | LOGI("Thumb change page property error."); 603 | break; 604 | } 605 | 606 | if(BuildThumbJumpCode(pstInlineHook->pHookAddr, pstInlineHook->pStubShellCodeAddr) == false) 607 | { 608 | LOGI("Thumb build jump opcodes for new entry fail."); 609 | break; 610 | } 611 | 612 | bRet = true; 613 | break; 614 | } 615 | 616 | return bRet; 617 | } 618 | 619 | /** 620 | * Thumb:删除hook点,恢复原指令 621 | * 622 | * @param pstInlineHook inlinehook信息 623 | * @return bool 删除hook点是否成功 624 | */ 625 | bool RestroeThumbHookTarget(THUMB_INLINE_HOOK_INFO* pstInlineHook) 626 | { 627 | bool bRet = false; 628 | 629 | while(1) 630 | { 631 | if(pstInlineHook == NULL) 632 | { 633 | LOGI("Thumb cover old instructions, pstInlineHook is null"); 634 | break; 635 | } 636 | 637 | if(ChangePageProperty(pstInlineHook->pHookAddr, pstInlineHook->thumb2OpcodeLen) == false) 638 | { 639 | LOGI("Thumb change page property error."); 640 | break; 641 | } 642 | 643 | memcpy(pstInlineHook->pHookAddr, pstInlineHook->szbyBackupOpcodes, pstInlineHook->thumb2OpcodeLen); 644 | cacheflush(*((uint32_t*)pstInlineHook->pHookAddr), pstInlineHook->thumb2OpcodeLen, 0); 645 | 646 | bRet = true; 647 | break; 648 | } 649 | 650 | return bRet; 651 | } 652 | 653 | /** 654 | * Thumb:对外提供hook的入口函数 655 | * 656 | * @param pstInlineHook inlinehook信息 657 | * @return bool 是否hook成功 658 | */ 659 | bool HookThumb(THUMB_INLINE_HOOK_INFO* pstInlineHook) 660 | { 661 | bool bRet = false; 662 | 663 | while(1) 664 | { 665 | if(pstInlineHook == NULL) 666 | { 667 | LOGI("Thumb pstInlineHook is null."); 668 | break; 669 | } 670 | 671 | if(InitThumbHookInfo(pstInlineHook) == false) 672 | { 673 | LOGI("Init Thumb HookInfo fail."); 674 | break; 675 | } 676 | LOGI("ARM InitThumbHookInfo completed."); 677 | 678 | if(BuildThumbStub(pstInlineHook) == false) 679 | { 680 | LOGI("Thumb BuildStub fail."); 681 | break; 682 | } 683 | LOGI("ARM BuildStub completed."); 684 | 685 | if(BuildThumbOldFunction(pstInlineHook) == false) 686 | { 687 | LOGI("BuildThumbOldFunction fail."); 688 | break; 689 | } 690 | LOGI("BuildThumbOldFunction completed."); 691 | 692 | if(RebuildThumbHookTarget(pstInlineHook) == false) 693 | { 694 | LOGI("RebuildHookAddress fail."); 695 | break; 696 | } 697 | LOGI("RebuildThumbHookAddress completed."); 698 | 699 | bRet = true; 700 | break; 701 | } 702 | 703 | return bRet; 704 | } 705 | -------------------------------------------------------------------------------- /jni/hook/InlineHook.h: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | * Author :yong夜 3 | * 4 | * 声明Inline Hook过程中用到的所有功能函数、头文件 5 | * 目前支持32位系统的arm32、thumb-2指令集 6 | * *************************************************/ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #ifndef BYTE 19 | #define BYTE unsigned char 20 | #endif 21 | 22 | #define ARM32OPCODEMAXLEN 8 //32bit ARM指令集需要替换的指令长度 23 | #define THUMB32OPCODEMAXLEN 10 24 | 25 | #define LOG_TAG "Inline Hook" 26 | #define LOGI(format, args...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, format, ##args) 27 | 28 | #define CHECK_BIT0(addr) (addr & 1) 29 | #define SET_BIT0(addr) (addr & 0xFFFFFFFE) 30 | #define SET_BIT1(addr) (addr | 1) 31 | 32 | /* arm shellcode里用到的参数、变量*/ 33 | extern unsigned long _shellcode_start_s; 34 | extern unsigned long _shellcode_end_s; 35 | extern unsigned long _hookstub_function_addr_s; //根函数地址 36 | extern unsigned long _old_function_addr_s; //原指令地址 37 | 38 | /* thumb-2 shellcode里用到的参数和变量*/ 39 | extern unsigned long _shellcode_start_s_thumb; 40 | extern unsigned long _shellcode_end_s_thumb; 41 | extern unsigned long _hookstub_function_addr_s_thumb; //根函数地址 42 | extern unsigned long _old_function_addr_s_thumb; 43 | 44 | /* hook点信息*/ 45 | typedef struct armHookPointInfo 46 | { 47 | void *pHookAddr; //需要hook的位置 48 | void *pStubShellCodeAddr; //桩函数(shellcode)地址 49 | void (*onCallBack)(struct pt_regs *); //用户自定义的替换函数 50 | void **ppOldFuncAddr; //*ppOldFuncAddr即指向原指令函数处的指针 51 | BYTE szbyBackupOpcodes[ARM32OPCODEMAXLEN]; //原指令的opcode 52 | } ARM_INLINE_HOOK_INFO; 53 | 54 | typedef struct thumbHookPointInfo 55 | { 56 | void *pHookAddr; 57 | void *pStubShellCodeAddr; 58 | void (*onCallBack)(struct pt_regs *); 59 | void **ppOldFuncAddr; 60 | int thumb2OpcodeLen; 61 | BYTE szbyBackupOpcodes[THUMB32OPCODEMAXLEN]; 62 | } THUMB_INLINE_HOOK_INFO; 63 | 64 | /* common function */ 65 | bool ChangePageProperty(void *pAddress, size_t size); 66 | 67 | extern void * GetModuleBaseAddr(pid_t pid, char* pszModuleName); 68 | 69 | /* For 32 bit Arm instruction set */ 70 | bool InitArmHookInfo(ARM_INLINE_HOOK_INFO* pstInlineHook); 71 | 72 | bool BuildArmStub(ARM_INLINE_HOOK_INFO* pstInlineHook); 73 | 74 | bool BuildArmJumpCode(void *pCurAddress , void *pJumpAddress); 75 | 76 | bool BuildArmOldFunction(ARM_INLINE_HOOK_INFO* pstInlineHook); 77 | 78 | bool RebuildArmHookTarget(ARM_INLINE_HOOK_INFO* pstInlineHook); 79 | 80 | extern bool RestroeArmHookTarget(ARM_INLINE_HOOK_INFO* pstInlineHook); 81 | 82 | extern bool HookArm(ARM_INLINE_HOOK_INFO* pstInlineHook); 83 | 84 | /* For Thumb-2 instruction set */ 85 | bool InitThumbHookInfo(THUMB_INLINE_HOOK_INFO* pstInlineHook); 86 | 87 | bool BuildThumbStub(THUMB_INLINE_HOOK_INFO* pstInlineHook); 88 | 89 | bool BuildThumbJumpCode(void *pCurAddress , void *pJumpAddress); 90 | 91 | bool BuildThumbOldFunction(THUMB_INLINE_HOOK_INFO* pstInlineHook); 92 | 93 | bool RebuildThumbHookTarget(THUMB_INLINE_HOOK_INFO* pstInlineHook); 94 | 95 | extern bool RestroeThumbHookTarget(THUMB_INLINE_HOOK_INFO* pstInlineHook); 96 | 97 | extern bool HookThumb(THUMB_INLINE_HOOK_INFO* pstInlineHook); 98 | -------------------------------------------------------------------------------- /jni/hook/armshellcode.s: -------------------------------------------------------------------------------- 1 | .global _shellcode_start_s 2 | .global _shellcode_end_s 3 | .global _hookstub_function_addr_s 4 | .global _old_function_addr_s 5 | 6 | .data 7 | _shellcode_start_s: 8 | push {r0, r1, r2, r3} 9 | mrs r0, cpsr 10 | str r0, [sp, #0xC] 11 | str r14, [sp, #8] 12 | add r14, sp, #0x10 13 | str r14, [sp, #4] 14 | pop {r0} 15 | push {r0-r12} 16 | mov r0, sp 17 | ldr r3, _hookstub_function_addr_s 18 | blx r3 19 | ldr r0, [sp, #0x3C] 20 | msr cpsr, r0 21 | ldmfd sp!, {r0-r12} 22 | ldr r14, [sp, #4] 23 | ldr sp, [r13] 24 | ldr pc, _old_function_addr_s 25 | 26 | _hookstub_function_addr_s: 27 | .word 0xffffffff 28 | 29 | _old_function_addr_s: 30 | .word 0xffffffff 31 | 32 | _shellcode_end_s: 33 | 34 | .end 35 | -------------------------------------------------------------------------------- /jni/hook/thumb2shellcode.s: -------------------------------------------------------------------------------- 1 | .global _shellcode_start_s_thumb 2 | .global _shellcode_end_s_thumb 3 | .global _hookstub_function_addr_s_thumb 4 | .global _old_function_addr_s_thumb 5 | 6 | .data 7 | _shellcode_start_s_thumb: 8 | push {r0, r1, r2, r3} 9 | mrs r0, cpsr 10 | str r0, [sp, #0xC] 11 | str r14, [sp, #8] 12 | add r14, sp, #0x10 13 | str r14, [sp, #4] 14 | pop {r0} 15 | push {r0-r12} 16 | mov r0, sp 17 | ldr r3, _hookstub_function_addr_s_thumb 18 | blx r3 19 | ldr r3, _old_function_addr_s_thumb 20 | bic r3, r3, #1 21 | add r3, r3, #0x1 22 | str r3, _old_function_addr_s_thumb 23 | ldr r0, [sp, #0x3C] 24 | ldmfd sp!, {r0-r12} 25 | ldr r14, [sp, #4] 26 | ldr sp, [r13] 27 | ldr pc, _old_function_addr_s_thumb 28 | 29 | _hookstub_function_addr_s_thumb: 30 | .word 0xffffffff 31 | 32 | _old_function_addr_s_thumb: 33 | .word 0xffffffff 34 | 35 | _shellcode_end_s_thumb: 36 | 37 | .end 38 | 39 | --------------------------------------------------------------------------------