├── .gitignore ├── CMakeLists.txt ├── README.md ├── build.sh └── src ├── elf_common.cc ├── elf_common.h ├── elf_file.cc ├── elf_file.h ├── elf_hooker.cc ├── elf_hooker.h ├── elf_log.h ├── elf_mapped.cc ├── elf_mapped.h ├── elf_module.cc ├── elf_module.h ├── elf_soinfo.h └── main.cc /.gitignore: -------------------------------------------------------------------------------- 1 | objs 2 | libs 3 | bin 4 | *.so 5 | build/ 6 | 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.18.1) 2 | 3 | project("elfhooker") 4 | 5 | SET(files src/elf_common.cc 6 | src/elf_hooker.cc 7 | src/elf_module.cc 8 | src/elf_file.cc 9 | src/elf_mapped.cc) 10 | 11 | SET(headers src/elf_common.h 12 | src/elf_hooker.h 13 | src/elf_module.h 14 | src/elf_log.h) 15 | 16 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer") 17 | SET(CMAKE_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} -fno-omit-frame-pointer") 18 | MESSAGE(WANNING "source dir: ${PROJECT_SOURCE_DIR}") 19 | 20 | 21 | ADD_LIBRARY(elfhook STATIC ${files}) 22 | SET_TARGET_PROPERTIES(elfhook PROPERTIES PUBLIC_HEADER "${headers}") 23 | INSTALL(TARGETS elfhook 24 | ARCHIVE 25 | DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/${ANDROID_ABI} 26 | PUBLIC_HEADER 27 | DESTINATION ${CMAKE_INSTALL_PREFIX}/inc) 28 | 29 | IF (${ANDROID_ABI} STREQUAL "armeabi-v7a") 30 | SET(HOOKER "hooker.32") 31 | ELSEIF(${ANDROID_ABI} STREQUAL "arm64-v8a") 32 | SET(HOOKER "hooker.64") 33 | ELSE() 34 | MESSAGE(FATAL_ERROR "unsupport ARCH_API: ${ANDROID_ABI}" ) 35 | ENDIF() 36 | 37 | ADD_EXECUTABLE(${HOOKER} src/main.cc) 38 | TARGET_LINK_LIBRARIES(${HOOKER} elfhook) 39 | INSTALL(TARGETS ${HOOKER} 40 | RUNTIME 41 | DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 0x01 Brief About ElfHook 2 | 3 |   ElfHook的代码参考boyliang的AllHookInOne 4 | 5 | - .dynmaic节中没有DT\_HAST,使用DT\_GNU\_HASH的情况下. 6 | 7 | 8 | - 计算动态库加载的base\_addr是错误的,应该使用bias\_addr来计算出ehdr、phdr和shdr之外的所有地址。 9 | 10 | - 替换函数时,修改page的读写权限时,在SEAndroid上PROT\_EXEC和PROT\_WRITE同时设置**可能**会导致异常, 11 | 12 | - 在dlopen返回时,通过返回值获得动态库加载的base\_addr 13 | 14 | - elfhook 支持 aarch64 (arm64-v8a),soinfo的处理暂**不支持** 15 | 16 | - android 6以下系统使用/proc/self/maps来检索进程内动态库,android 7以上使用soinfo_list 17 | 18 | 19 | ref: 20 | 21 |  AllHookInOne : [https://github.com/boyliang/AllHookInOne.git] 22 | 23 |  AllHookInOne说明 : [http://bbs.pediy.com/showthread.php?p=1328038] 24 | 25 |  bionic : [https://android.googlesource.com/platform/bionic] 26 | 27 | 28 | ## 0x02 How To Build 29 | 30 | #### Export android ndk path 31 | 32 | > export -p PATH=$PATH:$ANDROID_NDK 33 | 34 | 35 | #### Build 36 | 37 | > ./build.sh arm // 构建arm32 target 38 | 39 | > ./build.sh arm64 // 构建arm64 target 40 | 41 | > ./build.sh both // 构建arm32和arm64 targets 42 | 43 | 44 | ## 0x03 How To Use 45 | 46 | 47 | elf\_file用于解析文件形式的elf文件, elf\_module用于解析加载到内存中的elf文件, elf_hooker 封装linker中解析出来的私有方法和对象,例如dlopen\_ext、soinfo\_list等 48 | 49 | ### 3.1 elf\_hooker接口 50 | 51 | - bool elf\_hooker::load() 52 | 53 | elf\_hooker初始化,解析出linker中在hook过程中需要使用到的变量和方法的地址。 54 | 55 | - void elf\_hooker::dump\_module\_list() 56 | 57 | 打印在android 6以下版本,使用/proc/self/maps中解析出来当前已经加载的所有动态库的名字和基地址 58 | 59 | - void elf\_hooker::dump\_soinfo\_list() 60 | 61 | 打印在android 7以上版本,soinfo\_list链表中所有soinfo对象的动态库文件路径和内存中的基地址 62 | 63 | 64 | - void elf\_hooker::set\_prehook\_cb( prehook_cb ): 65 | 66 | 设置回调函数,用于检查该动态库是否hook 67 | 68 | > bool prehook\_cb(const char* module_name); 69 | 70 | > 参数: 71 | >> module\_name: 动态库路径 72 | 73 | >返回值: 74 | > 75 | >> true: 可以hook 76 | >> 77 | >> false: 不需要hook 78 | 79 | - void elf_hooker::hook\_all\_modules(struct elf\_rebinds * rebinds) 80 | 81 | 劫持elf\_hooker中解析出来当前已加载所有动态库 82 | 83 | ```` 84 | struct elf_rebinds { 85 | const char * func_name; 86 | void * pfn_new; 87 | void ** ppfn_old; 88 | }; 89 | ```` 90 | 91 | >  func\_name: 要劫持的函数名. 92 | 93 | >  pfn\_new: 新函数地址 94 | 95 | >  ppfn\_old: 返回的劫持前原函数地址, **ppfn\_old非空** 96 | 97 | ### 3.2 elf\_module接口 98 | 99 | - elf\_module::elf\_module(ElfW(Addr) base\_addr, const char* module\_name) 100 | 101 | elf\_module构造函数,传入elf内存基地址和文件路径作为参数,如果使用无参数的默认构造函数,则在调用get\_segment\_view前需要调用set\_base\_addr()和 set\_module\_name()设置基地址和路径。 102 | 103 | - bool elf\_module::get_segment_view() 104 | 105 | 解析elf格式 106 | 107 | - bool elf\_module:hook(const char \* symbol, void \* replace\_func, void \*\* old\_func); 108 | 109 | 劫持当前模块中的symbol函数, 110 | 111 | >   symbol: 要劫持的函数名. 112 | 113 | >   replace\_func: 新函数地址 114 | 115 | >   old\_func: 返回的劫持前原函数地址, **old\_func非空** 116 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | CURRENT_PATH=$(cd "$(dirname "$0")";pwd) 3 | ANDROID_NDK_HOME=/Users/wadahana/Library/Android/ndk 4 | BUILD_DIR=${CURRENT_PATH}/build 5 | API_VERSION="android-28" 6 | echo "build: $BUILD_DIR" 7 | 8 | function build() { 9 | if [ $1 == "arm" ]; then 10 | ARCH="armeabi-v7a" 11 | elif [ $1 == "arm64" ]; then 12 | ARCH="arm64-v8a" 13 | elif [ $1 == "x86" ]; then 14 | ARCH="x86" 15 | elif [ $1 == "x86_64" ]; then 16 | ARCH="x86_64" 17 | fi 18 | OUTPUT_DIR="${BUILD_DIR}/output" 19 | BUILD_DIR="${BUILD_DIR}/${ARCH}" 20 | mkdir -p "${BUILD_DIR}" 21 | mkdir -p "${OUTPUT_DIR}" 22 | pushd ${BUILD_DIR} 23 | cmake -DCMAKE_ANDROID_NDK=${ANDROID_NDK_HOME} \ 24 | -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_HOME}/build/cmake/android.toolchain.cmake \ 25 | -DANDROID_PLATFORM=${API_VERSION} \ 26 | -DCMAKE_SYSTEM_NAME=Android \ 27 | -DCMAKE_ANDROID_ARCH_ABI=${ARCH} \ 28 | -DCMAKE_INSTALL_PREFIX=${OUTPUT_DIR} \ 29 | ../.. 30 | make VERBOSE=1 31 | popd 32 | } 33 | 34 | if [ $1 == "arm" ]; then 35 | build "arm" 36 | elif [ $1 == "arm64" ]; then 37 | build "arm64" 38 | elif [ $1 == "both" ]; then 39 | build "arm" 40 | build "arm64" 41 | else 42 | echo "build.sh [arm|arm64]" 43 | exit 0 44 | fi 45 | 46 | -------------------------------------------------------------------------------- /src/elf_common.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "elf_common.h" 6 | 7 | bool safe_add(off64_t* out, off64_t a, size_t b) { 8 | assert(a >= 0); 9 | if (static_cast(INT64_MAX - a) < b) { 10 | return false; 11 | } 12 | *out = a + b; 13 | return true; 14 | } 15 | 16 | void dump_hex(uint8_t * pbuf, int size) { 17 | int i = 0; 18 | for (int j = 0; j < size; j += 16) { 19 | i = j; 20 | fprintf(stderr, "%02X %02X %02X %02X %02X %02X %02X %02X ", 21 | pbuf[i + 0], pbuf[i + 1], pbuf[i + 2], pbuf[i + 3], 22 | pbuf[i + 4], pbuf[i + 5], pbuf[i + 6], pbuf[i + 7]); 23 | fprintf(stderr, "%02X %02X %02X %02X %02X %02X %02X %02X\n", 24 | pbuf[i + 8], pbuf[i + 9], pbuf[i + 10], pbuf[i + 11], 25 | pbuf[i + 12], pbuf[i + 13], pbuf[i + 14], pbuf[i + 15]); 26 | } 27 | for (int j = i; j < size; j += 1) { 28 | fprintf(stderr, "%02X ", pbuf[j]); 29 | } 30 | fprintf(stderr, "\n"); 31 | return; 32 | } -------------------------------------------------------------------------------- /src/elf_common.h: -------------------------------------------------------------------------------- 1 | #if !defined (__ELF_COMMON_H__) 2 | #define __ELF_COMMON_H__ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #if defined(__LP64__) 10 | #define ElfW(type) Elf64_##type 11 | static inline ElfW(Word) elf_r_sym(ElfW(Xword) info) { return ELF64_R_SYM(info); } 12 | static inline ElfW(Xword) elf_r_type(ElfW(Xword) info) { return ELF64_R_TYPE(info); } 13 | #else 14 | #define ElfW(type) Elf32_##type 15 | static inline ElfW(Word) elf_r_sym(ElfW(Word) info) { return ELF32_R_SYM(info); } 16 | static inline ElfW(Word) elf_r_type(ElfW(Word) info) { return ELF32_R_TYPE(info); } 17 | #endif 18 | 19 | #define R_GENERIC_NONE 0 // R_*_NONE is always 0 20 | 21 | #if defined (__aarch64__) 22 | 23 | #define R_GENERIC_JUMP_SLOT R_AARCH64_JUMP_SLOT 24 | // R_AARCH64_ABS64 is classified as a static relocation but it is common in DSOs. 25 | #define R_GENERIC_ABSOLUTE R_AARCH64_ABS64 26 | #define R_GENERIC_GLOB_DAT R_AARCH64_GLOB_DAT 27 | #define R_GENERIC_RELATIVE R_AARCH64_RELATIVE 28 | #define R_GENERIC_IRELATIVE R_AARCH64_IRELATIVE 29 | #define R_GENERIC_COPY R_AARCH64_COPY 30 | #define R_GENERIC_TLS_DTPMOD R_AARCH64_TLS_DTPMOD 31 | #define R_GENERIC_TLS_DTPREL R_AARCH64_TLS_DTPREL 32 | #define R_GENERIC_TLS_TPREL R_AARCH64_TLS_TPREL 33 | #define R_GENERIC_TLSDESC R_AARCH64_TLSDESC 34 | 35 | #elif defined (__arm__) 36 | 37 | #define __work_around_b_24465209__ (1) 38 | #define R_GENERIC_JUMP_SLOT R_ARM_JUMP_SLOT 39 | // R_ARM_ABS32 is classified as a static relocation but it is common in DSOs. 40 | #define R_GENERIC_ABSOLUTE R_ARM_ABS32 41 | #define R_GENERIC_GLOB_DAT R_ARM_GLOB_DAT 42 | #define R_GENERIC_RELATIVE R_ARM_RELATIVE 43 | #define R_GENERIC_IRELATIVE R_ARM_IRELATIVE 44 | #define R_GENERIC_COPY R_ARM_COPY 45 | #define R_GENERIC_TLS_DTPMOD R_ARM_TLS_DTPMOD32 46 | #define R_GENERIC_TLS_DTPREL R_ARM_TLS_DTPOFF32 47 | #define R_GENERIC_TLS_TPREL R_ARM_TLS_TPOFF32 48 | #define R_GENERIC_TLSDESC R_ARM_TLS_DESC 49 | 50 | #elif defined (__i386__) 51 | 52 | #define __work_around_b_24465209__ (1) 53 | #define R_GENERIC_JUMP_SLOT R_386_JMP_SLOT 54 | #define R_GENERIC_ABSOLUTE R_386_32 55 | #define R_GENERIC_GLOB_DAT R_386_GLOB_DAT 56 | #define R_GENERIC_RELATIVE R_386_RELATIVE 57 | #define R_GENERIC_IRELATIVE R_386_IRELATIVE 58 | #define R_GENERIC_COPY R_386_COPY 59 | #define R_GENERIC_TLS_DTPMOD R_386_TLS_DTPMOD32 60 | #define R_GENERIC_TLS_DTPREL R_386_TLS_DTPOFF32 61 | #define R_GENERIC_TLS_TPREL R_386_TLS_TPOFF 62 | #define R_GENERIC_TLSDESC R_386_TLS_DESC 63 | 64 | #elif defined (__x86_64__) 65 | 66 | #define R_GENERIC_JUMP_SLOT R_X86_64_JUMP_SLOT 67 | #define R_GENERIC_ABSOLUTE R_X86_64_64 68 | #define R_GENERIC_GLOB_DAT R_X86_64_GLOB_DAT 69 | #define R_GENERIC_RELATIVE R_X86_64_RELATIVE 70 | #define R_GENERIC_IRELATIVE R_X86_64_IRELATIVE 71 | #define R_GENERIC_COPY R_X86_64_COPY 72 | #define R_GENERIC_TLS_DTPMOD R_X86_64_DTPMOD64 73 | #define R_GENERIC_TLS_DTPREL R_X86_64_DTPOFF64 74 | #define R_GENERIC_TLS_TPREL R_X86_64_TPOFF64 75 | #define R_GENERIC_TLSDESC R_X86_64_TLSDESC 76 | 77 | #endif 78 | 79 | 80 | #define PAGE_START(addr) (~(getpagesize() - 1) & (addr)) 81 | #define PAGE_END(addr) PAGE_START((addr) + (PAGE_SIZE-1)) 82 | #define PAGE_OFFSET(x) ((x) & ~PAGE_MASK) 83 | 84 | #define SAFE_SET_VALUE(t, v) if(t) *(t) = (v) 85 | 86 | #define MAYBE_MAP_FLAG(x, from, to) (((x) & (from)) ? (to) : 0) 87 | #define PFLAGS_TO_PROT(x) (MAYBE_MAP_FLAG((x), PF_X, PROT_EXEC) | \ 88 | MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \ 89 | MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE)) 90 | 91 | #define powerof2(x) ((((x)-1)&(x))==0) 92 | 93 | inline static int GetTargetElfMachine() 94 | { 95 | #if defined(__arm__) 96 | return EM_ARM; 97 | #elif defined(__aarch64__) 98 | return EM_AARCH64; 99 | #elif defined(__i386__) 100 | return EM_386; 101 | #elif defined(__mips__) 102 | return EM_MIPS; 103 | #elif defined(__x86_64__) 104 | return EM_X86_64; 105 | #endif 106 | } 107 | 108 | #define CHECK(predicate) \ 109 | do { \ 110 | if (!(predicate)) { \ 111 | log_fatal("%s:%d: %s CHECK '" #predicate "' failed", \ 112 | __FILE__, __LINE__, __FUNCTION__); \ 113 | } \ 114 | } while(0) 115 | 116 | 117 | 118 | void dump_hex(uint8_t * pbuf, int size); 119 | bool safe_add(off64_t* out, off64_t a, size_t b); 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /src/elf_file.cc: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include "elf_common.h" 15 | #include "elf_file.h" 16 | #include "elf_module.h" 17 | #include "elf_log.h" 18 | 19 | elf_file::elf_file() { 20 | this->m_phdr = NULL; 21 | this->m_shdr = NULL; 22 | this->m_dynamic = NULL; 23 | this->m_dynsym = NULL; 24 | this->m_symtab = NULL; 25 | this->m_dynstr = NULL; 26 | this->m_strtab = NULL; 27 | this->m_shstrtab = NULL; 28 | this->m_dynamic_size = 0; 29 | this->m_dynsym_size = 0; 30 | this->m_symtab_size = 0; 31 | this->m_dynstr_size = 0; 32 | this->m_strtab_size = 0; 33 | this->m_shstrtab_size = 0; 34 | } 35 | 36 | elf_file::~elf_file() { 37 | if (this->m_fd >= 0) { 38 | close(this->m_fd); 39 | } 40 | } 41 | 42 | bool elf_file::load(const char * realpath) { 43 | int fd = open(realpath, O_RDONLY | O_CLOEXEC); 44 | if (fd < 0) { 45 | log_error("open \"%s\" fail, error: %s", realpath, strerror(errno)); 46 | return false; 47 | } 48 | struct stat file_stat; 49 | if (fstat(fd, &file_stat) < 0) { 50 | log_error("get \"%s\" filesz fail, error: %s", realpath, strerror(errno)); 51 | return false; 52 | } 53 | 54 | this->m_file_size = file_stat.st_size; 55 | this->m_realpath = realpath; 56 | this->m_soname = basename(realpath); 57 | this->m_fd = fd; 58 | 59 | pread(this->m_fd, &m_ehdr, sizeof(m_ehdr), 0); 60 | if (!elf_module::is_elf_module((void *)&this->m_ehdr)) { 61 | log_error("%s check elf header fail.\n", this->get_realpath()); 62 | return false; 63 | } 64 | if (!this->read_program_headers() || 65 | !this->read_section_headers() || 66 | !this->read_sections()) { 67 | return false; 68 | } 69 | return true; 70 | } 71 | 72 | bool elf_file::check_file_range(ElfW(Addr) offset, size_t size, size_t alignment) { 73 | off64_t range_start; 74 | off64_t range_end; 75 | return offset > 0 && 76 | safe_add(&range_start, 0, offset) && 77 | safe_add(&range_end, range_start, size) && 78 | (range_start < m_file_size) && 79 | (range_end <= m_file_size) && 80 | ((offset % alignment) == 0); 81 | } 82 | 83 | bool elf_file::read_program_headers() { 84 | this->m_phdr_num = m_ehdr.e_phnum; 85 | 86 | if (this->m_phdr_num == 0) { 87 | log_error("\"%s\" has no program headers", this->get_realpath()); 88 | return false; 89 | } 90 | 91 | if (this->m_phdr_num < 1 || this->m_phdr_num > 65536/sizeof(ElfW(Phdr))) { 92 | log_error("\"%s\" has invalid e_phnum: %u", this->get_soname(), this->m_phdr_num); 93 | return false; 94 | } 95 | 96 | // Boundary checks 97 | size_t size = this->m_phdr_num * sizeof(ElfW(Phdr)); 98 | if (!check_file_range(this->m_ehdr.e_phoff, size, 4)) { 99 | log_error("\"%s\" has invalid phdr offset/size: %zu/%zu\n", 100 | this->get_soname(), 101 | static_cast(this->m_ehdr.e_phoff), 102 | size); 103 | return false; 104 | } 105 | if (!this->m_phdr_fragment.map(this->m_fd, 0, m_ehdr.e_phoff, size)) { 106 | log_error("\"%s\" phdr mmap failed: %s\n", this->get_realpath(), strerror(errno)); 107 | return false; 108 | } 109 | 110 | this->m_phdr = static_cast(m_phdr_fragment.data()); 111 | return true; 112 | } 113 | 114 | bool elf_file::read_section_headers() { 115 | this->m_shdr_num = this->m_ehdr.e_shnum; 116 | if (this->m_shdr_num == 0) { 117 | log_error("\"%s\" has no section headers\n", this->get_realpath()); 118 | return false; 119 | } 120 | 121 | if (this->m_ehdr.e_shstrndx >= this->m_shdr_num) { 122 | log_error("\"%s\" section headers nums less than e_shstrndx\n", this->get_realpath()); 123 | return false; 124 | } 125 | 126 | size_t size = this->m_shdr_num * sizeof(ElfW(Shdr)); 127 | if (!check_file_range(this->m_ehdr.e_shoff, size, 4)) { 128 | log_error("\"%s\" has invalid shdr offset/size: %zu/%zu", 129 | this->get_realpath(), 130 | static_cast(this->m_ehdr.e_shoff), 131 | size); 132 | return false; 133 | } 134 | 135 | if (!this->m_shdr_fragment.map(this->m_fd, 0, this->m_ehdr.e_shoff, size)) { 136 | log_error("\"%s\" shdr mmap failed: %s", this->get_realpath(), strerror(errno)); 137 | return false; 138 | } 139 | 140 | this->m_shdr = static_cast(this->m_shdr_fragment.data()); 141 | 142 | ElfW(Shdr) * shstrtab_shdr = &this->m_shdr[this->m_ehdr.e_shstrndx]; 143 | if (!this->check_file_range(shstrtab_shdr->sh_offset, shstrtab_shdr->sh_size, 1)) { 144 | log_error("\"%s\" has invalid shdr offset/size: %zu/%zu", 145 | this->get_realpath(), 146 | static_cast(this->m_ehdr.e_shoff), 147 | size); 148 | return false; 149 | } 150 | if (!this->m_shstrtab_fragment.map(this->m_fd, 0, shstrtab_shdr->sh_offset, shstrtab_shdr->sh_size)) { 151 | log_error("\"%s\" shstrtab mmap failed: %s", this->get_realpath(), strerror(errno)); 152 | return false; 153 | } 154 | this->m_shstrtab = static_cast(this->m_shstrtab_fragment.data()); 155 | this->m_shstrtab_size = shstrtab_shdr->sh_size; 156 | return true; 157 | } 158 | 159 | bool elf_file::read_sections() { 160 | 161 | ElfW(Shdr) * dynamic_shdr = NULL; 162 | ElfW(Shdr) * dynsym_shdr = NULL; 163 | ElfW(Shdr) * strtab_shdr = NULL; 164 | ElfW(Shdr) * dynstr_shdr = NULL; 165 | ElfW(Shdr) * symtab_shdr = NULL; 166 | 167 | for (size_t i = 0; i < this->m_shdr_num; ++i) { 168 | const char * sh_name = &this->m_shstrtab[this->m_shdr[i].sh_name]; 169 | // log_dbg("%-30s %d\n", sh_name, this->m_shdr[i].sh_type); 170 | if (this->m_shdr[i].sh_type == SHT_DYNAMIC) { 171 | dynamic_shdr = &this->m_shdr[i]; 172 | } else if (this->m_shdr[i].sh_type == SHT_DYNSYM) { 173 | dynsym_shdr = &this->m_shdr[i]; 174 | } else if (this->m_shdr[i].sh_type == SHT_STRTAB) { 175 | if (strncmp(sh_name, ".strtab", 7) == 0) { 176 | strtab_shdr = &this->m_shdr[i]; 177 | } else if (strncmp(sh_name, ".dynstr", 7) == 0) { 178 | dynstr_shdr = &this->m_shdr[i]; 179 | } 180 | } else if (this->m_shdr[i].sh_type == SHT_SYMTAB) { 181 | if (strncmp(sh_name, ".symtab", 7) == 0) { 182 | symtab_shdr = &this->m_shdr[i]; 183 | } 184 | } 185 | } 186 | 187 | if (dynamic_shdr) 188 | log_dbg(".dynamic %p, %p, %zd\n", (void*)dynamic_shdr, (void*)dynamic_shdr->sh_offset, (size_t)dynamic_shdr->sh_size); 189 | if (dynsym_shdr) 190 | log_dbg(".dynsym %p, %p, %zd\n", (void*)dynsym_shdr, (void*)dynsym_shdr->sh_offset, (size_t)dynsym_shdr->sh_size); 191 | if (dynstr_shdr) 192 | log_dbg(".dynstr %p, %p, %zd\n", (void*)dynstr_shdr, (void*)dynstr_shdr->sh_offset, (size_t)dynstr_shdr->sh_size); 193 | if (symtab_shdr) 194 | log_dbg(".symtab %p, %p, %zd\n", (void*)symtab_shdr, (void*)symtab_shdr->sh_offset, (size_t)symtab_shdr->sh_size); 195 | if (strtab_shdr) 196 | log_dbg(".strtab %p, %p, %zd\n", (void*)strtab_shdr, (void*)strtab_shdr->sh_offset, (size_t)strtab_shdr->sh_size); 197 | 198 | if (dynamic_shdr && 199 | check_file_range(dynamic_shdr->sh_offset, dynamic_shdr->sh_size, 4)) { 200 | if (!this->m_dynamic_fragment.map(this->m_fd, 0, dynamic_shdr->sh_offset, dynamic_shdr->sh_size)) { 201 | log_warn("dynamic map fail, %s\n", strerror(errno)); 202 | } 203 | this->m_dynamic = static_cast(this->m_dynamic_fragment.data()); 204 | this->m_dynamic_size = dynamic_shdr->sh_size; 205 | } 206 | if (dynsym_shdr && check_file_range(dynsym_shdr->sh_offset, dynsym_shdr->sh_size, 4)) { 207 | if (!this->m_dynsym_fragment.map(this->m_fd, 0, dynsym_shdr->sh_offset, dynsym_shdr->sh_size) ) { 208 | log_warn("dynsym map fail, %s\n", strerror(errno)); 209 | } 210 | this->m_dynsym = static_cast(this->m_dynsym_fragment.data()); 211 | this->m_dynsym_size = dynsym_shdr->sh_size; 212 | } 213 | if (symtab_shdr && 214 | check_file_range(symtab_shdr->sh_offset, symtab_shdr->sh_size, 4)) { 215 | if (!this->m_symtab_fragment.map(this->m_fd, 0, symtab_shdr->sh_offset, symtab_shdr->sh_size)) { 216 | log_warn("symtab map fail, %s\n", strerror(errno)); 217 | } 218 | this->m_symtab = static_cast(this->m_symtab_fragment.data()); 219 | this->m_symtab_size = symtab_shdr->sh_size; 220 | } 221 | if (dynstr_shdr && 222 | check_file_range(dynstr_shdr->sh_offset, dynstr_shdr->sh_size, 1)) { 223 | if (!this->m_dynstr_fragment.map(this->m_fd, 0, dynstr_shdr->sh_offset, dynstr_shdr->sh_size)) { 224 | log_warn("dynstr map fail, %s\n", strerror(errno)); 225 | } 226 | this->m_dynstr = static_cast(this->m_dynstr_fragment.data()); 227 | this->m_dynstr_size = dynstr_shdr->sh_size; 228 | } 229 | if (strtab_shdr && 230 | check_file_range(strtab_shdr->sh_offset, strtab_shdr->sh_size, 1)) { 231 | if (!this->m_strtab_fragment.map(this->m_fd, 0, strtab_shdr->sh_offset, strtab_shdr->sh_size)) { 232 | log_warn("strtab map fail, %s\n", strerror(errno)); 233 | } 234 | this->m_strtab = static_cast(this->m_strtab_fragment.data()); 235 | this->m_strtab_size = strtab_shdr->sh_size; 236 | } 237 | return true; 238 | } 239 | 240 | ElfW(Sym) * elf_file::find_symbol(const char * name, int type) { 241 | ElfW(Sym) * sym = this->m_symtab; 242 | const char * strtab = this->m_strtab; 243 | for (int i = 0; i < this->m_symtab_size/sizeof(ElfW(Sym)); i++) { 244 | const char * sym_name = sym[i].st_name + strtab; 245 | if (type == -1 || type == ELF_ST_TYPE(sym[i].st_info)) { 246 | if (strcmp(name, sym_name) == 0) { 247 | return &sym[i]; 248 | } 249 | } 250 | } 251 | return NULL; 252 | } 253 | 254 | ElfW(Sym) * elf_file::find_dynamic_symbol(const char * name, int type) { 255 | ElfW(Sym) * sym = this->m_dynsym; 256 | const char * strtab = this->m_dynstr; 257 | for (int i = 0; i < this->m_dynsym_size/sizeof(ElfW(Sym)); i++) { 258 | const char * sym_name = sym[i].st_name + strtab; 259 | if (type == -1 || type == ELF_ST_TYPE(sym[i].st_info)) { 260 | if (strcmp(name, sym_name) == 0) { 261 | return &sym[i]; 262 | } 263 | } 264 | } 265 | return NULL; 266 | } 267 | 268 | bool elf_file::find_function(const char * name, uintptr_t & offset) { 269 | bool retval = false; 270 | ElfW(Sym) * sym = this->find_dynamic_symbol(name, STT_FUNC); 271 | if (!sym) { 272 | sym = this->find_symbol(name, STT_FUNC); 273 | } 274 | if (sym) { 275 | offset = static_cast(sym->st_value); 276 | retval = true; 277 | } 278 | return retval; 279 | } 280 | 281 | bool elf_file::find_variable(const char * name, uintptr_t & offset, size_t & size) { 282 | bool retval = false; 283 | ElfW(Sym) * sym = this->find_dynamic_symbol(name, STT_OBJECT); 284 | if (!sym) { 285 | sym = this->find_symbol(name, STT_OBJECT); 286 | } 287 | if (sym) { 288 | offset = static_cast(sym->st_value); 289 | size = static_cast(sym->st_size); 290 | retval = true; 291 | } 292 | return retval; 293 | } 294 | -------------------------------------------------------------------------------- /src/elf_file.h: -------------------------------------------------------------------------------- 1 | #if !defined(__ELF_FILE_H__) 2 | #define __ELF_FILE_H__ 3 | 4 | #include 5 | #include "elf_common.h" 6 | 7 | #include "elf_mapped.h" 8 | 9 | class elf_file { 10 | 11 | public: 12 | elf_file(); 13 | ~elf_file(); 14 | 15 | bool load(const char * filename); 16 | 17 | inline const char * get_soname() {return m_soname.c_str();} 18 | inline const int get_fd(){return m_fd;} 19 | inline const char * get_realpath() {return m_realpath.c_str();} 20 | 21 | ElfW(Sym) * find_symbol(const char * name, int type = -1); 22 | ElfW(Sym) * find_dynamic_symbol(const char * name, int type = -1); 23 | 24 | bool find_function(const char * name, uintptr_t & offset); 25 | bool find_variable(const char * name, uintptr_t & offset, size_t & size); 26 | 27 | void dump_elf_header(void); 28 | void dump_section_headers(void); 29 | void dump_program_headers(void); 30 | void dump_dynamics(void); 31 | void dump_symbols(void); 32 | void dump_dynamic_symbols(void); 33 | 34 | protected: 35 | 36 | bool read_program_headers(); 37 | bool read_section_headers(); 38 | bool read_sections(); 39 | 40 | bool check_file_range(ElfW(Addr) offset, size_t size, size_t alignment); 41 | 42 | protected: 43 | 44 | uintptr_t m_base_addr; 45 | std::string m_soname; 46 | std::string m_realpath; 47 | bool m_is_loaded; 48 | int m_fd; 49 | int m_file_size; 50 | 51 | elf_mapped m_phdr_fragment; 52 | elf_mapped m_shdr_fragment; 53 | elf_mapped m_dynamic_fragment; 54 | elf_mapped m_dynstr_fragment; 55 | elf_mapped m_dynsym_fragment; 56 | elf_mapped m_strtab_fragment; 57 | elf_mapped m_symtab_fragment; 58 | elf_mapped m_shstrtab_fragment; 59 | 60 | protected: 61 | 62 | ElfW(Ehdr) m_ehdr; 63 | 64 | ElfW(Phdr) *m_phdr; 65 | ElfW(Word) m_phdr_num; 66 | 67 | ElfW(Shdr) *m_shdr; 68 | ElfW(Word) m_shdr_num; 69 | 70 | 71 | 72 | 73 | ElfW(Dyn) *m_dynamic; 74 | ElfW(Sym) *m_dynsym; 75 | ElfW(Sym) *m_symtab; 76 | const char *m_dynstr; 77 | const char *m_strtab; 78 | const char *m_shstrtab; 79 | ElfW(Word) m_dynamic_size; 80 | ElfW(Word) m_dynsym_size; 81 | ElfW(Word) m_symtab_size; 82 | ElfW(Word) m_dynstr_size; 83 | ElfW(Word) m_strtab_size; 84 | ElfW(Word) m_shstrtab_size; 85 | 86 | }; 87 | #endif 88 | -------------------------------------------------------------------------------- /src/elf_hooker.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include "elf_hooker.h" 21 | #include "elf_common.h" 22 | #include "elf_file.h" 23 | #include "elf_soinfo.h" 24 | #include "elf_log.h" 25 | 26 | extern "C" void* elf_dlopen_ext(const char* filename, int flag, void* extinfo, void* caller_addr, void* raw); 27 | 28 | elf_hooker::elf_hooker() { 29 | 30 | this->m_modules.clear(); 31 | this->m_is_loaded = false; 32 | this->m_is_use_solist = false; 33 | this->m_prehook_cb = NULL; 34 | this->m_origin_dlopen = NULL; //(fn_dlopen)dlsym(NULL, "dlopen"); 35 | this->m_origin_dlopen_ext = NULL; 36 | this->m_origin_soinfo_map_find = NULL; 37 | this->m_dl_mutex_lock = NULL; 38 | this->m_dl_mutex_unlock = NULL; 39 | this->m_soinfo_handles_map = NULL; 40 | this->m_dl_mutex = NULL; 41 | } 42 | 43 | elf_hooker::~elf_hooker() { 44 | this->m_modules.clear(); 45 | this->m_prehook_cb = NULL; 46 | this->m_origin_dlopen_ext = NULL; 47 | this->m_origin_soinfo_map_find = NULL; 48 | this->m_soinfo_handles_map = NULL; 49 | this->m_dl_mutex = NULL; 50 | this->m_dl_mutex_lock = NULL; 51 | this->m_dl_mutex_unlock = NULL; 52 | } 53 | 54 | bool elf_hooker::phrase_proc_base_addr(char* addr, void** pbase_addr, void** pend_addr) { 55 | char* split = strchr(addr, '-'); 56 | if (split != NULL) { 57 | if (pbase_addr != NULL) { 58 | *pbase_addr = (void *) strtoul(addr, NULL, 16); 59 | } 60 | if (pend_addr != NULL) { 61 | *pend_addr = (void *) strtoul(split + 1, NULL, 16); 62 | } 63 | return true; 64 | } 65 | return false; 66 | } 67 | 68 | bool elf_hooker::phrase_dev_num(char* devno, int *pmajor, int *pminor) { 69 | *pmajor = 0; 70 | *pminor = 0; 71 | if (devno != NULL) { 72 | char* colon_pos = strchr(devno, ':'); 73 | if (colon_pos != NULL) { 74 | *pmajor = strtoul(devno, NULL, 16); 75 | *pminor = strtoul(colon_pos + 1, NULL, 16); 76 | return true; 77 | } 78 | } 79 | return false; 80 | } 81 | 82 | bool elf_hooker::build_all_modules() { 83 | if (m_modules.empty()) { 84 | phrase_proc_maps(NULL, this->m_modules, true); 85 | } 86 | return !m_modules.empty(); 87 | } 88 | 89 | int elf_hooker::phrase_proc_maps(const char* so_name, std::map & modules, bool lock) { 90 | log_info("phrase_proc_maps() -->\n"); 91 | modules.clear(); 92 | FILE* fd = fopen("/proc/self/maps", "r"); 93 | if (fd != NULL) { 94 | char buff[2048+1]; 95 | while(fgets(buff, 2048, fd) != NULL) { 96 | if (so_name != NULL && strstr(buff, so_name) == NULL) { 97 | continue; 98 | } 99 | const char *sep = "\t \r\n"; 100 | char * line = NULL; 101 | char * addr = strtok_r(buff, sep, &line); 102 | if (!addr) { 103 | continue; 104 | } 105 | 106 | char *flags = strtok_r(NULL, sep, &line); 107 | if (!flags || flags[0] != 'r' || flags[3] == 's') { 108 | /* 109 | 1. mem section cound NOT be read, without 'r' flag. 110 | 2. read from base addr of /dev/mail module would crash. 111 | i dont know how to handle it, just skip it. 112 | 113 | 1f5573000-1f58f7000 rw-s 1f5573000 00:0c 6287 /dev/mali0 114 | 115 | */ 116 | continue; 117 | } 118 | strtok_r(NULL, sep, &line); // offsets 119 | char *dev = strtok_r(NULL, sep, &line); // dev number. 120 | int major = 0, minor = 0; 121 | if (!phrase_dev_num(dev, &major, &minor) || major == 0) { 122 | /* 123 | if dev major number equal to 0, mean the module must NOT be 124 | a shared or executable object loaded from disk. 125 | e.g: 126 | lookup symbol from [vdso] would crash. 127 | 7f7b48a000-7f7b48c000 r-xp 00000000 00:00 0 [vdso] 128 | */ 129 | continue; 130 | } 131 | 132 | strtok_r(NULL, sep, &line); // node 133 | 134 | char* filename = strtok_r(NULL, sep, &line); //module name 135 | if (!filename) { 136 | continue; 137 | } 138 | std::string module_name = filename; 139 | std::map::iterator itor = modules.find(module_name); 140 | if (itor == modules.end()) { 141 | void* base_addr = NULL; 142 | void* end_addr = NULL; 143 | if (phrase_proc_base_addr(addr, &base_addr, &end_addr) && elf_module::is_elf_module(base_addr)) { 144 | if (lock && this->get_sdk_version() < 24) { 145 | void * handle = this->dlopen(filename, RTLD_LAZY); 146 | } 147 | elf_module module(reinterpret_cast(base_addr), module_name.c_str()); 148 | modules.insert(std::pair(module_name, module)); 149 | } 150 | } 151 | if (so_name != NULL) { 152 | break; 153 | } 154 | } 155 | fclose(fd); 156 | } 157 | return modules.size(); 158 | } 159 | 160 | void elf_hooker::dump_module_list() { 161 | log_info("module_list: \n"); 162 | for (std::map::iterator itor = m_modules.begin(); 163 | itor != m_modules.end(); 164 | itor++ ) { 165 | log_info("\tbase_addr: %p module_name: %s\n", 166 | reinterpret_cast(itor->second.get_base_addr()), 167 | itor->second.get_module_name()); 168 | } 169 | return; 170 | } 171 | 172 | void elf_hooker::dump_soinfo_list() { 173 | log_info("soinfo_list: \n"); 174 | if (this->m_is_use_solist) { 175 | this->dl_mutex_lock(); 176 | struct soinfo * soinfo = reinterpret_cast(m_soinfo_list); 177 | while (soinfo) { 178 | const char * realpath = get_realpath_from_soinfo(soinfo); 179 | realpath = (realpath == NULL) ? "None" : realpath; 180 | log_info("\tbaseAddr: %p soName: %s\n", 181 | reinterpret_cast(soinfo->base), 182 | realpath); 183 | soinfo = reinterpret_cast(soinfo->next); 184 | } // while 185 | this->dl_mutex_unlock(); 186 | } 187 | return; 188 | } 189 | 190 | bool elf_hooker::new_module(const char * filename, elf_module & module) { 191 | bool found = false; 192 | if (this->m_is_use_solist && this->m_soinfo_list) { 193 | this->dl_mutex_lock(); 194 | struct soinfo * ss = reinterpret_cast(m_soinfo_list); 195 | while(ss) { 196 | const char * realpath = get_realpath_from_soinfo(ss); 197 | if (ss != NULL) { 198 | void * base_addr = base_addr_from_soinfo(ss); 199 | if (base_addr != NULL && strstr(realpath, filename)) { 200 | // log_dbg("new_module found: %s, base: %p", realpath, base_addr); 201 | module.set_module_name(realpath); 202 | module.set_base_addr((ElfW(Addr))base_addr); 203 | found = true; 204 | break; 205 | } 206 | } 207 | ss = reinterpret_cast(ss->next); 208 | } // while 209 | this->dl_mutex_unlock(); 210 | } else { 211 | std::string module_name = filename; 212 | std::map::iterator itor = m_modules.find(module_name); 213 | if (itor != m_modules.end()) { 214 | module = itor->second; 215 | found = true; 216 | } 217 | } 218 | return found; 219 | } 220 | 221 | void elf_hooker::hook_all_modules(struct elf_rebinds * rebinds) { 222 | if (this->m_is_use_solist && this->m_soinfo_list) { 223 | this->dl_mutex_lock(); 224 | struct soinfo * ss = reinterpret_cast(m_soinfo_list); 225 | while(ss) { 226 | const char * realpath = get_realpath_from_soinfo(ss); 227 | void * base_addr = base_addr_from_soinfo(ss); 228 | if (base_addr != NULL) { 229 | if (!this->m_prehook_cb || this->m_prehook_cb(realpath)) { 230 | log_info("Hook module(%s), base(%p)\n", realpath, base_addr); 231 | elf_module module(reinterpret_cast(base_addr), realpath); 232 | for (int i = 0; rebinds[i].func_name != NULL; i++) { 233 | this->hook(&module, rebinds[i].func_name, rebinds[i].pfn_new, rebinds[i].ppfn_old); 234 | } 235 | } 236 | } 237 | 238 | ss = reinterpret_cast(ss->next); 239 | } // while 240 | this->dl_mutex_unlock(); 241 | } else { 242 | // for android 6 below 243 | for (std::map::iterator itor = m_modules.begin(); 244 | itor != m_modules.end(); 245 | itor++ ) { 246 | elf_module module = itor->second; 247 | if (!this->m_prehook_cb || this->m_prehook_cb(module.get_module_name())) { 248 | log_info("Hook Module(%s), base(%p)\n", module.get_module_name(), (void*)module.get_base_addr()); 249 | for (int i = 0; rebinds[i].func_name != NULL; i++) { 250 | this->hook(&module, rebinds[i].func_name, rebinds[i].pfn_new, rebinds[i].ppfn_old); 251 | } 252 | } 253 | } 254 | } 255 | return; 256 | } 257 | 258 | void elf_hooker::dump_proc_maps() 259 | { 260 | FILE* fd = fopen("/proc/self/maps", "r"); 261 | if (fd != NULL) 262 | { 263 | char buff[2048+1]; 264 | while(fgets(buff, 2048, fd) != NULL) 265 | { 266 | log_info("line:%s", buff); 267 | } 268 | fclose(fd); 269 | } 270 | return; 271 | } 272 | 273 | void * elf_hooker::dlopen(const char * soname, int flags) { 274 | if (this->get_sdk_version() < 24) { 275 | if (this->m_origin_dlopen) { 276 | return this->m_origin_dlopen(soname, flags); 277 | } 278 | return ::dlopen(soname, flags); 279 | } else if (this->m_origin_dlopen_ext){ 280 | struct soinfo * ss = find_loaded_soinfo("libc.so"); 281 | if (ss && ss->base) { 282 | return this->m_origin_dlopen_ext(soname, flags, NULL, (void*)ss->base); 283 | } 284 | } 285 | return NULL; 286 | } 287 | 288 | void * elf_hooker::dlopen_ext(const char * soname, int flags, void * extinfo, void * caller_addr) { 289 | if (this->m_origin_dlopen_ext) { 290 | return this->m_origin_dlopen_ext(soname, flags, NULL, caller_addr); 291 | // return elf_dlopen_ext(soname, flags, NULL, caller_addr, (void*)this->m_origin_dlopen_ext); 292 | } 293 | return NULL; 294 | } 295 | 296 | uint32_t elf_hooker::get_sdk_version() { 297 | char sdk[32] = {0}; 298 | __system_property_get("ro.build.version.sdk", sdk); 299 | return atoi(sdk); 300 | } 301 | 302 | void * elf_hooker::base_addr_from_soinfo(void * soinfo_addr) { 303 | struct soinfo * soinfo = reinterpret_cast(soinfo_addr); 304 | if (soinfo != NULL) { 305 | return reinterpret_cast(soinfo->base); 306 | } 307 | return NULL; 308 | } 309 | 310 | struct soinfo * elf_hooker::find_loaded_soinfo(const char* soname) { 311 | struct soinfo * ss = NULL; 312 | if (m_soinfo_list) { 313 | this->dl_mutex_lock(); 314 | struct soinfo * soinfo = reinterpret_cast(m_soinfo_list); 315 | while(soinfo) { 316 | const char * realpath = get_realpath_from_soinfo(soinfo); 317 | if (strstr((char*)realpath, soname)) { 318 | ss = soinfo; 319 | break; 320 | } // strstr 321 | soinfo = reinterpret_cast(soinfo->next); 322 | } // while 323 | this->dl_mutex_unlock(); 324 | } 325 | return ss; 326 | } 327 | 328 | struct soinfo * elf_hooker::soinfo_from_handle(void * handle) { 329 | struct soinfo * soinfo = NULL; 330 | if ((reinterpret_cast(handle) & 1) == 0) { 331 | return reinterpret_cast(handle); 332 | } 333 | if (this->m_soinfo_handles_map) { 334 | if (this->m_origin_soinfo_map_find) { 335 | void *itor = this->m_origin_soinfo_map_find(this->m_soinfo_handles_map, 336 | reinterpret_cast(&handle)); 337 | if (itor != NULL) { // itor != g_soinfo_handles_map.end() 338 | #if (__LP64__) 339 | soinfo = reinterpret_cast(*(uint64_t * )((uintptr_t) itor + 0x0c)); // index != 0x0c 340 | #else 341 | soinfo = reinterpret_cast(*(uint32_t * )((uintptr_t) itor + 0x0c)); 342 | #endif 343 | log_dbg("soinfo_from_handle()-> handle(%p), soinfo:(%p)\n", handle, soinfo); 344 | } 345 | } else { 346 | typedef std::unordered_map soinfo_map_t; 347 | soinfo_map_t * p_soinfo_map = reinterpret_cast(this->m_soinfo_handles_map); 348 | log_info("soinfo_map.size() %zu", (size_t)p_soinfo_map->size()); 349 | auto itor = p_soinfo_map->find(uintptr_t(handle)); 350 | if (itor != p_soinfo_map->end()) { 351 | soinfo = (struct soinfo *)itor->second; 352 | } 353 | } 354 | } 355 | return reinterpret_cast(soinfo); 356 | } 357 | 358 | bool elf_hooker::load() { 359 | #if defined(__LP64__) 360 | const char* linker_file = "/system/bin/linker64"; 361 | #else 362 | const char* linker_file = "/system/bin/linker"; 363 | #endif 364 | if (this->m_is_loaded) { 365 | return this->m_is_loaded; 366 | } 367 | std::map modules; 368 | ElfW(Addr) base_addr = static_cast(NULL); 369 | ElfW(Addr) bias_addr = static_cast(NULL); 370 | if (this->phrase_proc_maps(linker_file, modules, false) > 0) { 371 | std::map::iterator itor = modules.find(linker_file); 372 | if (itor != modules.end()) { 373 | elf_module module = itor->second; 374 | if (module.get_segment_view()) { 375 | bias_addr = module.get_bias_addr(); 376 | } 377 | } 378 | } 379 | if (!bias_addr) { 380 | log_error("phrase bias_addr fail\n"); 381 | return false; 382 | } 383 | 384 | int sdk_version; 385 | elf_file sfile; 386 | uintptr_t dlopen_offset = static_cast(NULL); 387 | if (!sfile.load(linker_file)) { 388 | log_error("read %s fail\n", linker_file); 389 | goto fail; 390 | } 391 | dlopen_offset = static_cast(NULL); 392 | if (!sfile.find_function("__dl_dlopen", dlopen_offset)) { 393 | log_warn("find dlopen function offset fail\n"); 394 | } 395 | log_dbg("dlopen_offset:(%p)\n", (void *)dlopen_offset); 396 | if (dlopen_offset) { 397 | this->m_origin_dlopen = reinterpret_cast(bias_addr + dlopen_offset); 398 | log_info("m_origin_dlopen:(%p)", m_origin_dlopen); 399 | } 400 | sdk_version = get_sdk_version(); 401 | if (get_sdk_version() >= 23) { 402 | // android [6, 7, 8] 403 | m_modules = modules; 404 | log_info("modules->size() %zu\n", (size_t)modules.size()); 405 | log_info("bias_addr: %p\n", (void*)bias_addr); 406 | 407 | if (bias_addr != static_cast(NULL)) { 408 | // find soinfo_handles_map 409 | uintptr_t soinfo_handles_map_offset = static_cast(NULL); 410 | size_t soinfo_handles_map_size = 0; 411 | if (!sfile.find_variable("__dl__ZL20g_soinfo_handles_map", 412 | soinfo_handles_map_offset, 413 | soinfo_handles_map_size)) { 414 | if (!sfile.find_variable("__dl_g_soinfo_handles_map", 415 | soinfo_handles_map_offset, 416 | soinfo_handles_map_size)) { 417 | log_warn("find g_soinfo_handles_map variable offset fail\n"); 418 | } 419 | } 420 | log_dbg("soinfo_handler_map_offset:(%p), soinfo_handler_map_size(%zu)\n", (void *)soinfo_handles_map_offset, soinfo_handles_map_size); 421 | if (soinfo_handles_map_offset) { 422 | this->m_soinfo_handles_map = reinterpret_cast(bias_addr + soinfo_handles_map_offset); 423 | } 424 | 425 | uintptr_t soinfo_map_find_offset = static_cast(NULL); 426 | if (!sfile.find_function("__dl__ZNSt3__112__hash_tableINS_17__hash_value_typeIjP6soinfoEENS_22__unordered_map_hasherIjS4_NS_4hashIjEELb1EEENS_21__unordered_map_equalIjS4_NS_8equal_toIjEELb1EEENS_9allocatorIS4_EEE4findIjEENS_15__hash_iteratorIPNS_11__hash_nodeIS4_PvEEEERKT_", 427 | soinfo_map_find_offset)) { 428 | if (!sfile.find_function("__dl__ZNSt3__112__hash_tableINS_17__hash_value_typeIjNS_4pairI9MapStringS3_EEEENS_22__unordered_map_hasherIjS5_NS_4hashIjEELb1EEENS_21__unordered_map_equalIjS5_NS_8equal_toIjEELb1EEENS_9allocatorIS5_EEE4findIjEENS_15__hash_iteratorIPNS_11__hash_nodeIS5_PvEEEERKT_", 429 | soinfo_map_find_offset)) { 430 | log_warn("find soinfo_map's find function offset fail\n"); 431 | } 432 | } 433 | log_dbg("soinfo_map_find_offset:(%p)\n", (void *)soinfo_map_find_offset); 434 | if (soinfo_map_find_offset) { 435 | this->m_origin_soinfo_map_find = reinterpret_cast(bias_addr + soinfo_map_find_offset); 436 | } 437 | //find dlopen_ext 438 | uintptr_t dlopen_ext_offset = static_cast(NULL); 439 | if (this->get_sdk_version() < 28) { 440 | if (!sfile.find_function("__dl__ZL10dlopen_extPKciPK17android_dlextinfoPKv", dlopen_ext_offset)) { 441 | if (!sfile.find_function("__dl__ZL10dlopen_extPKciPK17android_dlextinfoPv", dlopen_ext_offset)) { 442 | log_warn("find dlopen_ext function offset fail\n"); 443 | } 444 | } 445 | log_dbg("dlopen_ext_offset:(%p)\n", (void *)dlopen_ext_offset); 446 | } else { 447 | if (!sfile.find_function("__dl___loader_android_dlopen_ext", dlopen_ext_offset)) { 448 | log_info("find dlopen_ext function offset fail\n"); 449 | } 450 | log_info("dlopen_ext_offset:(%p)\n", (void *)dlopen_ext_offset); 451 | } 452 | if (dlopen_ext_offset) { 453 | this->m_origin_dlopen_ext = reinterpret_cast(bias_addr + dlopen_ext_offset); 454 | log_info("m_origin_dlopen_ext:(%p)\n", m_origin_dlopen_ext); 455 | } 456 | // find solist 457 | uintptr_t solist_offset = static_cast(NULL); 458 | size_t solist_size = 0; 459 | if (!sfile.find_variable("__dl__ZL6solist", solist_offset, solist_size)) { 460 | log_warn("find solist variable offset fail!\n"); 461 | } 462 | log_dbg("solist_offset:(%p)\n", (void *)solist_offset); 463 | if (solist_offset) { 464 | uintptr_t addr = *(uintptr_t*)(bias_addr + solist_offset); 465 | this->m_soinfo_list = reinterpret_cast(addr); 466 | log_dbg("m_soinfo_list:(%p)\n", (void*)m_soinfo_list); 467 | } 468 | // find dl_mutex, pthread_mutex_lock and pthread_mutex_unlock 469 | uintptr_t dl_mutex_offset = static_cast(NULL); 470 | size_t dl_mutex_size = 0; 471 | if (!sfile.find_variable("__dl__ZL10g_dl_mutex", dl_mutex_offset, dl_mutex_size)) { 472 | log_warn("find g_dl_mutex variable offset fail!\n"); 473 | } 474 | log_dbg("dl_mutex_offset:(%p)\n", (void *)dl_mutex_offset); 475 | if (dl_mutex_offset) { 476 | uint32_t * addr = reinterpret_cast(bias_addr + dl_mutex_offset); 477 | this->m_dl_mutex = reinterpret_cast(addr); 478 | log_dbg("m_dl_mutex:(%p)\n", (uint32_t*)m_dl_mutex); 479 | } 480 | 481 | uintptr_t dl_mutex_lock_offset = static_cast(NULL); 482 | if (!sfile.find_function("__dl_pthread_mutex_lock", dl_mutex_lock_offset)) { 483 | log_warn("find __dl_pthread_mutex_lock funciton offset fail!\n"); 484 | } 485 | log_dbg("dl_mutex_lock_offset:(%p)\n", (void *)dl_mutex_lock_offset); 486 | if (dl_mutex_lock_offset) { 487 | this->m_dl_mutex_lock = reinterpret_cast(bias_addr + dl_mutex_lock_offset); 488 | log_dbg("m_dl_mutex_lock:(%p)\n", (uint32_t*)m_dl_mutex_lock); 489 | } 490 | 491 | uintptr_t dl_mutex_unlock_offset = static_cast(NULL); 492 | if (!sfile.find_function("__dl_pthread_mutex_unlock", dl_mutex_unlock_offset)) { 493 | log_warn("find __dl_pthread_mutex_lock funciton offset fail!\n"); 494 | } 495 | log_dbg("dl_mutex_unlock_offset:(%p)\n", (void *)dl_mutex_unlock_offset); 496 | if (dl_mutex_unlock_offset) { 497 | this->m_dl_mutex_unlock = reinterpret_cast(bias_addr + dl_mutex_unlock_offset); 498 | log_dbg("m_dl_mutex_unlock:(%p)\n", (uint32_t*)m_dl_mutex_unlock); 499 | } 500 | if (this->m_soinfo_list && this->m_dl_mutex && this->m_dl_mutex_lock && this->m_dl_mutex_unlock) { 501 | this->m_is_use_solist = true; 502 | } 503 | } /* bias_addr != NULL */ 504 | if (this->m_is_use_solist) { 505 | this->m_is_loaded = true; 506 | log_info("sdk version (%d), loaded(%d), modules.size(%zu) with solist\n", sdk_version, this->m_is_loaded, (size_t)m_modules.size()); 507 | return this->m_is_loaded; 508 | } 509 | } /* get_sdk_version() >= 23 */ 510 | 511 | fail: 512 | this->m_is_loaded = this->build_all_modules(); 513 | log_info("sdk version (%d), loaded(%d), modules.size(%zu)\n", sdk_version, this->m_is_loaded, (size_t)m_modules.size()); 514 | this->m_is_use_solist = false; 515 | return this->m_is_loaded; 516 | } 517 | 518 | bool elf_hooker::find_function_addr(const char * module_name, const char * sym_name, uintptr_t & func_addr) { 519 | elf_module module; 520 | func_addr = (uintptr_t)NULL; 521 | if(!this->new_module(module_name, module)) { 522 | log_error("module(%s) not found!\n", module_name); 523 | return false; 524 | } 525 | 526 | if (!module.get_segment_view()) { 527 | log_error("load elf module(%s) fail\n", module_name); 528 | return false; 529 | } 530 | 531 | uintptr_t bias_addr = module.get_bias_addr(); 532 | if (bias_addr) { 533 | elf_file sfile; 534 | if (!sfile.load(module_name)) { 535 | log_error("read module (%s) fail\n", module_name); 536 | return false; 537 | } 538 | uintptr_t offset; 539 | if (!sfile.find_function(sym_name, offset)) { 540 | log_error("count not find sym (%s)\n", sym_name); 541 | return false; 542 | } 543 | if (offset) { 544 | func_addr = bias_addr + offset; 545 | return true; 546 | } 547 | } 548 | return false; 549 | } 550 | 551 | const char * elf_hooker::get_realpath_from_soinfo(struct soinfo * soinfo) { 552 | const char * soname = ""; 553 | #if defined(__work_around_b_24465209__) 554 | if (soinfo) { 555 | const char * realpath = NULL; 556 | uint32_t * p = reinterpret_cast((uintptr_t)soinfo + 0x17C); 557 | uint8_t b = *reinterpret_cast((uintptr_t)soinfo + 0x17C); 558 | if (b & 0x01) { 559 | realpath = *reinterpret_cast((uintptr_t)soinfo + 0x184); 560 | } else { 561 | realpath = reinterpret_cast((uintptr_t)soinfo + 0x17D); 562 | } 563 | if (realpath) { 564 | soname = realpath; 565 | } 566 | if (!soname) { 567 | soname = (const char *)soinfo->old_name; 568 | } 569 | } 570 | #else 571 | if (soinfo) { 572 | const char * realpath = NULL; 573 | uint32_t * p = reinterpret_cast((uintptr_t)soinfo + 0x1A0); 574 | uint8_t b = *reinterpret_cast((uintptr_t)soinfo + 0x1A0); 575 | if (b & 0x01) { 576 | realpath = *reinterpret_cast((uintptr_t)soinfo + 0x1B0); 577 | } else { 578 | realpath = reinterpret_cast((uintptr_t)soinfo + 0x1A1); 579 | } 580 | if (realpath) { 581 | soname = realpath; 582 | } 583 | } 584 | #endif 585 | return soname; 586 | } 587 | 588 | -------------------------------------------------------------------------------- /src/elf_hooker.h: -------------------------------------------------------------------------------- 1 | #if !defined(__ELF_HOOKER_H__) 2 | #define __ELF_HOOKER_H__ 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include "elf_module.h" 9 | 10 | struct elf_rebinds { 11 | const char * func_name; 12 | void * pfn_new; 13 | void ** ppfn_old; 14 | }; 15 | 16 | class elf_hooker { 17 | 18 | public: 19 | 20 | typedef void * (*fn_dlopen)(const void * soname, int flags); 21 | typedef void * (*fn_soinfo_map_find)(void * map, uintptr_t * handle); 22 | typedef void * (*fn_dlopen_ext)(const char * soname, int flags, void * extinfo, void * caller_addr); 23 | typedef void (*fn_dl_mutex_lock)(void * mutex); 24 | typedef void (*fn_dl_mutex_unlock)(void * mutex); 25 | 26 | elf_hooker(); 27 | ~elf_hooker(); 28 | 29 | static uint32_t get_sdk_version(); 30 | static void* base_addr_from_soinfo(void* soinfo_addr); 31 | bool load(); 32 | bool build_all_modules(); 33 | bool find_function_addr(const char * module_name, const char * sym_name, uintptr_t & func_addr); 34 | 35 | bool new_module(const char * filename, elf_module & module); 36 | 37 | 38 | struct soinfo * find_loaded_soinfo(const char* soname); 39 | struct soinfo * soinfo_from_handle(void * handle); 40 | const char * get_realpath_from_soinfo(struct soinfo * soinfo); 41 | 42 | /* * 43 | prehook_cb invoked before really hook, 44 | if prehook_cb NOT set or return true, this module will be hooked, 45 | if prehook_cb set and return false, this module will NOT be hooked, 46 | */ 47 | inline void set_prehook_cb(bool (*pfn)(const char *)) { this->m_prehook_cb = pfn; } 48 | inline bool hook(elf_module* module, const char *func_name, void *pfn_new, void **ppfn_old) { 49 | return module->hook(func_name, pfn_new, ppfn_old); 50 | } 51 | 52 | void hook_all_modules(struct elf_rebinds * rebinds); 53 | void dump_soinfo_list(); 54 | void dump_module_list(); 55 | void dump_proc_maps(); 56 | 57 | void * dlopen(const char * soname, int flags); 58 | void * dlopen_ext(const char * soname, int flags, void * extinfo, void * caller_addr); 59 | 60 | void dl_mutex_lock() { 61 | if (this->m_is_use_solist) { 62 | this->m_dl_mutex_lock(this->m_dl_mutex); 63 | } 64 | } 65 | 66 | void dl_mutex_unlock() { 67 | if (this->m_is_use_solist) { 68 | this->m_dl_mutex_unlock(this->m_dl_mutex); 69 | } 70 | } 71 | 72 | protected: 73 | 74 | int phrase_proc_maps(const char* so_name, std::map & modules, bool lock); 75 | bool phrase_proc_base_addr(char* addr, void** pbase_addr, void** pend_addr); 76 | bool phrase_dev_num(char* devno, int *pmajor, int *pminor); 77 | 78 | protected: 79 | 80 | bool m_is_loaded; 81 | bool m_is_use_solist; 82 | fn_dlopen m_origin_dlopen; 83 | fn_dlopen_ext m_origin_dlopen_ext; 84 | fn_soinfo_map_find m_origin_soinfo_map_find; 85 | fn_dl_mutex_lock m_dl_mutex_lock; 86 | fn_dl_mutex_unlock m_dl_mutex_unlock; 87 | void *m_dl_mutex; 88 | void *m_soinfo_handles_map; 89 | void *m_soinfo_list; 90 | std::map m_modules; 91 | bool (*m_prehook_cb)(const char* module_name); 92 | 93 | }; 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /src/elf_log.h: -------------------------------------------------------------------------------- 1 | #if !defined(__ELF_LOG_H__) 2 | #define __ELF_LOG_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define LOG_TO_CONSOLE (0) 9 | 10 | #if (LOG_TO_CONSOLE) 11 | 12 | #define log_info(...) do{ fprintf(stdout, __VA_ARGS__); } while(0) 13 | #define log_error(...) do{ fprintf(stdout, __VA_ARGS__); } while(0) 14 | #define log_warn(...) do{ fprintf(stdout, __VA_ARGS__); } while(0) 15 | #define log_fatal(...) do{ fprintf(stdout, __VA_ARGS__); } while(0) 16 | #define log_dbg(...) do{ fprintf(stdout, __VA_ARGS__); } while(0) 17 | 18 | #else 19 | 20 | #define sTag ("ELFKooH") 21 | #define log_info(...) do{ __android_log_print(ANDROID_LOG_INFO, sTag, __VA_ARGS__); }while(0) 22 | #define log_error(...) do{ __android_log_print(ANDROID_LOG_ERROR, sTag, __VA_ARGS__); }while(0) 23 | #define log_warn(...) do{ __android_log_print(ANDROID_LOG_WARN, sTag, __VA_ARGS__); }while(0) 24 | #define log_dbg(...) do{ __android_log_print(ANDROID_LOG_DEBUG, sTag, __VA_ARGS__); }while(0) 25 | #define log_fatal(...) do{ __android_log_print(ANDROID_LOG_FATAL, sTag, __VA_ARGS__); }while(0) 26 | 27 | #endif 28 | 29 | #endif 30 | 31 | -------------------------------------------------------------------------------- /src/elf_mapped.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "elf_common.h" 10 | #include "elf_mapped.h" 11 | #include "elf_log.h" 12 | 13 | elf_mapped::elf_mapped() : m_map_start(NULL), 14 | m_map_size(0), 15 | m_data(NULL), 16 | m_size (0) { 17 | } 18 | 19 | elf_mapped::~elf_mapped() { 20 | if (m_map_start != NULL) { 21 | munmap(m_map_start, m_map_size); 22 | } 23 | } 24 | 25 | bool elf_mapped::map(int fd, off64_t base_offset, size_t elf_offset, size_t size) { 26 | off64_t offset; 27 | 28 | CHECK(safe_add(&offset, base_offset, elf_offset)); 29 | off64_t page_min = PAGE_START(offset); 30 | off64_t end_offset; 31 | 32 | CHECK(safe_add(&end_offset, offset, size)); 33 | CHECK(safe_add(&end_offset, end_offset, PAGE_OFFSET(offset))); 34 | 35 | size_t map_size = static_cast(end_offset - page_min); 36 | CHECK(map_size >= size); 37 | 38 | uint8_t* map_start = static_cast( 39 | mmap64(NULL, map_size, PROT_READ, MAP_PRIVATE, fd, page_min)); 40 | 41 | if (map_start == MAP_FAILED) { 42 | return false; 43 | } 44 | 45 | m_map_start = map_start; 46 | m_map_size = map_size; 47 | 48 | m_data = map_start + PAGE_OFFSET(offset); 49 | m_size = size; 50 | 51 | return true; 52 | } 53 | -------------------------------------------------------------------------------- /src/elf_mapped.h: -------------------------------------------------------------------------------- 1 | #if !defined(__ELF_MAPPED_H__) 2 | #define __ELF_MAPPED_H__ 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | class elf_mapped { 11 | public: 12 | elf_mapped(); 13 | ~elf_mapped(); 14 | 15 | bool map(int fd, off64_t base_offset, size_t elf_offset, size_t size); 16 | 17 | void* data() const { return m_data; } 18 | size_t size() const { return m_size; } 19 | 20 | private: 21 | void* m_map_start; 22 | size_t m_map_size; 23 | void* m_data; 24 | size_t m_size; 25 | }; 26 | 27 | #endif /* __ELF_MAPPED_H__ */ 28 | 29 | -------------------------------------------------------------------------------- /src/elf_module.cc: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "elf_common.h" 14 | #include "elf_module.h" 15 | #include "elf_log.h" 16 | 17 | elf_module::elf_module(ElfW(Addr) base_addr, const char* module_name) 18 | { 19 | this->m_base_addr = base_addr; 20 | this->m_module_name = module_name == NULL ? "" : module_name; 21 | this->m_bias_addr = 0; 22 | this->m_is_loaded = false; 23 | 24 | this->m_ehdr = NULL; 25 | this->m_phdr = NULL; 26 | this->m_shdr = NULL; 27 | 28 | this->m_dyn_ptr = NULL; 29 | this->m_dyn_size = 0; 30 | 31 | this->m_sym_ptr = NULL; 32 | this->m_sym_size = 0; 33 | 34 | this->m_relplt_addr = 0; 35 | this->m_relplt_bytes = 0; 36 | this->m_reldyn_addr = 0; 37 | this->m_reldyn_bytes = 0; 38 | 39 | this->m_symstr_ptr = NULL; 40 | this->m_shstr_ptr = NULL; 41 | 42 | this->set_is_gnu_hash(false); 43 | this->set_is_use_rela(false); 44 | 45 | return; 46 | } 47 | 48 | elf_module::~elf_module() { 49 | this->m_is_loaded = false; 50 | return; 51 | } 52 | 53 | bool elf_module::is_elf_module(void* base_addr) { 54 | ElfW(Ehdr) *ehdr = reinterpret_cast(base_addr); 55 | if (!ehdr) { 56 | return false; 57 | } 58 | if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) { 59 | return false; 60 | } 61 | int elf_class = ehdr->e_ident[EI_CLASS]; 62 | #if defined(__LP64__) 63 | if (elf_class != ELFCLASS64) { 64 | return false; 65 | } 66 | #else 67 | if (elf_class != ELFCLASS32) { 68 | return false; 69 | } 70 | #endif 71 | if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { 72 | return false; 73 | } 74 | if (ehdr->e_version != EV_CURRENT) { 75 | return false; 76 | } 77 | if (ehdr->e_machine != GetTargetElfMachine()) { 78 | return false; 79 | } 80 | return true; 81 | } 82 | 83 | ElfW(Addr) elf_module::caculate_bias_addr(const ElfW(Ehdr)* elf) { 84 | ElfW(Addr) offset = elf->e_phoff; 85 | const ElfW(Phdr)* phdr_table = reinterpret_cast(reinterpret_cast(elf) + offset); 86 | const ElfW(Phdr)* phdr_end = phdr_table + elf->e_phnum; 87 | 88 | for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_end; phdr++) { 89 | if (phdr->p_type == PT_LOAD) { 90 | return reinterpret_cast(elf) + phdr->p_offset - phdr->p_vaddr; 91 | } 92 | } 93 | return 0; 94 | } 95 | 96 | bool elf_module::get_segment_view(void) { 97 | if (this->m_is_loaded) { 98 | return true; 99 | } 100 | 101 | this->m_ehdr = reinterpret_cast(this->get_base_addr()); 102 | this->m_shdr = reinterpret_cast(this->get_base_addr() + this->m_ehdr->e_shoff); 103 | this->m_phdr = reinterpret_cast(this->get_base_addr() + this->m_ehdr->e_phoff); 104 | 105 | if (!this->m_bias_addr) { 106 | this->m_bias_addr = this->caculate_bias_addr(this->m_ehdr); 107 | } 108 | if (this->m_bias_addr == NULL) { 109 | log_error("[-] bias_addr is null"); 110 | } 111 | if (this->m_ehdr->e_type == ET_EXEC || this->m_ehdr->e_type == ET_DYN) { 112 | //log_info("[+] Executable File or Shared Object, ElfHook Process..\n"); 113 | } else { 114 | //log_info("[-] (%08x) Elf object, NOT Need Process..\n", this->m_ehdr->e_type); 115 | return false; 116 | } 117 | 118 | this->m_shstr_ptr = NULL; 119 | 120 | ElfW(Phdr) *dynamic = NULL; 121 | ElfW(Word) size = 0; 122 | this->get_segment_info(PT_DYNAMIC, &dynamic, &size, &this->m_dyn_ptr); 123 | if(!dynamic) { 124 | log_warn("[-] could't find PT_DYNAMIC segment\n"); 125 | return false; 126 | } 127 | 128 | ElfW(Dyn) *dyn = this->m_dyn_ptr; 129 | this->set_is_gnu_hash(false); 130 | this->m_dyn_size = size / sizeof(Elf32_Dyn); 131 | for(int i = 0; i < (int)this->m_dyn_size && dyn->d_tag != DT_NULL; i += 1, dyn += 1) { 132 | switch(dyn->d_tag) { 133 | case DT_SYMTAB: 134 | this->m_sym_ptr = reinterpret_cast(this->get_bias_addr() + dyn->d_un.d_ptr); 135 | break; 136 | case DT_STRTAB: 137 | this->m_symstr_ptr = reinterpret_cast(this->get_bias_addr() + dyn->d_un.d_ptr); 138 | break; 139 | case DT_PLTREL: 140 | if (dyn->d_un.d_val == DT_RELA) { 141 | this->set_is_use_rela(true); 142 | } 143 | break; 144 | case DT_REL: 145 | //case DT_ANDROID_REL: 146 | this->m_reldyn_addr = (ElfW(Addr))(this->get_bias_addr() + dyn->d_un.d_ptr); 147 | break; 148 | case DT_RELSZ: 149 | //case DT_ANDROID_RELSZ: 150 | this->m_reldyn_bytes = dyn->d_un.d_val; 151 | break; 152 | case DT_JMPREL: 153 | this->m_relplt_addr = this->get_bias_addr() + dyn->d_un.d_ptr; 154 | break; 155 | case DT_PLTRELSZ: 156 | this->m_relplt_bytes = dyn->d_un.d_val; 157 | break; 158 | case DT_HASH: { 159 | uint32_t *rawdata = reinterpret_cast(this->get_bias_addr() + dyn->d_un.d_ptr); 160 | this->m_nbucket = rawdata[0]; 161 | this->m_nchain = rawdata[1]; 162 | this->m_bucket = rawdata + 2; 163 | this->m_chain = this->m_bucket + this->m_nbucket; 164 | this->m_sym_size = this->m_nchain; 165 | // log_dbg("nbucket: %d, nchain: %d, bucket: %p, chain:%p\n", this->m_nbucket, this->m_nchain, this->m_bucket, this->m_chain); 166 | break; 167 | } 168 | case DT_GNU_HASH: { 169 | uint32_t *rawdata = reinterpret_cast(this->get_bias_addr() + dyn->d_un.d_ptr); 170 | this->m_gnu_nbucket = rawdata[0]; 171 | this->m_gnu_symndx = rawdata[1]; 172 | this->m_gnu_maskwords = rawdata[2]; 173 | this->m_gnu_shift2 = rawdata[3]; 174 | this->m_gnu_bloom_filter = reinterpret_cast(this->get_bias_addr() + dyn->d_un.d_ptr + 16); 175 | this->m_gnu_bucket = reinterpret_cast(this->m_gnu_bloom_filter + this->m_gnu_maskwords); 176 | this->m_gnu_chain = this->m_gnu_bucket + this->m_gnu_nbucket - this->m_gnu_symndx; 177 | 178 | 179 | if (!powerof2(this->m_gnu_maskwords)) { 180 | log_error("[-] invalid maskwords for gnu_hash = 0x%x, in \"%s\" expecting power to two", 181 | this->m_gnu_maskwords, get_module_name()); 182 | return false; 183 | } 184 | this->m_gnu_maskwords -= 1; 185 | this->set_is_gnu_hash(true); 186 | 187 | log_dbg("bbucket(%d), symndx(%d), maskworks(%d), shift2(%d)\n", 188 | this->m_gnu_nbucket, this->m_gnu_symndx, 189 | this->m_gnu_maskwords, this->m_gnu_shift2); 190 | break; 191 | } 192 | } 193 | } 194 | return true; 195 | } 196 | 197 | template 198 | void elf_module::get_section_info(const char *name, ElfW(Shdr) **ppShdr, ElfW(Word) *pSize, T *data) { 199 | ElfW(Shdr)* _shdr = this->find_section_by_name(name); 200 | if(_shdr) { 201 | SAFE_SET_VALUE(pSize, _shdr->sh_size / _shdr->sh_entsize); 202 | SAFE_SET_VALUE(data, reinterpret_cast(this->get_bias_addr() + _shdr->sh_offset)); 203 | } else { 204 | log_warn("[-] Could not found section %s\n", name); 205 | } 206 | SAFE_SET_VALUE(ppShdr, _shdr); 207 | } 208 | 209 | template 210 | void elf_module::get_segment_info(const ElfW(Word) type, ElfW(Phdr) **ppPhdr, ElfW(Word) *pSize, T *data) { 211 | ElfW(Phdr)* _phdr = find_segment_by_type(type); 212 | if (_phdr) { 213 | SAFE_SET_VALUE(data, reinterpret_cast(this->get_bias_addr() + _phdr->p_vaddr)); 214 | SAFE_SET_VALUE(pSize, _phdr->p_memsz); 215 | } else { 216 | log_warn("[-] Could not found segment type is %d\n", type); 217 | } 218 | SAFE_SET_VALUE(ppPhdr, _phdr); 219 | } 220 | 221 | ElfW(Shdr)* elf_module::find_section_by_name(const char *sname) { 222 | ElfW(Shdr)* target = NULL; 223 | ElfW(Shdr)* shdr = this->m_shdr; 224 | for(int i = 0; i < this->m_ehdr->e_shnum; i += 1) { 225 | const char * name = (const char *)(shdr[i].sh_name + this->m_shstr_ptr); 226 | if(!strncmp(name, sname, strlen(sname))) { 227 | target = (ElfW(Shdr)*)(shdr + i); 228 | break; 229 | } 230 | } 231 | return target; 232 | } 233 | 234 | ElfW(Phdr)* elf_module::find_segment_by_type(const ElfW(Word) type) { 235 | ElfW(Phdr)* target = NULL; 236 | ElfW(Phdr)* phdr = this->m_phdr; 237 | for(int i = 0; i < this->m_ehdr->e_phnum; i += 1) { 238 | if(phdr[i].p_type == type) { 239 | target = phdr + i; 240 | break; 241 | } 242 | } 243 | return target; 244 | } 245 | 246 | uint32_t elf_module::elf_hash(const char * name) { 247 | const unsigned char *tmp = (const unsigned char *) name; 248 | uint32_t h = 0, g; 249 | while (*tmp) { 250 | h = (h << 4) + *tmp++; 251 | g = h & 0xf0000000; 252 | h ^= g; 253 | h ^= g >> 24; 254 | } 255 | return h; 256 | } 257 | 258 | uint32_t elf_module::gnu_hash(const char * s) { 259 | uint32_t h = 5381; 260 | for (unsigned char c = *s; c != '\0'; c = *++s) { 261 | h = h * 33 + c; 262 | } 263 | return h; 264 | } 265 | 266 | bool elf_module::elf_lookup(char const* symbol, ElfW(Sym) **sym, int *symidx) { 267 | ElfW(Sym)* target = NULL; 268 | if (!this->m_bucket || !this-> m_chain) { 269 | return false; 270 | } 271 | uint32_t hash = elf_hash(symbol); 272 | uint32_t index = this->m_bucket[hash % this->m_nbucket]; 273 | 274 | if (!strcmp(this->m_symstr_ptr + this->m_sym_ptr[index].st_name, symbol)) { 275 | target = this->m_sym_ptr + index; 276 | } 277 | if (!target) { 278 | do { 279 | index = this->m_chain[index]; 280 | if (!strcmp(this->m_symstr_ptr + this->m_sym_ptr[index].st_name, symbol)) { 281 | target = this->m_sym_ptr + index; 282 | break; 283 | } 284 | } while (index != 0); 285 | } 286 | if(target) { 287 | SAFE_SET_VALUE(sym, target); 288 | SAFE_SET_VALUE(symidx, index); 289 | return true; 290 | } 291 | return false; 292 | } 293 | 294 | bool elf_module::gnu_lookup(char const* symbol, ElfW(Sym) **sym, int *symidx) { 295 | uint32_t hash = this->gnu_hash(symbol); 296 | uint32_t h2 = hash >> this->m_gnu_shift2; 297 | 298 | if (!this->m_gnu_bloom_filter || !this->m_gnu_bucket || !this->m_gnu_chain) { 299 | return false; 300 | } 301 | 302 | uint32_t bloom_mask_bits = sizeof(ElfW(Addr)) * 8; 303 | uint32_t word_num = (hash / bloom_mask_bits) & this->m_gnu_maskwords; 304 | ElfW(Addr) bloom_word = this->m_gnu_bloom_filter[word_num]; 305 | 306 | *sym = NULL; 307 | *symidx = 0; 308 | 309 | // test against bloom filter 310 | if ((1 & (bloom_word >> (hash % bloom_mask_bits)) & (bloom_word >> (h2 % bloom_mask_bits))) == 0) { 311 | return false; 312 | } 313 | 314 | // bloom test says "probably yes"... 315 | uint32_t n = this->m_gnu_bucket[hash % this->m_gnu_nbucket]; 316 | 317 | if (n == 0) { 318 | return false; 319 | } 320 | do { 321 | ElfW(Sym)* s = this->m_sym_ptr + n; 322 | if (((this->m_gnu_chain[n] ^ hash) >> 1) == 0 && 323 | strcmp((this->m_symstr_ptr + s->st_name), symbol) == 0) { 324 | *symidx = n; 325 | *sym = s; 326 | return true; 327 | } 328 | } while ((this->m_gnu_chain[n++] & 1) == 0); 329 | return false; 330 | } 331 | 332 | bool elf_module::find_symbol_by_name(const char *symbol, ElfW(Sym) **sym, int *symidx) { 333 | bool result = false; 334 | if (!this->m_symstr_ptr || !this->m_sym_ptr) { 335 | log_warn("NOT symstr or symtab..\n"); 336 | return false; 337 | } 338 | 339 | if (this->get_is_gnu_hash()) { 340 | result = gnu_lookup(symbol, sym, symidx); 341 | if (!result) { 342 | for(int i = 0; i < (int)this->m_gnu_symndx; i++) { 343 | char const* symName = reinterpret_cast(this->m_sym_ptr[i].st_name + this->m_symstr_ptr); 344 | if (strcmp(symName, symbol) == 0) { 345 | // found symbol 346 | *symidx = i; 347 | *sym = this->m_sym_ptr + i; 348 | result = true; 349 | log_dbg("[+] Found %s in %s (%p) %zd@gnu_hash\n", 350 | symbol, 351 | this->get_module_name(), 352 | reinterpret_cast((*sym)->st_value), 353 | static_cast((*sym)->st_size)); 354 | } 355 | } 356 | } 357 | if (!result) { 358 | // log_dbg("[-] NOT Found %s in %s@%p\n", 359 | // symbol, 360 | // this->get_module_name(), 361 | // reinterpret_cast(this->get_base_addr())); 362 | } 363 | return result; 364 | } 365 | result = elf_lookup(symbol, sym, symidx); 366 | if (false && result) { 367 | log_dbg("[+] Found %s in %s (%p) %zd@elf_hash\n", 368 | symbol, 369 | this->get_module_name(), 370 | reinterpret_cast((*sym)->st_value), 371 | static_cast((*sym)->st_size)); 372 | } else { 373 | log_dbg("[-] NOT Found %s in %s@%p\n", 374 | symbol, 375 | this->get_module_name(), 376 | reinterpret_cast(this->get_base_addr())); 377 | } 378 | return result; 379 | } 380 | 381 | bool elf_module::hook(const char *symbol, void *replace_func, void **old_func) { 382 | ElfW(Sym) *sym = NULL; 383 | int symidx = 0; 384 | 385 | assert(old_func); 386 | assert(replace_func); 387 | assert(symbol); 388 | 389 | if (!this->m_is_loaded) { 390 | this->m_is_loaded = this->get_segment_view(); 391 | if (!this->m_is_loaded) { 392 | return false; 393 | } 394 | } 395 | 396 | this->find_symbol_by_name(symbol, &sym, &symidx); 397 | if(!sym) { 398 | return false; 399 | } else { 400 | log_dbg("[+] symbol %s, sym %p, symidx %d.\n", symbol, sym, symidx); 401 | } 402 | 403 | int relplt_counts = this->get_is_use_rela() ? this->m_relplt_bytes / sizeof(ElfW(Rela)) : this->m_relplt_bytes / sizeof(ElfW(Rel)); 404 | for (uint32_t i = 0; i < relplt_counts; i++) { 405 | unsigned long r_info = 0; // for Elf32 it's Elf32_Word, but Elf64 it's Elf64_Xword. 406 | ElfW(Addr) r_offset = 0; 407 | if (this->get_is_use_rela()) { 408 | ElfW(Rela) *rela = reinterpret_cast(this->m_relplt_addr + sizeof(ElfW(Rela)) * i); 409 | r_info = (unsigned long)rela->r_info; 410 | r_offset = rela->r_offset; 411 | } else { 412 | ElfW(Rel) *rel = reinterpret_cast(this->m_relplt_addr + sizeof(ElfW(Rel)) * i); 413 | r_info = (unsigned long)rel->r_info; 414 | r_offset = rel->r_offset; 415 | } 416 | 417 | if (elf_r_sym(r_info) == symidx && elf_r_type(r_info) == R_GENERIC_JUMP_SLOT) { 418 | void *addr = (void *) (this->get_bias_addr() + r_offset); 419 | if (this->replace_function(addr, replace_func, old_func)) { 420 | log_warn("replace_function fail\n"); 421 | return false; 422 | } 423 | break; 424 | } 425 | } 426 | 427 | int reldyn_counts = this->get_is_use_rela() ? this->m_reldyn_bytes / sizeof(ElfW(Rela)) : this->m_reldyn_bytes / sizeof(ElfW(Rel)); 428 | for (uint32_t i = 0; i < reldyn_counts; i++) { 429 | unsigned long r_info = 0; // for Elf32 it's Elf32_Word, but Elf64 it's Elf64_Xword. 430 | ElfW(Addr) r_offset = 0; 431 | if (this->get_is_use_rela()) { 432 | ElfW(Rela) *rela = reinterpret_cast(this->m_reldyn_addr + sizeof(ElfW(Rela)) * i); 433 | r_info = (unsigned long)rela->r_info; 434 | r_offset = rela->r_offset; 435 | } else { 436 | ElfW(Rel) *rel = reinterpret_cast(this->m_reldyn_addr + sizeof(ElfW(Rel)) * i); 437 | r_info = (unsigned long)rel->r_info; 438 | r_offset = rel->r_offset; 439 | } 440 | 441 | if (elf_r_sym(r_info) == symidx && 442 | (elf_r_type(r_info) == R_GENERIC_ABSOLUTE || 443 | elf_r_type(r_info) == R_GENERIC_GLOB_DAT)) { 444 | void *addr = (void *) (this->get_bias_addr() + r_offset); 445 | if (this->replace_function(addr, replace_func, old_func)) { 446 | return false; 447 | } 448 | } 449 | } 450 | return true; 451 | } 452 | 453 | int elf_module::set_mem_access(ElfW(Addr) addr, int prots) { 454 | void *page_start_addr = (void *)PAGE_START(addr); 455 | return mprotect(page_start_addr, getpagesize(), prots); 456 | } 457 | 458 | int elf_module::get_mem_access(ElfW(Addr) addr, uint32_t* pprot) { 459 | int result = -1; 460 | 461 | const ElfW(Phdr)* phdr_table = this->m_phdr; 462 | const ElfW(Phdr)* phdr_end = phdr_table + this->m_ehdr->e_phnum; 463 | 464 | for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_end; phdr++) { 465 | if (phdr->p_type == PT_LOAD) { 466 | ElfW(Addr) seg_start = this->get_bias_addr() + phdr->p_vaddr; 467 | ElfW(Addr) seg_end = seg_start + phdr->p_memsz; 468 | 469 | ElfW(Addr) seg_page_start = PAGE_START(seg_start); 470 | ElfW(Addr) seg_page_end = PAGE_END(seg_end); 471 | 472 | if (addr >= seg_page_start && addr < seg_page_end){ 473 | *pprot = PFLAGS_TO_PROT(phdr->p_flags), 474 | result = 0; 475 | } 476 | } 477 | } 478 | return result; 479 | } 480 | 481 | void elf_module::clear_cache(void* addr, size_t len) { 482 | uint8_t *p = (uint8_t*)addr; 483 | uint8_t *q = p + len; 484 | #if __arm__ 485 | __builtin___clear_cache((char *)p, (char*)q); 486 | #elif __aarch64__ 487 | # define ASM __asm__ __volatile__ 488 | long icachez, dcachez, ctr_el0; 489 | ASM( "mrs %0, ctr_el0\n" : "=r"(ctr_el0) ::); // read CTR_EL0 490 | dcachez = 4l << ((ctr_el0 >> 16) & 0xF); // extract data cache line size 491 | icachez = 4l << (ctr_el0 & 0xF); // extract icache line size 492 | do { 493 | ASM("dc cvau, %0\n" :: "r"(p) :); // spill dcache line 494 | } while((p += dcachez) < q); 495 | ASM("dsb ish\n" :::); // barrier, let dcache operations retire 496 | p = (uint8_t*)addr; 497 | do { 498 | ASM("ic ivau, %0\n" :: "r"(p) :); // invalidate icache line 499 | } while((p += icachez) < q); 500 | ASM("dsb ish\n" :::); // barrier, let icache operations retire 501 | ASM("isb\n" :::); // instruction barrier 502 | #endif 503 | } 504 | // Fatal signal 31 (SIGSYS), code 1 (SYS_SECCOMP) in tid 14930 (.tdx.AndroidNew), pid 14930 (.tdx.AndroidNew) 505 | 506 | bool elf_module::replace_function(void* addr, void *replace_func, void **old_func) { 507 | bool res = false; 508 | uint32_t old_prots = PROT_READ; 509 | uint32_t prots = old_prots; 510 | if(*(void **)addr == replace_func) { 511 | log_warn("[-] addr %p had been replace.\n", addr); 512 | goto fail; 513 | } 514 | 515 | if(!*old_func){ 516 | *old_func = *(void **)addr; 517 | } 518 | 519 | if (get_mem_access(reinterpret_cast(addr), &old_prots)) { 520 | log_error("[-] read mem access fails, error %s.\n", strerror(errno)); 521 | res = true; 522 | goto fail; 523 | } 524 | 525 | prots = old_prots | PROT_WRITE; 526 | if ((prots & PROT_WRITE) != 0) { // make sure we're never simultaneously writable / executable 527 | prots &= ~PROT_EXEC; 528 | } 529 | 530 | if(set_mem_access(reinterpret_cast(addr), prots)) { 531 | log_error("[-] modify mem access fails, error %s.\n", strerror(errno)); 532 | res = true; 533 | goto fail; 534 | } 535 | 536 | *(void **)addr = replace_func; 537 | clear_cache(addr, getpagesize()); 538 | log_dbg("[+] old_func is %p, replace_func is %p, new_func %p.\n", *old_func, replace_func, reinterpret_cast(*(void**)addr)); 539 | 540 | fail: 541 | return res; 542 | } 543 | 544 | 545 | void elf_module::dump_elf_header(void) { 546 | static char alpha_tab[17] = "0123456789ABCDEF"; 547 | char buff[EI_NIDENT*3+1]; 548 | 549 | ElfW(Ehdr)* ehdr = this->m_ehdr; 550 | 551 | log_info("Elf Header :\n"); 552 | for(int i = 0; i < EI_NIDENT; i++) { 553 | uint8_t ch = ehdr->e_ident[i]; 554 | buff[i*3 + 0] = alpha_tab[(int)((ch >> 4) & 0x0F)]; 555 | buff[i*3 + 1] = alpha_tab[(int)(ch & 0x0F)]; 556 | buff[i*3 + 2] = ' '; 557 | } 558 | buff[EI_NIDENT*3] = '\0'; 559 | 560 | log_info("e_ident: %s\n", buff); 561 | log_info("e_type: %x\n", ehdr->e_type); 562 | log_info("e_machine: %x\n", ehdr->e_machine); 563 | log_info("e_version: %x\n", ehdr->e_version); 564 | log_info("e_entry: %lx\n", (unsigned long)ehdr->e_entry); 565 | log_info("e_phoff: %lx\n", (unsigned long)ehdr->e_phoff); 566 | log_info("e_shoff: %lx\n", (unsigned long)ehdr->e_shoff); 567 | log_info("e_flags: %x\n", ehdr->e_flags); 568 | log_info("e_ehsize: %x\n", ehdr->e_ehsize); 569 | log_info("e_phentsize: %x\n", ehdr->e_phentsize); 570 | log_info("e_phnum: %x\n", ehdr->e_phnum); 571 | log_info("e_shentsize: %x\n", ehdr->e_shentsize); 572 | log_info("e_shnum: %x\n", ehdr->e_shnum); 573 | log_info("e_shstrndx: %x\n", ehdr->e_shstrndx); 574 | } 575 | 576 | void elf_module::dump_sections(void) { 577 | ElfW(Half) shnum = this->m_ehdr->e_shnum; 578 | ElfW(Shdr) *shdr = this->m_shdr; 579 | 580 | log_info("Sections: :%d\n",shnum); 581 | for(int i = 0; i < shnum; i += 1, shdr += 1) { 582 | const char *name = shdr->sh_name == 0 || !this->m_shstr_ptr ? "UNKOWN" : (const char *)(shdr->sh_name + this->m_shstr_ptr); 583 | log_info("[%.2d] %-20s 0x%lx\n", i, name, (unsigned long)shdr->sh_addr); 584 | } 585 | 586 | log_info("Sections: end\n"); 587 | } 588 | 589 | void elf_module::dump_sections2() { 590 | ElfW(Half) shnum = this->m_ehdr->e_shnum; 591 | ElfW(Shdr) *shdr = this->m_shdr; 592 | 593 | log_info("Sections: :%d\n",shnum); 594 | for(int i = 0; i < shnum; i += 1, shdr += 1) { 595 | log_info("Name(%x);Type(%x);Addr(%lx);offset(%lx);entSize(%lx)\n", 596 | shdr->sh_name, 597 | shdr->sh_type, 598 | (unsigned long)shdr->sh_addr, 599 | (unsigned long)shdr->sh_offset, 600 | (unsigned long)shdr->sh_entsize); 601 | } 602 | log_info("Sections: end\n"); 603 | } 604 | 605 | void elf_module::dump_segments(void) { 606 | ElfW(Phdr) *phdr = this->m_phdr; 607 | ElfW(Half) phnum = this->m_ehdr->e_phnum; 608 | 609 | log_info("Segments: \n"); 610 | for(int i = 0; i < phnum; i++) { 611 | log_info("[%.2d] %-.8x 0x%lx 0x%lx %lu %lu\n", 612 | i, 613 | phdr[i].p_type, 614 | (unsigned long)phdr[i].p_vaddr, 615 | (unsigned long)phdr[i].p_paddr, 616 | (unsigned long)phdr[i].p_filesz, 617 | (unsigned long)phdr[i].p_memsz); 618 | 619 | } 620 | } 621 | const static struct dyn_name_map_t { 622 | const char* dyn_name; 623 | int dyn_tag; 624 | } dyn_name_maps[30] = { 625 | {"DT_NULL", 0}, 626 | {"DT_NEEDED", 1}, 627 | {"DT_PLTRELSZ",2}, 628 | {"DT_PLTGOT", 3}, 629 | {"DT_HASH", 4}, 630 | {"DT_STRTAB", 5}, 631 | {"DT_SYMTAB", 6}, 632 | {"DT_RELA", 7}, 633 | {"DT_RELASZ", 8}, 634 | {"DT_RELAENT", 9}, 635 | {"DT_STRSZ", 10}, 636 | {"DT_SYMENT", 11}, 637 | {"DT_INIT", 12}, 638 | {"DT_FINI", 13}, 639 | {"DT_SONAME", 14}, 640 | {"DT_RPATH", 15}, 641 | {"DT_SYMBOLIC",16}, 642 | {"DT_REL", 17}, 643 | {"DT_RELSZ", 18}, 644 | {"DT_RELENT", 19}, 645 | {"DT_PLTREL", 20}, 646 | {"DT_DEBUG", 21}, 647 | {"DT_TEXTREL", 22}, 648 | {"DT_JMPREL", 23}, 649 | {"DT_LOPROC", 0x70000000}, 650 | {"DT_HIPROC", 0x7fffffff}, 651 | {"DT_GNU_HASH", DT_GNU_HASH}, 652 | {"DT_ANDROID_REL", DT_ANDROID_REL}, 653 | {"DT_ANDROID_RELSZ",DT_ANDROID_RELSZ}, 654 | {NULL, 0} 655 | }; 656 | 657 | const char* elf_module::convert_dynamic_tag_to_name(int d_tag) { 658 | for(int i = 0; dyn_name_maps[i].dyn_name != NULL; i++) { 659 | if (dyn_name_maps[i].dyn_tag == d_tag) { 660 | return dyn_name_maps[i].dyn_name; 661 | } 662 | } 663 | return "UNKNOW"; 664 | } 665 | 666 | void elf_module::dump_dynamics(void) { 667 | ElfW(Dyn) *dyn = this->m_dyn_ptr; 668 | 669 | log_info(".dynamic section info:\n"); 670 | const char *type = NULL; 671 | 672 | for(int i = 0; i < (int)this->m_dyn_size; i++) { 673 | type = convert_dynamic_tag_to_name(dyn[i].d_tag); 674 | log_info("[%.2d] %-14s 0x%lx 0x%lx\n", 675 | i, 676 | type, 677 | (unsigned long)dyn[i].d_tag, 678 | (unsigned long)dyn[i].d_un.d_val); 679 | if(dyn[i].d_tag == DT_NULL) { 680 | break; 681 | } 682 | } 683 | return; 684 | } 685 | 686 | void elf_module::dump_symbols(void) { 687 | ElfW(Sym) *sym = this->m_sym_ptr; 688 | 689 | log_info("dynsym section info: \n"); 690 | if (this->get_is_gnu_hash()) { 691 | 692 | } else { 693 | for(int i=0; i< (int)this->m_sym_size; i++) { 694 | log_info("[%2d] %-20s\n", i, sym[i].st_name + this->m_symstr_ptr); 695 | } 696 | } 697 | 698 | return; 699 | } 700 | 701 | void elf_module::dump_rel_info(void) { 702 | ElfW(Rel)* rels[] = {reinterpret_cast(this->m_reldyn_addr), 703 | reinterpret_cast(this->m_relplt_addr)}; 704 | ElfW(Word) resszs[] = {static_cast(this->m_reldyn_bytes/sizeof(ElfW(Rel))), 705 | static_cast(this->m_relplt_bytes/sizeof(ElfW(Rel)))}; 706 | ElfW(Sym) *sym = this->m_sym_ptr; 707 | log_info("rel section info:\n"); 708 | for(int i = 0; i < (int)(sizeof(rels)/sizeof(rels[0])); i++) { 709 | ElfW(Rel) *rel = rels[i]; 710 | ElfW(Word) relsz = resszs[i]; 711 | for(int j = 0; j < (int)relsz; j += 1) { 712 | const char *name = sym[elf_r_sym(rel[j].r_info)].st_name + this->m_symstr_ptr; 713 | log_info("[%.2d-%.4d] 0x%lx 0x%lx %-10s\n", 714 | i, j, 715 | (unsigned long)rel[j].r_offset, 716 | (unsigned long)rel[j].r_info, 717 | name); 718 | } 719 | } 720 | return; 721 | } 722 | 723 | void elf_module::dump_rela_info(void) { 724 | ElfW(Rela)* relas[] = {reinterpret_cast(this->m_reldyn_addr), 725 | reinterpret_cast(this->m_relplt_addr)}; 726 | ElfW(Word) resszs[] = {static_cast(this->m_reldyn_bytes/sizeof(ElfW(Rela))), 727 | static_cast(this->m_relplt_bytes/sizeof(ElfW(Rela)))}; 728 | 729 | ElfW(Sym) *sym = this->m_sym_ptr; 730 | 731 | log_info("rel section info:\n"); 732 | for(int i = 0; i < (int)(sizeof(relas)/sizeof(relas[0])); i++) { 733 | ElfW(Rela) *rela = relas[i]; 734 | ElfW(Word) relsz = resszs[i]; 735 | for(int j = 0; j < (int)relsz; j += 1) { 736 | const char *name = sym[elf_r_sym(rela[j].r_info)].st_name + this->m_symstr_ptr; 737 | log_info("[%.2d-%.4d] 0x%lx 0x%lx 0x%ld %-10s\n", 738 | i, j, 739 | (unsigned long)rela[j].r_offset, 740 | (unsigned long)rela[j].r_info, 741 | (unsigned long)rela[j].r_addend, 742 | name); 743 | } 744 | } 745 | return; 746 | } 747 | 748 | -------------------------------------------------------------------------------- /src/elf_module.h: -------------------------------------------------------------------------------- 1 | #if !defined(__ELFHOOK_H__) 2 | #define __ELFHOOK_H__ 3 | 4 | #include 5 | #include 6 | #include "elf_common.h" 7 | 8 | 9 | class elf_module { 10 | 11 | public: 12 | 13 | elf_module() { new (this) elf_module(0, ""); } 14 | 15 | elf_module(ElfW(Addr) base_addr, const char* module_name); 16 | ~elf_module(); 17 | 18 | static bool is_elf_module(void* base_addr); 19 | 20 | inline void set_base_addr(ElfW(Addr) base_addr){ this->m_base_addr = base_addr;} 21 | inline void set_module_name(const char * soname) {this->m_module_name = soname;} 22 | inline const char* get_module_name() { return this->m_module_name.c_str(); } 23 | inline ElfW(Addr) get_base_addr() { return this->m_base_addr; } 24 | inline ElfW(Addr) get_bias_addr() { return this->m_bias_addr; } 25 | inline bool get_is_gnu_hash() { return this->m_is_gnu_hash; } 26 | inline void set_is_gnu_hash(bool flag) { this->m_is_gnu_hash = flag; } 27 | inline bool get_is_use_rela() { return this->m_is_use_rela; } 28 | inline void set_is_use_rela(bool flag) { this->m_is_use_rela = flag; } 29 | 30 | bool hook(const char *symbol, void *replace_func, void **old_func); 31 | bool get_segment_view(void); 32 | 33 | void dump_elf_header(void); 34 | void dump_sections(); 35 | void dump_sections2(); 36 | void dump_segments(); 37 | void dump_dynamics(); 38 | void dump_symbols(); 39 | void dump_rel_info(); 40 | void dump_rela_info(); 41 | 42 | protected: 43 | 44 | ElfW(Addr) caculate_bias_addr(const ElfW(Ehdr)* elf); 45 | 46 | uint32_t elf_hash(const char *name); 47 | uint32_t gnu_hash(const char *name); 48 | 49 | ElfW(Phdr)* find_segment_by_type(const ElfW(Word) type); 50 | ElfW(Shdr)* find_section_by_name(const char *sname); 51 | 52 | bool gnu_lookup(char const* symbol, ElfW(Sym) **sym, int *symidx); 53 | bool elf_lookup(char const* symbol, ElfW(Sym) **sym, int *symidx); 54 | bool find_symbol_by_name(const char *symbol, ElfW(Sym) **sym, int *symidx); 55 | 56 | template 57 | void get_segment_info(const ElfW(Word) type, ElfW(Phdr) **ppPhdr, ElfW(Word) *pSize, T *data); 58 | template 59 | void get_section_info(const char *name, ElfW(Shdr) **ppShdr, ElfW(Word) *pSize, T *data); 60 | 61 | void clear_cache(void *addr, size_t len); 62 | int get_mem_access(ElfW(Addr) addr, uint32_t* pprot); 63 | int set_mem_access(ElfW(Addr) 64 | addr, int prots); 65 | bool replace_function(void *addr, void *replace_func, void **old_func); 66 | 67 | const char* convert_dynamic_tag_to_name(int d_tag); 68 | 69 | protected: 70 | 71 | ElfW(Addr) m_base_addr; 72 | ElfW(Addr) m_bias_addr; 73 | std::string m_module_name; 74 | bool m_is_loaded; 75 | protected: 76 | 77 | ElfW(Ehdr) *m_ehdr; 78 | ElfW(Phdr) *m_phdr; 79 | ElfW(Shdr) *m_shdr; 80 | 81 | ElfW(Dyn) *m_dyn_ptr; 82 | ElfW(Word) m_dyn_size; 83 | 84 | ElfW(Sym) *m_sym_ptr; 85 | ElfW(Word) m_sym_size; 86 | 87 | ElfW(Addr) m_relplt_addr; 88 | ElfW(Addr) m_reldyn_addr; 89 | 90 | ElfW(Word) m_relplt_bytes; 91 | ElfW(Word) m_reldyn_bytes; 92 | 93 | protected: 94 | //for elf hash 95 | uint32_t m_nbucket; 96 | uint32_t m_nchain; 97 | uint32_t *m_bucket; 98 | uint32_t *m_chain; 99 | 100 | //for gnu hash 101 | uint32_t m_gnu_nbucket; 102 | uint32_t m_gnu_symndx; 103 | uint32_t m_gnu_maskwords; 104 | uint32_t m_gnu_shift2; 105 | uint32_t *m_gnu_bucket; 106 | uint32_t *m_gnu_chain; 107 | ElfW(Addr) *m_gnu_bloom_filter; 108 | 109 | bool m_is_gnu_hash; 110 | bool m_is_use_rela; 111 | 112 | protected: 113 | 114 | const char *m_shstr_ptr; 115 | const char *m_symstr_ptr; 116 | }; 117 | 118 | 119 | #endif 120 | -------------------------------------------------------------------------------- /src/elf_soinfo.h: -------------------------------------------------------------------------------- 1 | #if !defined(__ELFKIT_SOINFO_H__) 2 | #define __ELFKIT_SOINFO_H__ 3 | 4 | #define SOINFO_NAME_LEN (128) 5 | 6 | struct soinfo { 7 | #if defined(__work_around_b_24465209__) 8 | char old_name[SOINFO_NAME_LEN]; 9 | #endif 10 | const ElfW(Phdr) *phdr; 11 | size_t phnum; 12 | #if defined(__work_around_b_24465209__) 13 | ElfW(Addr) unused0; 14 | #endif 15 | ElfW(Addr) base; 16 | size_t size; 17 | #if defined(__work_around_b_24465209__) 18 | uint32_t unused1; // DO NOT USE, maintained for compatibility. 19 | #endif 20 | ElfW(Dyn) *dynamic; 21 | 22 | #if defined(__work_around_b_24465209__) 23 | uint32_t unused2; // DO NOT USE, maintained for compatibility 24 | uint32_t unused3; // DO NOT USE, maintained for compatibility 25 | #endif 26 | struct soinfo *next; 27 | uint32_t flags; 28 | const char *strtab; 29 | ElfW(Sym) *symtab; 30 | size_t nbucket; 31 | size_t nchain; 32 | uint32_t *bucket; 33 | uint32_t *chain; 34 | 35 | #if defined(__mips__) || !defined(__LP64__) 36 | ElfW(Addr)** plt_got; 37 | #endif 38 | 39 | #if defined(USE_RELA) 40 | ElfW(Rela)* plt_rela; 41 | size_t plt_rela_count; 42 | 43 | ElfW(Rela)* rela; 44 | size_t rela_count; 45 | #else 46 | ElfW(Rel)* plt_rel; 47 | size_t plt_rel_count; 48 | 49 | ElfW(Rel)* rel; 50 | size_t rel_count; 51 | #endif 52 | }; 53 | 54 | #endif -------------------------------------------------------------------------------- /src/main.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "elf_hooker.h" 11 | #include "elf_log.h" 12 | 13 | static void* (*__old_impl_dlopen)(const char* filename, int flag); 14 | 15 | static int (*__old_impl_connect)(int sockfd,struct sockaddr * serv_addr,int addrlen); 16 | 17 | static void* (*__old_impl_android_dlopen_ext)(const char* filename, int flags, const void* extinfo); 18 | 19 | extern "C" { 20 | 21 | static void* __nativehook_impl_dlopen(const char* filename, int flag) { 22 | log_info("__nativehook_impl_dlopen -> (%s)\n", filename); 23 | void* res = __old_impl_dlopen(filename, flag); 24 | log_info("res: %p\n", res); 25 | return res; 26 | } 27 | 28 | static int __nativehook_impl_connect(int sockfd,struct sockaddr * serv_addr,int addrlen) { 29 | log_info("__nativehook_impl_connect ->\n"); 30 | int res = __old_impl_connect(sockfd, serv_addr, addrlen); 31 | return res; 32 | } 33 | 34 | static void* __nativehook_impl_android_dlopen_ext(const char* filename, int flags, const void* extinfo) { 35 | log_info("__nativehook_impl_android_dlopen_ext -> (%s)\n", filename); 36 | void* res = __old_impl_android_dlopen_ext(filename, flags, extinfo); 37 | log_info("res: %p\n", res); 38 | return res; 39 | } 40 | 41 | } 42 | 43 | static bool __prehook(const char* module_name) { 44 | if (strncmp(module_name, "[vdso]", 6) == 0) { 45 | return false; 46 | } 47 | return true; 48 | } 49 | 50 | 51 | static struct sigaction __origin_sa[NSIG]; 52 | 53 | static void __signal_handler(int sig, siginfo_t * info, void * content) { 54 | if (sig == SIGSEGV) { 55 | log_error(">>>>>> SIGSEGV <<<<<<"); 56 | return; 57 | } else if (sig == SIGSYS) { 58 | log_error(">>>>>> SIGSYS <<<<<<"); 59 | return; 60 | } 61 | } 62 | 63 | void __signal_setup() { 64 | struct sigaction sa; 65 | memset(&__origin_sa, 0, sizeof(struct sigaction) * NSIG); 66 | memset(&sa, 0, sizeof(struct sigaction)); 67 | 68 | sa.sa_sigaction = __signal_handler; 69 | sa.sa_flags = SA_NODEFER; 70 | sigaction(SIGSEGV, &sa, &__origin_sa[SIGSEGV]); 71 | sigaction(SIGSYS, &sa, &__origin_sa[SIGSYS]); 72 | return; 73 | } 74 | 75 | void __signal_dispose() { 76 | sigaction(SIGSEGV, &__origin_sa[SIGSEGV], NULL); 77 | sigaction(SIGSYS, &__origin_sa[SIGSYS], NULL); 78 | return; 79 | } 80 | 81 | int main(int argc, char* argv[]) { 82 | char ch = 0; 83 | elf_hooker hooker; 84 | 85 | // __signal_setup(); 86 | 87 | void* h = dlopen("libart.so", RTLD_LAZY); 88 | void* f = dlsym(h,"artAllocObjectFromCodeResolvedRegion"); 89 | log_info("artAllocObjectFromCodeResolvedRegion : %p\n", f); 90 | 91 | hooker.set_prehook_cb(__prehook); 92 | hooker.load(); 93 | hooker.dump_soinfo_list(); 94 | 95 | struct elf_rebinds rebinds[4] = { 96 | {"dlopen", (void *)__nativehook_impl_dlopen, (void **)&__old_impl_dlopen}, 97 | {"connect", (void *)__nativehook_impl_connect, (void **)&__old_impl_connect}, 98 | {"android_dlopen_ext", (void*)__nativehook_impl_android_dlopen_ext, (void**)&__old_impl_android_dlopen_ext}, 99 | {NULL, NULL, NULL}, 100 | }; 101 | hooker.hook_all_modules(rebinds); 102 | 103 | log_info("old dlopen: %p\n", __old_impl_dlopen); 104 | log_info("old connect: %p\n", __old_impl_connect); 105 | log_info("old android_dlopen_ext: %p\n", __old_impl_android_dlopen_ext); 106 | 107 | uintptr_t origin_dlopen = static_cast(NULL); 108 | #if __LP64__ 109 | const char * ldfile = "/system/lib64/libdl.so"; 110 | #else 111 | const char * ldfile = "/system/lib/libdl.so"; 112 | #endif 113 | hooker.find_function_addr(ldfile, "dlopen", origin_dlopen); 114 | fprintf(stderr ,"origin_dlopen: %p\n", (void*)origin_dlopen); 115 | 116 | void* caller_addr = __builtin_return_address(0); 117 | 118 | void* h_libc = hooker.dlopen_ext("libc.so", RTLD_LAZY, NULL, caller_addr); 119 | void* f_connect = dlsym(h_libc,"connect"); 120 | log_info("libc handle : %p\n", f_connect); 121 | 122 | do { 123 | ch = getc(stdin); 124 | } while(ch != 'q'); 125 | 126 | // __signal_dispose(); 127 | 128 | return 0; 129 | } 130 | 131 | --------------------------------------------------------------------------------