diff --git a/module/build.gradle b/module/build.gradle index ff85d0e..11a3ae0 100644 --- a/module/build.gradle +++ b/module/build.gradle @@ -16,7 +16,7 @@ android { def moduleId = "mipush_fake" def moduleProp = [ - name : "MiPushFakeForRiru", + name : "MiPushFake", version : "v12.0", versionCode: "12", author : "Timothy", diff --git a/module/src/main/cpp/CMakeLists.txt b/module/src/main/cpp/CMakeLists.txt index df55904..1bbf47d 100644 --- a/module/src/main/cpp/CMakeLists.txt +++ b/module/src/main/cpp/CMakeLists.txt @@ -1,6 +1,18 @@ cmake_minimum_required(VERSION 3.4.1) -set(MODULE_NAME "mipush_fake") +set(MODULE_NAME "riru_mipush_fake") + +SET(XHOOK_PATH ${CMAKE_CURRENT_SOURCE_DIR}/external/xhook/) + + +INCLUDE_DIRECTORIES( + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${XHOOK_PATH}/include +) + +LINK_DIRECTORIES( + ${XHOOK_PATH}/lib/ +) message("Build type: ${CMAKE_BUILD_TYPE}") @@ -22,6 +34,22 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${C_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINKER_FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_FLAGS}") -add_library(${MODULE_NAME} SHARED main.cpp) -target_link_libraries(${MODULE_NAME} log) + +SET(XHOOK_OBJS + external/xhook/xhook.c + external/xhook/xh_core.c + external/xhook/xh_elf.c + external/xhook/xh_jni.c + external/xhook/xh_log.c + external/xhook/xh_util.c + external/xhook/xh_version.c + ) +ADD_LIBRARY(libxhook STATIC ${XHOOK_OBJS}) +set_target_properties(libxhook PROPERTIES LINKER_LANGUAGE CXX C_STANDARD 11 OUTPUT_NAME "xhook") + + +add_library(${MODULE_NAME} SHARED hook.cpp riru.c main.cpp) +target_link_libraries(${MODULE_NAME} log libxhook) set_target_properties(${MODULE_NAME} PROPERTIES LINK_FLAGS_RELEASE -s) + + diff --git a/module/src/main/cpp/external/Android.mk b/module/src/main/cpp/external/Android.mk new file mode 100644 index 0000000..04d3e48 --- /dev/null +++ b/module/src/main/cpp/external/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := xhook +LOCAL_SRC_FILES := xhook/xhook.c \ + xhook/xh_core.c \ + xhook/xh_elf.c \ + xhook/xh_jni.c \ + xhook/xh_log.c \ + xhook/xh_util.c \ + xhook/xh_version.c +LOCAL_C_INCLUDES := $(LOCAL_PATH) +LOCAL_CFLAGS := -Wall -Wextra -Werror -fvisibility=hidden +LOCAL_CONLYFLAGS := -std=c11 +include $(BUILD_STATIC_LIBRARY) \ No newline at end of file diff --git a/module/src/main/cpp/external/xhook/include/xhook.h b/module/src/main/cpp/external/xhook/include/xhook.h new file mode 100644 index 0000000..93dd5b4 --- /dev/null +++ b/module/src/main/cpp/external/xhook/include/xhook.h @@ -0,0 +1,50 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2018-04-11. + +#ifndef XHOOK_H +#define XHOOK_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#define XHOOK_EXPORT __attribute__((visibility("default"))) + +int xhook_register(const char *pathname_regex_str, const char *symbol, + void *new_func, void **old_func) XHOOK_EXPORT; + +int xhook_ignore(const char *pathname_regex_str, const char *symbol) XHOOK_EXPORT; + +int xhook_refresh(int async) XHOOK_EXPORT; + +void xhook_clear() XHOOK_EXPORT; + +void xhook_enable_debug(int flag) XHOOK_EXPORT; + +void xhook_enable_sigsegv_protection(int flag) XHOOK_EXPORT; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/module/src/main/cpp/external/xhook/queue.h b/module/src/main/cpp/external/xhook/queue.h new file mode 100644 index 0000000..c2443be --- /dev/null +++ b/module/src/main/cpp/external/xhook/queue.h @@ -0,0 +1,554 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD: stable/9/sys/sys/queue.h 252365 2013-06-29 04:25:40Z lstewart $ + */ + +#ifndef QUEUE_H +#define QUEUE_H + +/* #include */ +#define __containerof(ptr, type, field) ((type *)((char *)(ptr) - ((char *)&((type *)0)->field))) + +/* + * This file defines four types of data structures: singly-linked lists, + * singly-linked tail queues, lists and tail queues. + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A singly-linked tail queue is headed by a pair of pointers, one to the + * head of the list and the other to the tail of the list. The elements are + * singly linked for minimum space and pointer manipulation overhead at the + * expense of O(n) removal for arbitrary elements. New elements can be added + * to the list after an existing element, at the head of the list, or at the + * end of the list. Elements being removed from the head of the tail queue + * should use the explicit macro for this purpose for optimum efficiency. + * A singly-linked tail queue may only be traversed in the forward direction. + * Singly-linked tail queues are ideal for applications with large datasets + * and few or no removals or for implementing a FIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may be traversed in either direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * For details on the use of these macros, see the queue(3) manual page. + * + * SLIST LIST STAILQ TAILQ + * _HEAD + + + + + * _HEAD_INITIALIZER + + + + + * _ENTRY + + + + + * _INIT + + + + + * _EMPTY + + + + + * _FIRST + + + + + * _NEXT + + + + + * _PREV - + - + + * _LAST - - + + + * _FOREACH + + + + + * _FOREACH_FROM + + + + + * _FOREACH_SAFE + + + + + * _FOREACH_FROM_SAFE + + + + + * _FOREACH_REVERSE - - - + + * _FOREACH_REVERSE_FROM - - - + + * _FOREACH_REVERSE_SAFE - - - + + * _FOREACH_REVERSE_FROM_SAFE - - - + + * _INSERT_HEAD + + + + + * _INSERT_BEFORE - + - + + * _INSERT_AFTER + + + + + * _INSERT_TAIL - - + + + * _CONCAT - - + + + * _REMOVE_AFTER + - + - + * _REMOVE_HEAD + - + - + * _REMOVE + + + + + * _SWAP + + + + + * + */ + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type, qual) \ + struct name { \ + struct type *qual slh_first; /* first element */ \ + } + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type, qual) \ + struct { \ + struct type *qual sle_next; /* next element */ \ + } + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ + } while (0) + +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ + } while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ + } while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + SLIST_NEXT(elm, field) = \ + SLIST_NEXT(SLIST_NEXT(elm, field), field); \ + } while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ + } while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = SLIST_FIRST((head)); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_REMOVE_AFTER(curelm, field); \ + } \ + } while (0) + +#define SLIST_SWAP(head1, head2, type) do { \ + struct type *swap_first = SLIST_FIRST(head1); \ + SLIST_FIRST(head1) = SLIST_FIRST(head2); \ + SLIST_FIRST(head2) = swap_first; \ + } while (0) + +/* + * List declarations. + */ +#define LIST_HEAD(name, type, qual) \ + struct name { \ + struct type *qual lh_first; /* first element */ \ + } + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type, qual) \ + struct { \ + struct type *qual le_next; /* next element */ \ + struct type *qual *le_prev; /* address of previous next element */ \ + } + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ + } while (0) + +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#define LIST_FIRST(head) ((head)->lh_first) + +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_PREV(elm, head, type, field) \ + ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \ + __containerof((elm)->field.le_prev, struct type, field.le_next)) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : LIST_FIRST((head))); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); \ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ + } while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ + } while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL) \ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ + } while (0) + +#define LIST_REMOVE(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ + } while (0) + +#define LIST_SWAP(head1, head2, type, field) do { \ + struct type *swap_tmp = LIST_FIRST((head1)); \ + LIST_FIRST((head1)) = LIST_FIRST((head2)); \ + LIST_FIRST((head2)) = swap_tmp; \ + if ((swap_tmp = LIST_FIRST((head1))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head1)); \ + if ((swap_tmp = LIST_FIRST((head2))) != NULL) \ + swap_tmp->field.le_prev = &LIST_FIRST((head2)); \ + } while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type, qual) \ + struct name { \ + struct type *qual stqh_first;/* first element */ \ + struct type *qual *stqh_last;/* addr of last next element */ \ + } + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type, qual) \ + struct { \ + struct type *qual stqe_next; /* next element */ \ + } + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ + } while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? NULL : \ + __containerof((head)->stqh_last, struct type, field.stqe_next)) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ + } while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ + } while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + } while (0) + +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ + } while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + } while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ + } while (0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ + } while (0) + +#define STAILQ_SWAP(head1, head2, type) do { \ + struct type *swap_first = STAILQ_FIRST(head1); \ + struct type **swap_last = (head1)->stqh_last; \ + STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_FIRST(head2) = swap_first; \ + (head2)->stqh_last = swap_last; \ + if (STAILQ_EMPTY(head1)) \ + (head1)->stqh_last = &STAILQ_FIRST(head1); \ + if (STAILQ_EMPTY(head2)) \ + (head2)->stqh_last = &STAILQ_FIRST(head2); \ + } while (0) + +/* + * Tail queue declarations. + */ +#define TAILQ_HEAD(name, type, qual) \ + struct name { \ + struct type *qual tqh_first; /* first element */ \ + struct type *qual *tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type, qual) \ + struct { \ + struct type *qual tqe_next; /* next element */ \ + struct type *qual *tqe_prev; /* address of previous next element */ \ + } + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + TAILQ_FIRST((head)) = NULL; \ + (head)->tqh_last = &TAILQ_FIRST((head)); \ + } while (0) + +#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) + +#define TAILQ_FIRST(head) ((head)->tqh_first) + +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) + +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) + +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = TAILQ_FIRST((head)); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_FROM(var, head, field) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var); \ + (var) = TAILQ_NEXT((var), field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \ + for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var); \ + (var) = TAILQ_PREV((var), headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \ + for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \ + (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \ + (var) = (tvar)) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \ + TAILQ_FIRST((head))->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_FIRST((head)) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \ + } while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + TAILQ_NEXT((elm), field) = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \ + } while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + &TAILQ_NEXT((elm), field); \ + else \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + TAILQ_NEXT((listelm), field) = (elm); \ + (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \ + } while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + TAILQ_NEXT((elm), field) = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &TAILQ_NEXT((elm), field); \ + } while (0) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ + } while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if ((TAILQ_NEXT((elm), field)) != NULL) \ + TAILQ_NEXT((elm), field)->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \ + } while (0) + +#define TAILQ_SWAP(head1, head2, type, field) do { \ + struct type *swap_first = (head1)->tqh_first; \ + struct type **swap_last = (head1)->tqh_last; \ + (head1)->tqh_first = (head2)->tqh_first; \ + (head1)->tqh_last = (head2)->tqh_last; \ + (head2)->tqh_first = swap_first; \ + (head2)->tqh_last = swap_last; \ + if ((swap_first = (head1)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head1)->tqh_first; \ + else \ + (head1)->tqh_last = &(head1)->tqh_first; \ + if ((swap_first = (head2)->tqh_first) != NULL) \ + swap_first->field.tqe_prev = &(head2)->tqh_first; \ + else \ + (head2)->tqh_last = &(head2)->tqh_first; \ + } while (0) + +#endif diff --git a/module/src/main/cpp/external/xhook/tree.h b/module/src/main/cpp/external/xhook/tree.h new file mode 100644 index 0000000..dc938ae --- /dev/null +++ b/module/src/main/cpp/external/xhook/tree.h @@ -0,0 +1,768 @@ +/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */ +/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ +/* $FreeBSD: stable/9/sys/sys/tree.h 189204 2009-03-01 04:57:23Z bms $ */ + +/*- + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TREE_H +#define TREE_H + +/* #include */ +#ifndef __unused +#define __unused __attribute__((__unused__)) +#endif + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ +attr struct type *name##_RB_INSERT(struct name *, struct type *); \ +attr struct type *name##_RB_FIND(struct name *, struct type *); \ +attr struct type *name##_RB_NFIND(struct name *, struct type *); \ +attr struct type *name##_RB_NEXT(struct type *); \ +attr struct type *name##_RB_PREV(struct type *); \ +attr struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) != NULL && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)) \ + != NULL) \ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)) \ + != NULL) \ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field)) != NULL) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field)) != NULL); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#endif diff --git a/module/src/main/cpp/external/xhook/xh_core.c b/module/src/main/cpp/external/xhook/xh_core.c new file mode 100644 index 0000000..4d14ba4 --- /dev/null +++ b/module/src/main/cpp/external/xhook/xh_core.c @@ -0,0 +1,656 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2018-04-11. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "queue.h" +#include "tree.h" +#include "xh_errno.h" +#include "xh_log.h" +#include "xh_elf.h" +#include "xh_version.h" +#include "xh_core.h" + +#define XH_CORE_DEBUG 0 + +//registered hook info collection +typedef struct xh_core_hook_info +{ +#if XH_CORE_DEBUG + char *pathname_regex_str; +#endif + regex_t pathname_regex; + char *symbol; + void *new_func; + void **old_func; + TAILQ_ENTRY(xh_core_hook_info,) link; +} xh_core_hook_info_t; +typedef TAILQ_HEAD(xh_core_hook_info_queue, xh_core_hook_info,) xh_core_hook_info_queue_t; + +//ignored hook info collection +typedef struct xh_core_ignore_info +{ +#if XH_CORE_DEBUG + char *pathname_regex_str; +#endif + regex_t pathname_regex; + char *symbol; //NULL meaning for all symbols + TAILQ_ENTRY(xh_core_ignore_info,) link; +} xh_core_ignore_info_t; +typedef TAILQ_HEAD(xh_core_ignore_info_queue, xh_core_ignore_info,) xh_core_ignore_info_queue_t; + +//required info from /proc/self/maps +typedef struct xh_core_map_info +{ + char *pathname; + uintptr_t base_addr; + xh_elf_t elf; + RB_ENTRY(xh_core_map_info) link; +} xh_core_map_info_t; +static __inline__ int xh_core_map_info_cmp(xh_core_map_info_t *a, xh_core_map_info_t *b) +{ + return strcmp(a->pathname, b->pathname); +} +typedef RB_HEAD(xh_core_map_info_tree, xh_core_map_info) xh_core_map_info_tree_t; +RB_GENERATE_STATIC(xh_core_map_info_tree, xh_core_map_info, link, xh_core_map_info_cmp) + +//signal handler for SIGSEGV +//for xh_elf_init(), xh_elf_hook(), xh_elf_check_elfheader() +static int xh_core_sigsegv_enable = 1; //enable by default +static struct sigaction xh_core_sigsegv_act_old; +static volatile int xh_core_sigsegv_flag = 0; +static sigjmp_buf xh_core_sigsegv_env; +static void xh_core_sigsegv_handler(int sig) +{ + (void)sig; + + if(xh_core_sigsegv_flag) + siglongjmp(xh_core_sigsegv_env, 1); + else + sigaction(SIGSEGV, &xh_core_sigsegv_act_old, NULL); +} +static int xh_core_add_sigsegv_handler() +{ + struct sigaction act; + + if(!xh_core_sigsegv_enable) return 0; + + if(0 != sigemptyset(&act.sa_mask)) return (0 == errno ? XH_ERRNO_UNKNOWN : errno); + act.sa_handler = xh_core_sigsegv_handler; + + if(0 != sigaction(SIGSEGV, &act, &xh_core_sigsegv_act_old)) + return (0 == errno ? XH_ERRNO_UNKNOWN : errno); + + return 0; +} +static void xh_core_del_sigsegv_handler() +{ + if(!xh_core_sigsegv_enable) return; + + sigaction(SIGSEGV, &xh_core_sigsegv_act_old, NULL); +} + + +static xh_core_hook_info_queue_t xh_core_hook_info = TAILQ_HEAD_INITIALIZER(xh_core_hook_info); +static xh_core_ignore_info_queue_t xh_core_ignore_info = TAILQ_HEAD_INITIALIZER(xh_core_ignore_info); +static xh_core_map_info_tree_t xh_core_map_info = RB_INITIALIZER(&xh_core_map_info); +static pthread_mutex_t xh_core_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t xh_core_cond = PTHREAD_COND_INITIALIZER; +static volatile int xh_core_inited = 0; +static volatile int xh_core_init_ok = 0; +static volatile int xh_core_async_inited = 0; +static volatile int xh_core_async_init_ok = 0; +static pthread_mutex_t xh_core_refresh_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_t xh_core_refresh_thread_tid; +static volatile int xh_core_refresh_thread_running = 0; +static volatile int xh_core_refresh_thread_do = 0; + + +int xh_core_register(const char *pathname_regex_str, const char *symbol, + void *new_func, void **old_func) +{ + xh_core_hook_info_t *hi; + regex_t regex; + + if(NULL == pathname_regex_str || NULL == symbol || NULL == new_func) return XH_ERRNO_INVAL; + + if(xh_core_inited) + { + XH_LOG_ERROR("do not register hook after refresh(): %s, %s", pathname_regex_str, symbol); + return XH_ERRNO_INVAL; + } + + if(0 != regcomp(®ex, pathname_regex_str, REG_NOSUB)) return XH_ERRNO_INVAL; + + if(NULL == (hi = malloc(sizeof(xh_core_hook_info_t)))) return XH_ERRNO_NOMEM; + if(NULL == (hi->symbol = strdup(symbol))) + { + free(hi); + return XH_ERRNO_NOMEM; + } +#if XH_CORE_DEBUG + if(NULL == (hi->pathname_regex_str = strdup(pathname_regex_str))) + { + free(hi->symbol); + free(hi); + return XH_ERRNO_NOMEM; + } +#endif + hi->pathname_regex = regex; + hi->new_func = new_func; + hi->old_func = old_func; + + pthread_mutex_lock(&xh_core_mutex); + TAILQ_INSERT_TAIL(&xh_core_hook_info, hi, link); + pthread_mutex_unlock(&xh_core_mutex); + + return 0; +} + +int xh_core_ignore(const char *pathname_regex_str, const char *symbol) +{ + xh_core_ignore_info_t *ii; + regex_t regex; + + if(NULL == pathname_regex_str) return XH_ERRNO_INVAL; + + if(xh_core_inited) + { + XH_LOG_ERROR("do not ignore hook after refresh(): %s, %s", pathname_regex_str, symbol ? symbol : "ALL"); + return XH_ERRNO_INVAL; + } + + if(0 != regcomp(®ex, pathname_regex_str, REG_NOSUB)) return XH_ERRNO_INVAL; + + if(NULL == (ii = malloc(sizeof(xh_core_ignore_info_t)))) return XH_ERRNO_NOMEM; + if(NULL != symbol) + { + if(NULL == (ii->symbol = strdup(symbol))) + { + free(ii); + return XH_ERRNO_NOMEM; + } + } + else + { + ii->symbol = NULL; //ignore all symbols + } +#if XH_CORE_DEBUG + if(NULL == (ii->pathname_regex_str = strdup(pathname_regex_str))) + { + free(ii->symbol); + free(ii); + return XH_ERRNO_NOMEM; + } +#endif + ii->pathname_regex = regex; + + pthread_mutex_lock(&xh_core_mutex); + TAILQ_INSERT_TAIL(&xh_core_ignore_info, ii, link); + pthread_mutex_unlock(&xh_core_mutex); + + return 0; +} + +static int xh_core_check_elf_header(uintptr_t base_addr, const char *pathname) +{ + if(!xh_core_sigsegv_enable) + { + return xh_elf_check_elfheader(base_addr); + } + else + { + int ret = XH_ERRNO_UNKNOWN; + + xh_core_sigsegv_flag = 1; + if(0 == sigsetjmp(xh_core_sigsegv_env, 1)) + { + ret = xh_elf_check_elfheader(base_addr); + } + else + { + ret = XH_ERRNO_SEGVERR; + XH_LOG_WARN("catch SIGSEGV when check_elfheader: %s", pathname); + } + xh_core_sigsegv_flag = 0; + return ret; + } +} + +static void xh_core_hook_impl(xh_core_map_info_t *mi) +{ + //init + if(0 != xh_elf_init(&(mi->elf), mi->base_addr, mi->pathname)) return; + + //hook + xh_core_hook_info_t *hi; + xh_core_ignore_info_t *ii; + int ignore; + TAILQ_FOREACH(hi, &xh_core_hook_info, link) //find hook info + { + if(0 == regexec(&(hi->pathname_regex), mi->pathname, 0, NULL, 0)) + { + ignore = 0; + TAILQ_FOREACH(ii, &xh_core_ignore_info, link) //find ignore info + { + if(0 == regexec(&(ii->pathname_regex), mi->pathname, 0, NULL, 0)) + { + if(NULL == ii->symbol) //ignore all symbols + return; + + if(0 == strcmp(ii->symbol, hi->symbol)) //ignore the current symbol + { + ignore = 1; + break; + } + } + } + + if(0 == ignore) + xh_elf_hook(&(mi->elf), hi->symbol, hi->new_func, hi->old_func); + } + } +} + +static void xh_core_hook(xh_core_map_info_t *mi) +{ + if(!xh_core_sigsegv_enable) + { + xh_core_hook_impl(mi); + } + else + { + xh_core_sigsegv_flag = 1; + if(0 == sigsetjmp(xh_core_sigsegv_env, 1)) + { + xh_core_hook_impl(mi); + } + else + { + XH_LOG_WARN("catch SIGSEGV when init or hook: %s", mi->pathname); + } + xh_core_sigsegv_flag = 0; + } +} + +static void xh_core_refresh_impl() +{ + char line[512]; + FILE *fp; + uintptr_t base_addr; + char perm[5]; + unsigned long offset; + int pathname_pos; + char *pathname; + size_t pathname_len; + xh_core_map_info_t *mi, *mi_tmp; + xh_core_map_info_t mi_key; + xh_core_hook_info_t *hi; + xh_core_ignore_info_t *ii; + int match; + xh_core_map_info_tree_t map_info_refreshed = RB_INITIALIZER(&map_info_refreshed); + + if(NULL == (fp = fopen("/proc/self/maps", "r"))) + { + XH_LOG_ERROR("fopen /proc/self/maps failed"); + return; + } + + while(fgets(line, sizeof(line), fp)) + { + if(sscanf(line, "%"PRIxPTR"-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) continue; + + //check permission + if(perm[0] != 'r') continue; + if(perm[3] != 'p') continue; //do not touch the shared memory + + //check offset + // + //We are trying to find ELF header in memory. + //It can only be found at the beginning of a mapped memory regions + //whose offset is 0. + if(0 != offset) continue; + + //get pathname + while(isspace(line[pathname_pos]) && pathname_pos < (int)(sizeof(line) - 1)) + pathname_pos += 1; + if(pathname_pos >= (int)(sizeof(line) - 1)) continue; + pathname = line + pathname_pos; + pathname_len = strlen(pathname); + if(0 == pathname_len) continue; + if(pathname[pathname_len - 1] == '\n') + { + pathname[pathname_len - 1] = '\0'; + pathname_len -= 1; + } + if(0 == pathname_len) continue; + if('[' == pathname[0]) continue; + + //check pathname + //if we need to hook this elf? + match = 0; + TAILQ_FOREACH(hi, &xh_core_hook_info, link) //find hook info + { + if(0 == regexec(&(hi->pathname_regex), pathname, 0, NULL, 0)) + { + TAILQ_FOREACH(ii, &xh_core_ignore_info, link) //find ignore info + { + if(0 == regexec(&(ii->pathname_regex), pathname, 0, NULL, 0)) + { + if(NULL == ii->symbol) + goto check_finished; + + if(0 == strcmp(ii->symbol, hi->symbol)) + goto check_continue; + } + } + + match = 1; + check_continue: + break; + } + } + check_finished: + if(0 == match) continue; + + //check elf header format + //We are trying to do ELF header checking as late as possible. + if(0 != xh_core_check_elf_header(base_addr, pathname)) continue; + + //check existed map item + mi_key.pathname = pathname; + if(NULL != (mi = RB_FIND(xh_core_map_info_tree, &xh_core_map_info, &mi_key))) + { + //exist + RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi); + + //repeated? + //We only keep the first one, that is the real base address + if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi)) + { +#if XH_CORE_DEBUG + XH_LOG_DEBUG("repeated map info when update: %s", line); +#endif + free(mi->pathname); + free(mi); + continue; + } + + //re-hook if base_addr changed + if(mi->base_addr != base_addr) + { + mi->base_addr = base_addr; + xh_core_hook(mi); + } + } + else + { + //not exist, create a new map info + if(NULL == (mi = (xh_core_map_info_t *)malloc(sizeof(xh_core_map_info_t)))) continue; + if(NULL == (mi->pathname = strdup(pathname))) + { + free(mi); + continue; + } + mi->base_addr = base_addr; + + //repeated? + //We only keep the first one, that is the real base address + if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi)) + { +#if XH_CORE_DEBUG + XH_LOG_DEBUG("repeated map info when create: %s", line); +#endif + free(mi->pathname); + free(mi); + continue; + } + + //hook + xh_core_hook(mi); //hook + } + } + fclose(fp); + + //free all missing map item, maybe dlclosed? + RB_FOREACH_SAFE(mi, xh_core_map_info_tree, &xh_core_map_info, mi_tmp) + { +#if XH_CORE_DEBUG + XH_LOG_DEBUG("remove missing map info: %s", mi->pathname); +#endif + RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi); + if(mi->pathname) free(mi->pathname); + free(mi); + } + + //save the new refreshed map info tree + xh_core_map_info = map_info_refreshed; + + XH_LOG_INFO("map refreshed"); + +#if XH_CORE_DEBUG + RB_FOREACH(mi, xh_core_map_info_tree, &xh_core_map_info) + XH_LOG_DEBUG(" %"PRIxPTR" %s\n", mi->base_addr, mi->pathname); +#endif +} + +static void *xh_core_refresh_thread_func(void *arg) +{ + (void)arg; + + pthread_setname_np(pthread_self(), "xh_refresh_loop"); + + while(xh_core_refresh_thread_running) + { + //waiting for a refresh task or exit + pthread_mutex_lock(&xh_core_mutex); + while(!xh_core_refresh_thread_do && xh_core_refresh_thread_running) + { + pthread_cond_wait(&xh_core_cond, &xh_core_mutex); + } + if(!xh_core_refresh_thread_running) + { + pthread_mutex_unlock(&xh_core_mutex); + break; + } + xh_core_refresh_thread_do = 0; + pthread_mutex_unlock(&xh_core_mutex); + + //refresh + pthread_mutex_lock(&xh_core_refresh_mutex); + xh_core_refresh_impl(); + pthread_mutex_unlock(&xh_core_refresh_mutex); + } + + return NULL; +} + +static void xh_core_init_once() +{ + if(xh_core_inited) return; + + pthread_mutex_lock(&xh_core_mutex); + + if(xh_core_inited) goto end; + + xh_core_inited = 1; + + //dump debug info + XH_LOG_INFO("%s\n", xh_version_str_full()); +#if XH_CORE_DEBUG + xh_core_hook_info_t *hi; + TAILQ_FOREACH(hi, &xh_core_hook_info, link) + XH_LOG_INFO(" hook: %s @ %s, (%p, %p)\n", hi->symbol, hi->pathname_regex_str, + hi->new_func, hi->old_func); + xh_core_ignore_info_t *ii; + TAILQ_FOREACH(ii, &xh_core_ignore_info, link) + XH_LOG_INFO(" ignore: %s @ %s\n", ii->symbol ? ii->symbol : "ALL ", + ii->pathname_regex_str); +#endif + + //register signal handler + if(0 != xh_core_add_sigsegv_handler()) goto end; + + //OK + xh_core_init_ok = 1; + + end: + pthread_mutex_unlock(&xh_core_mutex); +} + +static void xh_core_init_async_once() +{ + if(xh_core_async_inited) return; + + pthread_mutex_lock(&xh_core_mutex); + + if(xh_core_async_inited) goto end; + + xh_core_async_inited = 1; + + //create async refresh thread + xh_core_refresh_thread_running = 1; + if(0 != pthread_create(&xh_core_refresh_thread_tid, NULL, &xh_core_refresh_thread_func, NULL)) + { + xh_core_refresh_thread_running = 0; + goto end; + } + + //OK + xh_core_async_init_ok = 1; + + end: + pthread_mutex_unlock(&xh_core_mutex); +} + +int xh_core_refresh(int async) +{ + //init + xh_core_init_once(); + if(!xh_core_init_ok) return XH_ERRNO_UNKNOWN; + + if(async) + { + //init for async + xh_core_init_async_once(); + if(!xh_core_async_init_ok) return XH_ERRNO_UNKNOWN; + + //refresh async + pthread_mutex_lock(&xh_core_mutex); + xh_core_refresh_thread_do = 1; + pthread_cond_signal(&xh_core_cond); + pthread_mutex_unlock(&xh_core_mutex); + } + else + { + //refresh sync + pthread_mutex_lock(&xh_core_refresh_mutex); + xh_core_refresh_impl(); + pthread_mutex_unlock(&xh_core_refresh_mutex); + } + + return 0; +} + +void xh_core_clear() +{ + //stop the async refresh thread + if(xh_core_async_init_ok) + { + pthread_mutex_lock(&xh_core_mutex); + xh_core_refresh_thread_running = 0; + pthread_cond_signal(&xh_core_cond); + pthread_mutex_unlock(&xh_core_mutex); + + pthread_join(xh_core_refresh_thread_tid, NULL); + xh_core_async_init_ok = 0; + } + xh_core_async_inited = 0; + + //unregister the sig handler + if(xh_core_init_ok) + { + xh_core_del_sigsegv_handler(); + xh_core_init_ok = 0; + } + xh_core_inited = 0; + + pthread_mutex_lock(&xh_core_mutex); + pthread_mutex_lock(&xh_core_refresh_mutex); + + //free all map info + xh_core_map_info_t *mi, *mi_tmp; + RB_FOREACH_SAFE(mi, xh_core_map_info_tree, &xh_core_map_info, mi_tmp) + { + RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi); + if(mi->pathname) free(mi->pathname); + free(mi); + } + + //free all hook info + xh_core_hook_info_t *hi, *hi_tmp; + TAILQ_FOREACH_SAFE(hi, &xh_core_hook_info, link, hi_tmp) + { + TAILQ_REMOVE(&xh_core_hook_info, hi, link); +#if XH_CORE_DEBUG + free(hi->pathname_regex_str); +#endif + regfree(&(hi->pathname_regex)); + free(hi->symbol); + free(hi); + } + + //free all ignore info + xh_core_ignore_info_t *ii, *ii_tmp; + TAILQ_FOREACH_SAFE(ii, &xh_core_ignore_info, link, ii_tmp) + { + TAILQ_REMOVE(&xh_core_ignore_info, ii, link); +#if XH_CORE_DEBUG + free(ii->pathname_regex_str); +#endif + regfree(&(ii->pathname_regex)); + free(ii->symbol); + free(ii); + } + + pthread_mutex_unlock(&xh_core_refresh_mutex); + pthread_mutex_unlock(&xh_core_mutex); +} + +void xh_core_enable_debug(int flag) +{ + xh_log_priority = (flag ? ANDROID_LOG_DEBUG : ANDROID_LOG_WARN); +} + +void xh_core_enable_sigsegv_protection(int flag) +{ + xh_core_sigsegv_enable = (flag ? 1 : 0); +} diff --git a/module/src/main/cpp/external/xhook/xh_core.h b/module/src/main/cpp/external/xhook/xh_core.h new file mode 100644 index 0000000..3508794 --- /dev/null +++ b/module/src/main/cpp/external/xhook/xh_core.h @@ -0,0 +1,48 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2018-04-11. + +#ifndef XH_CORE_H +#define XH_CORE_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +int xh_core_register(const char *pathname_regex_str, const char *symbol, + void *new_func, void **old_func); + +int xh_core_ignore(const char *pathname_regex_str, const char *symbol); + +int xh_core_refresh(int async); + +void xh_core_clear(); + +void xh_core_enable_debug(int flag); + +void xh_core_enable_sigsegv_protection(int flag); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/module/src/main/cpp/external/xhook/xh_elf.c b/module/src/main/cpp/external/xhook/xh_elf.c new file mode 100644 index 0000000..286ed87 --- /dev/null +++ b/module/src/main/cpp/external/xhook/xh_elf.c @@ -0,0 +1,1042 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2018-04-11. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xh_errno.h" +#include "xh_log.h" +#include "xh_util.h" +#include "xh_elf.h" + +#define XH_ELF_DEBUG 0 + +#ifndef EI_ABIVERSION +#define EI_ABIVERSION 8 +#endif + +#if defined(__arm__) +#define XH_ELF_R_GENERIC_JUMP_SLOT R_ARM_JUMP_SLOT //.rel.plt +#define XH_ELF_R_GENERIC_GLOB_DAT R_ARM_GLOB_DAT //.rel.dyn +#define XH_ELF_R_GENERIC_ABS R_ARM_ABS32 //.rel.dyn +#elif defined(__aarch64__) +#define XH_ELF_R_GENERIC_JUMP_SLOT R_AARCH64_JUMP_SLOT +#define XH_ELF_R_GENERIC_GLOB_DAT R_AARCH64_GLOB_DAT +#define XH_ELF_R_GENERIC_ABS R_AARCH64_ABS64 +#elif defined(__i386__) +#define XH_ELF_R_GENERIC_JUMP_SLOT R_386_JMP_SLOT +#define XH_ELF_R_GENERIC_GLOB_DAT R_386_GLOB_DAT +#define XH_ELF_R_GENERIC_ABS R_386_32 +#elif defined(__x86_64__) +#define XH_ELF_R_GENERIC_JUMP_SLOT R_X86_64_JUMP_SLOT +#define XH_ELF_R_GENERIC_GLOB_DAT R_X86_64_GLOB_DAT +#define XH_ELF_R_GENERIC_ABS R_X86_64_64 +#endif + +#if defined(__LP64__) +#define XH_ELF_R_SYM(info) ELF64_R_SYM(info) +#define XH_ELF_R_TYPE(info) ELF64_R_TYPE(info) +#else +#define XH_ELF_R_SYM(info) ELF32_R_SYM(info) +#define XH_ELF_R_TYPE(info) ELF32_R_TYPE(info) +#endif + +//iterator for plain PLT +typedef struct +{ + uint8_t *cur; + uint8_t *end; + int is_use_rela; +} xh_elf_plain_reloc_iterator_t; + +static void xh_elf_plain_reloc_iterator_init(xh_elf_plain_reloc_iterator_t *self, + ElfW(Addr) rel, ElfW(Word) rel_sz, int is_use_rela) +{ + self->cur = (uint8_t *)rel; + self->end = self->cur + rel_sz; + self->is_use_rela = is_use_rela; +} + +static void *xh_elf_plain_reloc_iterator_next(xh_elf_plain_reloc_iterator_t *self) +{ + if(self->cur >= self->end) return NULL; + + self->cur += (self->is_use_rela ? sizeof(ElfW(Rela)) : sizeof(ElfW(Rel))); + return (void *)(self->cur); +} + +//sleb128 decoder +typedef struct +{ + uint8_t *cur; + uint8_t *end; +} xh_elf_sleb128_decoder_t; + +static void xh_elf_sleb128_decoder_init(xh_elf_sleb128_decoder_t *self, + ElfW(Addr) rel, ElfW(Word) rel_sz) +{ + self->cur = (uint8_t *)rel; + self->end = self->cur + rel_sz; +} + +static int xh_elf_sleb128_decoder_next(xh_elf_sleb128_decoder_t *self, size_t *ret) +{ + size_t value = 0; + static const size_t size = 8 * sizeof(value); + size_t shift = 0; + uint8_t byte; + + do + { + if(self->cur >= self->end) + return XH_ERRNO_FORMAT; + + byte = *(self->cur)++; + value |= ((size_t)(byte & 127) << shift); + shift += 7; + } while(byte & 128); + + if(shift < size && (byte & 64)) + { + value |= -((size_t)(1) << shift); + } + + *ret = value; + return 0; +} + +//iterator for sleb128 decoded packed PLT +typedef struct +{ + xh_elf_sleb128_decoder_t decoder; + size_t relocation_count; + size_t group_size; + size_t group_flags; + size_t group_r_offset_delta; + size_t relocation_index; + size_t relocation_group_index; + ElfW(Rela) rela; + ElfW(Rel) rel; + ElfW(Addr) r_offset; + size_t r_info; + ssize_t r_addend; + int is_use_rela; +} xh_elf_packed_reloc_iterator_t; + +const size_t RELOCATION_GROUPED_BY_INFO_FLAG = 1; +const size_t RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG = 2; +const size_t RELOCATION_GROUPED_BY_ADDEND_FLAG = 4; +const size_t RELOCATION_GROUP_HAS_ADDEND_FLAG = 8; + +static int xh_elf_packed_reloc_iterator_init(xh_elf_packed_reloc_iterator_t *self, + ElfW(Addr) rel, ElfW(Word) rel_sz, int is_use_rela) +{ + int r; + + memset(self, 0, sizeof(xh_elf_packed_reloc_iterator_t)); + xh_elf_sleb128_decoder_init(&(self->decoder), rel, rel_sz); + self->is_use_rela = is_use_rela; + + if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->relocation_count)))) return r; + if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), (size_t *)&(self->r_offset)))) return r; + return 0; +} + +static int xh_elf_packed_reloc_iterator_read_group_fields(xh_elf_packed_reloc_iterator_t *self) +{ + int r; + size_t val; + + if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->group_size)))) return r; + if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->group_flags)))) return r; + + if(self->group_flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) + if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &(self->group_r_offset_delta)))) return r; + + if(self->group_flags & RELOCATION_GROUPED_BY_INFO_FLAG) + if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), (size_t *)&(self->r_info)))) return r; + + if((self->group_flags & RELOCATION_GROUP_HAS_ADDEND_FLAG) && + (self->group_flags & RELOCATION_GROUPED_BY_ADDEND_FLAG)) + { + if(0 == self->is_use_rela) + { + XH_LOG_ERROR("unexpected r_addend in android.rel section"); + return XH_ERRNO_FORMAT; + } + if(0 != (r = xh_elf_sleb128_decoder_next(&(self->decoder), &val))) return r; + self->r_addend += (ssize_t)val; + } + else if(0 == (self->group_flags & RELOCATION_GROUP_HAS_ADDEND_FLAG)) + { + self->r_addend = 0; + } + + self->relocation_group_index = 0; + return 0; +} + +static void *xh_elf_packed_reloc_iterator_next(xh_elf_packed_reloc_iterator_t *self) +{ + size_t val; + + if(self->relocation_index >= self->relocation_count) return NULL; + + if(self->relocation_group_index == self->group_size) + { + if(0 != xh_elf_packed_reloc_iterator_read_group_fields(self)) return NULL; + } + + if(self->group_flags & RELOCATION_GROUPED_BY_OFFSET_DELTA_FLAG) + { + self->r_offset += self->group_r_offset_delta; + } + else + { + if(0 != xh_elf_sleb128_decoder_next(&(self->decoder), &val)) return NULL; + self->r_offset += val; + } + + if(0 == (self->group_flags & RELOCATION_GROUPED_BY_INFO_FLAG)) + if(0 != xh_elf_sleb128_decoder_next(&(self->decoder), &(self->r_info))) return NULL; + + if(self->is_use_rela && + (self->group_flags & RELOCATION_GROUP_HAS_ADDEND_FLAG) && + (0 == (self->group_flags & RELOCATION_GROUPED_BY_ADDEND_FLAG))) + { + if(0 != xh_elf_sleb128_decoder_next(&(self->decoder), &val)) return NULL; + self->r_addend += (ssize_t)val; + } + + self->relocation_index++; + self->relocation_group_index++; + + if(self->is_use_rela) + { + self->rela.r_offset = self->r_offset; + self->rela.r_info = self->r_info; + self->rela.r_addend = self->r_addend; + return (void *)(&(self->rela)); + } + else + { + self->rel.r_offset = self->r_offset; + self->rel.r_info = self->r_info; + return (void *)(&(self->rel)); + } +} + +//ELF header checker +int xh_elf_check_elfheader(uintptr_t base_addr) +{ + ElfW(Ehdr) *ehdr = (ElfW(Ehdr) *)base_addr; + + //check magic + if(0 != memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) return XH_ERRNO_FORMAT; + + //check class (64/32) +#if defined(__LP64__) + if(ELFCLASS64 != ehdr->e_ident[EI_CLASS]) return XH_ERRNO_FORMAT; +#else + if(ELFCLASS32 != ehdr->e_ident[EI_CLASS]) return XH_ERRNO_FORMAT; +#endif + + //check endian (little/big) + if(ELFDATA2LSB != ehdr->e_ident[EI_DATA]) return XH_ERRNO_FORMAT; + + //check version + if(EV_CURRENT != ehdr->e_ident[EI_VERSION]) return XH_ERRNO_FORMAT; + + //check type + if(ET_EXEC != ehdr->e_type && ET_DYN != ehdr->e_type) return XH_ERRNO_FORMAT; + + //check machine +#if defined(__arm__) + if(EM_ARM != ehdr->e_machine) return XH_ERRNO_FORMAT; +#elif defined(__aarch64__) + if(EM_AARCH64 != ehdr->e_machine) return XH_ERRNO_FORMAT; +#elif defined(__i386__) + if(EM_386 != ehdr->e_machine) return XH_ERRNO_FORMAT; +#elif defined(__x86_64__) + if(EM_X86_64 != ehdr->e_machine) return XH_ERRNO_FORMAT; +#else + return XH_ERRNO_FORMAT; +#endif + + //check version + if(EV_CURRENT != ehdr->e_version) return XH_ERRNO_FORMAT; + + return 0; +} + +//ELF hash func +static uint32_t xh_elf_hash(const uint8_t *name) +{ + uint32_t h = 0, g; + + while (*name) { + h = (h << 4) + *name++; + g = h & 0xf0000000; + h ^= g; + h ^= g >> 24; + } + + return h; +} + +//GNU hash func +static uint32_t xh_elf_gnu_hash(const uint8_t *name) +{ + uint32_t h = 5381; + + while(*name != 0) + { + h += (h << 5) + *name++; + } + return h; +} + +static ElfW(Phdr) *xh_elf_get_first_segment_by_type(xh_elf_t *self, ElfW(Word) type) +{ + ElfW(Phdr) *phdr; + + for(phdr = self->phdr; phdr < self->phdr + self->ehdr->e_phnum; phdr++) + { + if(phdr->p_type == type) + { + return phdr; + } + } + return NULL; +} + +static ElfW(Phdr) *xh_elf_get_first_segment_by_type_offset(xh_elf_t *self, ElfW(Word) type, ElfW(Off) offset) +{ + ElfW(Phdr) *phdr; + + for(phdr = self->phdr; phdr < self->phdr + self->ehdr->e_phnum; phdr++) + { + if(phdr->p_type == type && phdr->p_offset == offset) + { + return phdr; + } + } + return NULL; +} + +static int xh_elf_hash_lookup(xh_elf_t *self, const char *symbol, uint32_t *symidx) +{ + uint32_t hash = xh_elf_hash((uint8_t *)symbol); + const char *symbol_cur; + uint32_t i; + + for(i = self->bucket[hash % self->bucket_cnt]; 0 != i; i = self->chain[i]) + { + symbol_cur = self->strtab + self->symtab[i].st_name; + + if(0 == strcmp(symbol, symbol_cur)) + { + *symidx = i; + XH_LOG_INFO("found %s at symidx: %u (ELF_HASH)\n", symbol, *symidx); + return 0; + } + } + + return XH_ERRNO_NOTFND; +} + +static int xh_elf_gnu_hash_lookup_def(xh_elf_t *self, const char *symbol, uint32_t *symidx) +{ + uint32_t hash = xh_elf_gnu_hash((uint8_t *)symbol); + + static uint32_t elfclass_bits = sizeof(ElfW(Addr)) * 8; + size_t word = self->bloom[(hash / elfclass_bits) % self->bloom_sz]; + size_t mask = 0 + | (size_t)1 << (hash % elfclass_bits) + | (size_t)1 << ((hash >> self->bloom_shift) % elfclass_bits); + + //if at least one bit is not set, this symbol is surely missing + if((word & mask) != mask) return XH_ERRNO_NOTFND; + + //ignore STN_UNDEF + uint32_t i = self->bucket[hash % self->bucket_cnt]; + if(i < self->symoffset) return XH_ERRNO_NOTFND; + + //loop through the chain + while(1) + { + const char *symname = self->strtab + self->symtab[i].st_name; + const uint32_t symhash = self->chain[i - self->symoffset]; + + if((hash | (uint32_t)1) == (symhash | (uint32_t)1) && 0 == strcmp(symbol, symname)) + { + *symidx = i; + XH_LOG_INFO("found %s at symidx: %u (GNU_HASH DEF)\n", symbol, *symidx); + return 0; + } + + //chain ends with an element with the lowest bit set to 1 + if(symhash & (uint32_t)1) break; + + i++; + } + + return XH_ERRNO_NOTFND; +} + +static int xh_elf_gnu_hash_lookup_undef(xh_elf_t *self, const char *symbol, uint32_t *symidx) +{ + uint32_t i; + + for(i = 0; i < self->symoffset; i++) + { + const char *symname = self->strtab + self->symtab[i].st_name; + if(0 == strcmp(symname, symbol)) + { + *symidx = i; + XH_LOG_INFO("found %s at symidx: %u (GNU_HASH UNDEF)\n", symbol, *symidx); + return 0; + } + } + return XH_ERRNO_NOTFND; +} + +static int xh_elf_gnu_hash_lookup(xh_elf_t *self, const char *symbol, uint32_t *symidx) +{ + if(0 == xh_elf_gnu_hash_lookup_def(self, symbol, symidx)) return 0; + if(0 == xh_elf_gnu_hash_lookup_undef(self, symbol, symidx)) return 0; + return XH_ERRNO_NOTFND; +} + +static int xh_elf_find_symidx_by_name(xh_elf_t *self, const char *symbol, uint32_t *symidx) +{ + if(self->is_use_gnu_hash) + return xh_elf_gnu_hash_lookup(self, symbol, symidx); + else + return xh_elf_hash_lookup(self, symbol, symidx); +} + +static int xh_elf_replace_function(xh_elf_t *self, const char *symbol, ElfW(Addr) addr, void *new_func, void **old_func) +{ + void *old_addr; + unsigned int old_prot = 0; + unsigned int need_prot = PROT_READ | PROT_WRITE; + int r; + + //already replaced? + //here we assume that we always have read permission, is this a problem? + if(*(void **)addr == new_func) return 0; + + //get old prot + if(0 != (r = xh_util_get_addr_protect(addr, self->pathname, &old_prot))) + { + XH_LOG_ERROR("get addr prot failed. ret: %d", r); + return r; + } + + if(old_prot != need_prot) + { + //set new prot + if(0 != (r = xh_util_set_addr_protect(addr, need_prot))) + { + XH_LOG_ERROR("set addr prot failed. ret: %d", r); + return r; + } + } + + //save old func + old_addr = *(void **)addr; + if(NULL != old_func) *old_func = old_addr; + + //replace func + *(void **)addr = new_func; //segmentation fault sometimes + + if(old_prot != need_prot) + { + //restore the old prot + if(0 != (r = xh_util_set_addr_protect(addr, old_prot))) + { + XH_LOG_WARN("restore addr prot failed. ret: %d", r); + } + } + + //clear cache + xh_util_flush_instruction_cache(addr); + + XH_LOG_INFO("XH_HK_OK %p: %p -> %p %s %s\n", (void *)addr, old_addr, new_func, symbol, self->pathname); + return 0; +} + +static int xh_elf_check(xh_elf_t *self) +{ + if(0 == self->base_addr) + { + XH_LOG_ERROR("base_addr == 0\n"); + return 1; + } + if(0 == self->bias_addr) + { + XH_LOG_ERROR("bias_addr == 0\n"); + return 1; + } + if(NULL == self->ehdr) + { + XH_LOG_ERROR("ehdr == NULL\n"); + return 1; + } + if(NULL == self->phdr) + { + XH_LOG_ERROR("phdr == NULL\n"); + return 1; + } + if(NULL == self->strtab) + { + XH_LOG_ERROR("strtab == NULL\n"); + return 1; + } + if(NULL == self->symtab) + { + XH_LOG_ERROR("symtab == NULL\n"); + return 1; + } + if(NULL == self->bucket) + { + XH_LOG_ERROR("bucket == NULL\n"); + return 1; + } + if(NULL == self->chain) + { + XH_LOG_ERROR("chain == NULL\n"); + return 1; + } + if(1 == self->is_use_gnu_hash && NULL == self->bloom) + { + XH_LOG_ERROR("bloom == NULL\n"); + return 1; + } + + return 0; +} + +#if XH_ELF_DEBUG + +static void xh_elf_dump_elfheader(xh_elf_t *self) +{ + static char alpha_tab[17] = "0123456789ABCDEF"; + int i; + uint8_t ch; + char buff[EI_NIDENT * 3 + 1]; + + for(i = 0; i < EI_NIDENT; i++) + { + ch = self->ehdr->e_ident[i]; + buff[i * 3 + 0] = alpha_tab[(int)((ch >> 4) & 0x0F)]; + buff[i * 3 + 1] = alpha_tab[(int)(ch & 0x0F)]; + buff[i * 3 + 2] = ' '; + } + buff[EI_NIDENT * 3] = '\0'; + + XH_LOG_DEBUG("Elf Header:\n"); + XH_LOG_DEBUG(" Magic: %s\n", buff); + XH_LOG_DEBUG(" Class: %#x\n", self->ehdr->e_ident[EI_CLASS]); + XH_LOG_DEBUG(" Data: %#x\n", self->ehdr->e_ident[EI_DATA]); + XH_LOG_DEBUG(" Version: %#x\n", self->ehdr->e_ident[EI_VERSION]); + XH_LOG_DEBUG(" OS/ABI: %#x\n", self->ehdr->e_ident[EI_OSABI]); + XH_LOG_DEBUG(" ABI Version: %#x\n", self->ehdr->e_ident[EI_ABIVERSION]); + XH_LOG_DEBUG(" Type: %#x\n", self->ehdr->e_type); + XH_LOG_DEBUG(" Machine: %#x\n", self->ehdr->e_machine); + XH_LOG_DEBUG(" Version: %#x\n", self->ehdr->e_version); + XH_LOG_DEBUG(" Entry point address: %"XH_UTIL_FMT_X"\n", self->ehdr->e_entry); + XH_LOG_DEBUG(" Start of program headers: %"XH_UTIL_FMT_X" (bytes into file)\n", self->ehdr->e_phoff); + XH_LOG_DEBUG(" Start of section headers: %"XH_UTIL_FMT_X" (bytes into file)\n", self->ehdr->e_shoff); + XH_LOG_DEBUG(" Flags: %#x\n", self->ehdr->e_flags); + XH_LOG_DEBUG(" Size of this header: %u (bytes)\n", self->ehdr->e_ehsize); + XH_LOG_DEBUG(" Size of program headers: %u (bytes)\n", self->ehdr->e_phentsize); + XH_LOG_DEBUG(" Number of program headers: %u\n", self->ehdr->e_phnum); + XH_LOG_DEBUG(" Size of section headers: %u (bytes)\n", self->ehdr->e_shentsize); + XH_LOG_DEBUG(" Number of section headers: %u\n", self->ehdr->e_shnum); + XH_LOG_DEBUG(" Section header string table index: %u\n", self->ehdr->e_shstrndx); +} + +static void xh_elf_dump_programheader(xh_elf_t *self) +{ + ElfW(Phdr) *phdr = self->phdr; + size_t i; + + XH_LOG_DEBUG("Program Headers:\n"); + XH_LOG_DEBUG(" %-8s " \ + "%-"XH_UTIL_FMT_FIXED_S" " \ + "%-"XH_UTIL_FMT_FIXED_S" " \ + "%-"XH_UTIL_FMT_FIXED_S" " \ + "%-"XH_UTIL_FMT_FIXED_S" " \ + "%-"XH_UTIL_FMT_FIXED_S" " \ + "%-8s " \ + "%-s\n", + "Type", + "Offset", + "VirtAddr", + "PhysAddr", + "FileSiz", + "MemSiz", + "Flg", + "Align"); + for(i = 0; i < self->ehdr->e_phnum; i++, phdr++) + { + XH_LOG_DEBUG(" %-8x " \ + "%."XH_UTIL_FMT_FIXED_X" " \ + "%."XH_UTIL_FMT_FIXED_X" " \ + "%."XH_UTIL_FMT_FIXED_X" " \ + "%."XH_UTIL_FMT_FIXED_X" " \ + "%."XH_UTIL_FMT_FIXED_X" " \ + "%-8x " \ + "%"XH_UTIL_FMT_X"\n", + phdr->p_type, + phdr->p_offset, + phdr->p_vaddr, + phdr->p_paddr, + phdr->p_filesz, + phdr->p_memsz, + phdr->p_flags, + phdr->p_align); + } +} + +static void xh_elf_dump_dynamic(xh_elf_t *self) +{ + ElfW(Dyn) *dyn = self->dyn; + size_t dyn_cnt = (self->dyn_sz / sizeof(ElfW(Dyn))); + size_t i; + + XH_LOG_DEBUG("Dynamic section contains %zu entries:\n", dyn_cnt); + XH_LOG_DEBUG(" %-"XH_UTIL_FMT_FIXED_S" " \ + "%s\n", + "Tag", + "Val"); + for(i = 0; i < dyn_cnt; i++, dyn++) + { + XH_LOG_DEBUG(" %-"XH_UTIL_FMT_FIXED_X" " \ + "%-"XH_UTIL_FMT_X"\n", + dyn->d_tag, + dyn->d_un.d_val); + } +} + +static void xh_elf_dump_rel(xh_elf_t *self, const char *type, ElfW(Addr) rel_addr, ElfW(Word) rel_sz) +{ + ElfW(Rela) *rela; + ElfW(Rel) *rel; + ElfW(Word) cnt; + ElfW(Word) i; + ElfW(Sym) *sym; + + if(self->is_use_rela) + { + rela = (ElfW(Rela) *)(rel_addr); + cnt = rel_sz / sizeof(ElfW(Rela)); + } + else + { + rel = (ElfW(Rel) *)(rel_addr); + cnt = rel_sz / sizeof(ElfW(Rel)); + } + + XH_LOG_DEBUG("Relocation section '.rel%s%s' contains %u entries:\n", + (self->is_use_rela ? "a" : ""), type, cnt); + XH_LOG_DEBUG(" %-"XH_UTIL_FMT_FIXED_S" " \ + "%-"XH_UTIL_FMT_FIXED_S" " \ + "%-8s " \ + "%-8s " \ + "%-8s " \ + "%s\n", + "Offset", + "Info", + "Type", + "Sym.Idx", + "Sym.Val", + "Sym.Name"); + const char *fmt = " %."XH_UTIL_FMT_FIXED_X" " \ + "%."XH_UTIL_FMT_FIXED_X" " \ + "%.8x " \ + "%.8u " \ + "%.8x " \ + "%s\n"; + for(i = 0; i < cnt; i++) + { + if(self->is_use_rela) + { + sym = &(self->symtab[XH_ELF_R_SYM(rela[i].r_info)]); + XH_LOG_DEBUG(fmt, + rela[i].r_offset, + rela[i].r_info, + XH_ELF_R_TYPE(rela[i].r_info), + XH_ELF_R_SYM(rela[i].r_info), + sym->st_value, + self->strtab + sym->st_name); + } + else + { + sym = &(self->symtab[XH_ELF_R_SYM(rel[i].r_info)]); + XH_LOG_DEBUG(fmt, + rel[i].r_offset, + rel[i].r_info, + XH_ELF_R_TYPE(rel[i].r_info), + XH_ELF_R_SYM(rel[i].r_info), + sym->st_value, + self->strtab + sym->st_name); + } + } +} + +static void xh_elf_dump_symtab(xh_elf_t *self) +{ + if(self->is_use_gnu_hash) return; + + ElfW(Word) symtab_cnt = self->chain_cnt; + ElfW(Word) i; + + XH_LOG_DEBUG("Symbol table '.dynsym' contains %u entries:\n", symtab_cnt); + XH_LOG_DEBUG(" %-8s " \ + "%-"XH_UTIL_FMT_FIXED_S" " \ + "%s\n", + "Idx", + "Value", + "Name"); + for(i = 0; i < symtab_cnt; i++) + { + XH_LOG_DEBUG(" %-8u " \ + "%."XH_UTIL_FMT_FIXED_X" " \ + "%s\n", + i, + self->symtab[i].st_value, + self->strtab + self->symtab[i].st_name); + } +} + +static void xh_elf_dump(xh_elf_t *self) +{ + if(xh_log_priority < ANDROID_LOG_DEBUG) return; + + XH_LOG_DEBUG("Elf Pathname: %s\n", self->pathname); + XH_LOG_DEBUG("Elf bias addr: %p\n", (void *)self->bias_addr); + xh_elf_dump_elfheader(self); + xh_elf_dump_programheader(self); + xh_elf_dump_dynamic(self); + xh_elf_dump_rel(self, ".plt", self->relplt, self->relplt_sz); + xh_elf_dump_rel(self, ".dyn", self->reldyn, self->reldyn_sz); + xh_elf_dump_symtab(self); +} + +#endif + +int xh_elf_init(xh_elf_t *self, uintptr_t base_addr, const char *pathname) +{ + if(0 == base_addr || NULL == pathname) return XH_ERRNO_INVAL; + + //always reset + memset(self, 0, sizeof(xh_elf_t)); + + self->pathname = pathname; + self->base_addr = (ElfW(Addr))base_addr; + self->ehdr = (ElfW(Ehdr) *)base_addr; + self->phdr = (ElfW(Phdr) *)(base_addr + self->ehdr->e_phoff); //segmentation fault sometimes + + //find the first load-segment with offset 0 + ElfW(Phdr) *phdr0 = xh_elf_get_first_segment_by_type_offset(self, PT_LOAD, 0); + if(NULL == phdr0) + { + XH_LOG_ERROR("Can NOT found the first load segment. %s", pathname); + return XH_ERRNO_FORMAT; + } + +#if XH_ELF_DEBUG + if(0 != phdr0->p_vaddr) + XH_LOG_DEBUG("first load-segment vaddr NOT 0 (vaddr: %p). %s", + (void *)(phdr0->p_vaddr), pathname); +#endif + + //save load bias addr + if(self->base_addr < phdr0->p_vaddr) return XH_ERRNO_FORMAT; + self->bias_addr = self->base_addr - phdr0->p_vaddr; + + //find dynamic-segment + ElfW(Phdr) *dhdr = xh_elf_get_first_segment_by_type(self, PT_DYNAMIC); + if(NULL == dhdr) + { + XH_LOG_ERROR("Can NOT found dynamic segment. %s", pathname); + return XH_ERRNO_FORMAT; + } + + //parse dynamic-segment + self->dyn = (ElfW(Dyn) *)(self->bias_addr + dhdr->p_vaddr); + self->dyn_sz = dhdr->p_memsz; + ElfW(Dyn) *dyn = self->dyn; + ElfW(Dyn) *dyn_end = self->dyn + (self->dyn_sz / sizeof(ElfW(Dyn))); + uint32_t *raw; + for(; dyn < dyn_end; dyn++) + { + switch(dyn->d_tag) //segmentation fault sometimes + { + case DT_NULL: + //the end of the dynamic-section + dyn = dyn_end; + break; + case DT_STRTAB: + { + self->strtab = (const char *)(self->bias_addr + dyn->d_un.d_ptr); + if((ElfW(Addr))(self->strtab) < self->base_addr) return XH_ERRNO_FORMAT; + break; + } + case DT_SYMTAB: + { + self->symtab = (ElfW(Sym) *)(self->bias_addr + dyn->d_un.d_ptr); + if((ElfW(Addr))(self->symtab) < self->base_addr) return XH_ERRNO_FORMAT; + break; + } + case DT_PLTREL: + //use rel or rela? + self->is_use_rela = (dyn->d_un.d_val == DT_RELA ? 1 : 0); + break; + case DT_JMPREL: + { + self->relplt = (ElfW(Addr))(self->bias_addr + dyn->d_un.d_ptr); + if((ElfW(Addr))(self->relplt) < self->base_addr) return XH_ERRNO_FORMAT; + break; + } + case DT_PLTRELSZ: + self->relplt_sz = dyn->d_un.d_val; + break; + case DT_REL: + case DT_RELA: + { + self->reldyn = (ElfW(Addr))(self->bias_addr + dyn->d_un.d_ptr); + if((ElfW(Addr))(self->reldyn) < self->base_addr) return XH_ERRNO_FORMAT; + break; + } + case DT_RELSZ: + case DT_RELASZ: + self->reldyn_sz = dyn->d_un.d_val; + break; + case DT_ANDROID_REL: + case DT_ANDROID_RELA: + { + self->relandroid = (ElfW(Addr))(self->bias_addr + dyn->d_un.d_ptr); + if((ElfW(Addr))(self->relandroid) < self->base_addr) return XH_ERRNO_FORMAT; + break; + } + case DT_ANDROID_RELSZ: + case DT_ANDROID_RELASZ: + self->relandroid_sz = dyn->d_un.d_val; + break; + case DT_HASH: + { + raw = (uint32_t *)(self->bias_addr + dyn->d_un.d_ptr); + if((ElfW(Addr))raw < self->base_addr) return XH_ERRNO_FORMAT; + self->bucket_cnt = raw[0]; + self->chain_cnt = raw[1]; + self->bucket = &raw[2]; + self->chain = &(self->bucket[self->bucket_cnt]); + break; + } + case DT_GNU_HASH: + { + raw = (uint32_t *)(self->bias_addr + dyn->d_un.d_ptr); + if((ElfW(Addr))raw < self->base_addr) return XH_ERRNO_FORMAT; + self->bucket_cnt = raw[0]; + self->symoffset = raw[1]; + self->bloom_sz = raw[2]; + self->bloom_shift = raw[3]; + self->bloom = (ElfW(Addr) *)(&raw[4]); + self->bucket = (uint32_t *)(&(self->bloom[self->bloom_sz])); + self->chain = (uint32_t *)(&(self->bucket[self->bucket_cnt])); + self->is_use_gnu_hash = 1; + break; + } + default: + break; + } + } + + //check android rel/rela + if(0 != self->relandroid) + { + const char *rel = (const char *)self->relandroid; + if(self->relandroid_sz < 4 || + rel[0] != 'A' || + rel[1] != 'P' || + rel[2] != 'S' || + rel[3] != '2') + { + XH_LOG_ERROR("android rel/rela format error\n"); + return XH_ERRNO_FORMAT; + } + + self->relandroid += 4; + self->relandroid_sz -= 4; + } + + //check elf info + if(0 != xh_elf_check(self)) + { + XH_LOG_ERROR("elf init check failed. %s", pathname); + return XH_ERRNO_FORMAT; + } + +#if XH_ELF_DEBUG + xh_elf_dump(self); +#endif + + XH_LOG_INFO("init OK: %s (%s %s PLT:%u DYN:%u ANDROID:%u)\n", self->pathname, + self->is_use_rela ? "RELA" : "REL", + self->is_use_gnu_hash ? "GNU_HASH" : "ELF_HASH", + self->relplt_sz, self->reldyn_sz, self->relandroid_sz); + + return 0; +} + +static int xh_elf_find_and_replace_func(xh_elf_t *self, const char *section, + int is_plt, const char *symbol, + void *new_func, void **old_func, + uint32_t symidx, void *rel_common, + int *found) +{ + ElfW(Rela) *rela; + ElfW(Rel) *rel; + ElfW(Addr) r_offset; + size_t r_info; + size_t r_sym; + size_t r_type; + ElfW(Addr) addr; + int r; + + if(NULL != found) *found = 0; + + if(self->is_use_rela) + { + rela = (ElfW(Rela) *)rel_common; + r_info = rela->r_info; + r_offset = rela->r_offset; + } + else + { + rel = (ElfW(Rel) *)rel_common; + r_info = rel->r_info; + r_offset = rel->r_offset; + } + + //check sym + r_sym = XH_ELF_R_SYM(r_info); + if(r_sym != symidx) return 0; + + //check type + r_type = XH_ELF_R_TYPE(r_info); + if(is_plt && r_type != XH_ELF_R_GENERIC_JUMP_SLOT) return 0; + if(!is_plt && (r_type != XH_ELF_R_GENERIC_GLOB_DAT && r_type != XH_ELF_R_GENERIC_ABS)) return 0; + + //we found it + XH_LOG_INFO("found %s at %s offset: %p\n", symbol, section, (void *)r_offset); + if(NULL != found) *found = 1; + + //do replace + addr = self->bias_addr + r_offset; + if(addr < self->base_addr) return XH_ERRNO_FORMAT; + if(0 != (r = xh_elf_replace_function(self, symbol, addr, new_func, old_func))) + { + XH_LOG_ERROR("replace function failed: %s at %s\n", symbol, section); + return r; + } + + return 0; +} + +int xh_elf_hook(xh_elf_t *self, const char *symbol, void *new_func, void **old_func) +{ + uint32_t symidx; + void *rel_common; + xh_elf_plain_reloc_iterator_t plain_iter; + xh_elf_packed_reloc_iterator_t packed_iter; + int found; + int r; + + if(NULL == self->pathname) + { + XH_LOG_ERROR("not inited\n"); + return XH_ERRNO_ELFINIT; //not inited? + } + + if(NULL == symbol || NULL == new_func) return XH_ERRNO_INVAL; + + XH_LOG_INFO("hooking %s in %s\n", symbol, self->pathname); + + //find symbol index by symbol name + if(0 != (r = xh_elf_find_symidx_by_name(self, symbol, &symidx))) return 0; + + //replace for .rel(a).plt + if(0 != self->relplt) + { + xh_elf_plain_reloc_iterator_init(&plain_iter, self->relplt, self->relplt_sz, self->is_use_rela); + while(NULL != (rel_common = xh_elf_plain_reloc_iterator_next(&plain_iter))) + { + if(0 != (r = xh_elf_find_and_replace_func(self, + (self->is_use_rela ? ".rela.plt" : ".rel.plt"), 1, + symbol, new_func, old_func, + symidx, rel_common, &found))) return r; + if(found) break; + } + } + + //replace for .rel(a).dyn + if(0 != self->reldyn) + { + xh_elf_plain_reloc_iterator_init(&plain_iter, self->reldyn, self->reldyn_sz, self->is_use_rela); + while(NULL != (rel_common = xh_elf_plain_reloc_iterator_next(&plain_iter))) + { + if(0 != (r = xh_elf_find_and_replace_func(self, + (self->is_use_rela ? ".rela.dyn" : ".rel.dyn"), 0, + symbol, new_func, old_func, + symidx, rel_common, NULL))) return r; + } + } + + //replace for .rel(a).android + if(0 != self->relandroid) + { + xh_elf_packed_reloc_iterator_init(&packed_iter, self->relandroid, self->relandroid_sz, self->is_use_rela); + while(NULL != (rel_common = xh_elf_packed_reloc_iterator_next(&packed_iter))) + { + if(0 != (r = xh_elf_find_and_replace_func(self, + (self->is_use_rela ? ".rela.android" : ".rel.android"), 0, + symbol, new_func, old_func, + symidx, rel_common, NULL))) return r; + } + } + + return 0; +} diff --git a/module/src/main/cpp/external/xhook/xh_elf.h b/module/src/main/cpp/external/xhook/xh_elf.h new file mode 100644 index 0000000..1697dc4 --- /dev/null +++ b/module/src/main/cpp/external/xhook/xh_elf.h @@ -0,0 +1,85 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2018-04-11. + +#ifndef XH_ELF_H +#define XH_ELF_H 1 + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + const char *pathname; + + ElfW(Addr) base_addr; + ElfW(Addr) bias_addr; + + ElfW(Ehdr) *ehdr; + ElfW(Phdr) *phdr; + + ElfW(Dyn) *dyn; //.dynamic + ElfW(Word) dyn_sz; + + const char *strtab; //.dynstr (string-table) + ElfW(Sym) *symtab; //.dynsym (symbol-index to string-table's offset) + + ElfW(Addr) relplt; //.rel.plt or .rela.plt + ElfW(Word) relplt_sz; + + ElfW(Addr) reldyn; //.rel.dyn or .rela.dyn + ElfW(Word) reldyn_sz; + + ElfW(Addr) relandroid; //android compressed rel or rela + ElfW(Word) relandroid_sz; + + //for ELF hash + uint32_t *bucket; + uint32_t bucket_cnt; + uint32_t *chain; + uint32_t chain_cnt; //invalid for GNU hash + + //append for GNU hash + uint32_t symoffset; + ElfW(Addr) *bloom; + uint32_t bloom_sz; + uint32_t bloom_shift; + + int is_use_rela; + int is_use_gnu_hash; +} xh_elf_t; + +int xh_elf_init(xh_elf_t *self, uintptr_t base_addr, const char *pathname); +int xh_elf_hook(xh_elf_t *self, const char *symbol, void *new_func, void **old_func); + +int xh_elf_check_elfheader(uintptr_t base_addr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/module/src/main/cpp/external/xhook/xh_errno.h b/module/src/main/cpp/external/xhook/xh_errno.h new file mode 100644 index 0000000..e628cd7 --- /dev/null +++ b/module/src/main/cpp/external/xhook/xh_errno.h @@ -0,0 +1,37 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2018-04-11. + +#ifndef XH_ERRNO_H +#define XH_ERRNO_H 1 + +#define XH_ERRNO_UNKNOWN 1001 +#define XH_ERRNO_INVAL 1002 +#define XH_ERRNO_NOMEM 1003 +#define XH_ERRNO_REPEAT 1004 +#define XH_ERRNO_NOTFND 1005 +#define XH_ERRNO_BADMAPS 1006 +#define XH_ERRNO_FORMAT 1007 +#define XH_ERRNO_ELFINIT 1008 +#define XH_ERRNO_SEGVERR 1009 + +#endif diff --git a/module/src/main/cpp/external/xhook/xh_jni.c b/module/src/main/cpp/external/xhook/xh_jni.c new file mode 100644 index 0000000..f8ae223 --- /dev/null +++ b/module/src/main/cpp/external/xhook/xh_jni.c @@ -0,0 +1,59 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2018-04-11. + +#include +#include "xhook.h" + +#define JNI_API_DEF(f) Java_com_qiyi_xhook_NativeHandler_##f + +JNIEXPORT jint JNI_API_DEF(refresh)(JNIEnv *env, jobject obj, jboolean async) +{ + (void)env; + (void)obj; + + return xhook_refresh(async ? 1 : 0); +} + +JNIEXPORT void JNI_API_DEF(clear)(JNIEnv *env, jobject obj) +{ + (void)env; + (void)obj; + + xhook_clear(); +} + +JNIEXPORT void JNI_API_DEF(enableDebug)(JNIEnv *env, jobject obj, jboolean flag) +{ + (void)env; + (void)obj; + + xhook_enable_debug(flag ? 1 : 0); +} + +JNIEXPORT void JNI_API_DEF(enableSigSegvProtection)(JNIEnv *env, jobject obj, jboolean flag) +{ + (void)env; + (void)obj; + + xhook_enable_sigsegv_protection(flag ? 1 : 0); +} diff --git a/module/src/main/cpp/external/xhook/xh_log.c b/module/src/main/cpp/external/xhook/xh_log.c new file mode 100644 index 0000000..6cba947 --- /dev/null +++ b/module/src/main/cpp/external/xhook/xh_log.c @@ -0,0 +1,27 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2018-04-11. + +#include +#include "xh_log.h" + +android_LogPriority xh_log_priority = ANDROID_LOG_WARN; diff --git a/module/src/main/cpp/external/xhook/xh_log.h b/module/src/main/cpp/external/xhook/xh_log.h new file mode 100644 index 0000000..e108c4b --- /dev/null +++ b/module/src/main/cpp/external/xhook/xh_log.h @@ -0,0 +1,45 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2018-04-11. + +#ifndef XH_LOG_H +#define XH_LOG_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern android_LogPriority xh_log_priority; + +#define XH_LOG_TAG "xhook" +#define XH_LOG_DEBUG(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_DEBUG) __android_log_print(ANDROID_LOG_DEBUG, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0) +#define XH_LOG_INFO(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_INFO) __android_log_print(ANDROID_LOG_INFO, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0) +#define XH_LOG_WARN(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_WARN) __android_log_print(ANDROID_LOG_WARN, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0) +#define XH_LOG_ERROR(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_ERROR) __android_log_print(ANDROID_LOG_ERROR, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/module/src/main/cpp/external/xhook/xh_util.c b/module/src/main/cpp/external/xhook/xh_util.c new file mode 100644 index 0000000..0e2dca2 --- /dev/null +++ b/module/src/main/cpp/external/xhook/xh_util.c @@ -0,0 +1,121 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2018-04-11. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xh_util.h" +#include "xh_errno.h" +#include "xh_log.h" + +#define PAGE_START(addr) ((addr) & PAGE_MASK) +#define PAGE_END(addr) (PAGE_START(addr + sizeof(uintptr_t) - 1) + PAGE_SIZE) +#define PAGE_COVER(addr) (PAGE_END(addr) - PAGE_START(addr)) + +int xh_util_get_mem_protect(uintptr_t addr, size_t len, const char *pathname, unsigned int *prot) +{ + uintptr_t start_addr = addr; + uintptr_t end_addr = addr + len; + FILE *fp; + char line[512]; + uintptr_t start, end; + char perm[5]; + int load0 = 1; + int found_all = 0; + + *prot = 0; + + if(NULL == (fp = fopen("/proc/self/maps", "r"))) return XH_ERRNO_BADMAPS; + + while(fgets(line, sizeof(line), fp)) + { + if(NULL != pathname) + if(NULL == strstr(line, pathname)) continue; + + if(sscanf(line, "%"PRIxPTR"-%"PRIxPTR" %4s ", &start, &end, perm) != 3) continue; + + if(perm[3] != 'p') continue; + + if(start_addr >= start && start_addr < end) + { + if(load0) + { + //first load segment + if(perm[0] == 'r') *prot |= PROT_READ; + if(perm[1] == 'w') *prot |= PROT_WRITE; + if(perm[2] == 'x') *prot |= PROT_EXEC; + load0 = 0; + } + else + { + //others + if(perm[0] != 'r') *prot &= ~PROT_READ; + if(perm[1] != 'w') *prot &= ~PROT_WRITE; + if(perm[2] != 'x') *prot &= ~PROT_EXEC; + } + + if(end_addr <= end) + { + found_all = 1; + break; //finished + } + else + { + start_addr = end; //try to find the next load segment + } + } + } + + fclose(fp); + + if(!found_all) return XH_ERRNO_SEGVERR; + + return 0; +} + +int xh_util_get_addr_protect(uintptr_t addr, const char *pathname, unsigned int *prot) +{ + return xh_util_get_mem_protect(addr, sizeof(addr), pathname, prot); +} + +int xh_util_set_addr_protect(uintptr_t addr, unsigned int prot) +{ + if(0 != mprotect((void *)PAGE_START(addr), PAGE_COVER(addr), (int)prot)) + return 0 == errno ? XH_ERRNO_UNKNOWN : errno; + + return 0; +} + +void xh_util_flush_instruction_cache(uintptr_t addr) +{ + __builtin___clear_cache((void *)PAGE_START(addr), (void *)PAGE_END(addr)); +} diff --git a/module/src/main/cpp/external/xhook/xh_util.h b/module/src/main/cpp/external/xhook/xh_util.h new file mode 100644 index 0000000..b57f8dc --- /dev/null +++ b/module/src/main/cpp/external/xhook/xh_util.h @@ -0,0 +1,51 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2018-04-11. + +#ifndef XH_UTILS_H +#define XH_UTILS_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__LP64__) +#define XH_UTIL_FMT_LEN "16" +#define XH_UTIL_FMT_X "llx" +#else +#define XH_UTIL_FMT_LEN "8" +#define XH_UTIL_FMT_X "x" +#endif + +#define XH_UTIL_FMT_FIXED_X XH_UTIL_FMT_LEN XH_UTIL_FMT_X +#define XH_UTIL_FMT_FIXED_S XH_UTIL_FMT_LEN "s" + +int xh_util_get_mem_protect(uintptr_t addr, size_t len, const char *pathname, unsigned int *prot); +int xh_util_get_addr_protect(uintptr_t addr, const char *pathname, unsigned int *prot); +int xh_util_set_addr_protect(uintptr_t addr, unsigned int prot); +void xh_util_flush_instruction_cache(uintptr_t addr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/module/src/main/cpp/external/xhook/xh_version.c b/module/src/main/cpp/external/xhook/xh_version.c new file mode 100644 index 0000000..b237f16 --- /dev/null +++ b/module/src/main/cpp/external/xhook/xh_version.c @@ -0,0 +1,66 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2018-04-11. + +#include "xh_version.h" + +#define XH_VERSION_MAJOR 1 +#define XH_VERSION_MINOR 1 +#define XH_VERSION_EXTRA 9 + +#define XH_VERSION ((XH_VERSION_MAJOR << 16) | (XH_VERSION_MINOR << 8) | (XH_VERSION_EXTRA)) + +#define XH_VERSION_TO_STR_HELPER(x) #x +#define XH_VERSION_TO_STR(x) XH_VERSION_TO_STR_HELPER(x) + +#define XH_VERSION_STR XH_VERSION_TO_STR(XH_VERSION_MAJOR) "." \ + XH_VERSION_TO_STR(XH_VERSION_MINOR) "." \ + XH_VERSION_TO_STR(XH_VERSION_EXTRA) + +#if defined(__arm__) +#define XH_VERSION_ARCH "arm" +#elif defined(__aarch64__) +#define XH_VERSION_ARCH "aarch64" +#elif defined(__i386__) +#define XH_VERSION_ARCH "x86" +#elif defined(__x86_64__) +#define XH_VERSION_ARCH "x86_64" +#else +#define XH_VERSION_ARCH "unknown" +#endif + +#define XH_VERSION_STR_FULL "libxhook "XH_VERSION_STR" ("XH_VERSION_ARCH")" + +unsigned int xh_version() +{ + return XH_VERSION; +} + +const char *xh_version_str() +{ + return XH_VERSION_STR; +} + +const char *xh_version_str_full() +{ + return XH_VERSION_STR_FULL; +} diff --git a/module/src/main/cpp/external/xhook/xh_version.h b/module/src/main/cpp/external/xhook/xh_version.h new file mode 100644 index 0000000..b70b4f2 --- /dev/null +++ b/module/src/main/cpp/external/xhook/xh_version.h @@ -0,0 +1,41 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2018-04-11. + +#ifndef XH_VERSION_H +#define XH_VERSION_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned int xh_version(); + +const char *xh_version_str(); + +const char *xh_version_str_full(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/module/src/main/cpp/external/xhook/xhook.c b/module/src/main/cpp/external/xhook/xhook.c new file mode 100644 index 0000000..49dfc59 --- /dev/null +++ b/module/src/main/cpp/external/xhook/xhook.c @@ -0,0 +1,56 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2018-04-11. + +#include "xh_core.h" +#include "xhook.h" + +int xhook_register(const char *pathname_regex_str, const char *symbol, + void *new_func, void **old_func) +{ + return xh_core_register(pathname_regex_str, symbol, new_func, old_func); +} + +int xhook_ignore(const char *pathname_regex_str, const char *symbol) +{ + return xh_core_ignore(pathname_regex_str, symbol); +} + +int xhook_refresh(int async) +{ + return xh_core_refresh(async); +} + +void xhook_clear() +{ + return xh_core_clear(); +} + +void xhook_enable_debug(int flag) +{ + return xh_core_enable_debug(flag); +} + +void xhook_enable_sigsegv_protection(int flag) +{ + return xh_core_enable_sigsegv_protection(flag); +} diff --git a/module/src/main/cpp/external/xhook/xhook.h b/module/src/main/cpp/external/xhook/xhook.h new file mode 100644 index 0000000..93dd5b4 --- /dev/null +++ b/module/src/main/cpp/external/xhook/xhook.h @@ -0,0 +1,50 @@ +// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by caikelun on 2018-04-11. + +#ifndef XHOOK_H +#define XHOOK_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#define XHOOK_EXPORT __attribute__((visibility("default"))) + +int xhook_register(const char *pathname_regex_str, const char *symbol, + void *new_func, void **old_func) XHOOK_EXPORT; + +int xhook_ignore(const char *pathname_regex_str, const char *symbol) XHOOK_EXPORT; + +int xhook_refresh(int async) XHOOK_EXPORT; + +void xhook_clear() XHOOK_EXPORT; + +void xhook_enable_debug(int flag) XHOOK_EXPORT; + +void xhook_enable_sigsegv_protection(int flag) XHOOK_EXPORT; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/module/src/main/cpp/hook.cpp b/module/src/main/cpp/hook.cpp new file mode 100644 index 0000000..482a019 --- /dev/null +++ b/module/src/main/cpp/hook.cpp @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/riru.h" +#include "logging.h" + +#define XHOOK_REGISTER(NAME) \ + if (xhook_register(".*", #NAME, (void*) new_##NAME, (void **) &old_##NAME) == 0) { \ + if (riru_get_version() >= 8) { \ + void *f = riru_get_func(#NAME); \ + if (f != nullptr) \ + memcpy(&old_##NAME, &f, sizeof(void *)); \ + riru_set_func(#NAME, (void *) new_##NAME); \ + } \ + } else { \ + LOGE("failed to register hook " #NAME "."); \ + } + +#define NEW_FUNC_DEF(ret, func, ...) \ + static ret (*old_##func)(__VA_ARGS__); \ + static ret new_##func(__VA_ARGS__) + +NEW_FUNC_DEF(int, __system_property_get, const char *key, char *value) { + int res = old___system_property_get(key, value); + if (key) { + if (strcmp("ro.miui.ui.version.name", key) == 0) { + strcpy(value, "V9"); + //LOGI("system_property_get: %s -> %s", key, value); + } else if (strcmp("ro.miui.ui.version.code", key) == 0) { + strcpy(value, "7"); + //LOGI("system_property_get: %s -> %s", key, value); + } else if (strcmp("ro.miui.version.code_time", key) == 0) { + strcpy(value, "1527550858"); + //LOGI("system_property_get: %s -> %s", key, value); + } else if (strcmp("ro.miui.internal.storage", key) == 0) { + strcpy(value, "/sdcard/"); + //LOGI("system_property_get: %s -> %s", key, value); + } else if (strcmp("ro.product.manufacturer", key) == 0) { + strcpy(value, "Xiaomi"); + //LOGI("system_property_get: %s -> %s", key, value); + } else if (strcmp("ro.product.brand", key) == 0) { + strcpy(value, "Xiaomi"); + //LOGI("system_property_get: %s -> %s", key, value); + } else if (strcmp("ro.product.name", key) == 0) { + strcpy(value, "Xiaomi"); + //LOGI("system_property_get: %s -> %s", key, value); + } + + } + return res; +} + +NEW_FUNC_DEF(std::string, _ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_, const std::string &key, const std::string &default_value) { + std::string res = old__ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_(key, default_value); + + if (strcmp("ro.miui.ui.version.name", key.c_str()) == 0) { + res = "V9"; + //LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str()); + } else if (strcmp("ro.miui.ui.version.code", key.c_str()) == 0) { + res = "7"; + //LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str()); + } else if (strcmp("ro.miui.version.code_time", key.c_str()) == 0) { + res = "1527550858"; + //LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str()); + } else if (strcmp("ro.miui.internal.storage", key.c_str()) == 0) { + res = "/sdcard/"; + //LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str()); + } else if (strcmp("ro.product.manufacturer", key.c_str()) == 0) { + res = "Xiaomi"; + //LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str()); + } else if (strcmp("ro.product.brand", key.c_str()) == 0) { + res = "Xiaomi"; + //LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str()); + } else if (strcmp("ro.product.name", key.c_str()) == 0) { + res = "Xiaomi"; + //LOGI("android::base::GetProperty: %s -> %s", key.c_str(), res.c_str()); + } + return res; +} + +void install_hook(const char *package_name, int user) { + LOGI("install hook for %d:%s", user, package_name); + + XHOOK_REGISTER(__system_property_get); + + char sdk[PROP_VALUE_MAX + 1]; + if (__system_property_get("ro.build.version.sdk", sdk) > 0 && atoi(sdk) >= 28) { + XHOOK_REGISTER(_ZN7android4base11GetPropertyERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES9_); + } + + if (xhook_refresh(0) == 0) + xhook_clear(); + else + LOGE("failed to refresh hook"); +} \ No newline at end of file diff --git a/module/src/main/cpp/include/hook.h b/module/src/main/cpp/include/hook.h new file mode 100644 index 0000000..c922d85 --- /dev/null +++ b/module/src/main/cpp/include/hook.h @@ -0,0 +1,7 @@ +#ifndef HOOK_H +#define HOOK_H + +void install_hook(const char* package_name, int user); + + +#endif // HOOK_H diff --git a/module/src/main/cpp/include/logging.h b/module/src/main/cpp/include/logging.h new file mode 100644 index 0000000..d594a48 --- /dev/null +++ b/module/src/main/cpp/include/logging.h @@ -0,0 +1,22 @@ +#ifndef _LOGGING_H +#define _LOGGING_H + +#include "errno.h" +#include "android/log.h" + +#ifndef LOG_TAG +#define LOG_TAG "MiPushFakeForRiru" +#endif + +#ifdef DEBUG +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) +#else +#define LOGD(...) +#endif +#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +#define PLOGE(fmt, args...) LOGE(fmt " failed with %d: %s", ##args, errno, strerror(errno)) + +#endif // _LOGGING_H diff --git a/module/src/main/cpp/include/riru.h b/module/src/main/cpp/include/riru.h new file mode 100644 index 0000000..4c0ab3a --- /dev/null +++ b/module/src/main/cpp/include/riru.h @@ -0,0 +1,57 @@ +#ifndef RIRU_H +#define RIRU_H + +#ifdef __cplusplus +extern "C" { +#endif +__attribute__((visibility("default"))) void riru_set_module_name(const char *name); + +/** + * Get Riru version. + * + * @return Riru version + */ +int riru_get_version(); + +/* + * Get new_func address from last module which hook func. + * Use this as your old_func if you want to hook func. + * + * @param name a unique name + * @return new_func from last module or null + */ +void *riru_get_func(const char *name); + +/* + * Java native version of riru_get_func. + * + * @param className class name + * @param name method name + * @param signature method signature + * @return new_func address from last module or original address + */ +void *riru_get_native_method_func(const char *className, const char *name, const char *signature); + +/* + * Set new_func address for next module which wants to hook func. + * + * @param name a unique name + * @param func your new_func address + */ +void riru_set_func(const char *name, void *func); + +/* + * Java native method version of riru_set_func. + * + * @param className class name + * @param name method name + * @param signature method signature + * @param func your new_func address + */ +void riru_set_native_method_func(const char *className, const char *name, const char *signature, + void *func); +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/module/src/main/cpp/main.cpp b/module/src/main/cpp/main.cpp index e075b1f..dee44c2 100644 --- a/module/src/main/cpp/main.cpp +++ b/module/src/main/cpp/main.cpp @@ -1,7 +1,35 @@ #include #include +#include +#include +#include +#include +#include "logging.h" +#include "hook.h" + +#define CONFIG_PATH "/data/misc/riru/modules/mipush_fake" + +#define FAKE_CONFIGURATION_GLOBAL "/data/misc/riru/modules/mipush_fake/packages/ALL" // You can remove functions you don't need +static char package_name[256]; +static bool enable_hook; +static int uid; + +static std::vector globalPkgBlackList = {"com.google.android", + "de.robv.android.xposed.installer", + "com.xiaomi.xmsf", + "com.tencent.mm", + "top.trumeet.mipush"}; + + +bool isAppNeedHook(JNIEnv *pEnv, jstring pJstring); + +void injectBuild(JNIEnv *pEnv); + +static void appProcessPre(JNIEnv *env, jint _uid, jstring appDataDir, jstring packageName); + +static void appProcessPost(JNIEnv *env); extern "C" { #define EXPORT __attribute__((visibility("default"))) __attribute__((used)) @@ -12,11 +40,14 @@ EXPORT void nativeForkAndSpecializePre( jstring *instructionSet, jstring *appDataDir, jstring *packageName, jobjectArray *packagesForUID, jstring *sandboxId) { // packageName, packagesForUID, sandboxId are added from Android Q beta 2, removed from beta 5 + + appProcessPre(env, *_uid, *appDataDir, *packageName); } EXPORT int nativeForkAndSpecializePost(JNIEnv *env, jclass clazz, jint res) { if (res == 0) { // in app process + appProcessPost(env); } else { // in zygote process, res is child pid // don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66 @@ -32,11 +63,15 @@ EXPORT __attribute__((visibility("default"))) void specializeAppProcessPre( // this is added from Android Q beta, but seems Google disabled this in following updates // packageName, packagesForUID, sandboxId are added from Android Q beta 2, removed from beta 5 + + appProcessPre(env, *_uid, *appDataDir, *packageName); } EXPORT __attribute__((visibility("default"))) int specializeAppProcessPost( JNIEnv *env, jclass clazz) { // this is added from Android Q beta, but seems Google disabled this in following updates + + appProcessPost(env); return 0; } @@ -65,4 +100,105 @@ EXPORT int shouldSkipUid(int uid) { EXPORT void onModuleLoaded() { // called when the shared library of Riru core is loaded } +} + +void injectBuild(JNIEnv *env) { + if (env == nullptr) { + LOGW("failed to inject android.os.Build for %s due to env is null", package_name); + return; + } + LOGI("inject android.os.Build for %s ", package_name); + + jclass build_class = env->FindClass("android/os/Build"); + if (build_class == nullptr) { + LOGW("failed to inject android.os.Build for %s due to build is null", package_name); + return; + } + + jstring new_str = env->NewStringUTF("Xiaomi"); + + jfieldID brand_id = env->GetStaticFieldID(build_class, "BRAND", "Ljava/lang/String;"); + if (brand_id != nullptr) { + env->SetStaticObjectField(build_class, brand_id, new_str); + } + + jfieldID manufacturer_id = env->GetStaticFieldID(build_class, "MANUFACTURER", "Ljava/lang/String;"); + if (manufacturer_id != nullptr) { + env->SetStaticObjectField(build_class, manufacturer_id, new_str); + } + + jfieldID product_id = env->GetStaticFieldID(build_class, "PRODUCT", "Ljava/lang/String;"); + if (product_id != nullptr) { + env->SetStaticObjectField(build_class, product_id, new_str); + } + + if(env->ExceptionCheck()) + { + env->ExceptionClear(); + } + + env->DeleteLocalRef(new_str); +} + +bool isAppNeedHook(JNIEnv *env, jstring jAppDataDir) { + if (jAppDataDir == nullptr) { + return false; + } + + const char *appDataDir = env->GetStringUTFChars(jAppDataDir, nullptr); + + int user = 0; + while (true) { + // /data/user// + if (sscanf(appDataDir, "/data/%*[^/]/%d/%s", &user, package_name) == 2) + break; + + // /mnt/expand//user// + if (sscanf(appDataDir, "/mnt/expand/%*[^/]/%*[^/]/%d/%s", &user, package_name) == 2) + break; + + // /data/data/ + if (sscanf(appDataDir, "/data/%*[^/]/%s", package_name) == 1) + break; + + package_name[0] = '\0'; + return false; + } + env->ReleaseStringUTFChars(jAppDataDir, appDataDir); + + std::string pkgName = package_name; + for (auto &s : globalPkgBlackList) { + if (pkgName.find(s) != std::string::npos) { + return false; + } + } + + if (access(FAKE_CONFIGURATION_GLOBAL, F_OK) == 0) { + return true; + } + + if (access(CONFIG_PATH "/packages", R_OK) == 0) { + char path[PATH_MAX]; + snprintf(path, PATH_MAX, CONFIG_PATH "/packages/%d.%s", user, package_name); + return access(path, F_OK) == 0; + } + + return false; +} + + + +static void appProcessPre(JNIEnv *env, jint _uid, jstring appDataDir, jstring packageName) { + + uid = _uid; + enable_hook = isAppNeedHook(env, appDataDir); +} + + +static void appProcessPost(JNIEnv *env) { + + if (enable_hook) { + injectBuild(env); + install_hook(package_name, uid / 100000); + } } \ No newline at end of file diff --git a/module/src/main/cpp/riru.c b/module/src/main/cpp/riru.c new file mode 100644 index 0000000..3f6079d --- /dev/null +++ b/module/src/main/cpp/riru.c @@ -0,0 +1,76 @@ +#include +#include +#include + +#ifdef __LP64__ +#define LIB "/system/lib64/libmemtrack.so" +#else +#define LIB "/system/lib/libmemtrack.so" +#endif + +static void *riru_handle; +static char *riru_module_name; + +static void *get_handle() { + if (riru_handle == NULL) + riru_handle = dlopen(LIB, RTLD_NOW | RTLD_GLOBAL); + + return riru_handle; +} + +const char *riru_get_module_name() { + return riru_module_name; +} + +void riru_set_module_name(const char *name) { + riru_module_name = strdup(name); +} + +int riru_get_version() { + static void **sym; + void *handle; + if ((handle = get_handle()) == NULL) return -1; + if (sym == NULL) sym = dlsym(handle, "riru_get_version"); + if (sym) return ((int (*)()) sym)(); + return -1; +} + +void *riru_get_func(const char *name) { + static void **sym; + void *handle; + if ((handle = get_handle()) == NULL) return NULL; + if (sym == NULL) sym = dlsym(handle, "riru_get_func"); + if (sym) return ((void *(*)(const char *, const char *)) sym)(riru_get_module_name(), name); + return NULL; +} + +void *riru_get_native_method_func(const char *className, const char *name, const char *signature) { + static void **sym; + void *handle; + if ((handle = get_handle()) == NULL) return NULL; + if (sym == NULL) sym = dlsym(handle, "riru_get_native_method_func"); + if (sym) + return ((void *(*)(const char *, const char *, const char *, const char *)) sym)( + riru_get_module_name(), className, name, signature); + return NULL; +} + +void riru_set_func(const char *name, void *func) { + static void **sym; + void *handle; + if ((handle = get_handle()) == NULL) return; + if (sym == NULL) sym = dlsym(handle, "riru_set_func"); + if (sym) + ((void *(*)(const char *, const char *, void *)) sym)(riru_get_module_name(), name, func); +} + +void riru_set_native_method_func(const char *className, const char *name, const char *signature, + void *func) { + static void **sym; + void *handle; + if ((handle = get_handle()) == NULL) return; + if (sym == NULL) sym = dlsym(handle, "riru_set_native_method_func"); + if (sym) + ((void *(*)(const char *, const char *, const char *, const char *, void *)) sym)( + riru_get_module_name(), className, name, signature, func); +} \ No newline at end of file