├── .gitignore ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── inject ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── cpp │ ├── CMakeLists.txt │ ├── elfutil.cpp │ ├── elfutil.h │ ├── gotutil.cpp │ ├── include │ └── gotutil.h │ ├── inject.cpp │ └── mylog.h ├── settings.gradle ├── victim ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── cpp │ ├── CMakeLists.txt │ └── victim.c ├── victim_app ├── .gitignore ├── build.gradle ├── nativelib │ ├── .gitignore │ ├── build.gradle │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── cpp │ │ ├── CMakeLists.txt │ │ └── nativelib.cpp │ │ └── java │ │ └── com │ │ └── xhy │ │ └── nativelib │ │ └── NativeLib.java ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── xhy │ │ └── androidgothook │ │ └── MainActivity.java │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ └── activity_main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ └── strings.xml └── workdir ├── arm ├── 0-patch-victim-arm.bat └── 1-run-victim-patch-arm.bat ├── arm64 ├── 0-patch-victim-arm64.bat └── 1-run-victim-patch-arm64.bat └── edit-victim.py /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | *.iml 3 | .gradle 4 | /local.properties 5 | /.idea/caches 6 | /.idea/libraries 7 | /.idea/modules.xml 8 | /.idea/workspace.xml 9 | /.idea/navEditor.xml 10 | /.idea/assetWizardSettings.xml 11 | .DS_Store 12 | /build 13 | /captures 14 | .externalNativeBuild 15 | .cxx 16 | local.properties 17 | /workdir/victim-patch-arm* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Simple Android ARM&ARM64 GOT Hook 2 | 基于链接视图和执行视图,解析ELF,查找导入函数偏移值,替换函数地址。 3 | 4 | 详见: 5 | 6 | [简易Android ARM&ARM64 GOT Hook (一)](https://blog.xhyeax.com/2021/08/23/android-arm-got-hook/) 7 | 8 | [简易Android ARM&ARM64 GOT Hook (二)](https://blog.xhyeax.com/2021/08/30/android-arm-plt-hook/) 9 | 10 | ## 编译 11 | 使用`Android Studio`打开项目,点击`Make Project` 12 | 13 | 编译完成后,在`模块名/build/intermediates/cmake/debug/obj/CPU架构/`目录下, 14 | 15 | 可以找到生成的可执行文件(`victim`)和动态库(`libinject.so`) 16 | 17 | ## 测试 18 | ### 注入可执行文件 19 | `workdir`文件中包含补丁脚本、adb测试脚本。编译完成后按顺序运行即可 20 | 21 | ### 作为动态链接库使用 22 | `victim_app`模块是一个例子。 23 | 24 | 通过配置`CMakeLists.txt`将`libinject.so`作为动态库链接。 25 | 26 | 在`nativelib.cpp`的`JNI_OnLoad`中调用`hackBySegment`替换`getpid`函数。 27 | 28 | Java层调用`stringFromJNI`函数通过JNI获取pid并显示。 29 | 30 | 注释`inject.cpp`的`hack`函数体后,编译生成apk,运行即可。(不注释将查找一次`victim-patch`,不影响运行) 31 | 32 | ### 通过JNI加载 33 | 在Java层通过`System.loadLibrary`加载。最终调用到Native层的`LoadNativeLibrary`函数。 34 | 35 | 其中首先调用`dlopen`加载so(`linker`调用构造函数)。成功后查找`JNI_OnLoad`并调用。 36 | 37 | 原理见[andorid linker 解读1----从loadLibrary到dlopen](https://bbs.pediy.com/thread-264852.htm) 38 | 39 | ## 存在的问题 40 | 1. 未绕过`dlopen`命名空间限制,在`Android 7`以上无法打开非公共库 41 | 2. 未hook`dlopen`,无法实时修改加载模块的GOT表 42 | 3. 基于maps解析,兼容性可能存在一定问题 43 | 4. 基于静态注入,无法绕过完整性检测 44 | 5. 未提供卸载函数,无法恢复GOT表 45 | 6. ... 46 | 47 | ## 总结 48 | 通过本项目,学习了`GOT Hook`原理,ELF文件结构和导入符号的查找方式,目的基本达到。虽然功能还不够完善,但短期内应该不会再改动了(这次是真的了)。 49 | 50 | 实际应用可以考虑使用字节的[bhook](https://github.com/bytedance/bhook) 51 | 52 | ## 参考 53 | [android中基于plt/got的hook实现原理](https://blog.csdn.net/byhook/article/details/103500524) 54 | 55 | [聊聊Linux动态链接中的PLT和GOT(2)——延迟重定位](https://linyt.blog.csdn.net/article/details/51636753) 56 | 57 | [constructor属性函数在动态库加载中的执行顺序](https://zhuanlan.zhihu.com/p/108274829) 58 | 59 | [Android7.0以上命名空间详解(dlopen限制)](https://www.52pojie.cn/thread-948942-1-1.html) 60 | 61 | [Android中GOT表HOOK手动实现](https://blog.csdn.net/u011247544/article/details/78564564) 62 | 63 | [Android GOT Hook](https://www.cnblogs.com/mmmmar/p/8228391.html) 64 | 65 | [基于Android的ELF PLT/GOT符号和重定向过程ELF Hook实现](https://blog.csdn.net/L173864930/article/details/40507359) 66 | 67 | [ELF文件格式与got表hook简单实现](https://bbs.pediy.com/thread-267842.htm) 68 | 69 | [重定位节 - 链接程序和库指南](https://docs.oracle.com/cd/E26926_01/html/E25910/chapter6-54839.html) 70 | 71 | [符号表节 - 链接程序和库指南](https://docs.oracle.com/cd/E26926_01/html/E25910/chapter6-79797.html) 72 | 73 | [散列表节 - 链接程序和库指南](https://docs.oracle.com/cd/E26926_01/html/E25910/chapter6-48031.html) 74 | 75 | [动态节 - 链接程序和库指南](https://docs.oracle.com/cd/E26926_01/html/E25910/chapter6-42444.html) 76 | 77 | [ELF文件结构详解](https://bbs.pediy.com/thread-255670.htm) 78 | 79 | [PLT HOOK](https://zhuanlan.zhihu.com/p/269441842) 80 | 81 | [bhook](https://github.com/bytedance/bhook) 82 | 83 | [xhook](https://github.com/iqiyi/xhook) 84 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | buildscript { 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:7.1.3' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | mavenCentral() 19 | } 20 | } 21 | 22 | task clean(type: Delete) { 23 | delete rootProject.buildDir 24 | } 25 | 26 | ext { 27 | minSdkVersion = 21 28 | compileSdkVersion = 31 29 | targetSdkVersion = 31 30 | buildToolsVersion = "31.0.0" 31 | ndkVersion = "24.0.8215888" 32 | cmakeVersion = "3.18.1" 33 | abiFilters = "armeabi-v7a,arm64-v8a" 34 | javaVersion = JavaVersion.VERSION_1_8 35 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XhyEax/AndroidGotHook/8df7afebb12bb7d14d467f81a393f859b6adfdb1/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Aug 22 20:52:31 CST 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /inject/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /inject/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | } 4 | 5 | android { 6 | compileSdkVersion rootProject.ext.compileSdkVersion 7 | buildToolsVersion rootProject.ext.buildToolsVersion 8 | ndkVersion rootProject.ext.ndkVersion 9 | 10 | defaultConfig { 11 | minSdkVersion rootProject.ext.minSdkVersion 12 | targetSdkVersion rootProject.ext.targetSdkVersion 13 | consumerProguardFiles "consumer-rules.pro" 14 | externalNativeBuild { 15 | cmake { 16 | abiFilters rootProject.ext.abiFilters.split(",") 17 | } 18 | } 19 | } 20 | 21 | externalNativeBuild { 22 | cmake { 23 | path file('src/main/cpp/CMakeLists.txt') 24 | version rootProject.ext.cmakeVersion 25 | } 26 | } 27 | 28 | buildTypes { 29 | release { 30 | minifyEnabled false 31 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 32 | } 33 | } 34 | compileOptions { 35 | sourceCompatibility rootProject.ext.javaVersion 36 | targetCompatibility rootProject.ext.javaVersion 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /inject/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XhyEax/AndroidGotHook/8df7afebb12bb7d14d467f81a393f859b6adfdb1/inject/consumer-rules.pro -------------------------------------------------------------------------------- /inject/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /inject/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /inject/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.18.1) 2 | project(inject) 3 | 4 | set(CMAKE_CXX_STANDARD 11) 5 | include_directories(include/) 6 | add_library(inject SHARED inject.cpp elfutil.cpp gotutil.cpp) 7 | find_library(log-lib log) 8 | 9 | target_link_libraries(inject ${log-lib}) -------------------------------------------------------------------------------- /inject/src/main/cpp/elfutil.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Xhy on 2021/8/23. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "elfutil.h" 15 | #include "mylog.h" 16 | 17 | void getFullPath(const char *src, char *dest) { 18 | while (*src != '/') { 19 | *src++; 20 | } 21 | strncpy(dest, src, strlen(src) - 1); 22 | } 23 | 24 | uintptr_t getModuleBase(const char *module_name, char *moduleFullPath) { 25 | uintptr_t addr = 0; 26 | char buff[256] = "\n"; 27 | 28 | FILE *fp = fopen("/proc/self/maps", "r"); 29 | while (fgets(buff, sizeof(buff), fp)) { 30 | if (strstr(buff, "r-xp") && strstr(buff, module_name) && 31 | sscanf(buff, "%" SCNxPTR, &addr) == 1) { 32 | getFullPath(buff, moduleFullPath); 33 | LOGE("[%s] moduleBase: %" SCNxPTR, moduleFullPath, addr); 34 | return addr; 35 | } 36 | } 37 | // LOGE("buff: %s", buff); 38 | LOGE("[%s] moduleBase not found!\n", module_name); 39 | fclose(fp); 40 | return 0; 41 | } 42 | 43 | // 解析Section 44 | int getGOTOffsetAndSize(const char *modulePath, int &GOTSize) { 45 | int GOTOffset = 0; 46 | FILE *fp = fopen(modulePath, "r"); 47 | if (!fp) { 48 | LOGE("[%s] open failed!", modulePath); 49 | return 0; 50 | } 51 | ELFW(Ehdr) elf_header; 52 | ELFW(Shdr) elf_section_header; 53 | memset(&elf_header, 0, sizeof(elf_header)); 54 | memset(&elf_section_header, 0, sizeof(elf_section_header)); 55 | // 解析elf_header 56 | fseek(fp, 0, SEEK_SET); 57 | fread(&elf_header, sizeof(elf_header), 1, fp); 58 | // LOGE("elf_header e_shoff: %" SCNxPTR ", e_shstrndx: %d", elf_header.e_shoff, elf_header.e_shstrndx); 59 | // LOGE("elf_header e_shnum: %d", elf_header.e_shnum); 60 | // 获取字符串表在section header中的偏移 61 | fseek(fp, elf_header.e_shoff + elf_header.e_shstrndx * elf_header.e_shentsize, SEEK_SET); 62 | fread(&elf_section_header, sizeof(elf_section_header), 1, fp); 63 | int string_table_size = elf_section_header.sh_size; 64 | char *string_table = (char *) (malloc(string_table_size)); 65 | // 获取字符串表 66 | fseek(fp, elf_section_header.sh_offset, SEEK_SET); 67 | fread(string_table, string_table_size, 1, fp); 68 | // 遍历section header table, 查找.got 69 | fseek(fp, elf_header.e_shoff, SEEK_SET); 70 | for (int i = 0; i < elf_header.e_shnum; ++i) { 71 | fread(&elf_section_header, elf_header.e_shentsize, 1, fp); 72 | if (elf_section_header.sh_type == SHT_PROGBITS 73 | && 0 == strcmp(".got", string_table + elf_section_header.sh_name)) { 74 | GOTOffset = elf_section_header.sh_addr; 75 | GOTSize = elf_section_header.sh_size; 76 | break; 77 | } 78 | } 79 | free(string_table); 80 | fclose(fp); 81 | return GOTOffset; 82 | } 83 | 84 | static uint32_t elf_sysv_hash(const uint8_t *name) { 85 | uint32_t h = 0, g; 86 | 87 | while (*name) { 88 | h = (h << 4) + *name++; 89 | g = h & 0xf0000000; 90 | h ^= g; 91 | h ^= g >> 24; 92 | } 93 | return h; 94 | } 95 | 96 | // 解析Segment 97 | int getSymAddrDynamic(const char *module_name, const char *symName, uintptr_t *addrArray) { 98 | char moduleFullPath[256] = {0}; 99 | uintptr_t moduleBase = getModuleBase(module_name, moduleFullPath); 100 | if (moduleBase == 0) { 101 | return 0; 102 | } 103 | ELFW(Ehdr) elf_header; 104 | ELFW(Phdr) elf_program_header; 105 | memset(&elf_header, 0, sizeof(elf_header)); 106 | memset(&elf_program_header, 0, sizeof(elf_program_header)); 107 | // 解析elf_header 108 | memcpy(&elf_header, (const void *) moduleBase, sizeof(elf_header)); 109 | // LOGE("elf_header e_phoff: %" SCNxPTR ", e_phentsize: %d", elf_header.e_phoff, elf_header.e_phentsize); 110 | // LOGE("elf_header e_phnum: %d", elf_header.e_phnum); 111 | // 遍历program header table, 查找.dynamic 112 | int DYNOffset = 0; 113 | int DYNSize = 0; 114 | for (int i = 0; i < elf_header.e_phnum; ++i) { 115 | memcpy(&elf_program_header, 116 | (const void *) (moduleBase + elf_header.e_phoff + i * elf_header.e_phentsize), 117 | elf_header.e_phentsize); 118 | if (elf_program_header.p_type == PT_DYNAMIC) { 119 | DYNOffset = elf_program_header.p_vaddr; 120 | DYNSize = elf_program_header.p_memsz; 121 | break; 122 | } 123 | } 124 | if (DYNOffset == 0 || DYNSize == 0) { 125 | LOGE(".dynamic not found!"); 126 | return 0; 127 | } 128 | uintptr_t DYNBase = moduleBase + DYNOffset; 129 | // LOGE("DYNOffset: %" SCNxPTR " DYNSize: %" SCNxPTR "", DYNOffset, DYNSize); 130 | 131 | int addrArraySize = 0; 132 | // 保存各表 133 | ELFW(Sym) *dynsym; 134 | const char *dynstr; 135 | ELFW(Rel) *rel_dyn; 136 | ELFW(Rel) *rel_plt; 137 | ELFW(Rela) *rela_dyn; 138 | ELFW(Rela) *rela_plt; 139 | int rel_dyn_cnt, rel_plt_cnt; 140 | //.hash 141 | const uint32_t *buckets; 142 | uint32_t buckets_cnt; 143 | const uint32_t *chains; 144 | uint32_t chains_cnt; 145 | //遍历.dynamic 解析需要的表 (.rel.plt .rel.dyn .dynsym .dynstr和.hash) 146 | ELFW(Dyn) dyn; 147 | int dyn_entsize = sizeof(ELFW(Dyn)); 148 | for (int i = 0; i < DYNSize / dyn_entsize; ++i) { 149 | memcpy(&dyn, (const void *) (DYNBase + i * dyn_entsize), dyn_entsize); 150 | switch (dyn.d_tag) { 151 | // .rel.plt / .rela.plt 152 | case DT_JMPREL: 153 | #if defined(__LP64__) 154 | rela_plt = (ELFW(Rela) *) (moduleBase + dyn.d_un.d_ptr); 155 | #else 156 | rel_plt = (ELFW(Rel) *) (moduleBase + dyn.d_un.d_ptr); 157 | #endif 158 | break; 159 | case DT_PLTRELSZ: 160 | #if defined(__LP64__) 161 | rel_plt_cnt = dyn.d_un.d_val / sizeof(ELFW(Rela)); 162 | #else 163 | rel_plt_cnt = dyn.d_un.d_val / sizeof(ELFW(Rel)); 164 | #endif 165 | break; 166 | // .rel.dyn / .rela.dyn 167 | case DT_REL: 168 | case DT_RELA: 169 | #if defined(__LP64__) 170 | rela_dyn = (ELFW(Rela) *) (moduleBase + dyn.d_un.d_ptr); 171 | #else 172 | rel_dyn = (ELFW(Rel) *) (moduleBase + dyn.d_un.d_ptr); 173 | #endif 174 | break; 175 | case DT_RELSZ: 176 | case DT_RELASZ: 177 | rel_dyn_cnt = dyn.d_un.d_val / sizeof(ELFW(Rel)); 178 | break; 179 | // .dynsym 180 | case DT_SYMTAB: 181 | dynsym = (ELFW(Sym) *) (moduleBase + dyn.d_un.d_ptr); 182 | break; 183 | // .dynstr 184 | case DT_STRTAB: 185 | dynstr = (const char *) (moduleBase + dyn.d_un.d_ptr); 186 | break; 187 | // .hash 188 | case DT_HASH: 189 | auto rawdata = (const uint32_t *) (moduleBase + dyn.d_un.d_ptr); 190 | buckets_cnt = rawdata[0]; 191 | chains_cnt = rawdata[1]; 192 | buckets = &(rawdata[2]); 193 | chains = &(buckets[buckets_cnt]); 194 | break; 195 | } 196 | } 197 | //查找符号 .hash -> .dynsym -> .dynstr O(x) + O(1) + O(1) 198 | ELFW(Sym) *target = nullptr; 199 | uint32_t hash = elf_sysv_hash((const uint8_t *) symName); 200 | for (uint32_t i = buckets[hash % buckets_cnt]; 201 | 0 != i; i = chains[i]) { 202 | ELFW(Sym) *sym = dynsym + i; 203 | unsigned char type = ELF_ST_TYPE(sym->st_info); 204 | if (STT_FUNC != type && STT_GNU_IFUNC != type && STT_NOTYPE != type) 205 | continue; // find function only, allow no-type 206 | if (0 == strcmp(dynstr + sym->st_name, symName)) { 207 | target = sym; 208 | break; 209 | } 210 | } 211 | if (!target) { 212 | LOGE("target sym not found!"); 213 | return 0; 214 | } 215 | //遍历 .rel.plt / .rela.plt 和 .rel.dyn / .rela.dyn,获取偏移,计算内存地址 216 | for (int i = 0; i < rel_plt_cnt; i++) { 217 | #if defined(__LP64__) 218 | ELFW(Rela) &rel = rela_plt[i]; 219 | #else 220 | ELFW(Rel) &rel = rel_plt[i]; 221 | #endif 222 | if (&(dynsym[ELF_R_SYM(rel.r_info)]) == target && 223 | ELF_R_TYPE(rel.r_info) == ELF_R_JUMP_SLOT) { 224 | // LOGE("target r_offset: %" SCNxPTR "", rel.r_offset); 225 | addrArray[addrArraySize++] = moduleBase + rel.r_offset; 226 | } 227 | } 228 | for (int i = 0; i < rel_dyn_cnt; i++) { 229 | #if defined(__LP64__) 230 | ELFW(Rela) &rel = rela_dyn[i]; 231 | #else 232 | ELFW(Rel) &rel = rel_dyn[i]; 233 | #endif 234 | if (&(dynsym[ELF_R_SYM(rel.r_info)]) == target && 235 | (ELF_R_TYPE(rel.r_info) == ELF_R_ABS 236 | || ELF_R_TYPE(rel.r_info) == ELF_R_GLOB_DAT)) { 237 | // LOGE("target r_offset: %" SCNxPTR "", rel.r_offset); 238 | addrArray[addrArraySize++] = moduleBase + rel.r_offset; 239 | } 240 | } 241 | return addrArraySize; 242 | } -------------------------------------------------------------------------------- /inject/src/main/cpp/elfutil.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Xhy on 2021/8/23. 3 | // 4 | 5 | #ifndef ANDROIDGOTHOOK_ELFUTIL_H 6 | #define ANDROIDGOTHOOK_ELFUTIL_H 7 | 8 | #if defined(__LP64__) 9 | #define ELFW(what) Elf64_ ## what 10 | #define ELF_R_TYPE(what) ELF64_R_TYPE(what) 11 | #define ELF_R_SYM(what) ELF64_R_SYM(what) 12 | #else 13 | #define ELFW(what) Elf32_ ## what 14 | #define ELF_R_TYPE(what) ELF32_R_TYPE(what) 15 | #define ELF_R_SYM(what) ELF32_R_SYM(what) 16 | #endif 17 | 18 | #if defined(__arm__) 19 | #define ELF_R_JUMP_SLOT R_ARM_JUMP_SLOT //.rel.plt 20 | #define ELF_R_GLOB_DAT R_ARM_GLOB_DAT //.rel.dyn 21 | #define ELF_R_ABS R_ARM_ABS32 //.rel.dyn 22 | #elif defined(__aarch64__) 23 | #define ELF_R_JUMP_SLOT R_AARCH64_JUMP_SLOT 24 | #define ELF_R_GLOB_DAT R_AARCH64_GLOB_DAT 25 | #define ELF_R_ABS R_AARCH64_ABS64 26 | #endif 27 | 28 | uintptr_t getModuleBase(const char *modulePath, char *moduleFullPath); 29 | 30 | int getGOTOffsetAndSize(const char *modulePath, int &GOTSize); 31 | 32 | int getSymAddrDynamic(const char *moduleBase, const char *symName, uintptr_t *addrArray); 33 | 34 | #endif //ANDROIDGOTHOOK_ELFUTIL_H 35 | -------------------------------------------------------------------------------- /inject/src/main/cpp/gotutil.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Xhy on 2021/8/23. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "gotutil.h" 14 | #include "elfutil.h" 15 | #include "mylog.h" 16 | 17 | #define PAGE_START(addr) ((addr) & PAGE_MASK) 18 | #define PAGE_END(addr) (PAGE_START(addr) + PAGE_SIZE) 19 | 20 | #define MAX_ADDRARRAY_SIZE 32 21 | 22 | uintptr_t getGOTBase(int &GOTSize, const char *module_name) { 23 | uintptr_t GOTBase = 0; 24 | char moduleFullPath[256] = {0}; 25 | uintptr_t moduleBase = getModuleBase(module_name, moduleFullPath); 26 | if (moduleBase == 0) { 27 | return GOTBase; 28 | } 29 | int GOTOffset = getGOTOffsetAndSize(moduleFullPath, GOTSize); 30 | if (GOTOffset == 0) { 31 | LOGE("GOTOffset not found!\n"); 32 | return GOTBase; 33 | } 34 | 35 | GOTBase = moduleBase + GOTOffset; 36 | 37 | LOGE("GOTOffset: %" SCNxPTR " GOTBase: %" SCNxPTR " GOTSize: %d\n", GOTOffset, GOTBase, 38 | GOTSize); 39 | return GOTBase; 40 | } 41 | 42 | int getSymAddrInGOT(uintptr_t GOTBase, int GOTSize, uintptr_t ori, uintptr_t *addrArray) { 43 | if (GOTBase == 0) { 44 | LOGE("getSymAddrInGOT failed! addr [%" SCNxPTR "] is wrong\n", GOTBase); 45 | return 0; 46 | } 47 | 48 | int addrArraySize = 0; 49 | 50 | for (int i = 0; i < GOTSize; ++i) { 51 | uintptr_t addr = GOTBase + i * 4; 52 | uintptr_t item = *(uintptr_t *) (addr); 53 | if (item == ori) { 54 | // LOGE("GOT [%d]: %" SCNxPTR "\n", i, item); 55 | addrArray[addrArraySize++] = addr; 56 | } 57 | } 58 | if (addrArraySize == 0) { 59 | LOGE("getSymAddrInGOT %" SCNxPTR " not found!\n", ori); 60 | } 61 | 62 | return addrArraySize; 63 | } 64 | 65 | void replaceFunction(uintptr_t addr, uintptr_t replace, uintptr_t ori) { 66 | if (addr == 0) { 67 | LOGE("replace failed! addr [%" SCNxPTR "] is wrong\n", addr); 68 | return; 69 | } 70 | // 比对函数地址 71 | uintptr_t item = *(uintptr_t *) (addr); 72 | if (item == replace) { 73 | LOGE("[%" SCNxPTR "] function has been replaced.\n", addr); 74 | return; 75 | } 76 | if (item != ori) { 77 | LOGE("replace failed! unexpected function address [%" SCNxPTR "]=%" SCNxPTR "\n", addr, 78 | item); 79 | return; 80 | } 81 | //修改权限、替换地址、清空指令缓存 82 | LOGE("replace [%" SCNxPTR "]=%" SCNxPTR " with %" SCNxPTR "\n", addr, item, replace); 83 | mprotect((void *) PAGE_START(addr), PAGE_SIZE, PROT_READ | PROT_WRITE); 84 | *(uintptr_t *) addr = replace; 85 | __builtin___clear_cache((char *) PAGE_START(addr), (char *) PAGE_END(addr)); 86 | } 87 | 88 | uintptr_t hackGOT(const char *module_name, const char *target_lib, const char *target_func, 89 | uintptr_t replace) { 90 | // hackBySection or hackBySegment 91 | return hackBySegment(module_name, target_lib, target_func, replace); 92 | } 93 | 94 | // 基于链接视图解析ELF 95 | uintptr_t hackBySection(const char *module_name, const char *target_lib, const char *target_func, 96 | uintptr_t replace) { 97 | LOGE("hackBySection start.\n"); 98 | // 获取目标函数地址 99 | void *handle = dlopen(target_lib, RTLD_LAZY); 100 | auto ori = (uintptr_t) dlsym(handle, target_func); 101 | // LOGE("ori addr: %" SCNxPTR "\n", ori); 102 | int GOTSize = 0; 103 | // 获取GOT表地址及大小 (解析Section) 104 | uintptr_t GOTBase = getGOTBase(GOTSize, module_name); 105 | // 遍历GOT表,查找符号地址 106 | uintptr_t addrArray[MAX_ADDRARRAY_SIZE]; 107 | int addrArraySize = getSymAddrInGOT(GOTBase, GOTSize, ori, addrArray); 108 | // 替换地址 109 | for (int i = 0; i < addrArraySize; i++) { 110 | uintptr_t replaceAddr = addrArray[i]; 111 | replaceFunction(replaceAddr, replace, ori); 112 | } 113 | return ori; 114 | } 115 | 116 | // 基于执行视图解析ELF 117 | uintptr_t hackBySegment(const char *module_name, const char *target_lib, const char *target_func, 118 | uintptr_t replace) { 119 | LOGE("hackBySegment start.\n"); 120 | // 获取目标函数地址 121 | void *handle = dlopen(target_lib, RTLD_LAZY); 122 | auto ori = (uintptr_t) dlsym(handle, target_func); 123 | // LOGE("ori addr: %" SCNxPTR "\n", ori); 124 | // 获取符号地址 (解析Segment) 125 | uintptr_t addrArray[MAX_ADDRARRAY_SIZE]; 126 | int addrArraySize = getSymAddrDynamic(module_name, target_func, addrArray); 127 | // 替换地址 128 | for (int i = 0; i < addrArraySize; i++) { 129 | uintptr_t replaceAddr = addrArray[i]; 130 | replaceFunction(replaceAddr, replace, ori); 131 | } 132 | return ori; 133 | } 134 | -------------------------------------------------------------------------------- /inject/src/main/cpp/include/gotutil.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Xhy on 2021/8/23. 3 | // 4 | 5 | #ifndef ANDROIDGOTHOOK_GOTUTIL_H 6 | #define ANDROIDGOTHOOK_GOTUTIL_H 7 | 8 | uintptr_t getGOTBase(int &GOTSize, const char *modulePath); 9 | 10 | int getSymAddrInGOT(uintptr_t GOTBase, int GOTSize, uintptr_t ori, uintptr_t *addrArray); 11 | 12 | void replaceFunction(uintptr_t addr, uintptr_t replace, uintptr_t ori); 13 | 14 | uintptr_t hackGOT(const char *module_name, const char *target_lib, const char *target_func, 15 | uintptr_t replace); 16 | 17 | uintptr_t hackBySection(const char *module_name, const char *target_lib, const char *target_func, 18 | uintptr_t replace); 19 | 20 | uintptr_t hackBySegment(const char *module_name, const char *target_lib, const char *target_func, 21 | uintptr_t replace); 22 | 23 | #endif //ANDROIDGOTHOOK_GOTUTIL_H 24 | -------------------------------------------------------------------------------- /inject/src/main/cpp/inject.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "gotutil.h" 8 | #include "mylog.h" 9 | #include "gotutil.h" 10 | 11 | #if defined(__LP64__) 12 | #define MODULE_NAME "victim-patch-arm64" 13 | #else 14 | #define MODULE_NAME "victim-patch-arm" 15 | #endif 16 | 17 | typedef int (*getpid_fun)(); 18 | 19 | // 原方法的备份 20 | getpid_fun getpidOri; 21 | 22 | // 替换方法 23 | int getpidReplace() { 24 | LOGE("before hook getpid\n"); 25 | //调用原方法 26 | int pid = getpidOri(); 27 | LOGE("after hook getpid: %d\n", pid); 28 | return 233333; 29 | } 30 | 31 | void hack() { 32 | uintptr_t ori = hackGOT(MODULE_NAME, "libc.so", "getpid", 33 | (uintptr_t) getpidReplace); 34 | getpidOri = (getpid_fun) ori; 35 | } 36 | 37 | //so加载时由linker调用 38 | void __attribute__((constructor)) init() { 39 | LOGE("call from constructor\n"); 40 | hack(); 41 | LOGE("constructor finish.\n"); 42 | } 43 | 44 | // JNI LoadNativeLibrary中调用 45 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { 46 | if (nullptr == vm) return JNI_ERR; 47 | LOGE("call from JNI_OnLoad\n"); 48 | hack(); 49 | return JNI_VERSION_1_6; 50 | } -------------------------------------------------------------------------------- /inject/src/main/cpp/mylog.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Xhy on 2021/8/31. 3 | // 4 | 5 | #ifndef ANDROIDGOTHOOK_MYLOG_H 6 | #define ANDROIDGOTHOOK_MYLOG_H 7 | 8 | #include 9 | 10 | #if defined(__LP64__) 11 | #define LOG_TAG "NInject-64" 12 | #else 13 | #define LOG_TAG "NInject" 14 | #endif 15 | 16 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 17 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 18 | 19 | #endif //ANDROIDGOTHOOK_MYLOG_H 20 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "AndroidGotHook" 2 | include ':victim' 3 | include ':inject' 4 | include ':victim_app' 5 | include ':victim_app:nativelib' -------------------------------------------------------------------------------- /victim/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /victim/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | } 4 | 5 | android { 6 | compileSdkVersion rootProject.ext.compileSdkVersion 7 | buildToolsVersion rootProject.ext.buildToolsVersion 8 | ndkVersion rootProject.ext.ndkVersion 9 | 10 | defaultConfig { 11 | minSdkVersion rootProject.ext.minSdkVersion 12 | targetSdkVersion rootProject.ext.targetSdkVersion 13 | consumerProguardFiles "consumer-rules.pro" 14 | externalNativeBuild { 15 | cmake { 16 | abiFilters rootProject.ext.abiFilters.split(",") 17 | } 18 | } 19 | } 20 | 21 | externalNativeBuild { 22 | cmake { 23 | path file('src/main/cpp/CMakeLists.txt') 24 | version rootProject.ext.cmakeVersion 25 | } 26 | } 27 | 28 | buildTypes { 29 | release { 30 | minifyEnabled false 31 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 32 | } 33 | } 34 | compileOptions { 35 | sourceCompatibility rootProject.ext.javaVersion 36 | targetCompatibility rootProject.ext.javaVersion 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /victim/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XhyEax/AndroidGotHook/8df7afebb12bb7d14d467f81a393f859b6adfdb1/victim/consumer-rules.pro -------------------------------------------------------------------------------- /victim/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /victim/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /victim/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.18.1) 2 | project(victim) 3 | 4 | add_executable(victim victim.c) 5 | -------------------------------------------------------------------------------- /victim/src/main/cpp/victim.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef int (*getpid_fun)(); 5 | 6 | getpid_fun global_getpid = (getpid_fun) getpid; 7 | 8 | int main() { 9 | getpid_fun local_getpid = (getpid_fun) getpid; 10 | int pid = getpid(); 11 | int local_pid = local_getpid(); 12 | int global_pid = global_getpid(); 13 | 14 | printf("direct call: %d, local call: %d, global call: %d\n", 15 | pid, local_pid, global_pid); 16 | 17 | // getchar(); 18 | return 0; 19 | } -------------------------------------------------------------------------------- /victim_app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /victim_app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | compileSdkVersion rootProject.ext.compileSdkVersion 7 | buildToolsVersion rootProject.ext.buildToolsVersion 8 | 9 | defaultConfig { 10 | applicationId "com.xhy.androidgothook" 11 | minSdkVersion rootProject.ext.minSdkVersion 12 | targetSdkVersion rootProject.ext.targetSdkVersion 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | android { 17 | defaultConfig { 18 | ndk { 19 | abiFilters rootProject.ext.abiFilters.split(",") 20 | } 21 | } 22 | } 23 | } 24 | 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | compileOptions { 32 | sourceCompatibility rootProject.ext.javaVersion 33 | targetCompatibility rootProject.ext.javaVersion 34 | } 35 | 36 | buildFeatures { 37 | viewBinding true 38 | } 39 | packagingOptions { 40 | jniLibs { 41 | pickFirsts += ['**/libinject.so'] 42 | } 43 | } 44 | } 45 | 46 | dependencies { 47 | implementation project(path: ':victim_app:nativelib') 48 | } 49 | -------------------------------------------------------------------------------- /victim_app/nativelib/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /victim_app/nativelib/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | } 4 | 5 | android { 6 | compileSdk rootProject.ext.compileSdkVersion 7 | buildToolsVersion rootProject.ext.buildToolsVersion 8 | ndkVersion rootProject.ext.ndkVersion 9 | 10 | defaultConfig { 11 | minSdk rootProject.ext.minSdkVersion 12 | targetSdk rootProject.ext.targetSdkVersion 13 | 14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 15 | consumerProguardFiles "consumer-rules.pro" 16 | 17 | externalNativeBuild { 18 | cmake { 19 | abiFilters rootProject.ext.abiFilters.split(",") 20 | } 21 | } 22 | } 23 | 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 28 | } 29 | } 30 | externalNativeBuild { 31 | cmake { 32 | path "src/main/cpp/CMakeLists.txt" 33 | version rootProject.ext.cmakeVersion 34 | } 35 | } 36 | compileOptions { 37 | sourceCompatibility rootProject.ext.javaVersion 38 | targetCompatibility rootProject.ext.javaVersion 39 | } 40 | } 41 | 42 | dependencies { 43 | implementation project(':inject') 44 | } 45 | -------------------------------------------------------------------------------- /victim_app/nativelib/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XhyEax/AndroidGotHook/8df7afebb12bb7d14d467f81a393f859b6adfdb1/victim_app/nativelib/consumer-rules.pro -------------------------------------------------------------------------------- /victim_app/nativelib/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /victim_app/nativelib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /victim_app/nativelib/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about using CMake with Android Studio, read the 2 | # documentation: https://d.android.com/studio/projects/add-native-code.html 3 | 4 | # Sets the minimum version of CMake required to build the native library. 5 | 6 | cmake_minimum_required(VERSION 3.18.1) 7 | 8 | # Declares and names the project. 9 | 10 | project("nativelib") 11 | 12 | # Creates and names a library, sets it as either STATIC 13 | # or SHARED, and provides the relative paths to its source code. 14 | # You can define multiple libraries, and CMake builds them for you. 15 | # Gradle automatically packages shared libraries with your APK. 16 | 17 | add_library( # Sets the name of the library. 18 | nativelib 19 | 20 | # Sets the library as a shared library. 21 | SHARED 22 | 23 | # Provides a relative path to your source file(s). 24 | nativelib.cpp ) 25 | 26 | # Searches for a specified prebuilt library and stores the path as a 27 | # variable. Because CMake includes system libraries in the search path by 28 | # default, you only need to specify the name of the public NDK library 29 | # you want to add. CMake verifies that the library exists before 30 | # completing its build. 31 | 32 | find_library( # Sets the name of the path variable. 33 | log-lib 34 | 35 | # Specifies the name of the NDK library that 36 | # you want CMake to locate. 37 | log ) 38 | 39 | # Specifies libraries CMake should link to your target library. You 40 | # can link multiple libraries, such as libraries you define in this 41 | # build script, prebuilt third-party libraries, or system libraries. 42 | 43 | set(INJECT_BASE ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../inject) 44 | add_library(inject SHARED IMPORTED) 45 | set_target_properties(inject PROPERTIES 46 | IMPORTED_LOCATION ${INJECT_BASE}/build/intermediates/merged_native_libs/debug/out/lib/${ANDROID_ABI}/libinject.so 47 | INTERFACE_INCLUDE_DIRECTORIES ${INJECT_BASE}/src/main/cpp/include) 48 | 49 | include_directories(${INJECT_BASE}/src/main/cpp/include) 50 | 51 | target_link_libraries( # Specifies the target library. 52 | nativelib 53 | inject 54 | # Links the target library to the log library 55 | # included in the NDK. 56 | ${log-lib} ) -------------------------------------------------------------------------------- /victim_app/nativelib/src/main/cpp/nativelib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #define LOG_TAG "NInject-Native" 8 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 9 | 10 | #include "gotutil.h" 11 | 12 | typedef int (*getpid_fun)(); 13 | 14 | // 全局函数指针 15 | getpid_fun global_getpid = (getpid_fun) getpid; 16 | 17 | // 原方法的备份 18 | getpid_fun getpidOri; 19 | 20 | // 替换方法 21 | int getpidReplace() { 22 | LOGI("before hook getpid\n"); 23 | //调用原方法 24 | int pid = getpidOri(); 25 | LOGI("after hook getpid: %d\n", pid); 26 | return 23333; 27 | } 28 | 29 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { 30 | uintptr_t ori = hackGOT("libnativelib.so", "libc.so", "getpid", 31 | (uintptr_t) getpidReplace); 32 | getpidOri = (getpid_fun) ori; 33 | return JNI_VERSION_1_6; 34 | } 35 | 36 | extern "C" JNIEXPORT jstring JNICALL 37 | Java_com_xhy_nativelib_NativeLib_stringFromJNI( 38 | JNIEnv *env, 39 | jclass clazz) { 40 | auto local_getpid = getpid; 41 | int pid = getpid(); 42 | int local_pid = local_getpid(); 43 | int global_pid = global_getpid(); 44 | 45 | char buff[256]; 46 | sprintf(buff, "direct call: %d, local call: %d, global call: %d\n", 47 | pid, local_pid, global_pid); 48 | LOGI("%s", buff); 49 | 50 | return env->NewStringUTF(buff); 51 | } -------------------------------------------------------------------------------- /victim_app/nativelib/src/main/java/com/xhy/nativelib/NativeLib.java: -------------------------------------------------------------------------------- 1 | package com.xhy.nativelib; 2 | 3 | public class NativeLib { 4 | 5 | // Used to load the 'nativelib' library on application startup. 6 | static { 7 | System.loadLibrary("nativelib"); 8 | } 9 | 10 | /** 11 | * A native method that is implemented by the 'nativelib' native library, 12 | * which is packaged with this application. 13 | */ 14 | public static native String stringFromJNI(); 15 | } -------------------------------------------------------------------------------- /victim_app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /victim_app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /victim_app/src/main/java/com/xhy/androidgothook/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.xhy.androidgothook; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.widget.TextView; 6 | 7 | import com.xhy.nativelib.NativeLib; 8 | 9 | public class MainActivity extends Activity { 10 | 11 | @Override 12 | protected void onCreate(Bundle savedInstanceState) { 13 | super.onCreate(savedInstanceState); 14 | setContentView(R.layout.activity_main); 15 | TextView sample = findViewById(R.id.sample_text); 16 | sample.setText(NativeLib.stringFromJNI()); 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /victim_app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /victim_app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /victim_app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /victim_app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /victim_app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /victim_app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XhyEax/AndroidGotHook/8df7afebb12bb7d14d467f81a393f859b6adfdb1/victim_app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /victim_app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XhyEax/AndroidGotHook/8df7afebb12bb7d14d467f81a393f859b6adfdb1/victim_app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /victim_app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XhyEax/AndroidGotHook/8df7afebb12bb7d14d467f81a393f859b6adfdb1/victim_app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /victim_app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XhyEax/AndroidGotHook/8df7afebb12bb7d14d467f81a393f859b6adfdb1/victim_app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /victim_app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XhyEax/AndroidGotHook/8df7afebb12bb7d14d467f81a393f859b6adfdb1/victim_app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /victim_app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XhyEax/AndroidGotHook/8df7afebb12bb7d14d467f81a393f859b6adfdb1/victim_app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /victim_app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XhyEax/AndroidGotHook/8df7afebb12bb7d14d467f81a393f859b6adfdb1/victim_app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /victim_app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XhyEax/AndroidGotHook/8df7afebb12bb7d14d467f81a393f859b6adfdb1/victim_app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /victim_app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XhyEax/AndroidGotHook/8df7afebb12bb7d14d467f81a393f859b6adfdb1/victim_app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /victim_app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XhyEax/AndroidGotHook/8df7afebb12bb7d14d467f81a393f859b6adfdb1/victim_app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /victim_app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /victim_app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AndroidGotHook 3 | -------------------------------------------------------------------------------- /workdir/arm/0-patch-victim-arm.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cd .. 3 | python edit-victim.py armeabi-v7a 4 | pause -------------------------------------------------------------------------------- /workdir/arm/1-run-victim-patch-arm.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cd .. 3 | :Loop 4 | adb push ../inject/build/intermediates/cmake/debug/obj/armeabi-v7a/libinject.so /data/local/tmp/libinject-arm.so 5 | adb push victim-patch-arm /data/local/tmp/ 6 | adb shell chmod +x /data/local/tmp/victim-patch-arm 7 | adb shell /data/local/tmp/victim-patch-arm 8 | pause 9 | goto :Loop -------------------------------------------------------------------------------- /workdir/arm64/0-patch-victim-arm64.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cd .. 3 | python edit-victim.py arm64-v8a 4 | pause -------------------------------------------------------------------------------- /workdir/arm64/1-run-victim-patch-arm64.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cd .. 3 | :Loop 4 | adb push ../inject/build/intermediates/cmake/debug/obj/arm64-v8a/libinject.so /data/local/tmp/libinject-arm64.so 5 | adb push victim-patch-arm64 /data/local/tmp/ 6 | adb shell chmod +x /data/local/tmp/victim-patch-arm64 7 | adb shell /data/local/tmp/victim-patch-arm64 8 | pause 9 | goto :Loop -------------------------------------------------------------------------------- /workdir/edit-victim.py: -------------------------------------------------------------------------------- 1 | import lief,sys 2 | 3 | abi = "armeabi-v7a" 4 | tail = "-arm" 5 | if(len(sys.argv) > 1 and sys.argv[1] == "arm64-v8a"): 6 | abi = sys.argv[1] 7 | tail = "-arm64" 8 | 9 | path = "../victim/build/intermediates/cmake/debug/obj/"+abi+"/victim" 10 | elf = lief.parse(path) 11 | elf.add_library("/data/local/tmp/libinject"+tail+".so") 12 | elf.write("victim-patch"+tail) 13 | print("patch success") --------------------------------------------------------------------------------