├── Application.mk ├── example ├── Application.mk ├── hooktest.c └── Android.mk ├── relocate.h ├── include └── inlineHook.h ├── Android.mk ├── README.md ├── inlineHook.c └── relocate.c /Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi armeabi-v7a 2 | APP_PIE:= true -------------------------------------------------------------------------------- /example/Application.mk: -------------------------------------------------------------------------------- 1 | APP_ABI := armeabi-v7a 2 | APP_PIE:= true 3 | -------------------------------------------------------------------------------- /relocate.h: -------------------------------------------------------------------------------- 1 | #ifndef _RELOCATE_H 2 | #define _RELOCATE_H 3 | 4 | #include 5 | 6 | void relocateInstruction(uint32_t target_addr, void *orig_instructions, int length, void *trampoline_instructions, int *orig_boundaries, int *trampoline_boundaries, int *count); 7 | 8 | #endif -------------------------------------------------------------------------------- /include/inlineHook.h: -------------------------------------------------------------------------------- 1 | #ifndef _INLINEHOOK_H 2 | #define _INLINEHOOK_H 3 | 4 | #include 5 | 6 | enum ele7en_status { 7 | ELE7EN_ERROR_UNKNOWN = -1, 8 | ELE7EN_OK = 0, 9 | ELE7EN_ERROR_NOT_INITIALIZED, 10 | ELE7EN_ERROR_NOT_EXECUTABLE, 11 | ELE7EN_ERROR_NOT_REGISTERED, 12 | ELE7EN_ERROR_NOT_HOOKED, 13 | ELE7EN_ERROR_ALREADY_REGISTERED, 14 | ELE7EN_ERROR_ALREADY_HOOKED, 15 | ELE7EN_ERROR_SO_NOT_FOUND, 16 | ELE7EN_ERROR_FUNCTION_NOT_FOUND 17 | }; 18 | 19 | enum ele7en_status registerInlineHook(uint32_t target_addr, uint32_t new_addr, uint32_t **proto_addr); 20 | enum ele7en_status inlineUnHook(uint32_t target_addr); 21 | void inlineUnHookAll(); 22 | enum ele7en_status inlineHook(uint32_t target_addr); 23 | void inlineHookAll(); 24 | 25 | #endif -------------------------------------------------------------------------------- /example/hooktest.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "inlineHook.h" 4 | 5 | int (*old_puts)(const char *) = NULL; 6 | 7 | int new_puts(const char *string) 8 | { 9 | old_puts("inlineHook success"); 10 | } 11 | 12 | int hook() 13 | { 14 | if (registerInlineHook((uint32_t) puts, (uint32_t) new_puts, (uint32_t **) &old_puts) != ELE7EN_OK) { 15 | return -1; 16 | } 17 | if (inlineHook((uint32_t) puts) != ELE7EN_OK) { 18 | return -1; 19 | } 20 | 21 | return 0; 22 | } 23 | 24 | int unHook() 25 | { 26 | if (inlineUnHook((uint32_t) puts) != ELE7EN_OK) { 27 | return -1; 28 | } 29 | 30 | return 0; 31 | } 32 | 33 | int main() 34 | { 35 | puts("test"); 36 | hook(); 37 | puts("test"); 38 | unHook(); 39 | puts("test"); 40 | } -------------------------------------------------------------------------------- /Android.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2009 The Android Open Source Project 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # 16 | # 17 | LOCAL_PATH := $(call my-dir) 18 | 19 | include $(CLEAR_VARS) 20 | LOCAL_MODULE := hook 21 | LOCAL_SRC_FILES := inlineHook.c relocate.c 22 | include $(BUILD_STATIC_LIBRARY) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android-Inline-Hook 2 | thumb16 thumb32 arm32 inlineHook 3 | 4 | # Build 5 | ```ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk NDK_APPLICATION_MK=./Application.mk``` 6 | 7 | # Example 8 | ```C 9 | #include 10 | 11 | #include "inlineHook.h" 12 | 13 | int (*old_puts)(const char *) = NULL; 14 | 15 | int new_puts(const char *string) 16 | { 17 | old_puts("inlineHook success"); 18 | } 19 | 20 | int hook() 21 | { 22 | if (registerInlineHook((uint32_t) puts, (uint32_t) new_puts, (uint32_t **) &old_puts) != ELE7EN_OK) { 23 | return -1; 24 | } 25 | if (inlineHook((uint32_t) puts) != ELE7EN_OK) { 26 | return -1; 27 | } 28 | 29 | return 0; 30 | } 31 | 32 | int unHook() 33 | { 34 | if (inlineUnHook((uint32_t) puts) != ELE7EN_OK) { 35 | return -1; 36 | } 37 | 38 | return 0; 39 | } 40 | 41 | int main() 42 | { 43 | puts("test"); 44 | hook(); 45 | puts("test"); 46 | unHook(); 47 | puts("test"); 48 | } 49 | 50 | ``` 51 | 52 | # Contact 53 | If you find any bugs, please contact me(ele7enxxh@qq.com) 54 | -------------------------------------------------------------------------------- /example/Android.mk: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2009 The Android Open Source Project 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # 16 | # 17 | LOCAL_PATH := $(call my-dir) 18 | 19 | include $(CLEAR_VARS) 20 | LOCAL_MODULE := hook 21 | LOCAL_SRC_FILES := $(LOCAL_PATH)/../obj/local/armeabi-v7a/libhook.a 22 | LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../include 23 | include $(PREBUILT_STATIC_LIBRARY) 24 | 25 | include $(CLEAR_VARS) 26 | LOCAL_MODULE := hooktest 27 | LOCAL_SRC_FILES := hooktest.c 28 | LOCAL_STATIC_LIBRARIES := libhook 29 | include $(BUILD_EXECUTABLE) -------------------------------------------------------------------------------- /inlineHook.c: -------------------------------------------------------------------------------- 1 | /* 2 | thumb16 thumb32 arm32 inlineHook 3 | author: ele7enxxh 4 | mail: ele7enxxh@qq.com 5 | website: ele7enxxh.com 6 | modified time: 2015-01-23 7 | created time: 2015-11-30 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | // #include 17 | #include 18 | #include 19 | 20 | #include "relocate.h" 21 | #include "include/inlineHook.h" 22 | 23 | #ifndef PAGE_SIZE 24 | #define PAGE_SIZE 4096 25 | #endif 26 | 27 | #define PAGE_START(addr) (~(PAGE_SIZE - 1) & (addr)) 28 | #define SET_BIT0(addr) (addr | 1) 29 | #define CLEAR_BIT0(addr) (addr & 0xFFFFFFFE) 30 | #define TEST_BIT0(addr) (addr & 1) 31 | 32 | #define ACTION_ENABLE 0 33 | #define ACTION_DISABLE 1 34 | 35 | enum hook_status { 36 | REGISTERED, 37 | HOOKED, 38 | }; 39 | 40 | struct inlineHookItem { 41 | uint32_t target_addr; 42 | uint32_t new_addr; 43 | uint32_t **proto_addr; 44 | void *orig_instructions; 45 | int orig_boundaries[4]; 46 | int trampoline_boundaries[20]; 47 | int count; 48 | void *trampoline_instructions; 49 | int length; 50 | int status; 51 | int mode; 52 | }; 53 | 54 | struct inlineHookInfo { 55 | struct inlineHookItem item[1024]; 56 | int size; 57 | }; 58 | 59 | static struct inlineHookInfo info = {0}; 60 | 61 | static int getAllTids(pid_t pid, pid_t *tids) 62 | { 63 | char dir_path[32]; 64 | DIR *dir; 65 | int i; 66 | struct dirent *entry; 67 | pid_t tid; 68 | 69 | if (pid < 0) { 70 | snprintf(dir_path, sizeof(dir_path), "/proc/self/task"); 71 | } 72 | else { 73 | snprintf(dir_path, sizeof(dir_path), "/proc/%d/task", pid); 74 | } 75 | 76 | dir = opendir(dir_path); 77 | if (dir == NULL) { 78 | return 0; 79 | } 80 | 81 | i = 0; 82 | while((entry = readdir(dir)) != NULL) { 83 | tid = atoi(entry->d_name); 84 | if (tid != 0 && tid != getpid()) { 85 | tids[i++] = tid; 86 | } 87 | } 88 | closedir(dir); 89 | return i; 90 | } 91 | 92 | static bool doProcessThreadPC(struct inlineHookItem *item, struct pt_regs *regs, int action) 93 | { 94 | int offset; 95 | int i; 96 | 97 | switch (action) 98 | { 99 | case ACTION_ENABLE: 100 | offset = regs->ARM_pc - CLEAR_BIT0(item->target_addr); 101 | for (i = 0; i < item->count; ++i) { 102 | if (offset == item->orig_boundaries[i]) { 103 | regs->ARM_pc = (uint32_t) item->trampoline_instructions + item->trampoline_boundaries[i]; 104 | return true; 105 | } 106 | } 107 | break; 108 | case ACTION_DISABLE: 109 | offset = regs->ARM_pc - (int) item->trampoline_instructions; 110 | for (i = 0; i < item->count; ++i) { 111 | if (offset == item->trampoline_boundaries[i]) { 112 | regs->ARM_pc = CLEAR_BIT0(item->target_addr) + item->orig_boundaries[i]; 113 | return true; 114 | } 115 | } 116 | break; 117 | } 118 | 119 | return false; 120 | } 121 | 122 | static void processThreadPC(pid_t tid, struct inlineHookItem *item, int action) 123 | { 124 | struct pt_regs regs; 125 | 126 | if (ptrace(PTRACE_GETREGS, tid, NULL, ®s) == 0) { 127 | if (item == NULL) { 128 | int pos; 129 | 130 | for (pos = 0; pos < info.size; ++pos) { 131 | if (doProcessThreadPC(&info.item[pos], ®s, action) == true) { 132 | break; 133 | } 134 | } 135 | } 136 | else { 137 | doProcessThreadPC(item, ®s, action); 138 | } 139 | 140 | ptrace(PTRACE_SETREGS, tid, NULL, ®s); 141 | } 142 | } 143 | 144 | static pid_t freeze(struct inlineHookItem *item, int action) 145 | { 146 | int count; 147 | pid_t tids[1024]; 148 | pid_t pid; 149 | 150 | pid = -1; 151 | count = getAllTids(getpid(), tids); 152 | if (count > 0) { 153 | pid = fork(); 154 | 155 | if (pid == 0) { 156 | int i; 157 | 158 | for (i = 0; i < count; ++i) { 159 | if (ptrace(PTRACE_ATTACH, tids[i], NULL, NULL) == 0) { 160 | waitpid(tids[i], NULL, WUNTRACED); 161 | processThreadPC(tids[i], item, action); 162 | } 163 | } 164 | 165 | raise(SIGSTOP); 166 | 167 | for (i = 0; i < count; ++i) { 168 | ptrace(PTRACE_DETACH, tids[i], NULL, NULL); 169 | } 170 | 171 | raise(SIGKILL); 172 | } 173 | 174 | else if (pid > 0) { 175 | waitpid(pid, NULL, WUNTRACED); 176 | } 177 | } 178 | 179 | return pid; 180 | } 181 | 182 | static void unFreeze(pid_t pid) 183 | { 184 | if (pid < 0) { 185 | return; 186 | } 187 | 188 | kill(pid, SIGCONT); 189 | wait(NULL); 190 | } 191 | 192 | static bool isExecutableAddr(uint32_t addr) 193 | { 194 | FILE *fp; 195 | char line[1024]; 196 | uint32_t start; 197 | uint32_t end; 198 | 199 | fp = fopen("/proc/self/maps", "r"); 200 | if (fp == NULL) { 201 | return false; 202 | } 203 | 204 | while (fgets(line, sizeof(line), fp)) { 205 | if (strstr(line, "r-xp")) { 206 | start = strtoul(strtok(line, "-"), NULL, 16); 207 | end = strtoul(strtok(NULL, " "), NULL, 16); 208 | if (addr >= start && addr <= end) { 209 | fclose(fp); 210 | return true; 211 | } 212 | } 213 | } 214 | 215 | fclose(fp); 216 | 217 | return false; 218 | } 219 | 220 | static struct inlineHookItem *findInlineHookItem(uint32_t target_addr) 221 | { 222 | int i; 223 | 224 | for (i = 0; i < info.size; ++i) { 225 | if (info.item[i].target_addr == target_addr) { 226 | return &info.item[i]; 227 | } 228 | } 229 | 230 | return NULL; 231 | } 232 | 233 | static struct inlineHookItem *addInlineHookItem() { 234 | struct inlineHookItem *item; 235 | 236 | if (info.size >= 1024) { 237 | return NULL; 238 | } 239 | 240 | item = &info.item[info.size]; 241 | ++info.size; 242 | 243 | return item; 244 | } 245 | 246 | static void deleteInlineHookItem(int pos) 247 | { 248 | info.item[pos] = info.item[info.size - 1]; 249 | --info.size; 250 | } 251 | 252 | enum ele7en_status registerInlineHook(uint32_t target_addr, uint32_t new_addr, uint32_t **proto_addr) 253 | { 254 | struct inlineHookItem *item; 255 | 256 | if (!isExecutableAddr(target_addr) || !isExecutableAddr(new_addr)) { 257 | return ELE7EN_ERROR_NOT_EXECUTABLE; 258 | } 259 | 260 | item = findInlineHookItem(target_addr); 261 | if (item != NULL) { 262 | if (item->status == REGISTERED) { 263 | return ELE7EN_ERROR_ALREADY_REGISTERED; 264 | } 265 | else if (item->status == HOOKED) { 266 | return ELE7EN_ERROR_ALREADY_HOOKED; 267 | } 268 | else { 269 | return ELE7EN_ERROR_UNKNOWN; 270 | } 271 | } 272 | 273 | item = addInlineHookItem(); 274 | 275 | item->target_addr = target_addr; 276 | item->new_addr = new_addr; 277 | item->proto_addr = proto_addr; 278 | 279 | item->length = TEST_BIT0(item->target_addr) ? 12 : 8; 280 | item->orig_instructions = malloc(item->length); 281 | memcpy(item->orig_instructions, (void *) CLEAR_BIT0(item->target_addr), item->length); 282 | 283 | item->trampoline_instructions = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); 284 | relocateInstruction(item->target_addr, item->orig_instructions, item->length, item->trampoline_instructions, item->orig_boundaries, item->trampoline_boundaries, &item->count); 285 | 286 | item->status = REGISTERED; 287 | 288 | return ELE7EN_OK; 289 | } 290 | 291 | static void doInlineUnHook(struct inlineHookItem *item, int pos) 292 | { 293 | mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, PROT_READ | PROT_WRITE | PROT_EXEC); 294 | memcpy((void *) CLEAR_BIT0(item->target_addr), item->orig_instructions, item->length); 295 | mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, PROT_READ | PROT_EXEC); 296 | munmap(item->trampoline_instructions, PAGE_SIZE); 297 | free(item->orig_instructions); 298 | 299 | deleteInlineHookItem(pos); 300 | 301 | cacheflush(CLEAR_BIT0(item->target_addr), CLEAR_BIT0(item->target_addr) + item->length, 0); 302 | } 303 | 304 | enum ele7en_status inlineUnHook(uint32_t target_addr) 305 | { 306 | int i; 307 | 308 | for (i = 0; i < info.size; ++i) { 309 | if (info.item[i].target_addr == target_addr && info.item[i].status == HOOKED) { 310 | pid_t pid; 311 | 312 | pid = freeze(&info.item[i], ACTION_DISABLE); 313 | 314 | doInlineUnHook(&info.item[i], i); 315 | 316 | unFreeze(pid); 317 | 318 | return ELE7EN_OK; 319 | } 320 | } 321 | 322 | return ELE7EN_ERROR_NOT_HOOKED; 323 | } 324 | 325 | void inlineUnHookAll() 326 | { 327 | pid_t pid; 328 | int i; 329 | 330 | pid = freeze(NULL, ACTION_DISABLE); 331 | 332 | for (i = 0; i < info.size; ++i) { 333 | if (info.item[i].status == HOOKED) { 334 | doInlineUnHook(&info.item[i], i); 335 | --i; 336 | } 337 | } 338 | 339 | unFreeze(pid); 340 | } 341 | 342 | static void doInlineHook(struct inlineHookItem *item) 343 | { 344 | mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, PROT_READ | PROT_WRITE | PROT_EXEC); 345 | 346 | if (TEST_BIT0(item->target_addr)) { 347 | int i; 348 | 349 | i = 0; 350 | if (CLEAR_BIT0(item->target_addr) % 4 != 0) { 351 | ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xBF00; // NOP 352 | } 353 | ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF8DF; 354 | ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF000; // LDR.W PC, [PC] 355 | ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr & 0xFFFF; 356 | ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr >> 16; 357 | } 358 | else { 359 | ((uint32_t *) (item->target_addr))[0] = 0xe51ff004; // LDR PC, [PC, #-4] 360 | ((uint32_t *) (item->target_addr))[1] = item->new_addr; 361 | } 362 | 363 | mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, PROT_READ | PROT_EXEC); 364 | 365 | if (item->proto_addr != NULL) { 366 | *(item->proto_addr) = TEST_BIT0(item->target_addr) ? (uint32_t *) SET_BIT0((uint32_t) item->trampoline_instructions) : item->trampoline_instructions; 367 | } 368 | 369 | item->status = HOOKED; 370 | 371 | cacheflush(CLEAR_BIT0(item->target_addr), CLEAR_BIT0(item->target_addr) + item->length, 0); 372 | } 373 | 374 | enum ele7en_status inlineHook(uint32_t target_addr) 375 | { 376 | int i; 377 | struct inlineHookItem *item; 378 | 379 | item = NULL; 380 | for (i = 0; i < info.size; ++i) { 381 | if (info.item[i].target_addr == target_addr) { 382 | item = &info.item[i]; 383 | break; 384 | } 385 | } 386 | 387 | if (item == NULL) { 388 | return ELE7EN_ERROR_NOT_REGISTERED; 389 | } 390 | 391 | if (item->status == REGISTERED) { 392 | pid_t pid; 393 | 394 | pid = freeze(item, ACTION_ENABLE); 395 | 396 | doInlineHook(item); 397 | 398 | unFreeze(pid); 399 | 400 | return ELE7EN_OK; 401 | } 402 | else if (item->status == HOOKED) { 403 | return ELE7EN_ERROR_ALREADY_HOOKED; 404 | } 405 | else { 406 | return ELE7EN_ERROR_UNKNOWN; 407 | } 408 | } 409 | 410 | void inlineHookAll() 411 | { 412 | pid_t pid; 413 | int i; 414 | 415 | pid = freeze(NULL, ACTION_ENABLE); 416 | 417 | for (i = 0; i < info.size; ++i) { 418 | if (info.item[i].status == REGISTERED) { 419 | doInlineHook(&info.item[i]); 420 | } 421 | } 422 | 423 | unFreeze(pid); 424 | } 425 | -------------------------------------------------------------------------------- /relocate.c: -------------------------------------------------------------------------------- 1 | /* 2 | relocate instruction 3 | author: ele7enxxh 4 | mail: ele7enxxh@qq.com 5 | website: ele7enxxh.com 6 | modified time: 2016-10-17 7 | created time: 2015-01-17 8 | */ 9 | 10 | #include "relocate.h" 11 | 12 | #define ALIGN_PC(pc) (pc & 0xFFFFFFFC) 13 | 14 | enum INSTRUCTION_TYPE { 15 | // B