├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── compiler.xml ├── gradle.xml ├── jarRepositories.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── CMakeLists.txt ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── androidinject │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── cpp │ │ ├── InjectModule │ │ │ ├── CMakeLists.txt │ │ │ ├── InjectModule.cpp │ │ │ ├── PrintLog.h │ │ │ ├── common.h │ │ │ ├── fake_dlfcn.cpp │ │ │ ├── fake_dlfcn.h │ │ │ ├── load_dex.cpp │ │ │ └── load_dex.h │ │ ├── PtraceInject │ │ │ ├── CMakeLists.txt │ │ │ ├── PrintLog.h │ │ │ ├── PtraceInject.cpp │ │ │ ├── PtraceInject.h │ │ │ ├── lib_name.cpp │ │ │ ├── lib_name.h │ │ │ ├── main.cpp │ │ │ ├── module_utils.cpp │ │ │ ├── module_utils.h │ │ │ ├── ptrace_utils.cpp │ │ │ └── ptrace_utils.h │ │ └── native-lib │ │ │ ├── CMakeLists.txt │ │ │ ├── PrintLog.h │ │ │ └── native-lib.cpp │ ├── java │ │ └── com │ │ │ ├── app │ │ │ ├── context │ │ │ │ ├── ContextUtils.java │ │ │ │ └── RunUiInterface.java │ │ │ ├── logic │ │ │ │ └── LogicEntry.java │ │ │ ├── service │ │ │ │ ├── AbstractEntry.java │ │ │ │ └── Entry.java │ │ │ ├── signal │ │ │ │ └── IRecvListener.java │ │ │ ├── socket │ │ │ │ ├── JWebSocketClient.java │ │ │ │ └── WebSocketMessage.java │ │ │ ├── tools │ │ │ │ └── ScreenShot.java │ │ │ └── view │ │ │ │ ├── ViewInfo.java │ │ │ │ └── ViewManager.java │ │ │ └── example │ │ │ └── androidinject │ │ │ └── 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 │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── example │ └── androidinject │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── installcmd.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | 44 | 45 | 49 | 50 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 1.8 14 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SharkInject 2 | android8 arm64 注入方案 3 | 4 | -f:启动目标app 5 | -n:指定app包名 6 | -p:app pid 7 | -d:指定注入dex 8 | -so:指定注入so 9 | 10 | ``` 11 | ./SharkInject -f -n com.ss.android.ugc.aweme 12 | ``` 13 | `您也可以直接运行gradle的executeDevice Task` -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /libs -------------------------------------------------------------------------------- /app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.1) 2 | 3 | # LIB目录和BIN目录 4 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}) 5 | set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}) 6 | 7 | #设置头文件搜索路径(和此txt同个路径的头文件无需设置),可选 8 | #INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/common) 9 | 10 | #指定用到的系统库或者NDK库或者第三方库的搜索路径,可选。 11 | #LINK_DIRECTORIES(/usr/local/lib) 12 | 13 | #添加子目录,将自动找到子目录中的CMakeLists.txt 14 | ADD_SUBDIRECTORY(${PROJECT_SOURCE_DIR}/src/main/cpp/native-lib) 15 | ADD_SUBDIRECTORY(${PROJECT_SOURCE_DIR}/src/main/cpp/PtraceInject) 16 | ADD_SUBDIRECTORY(${PROJECT_SOURCE_DIR}/src/main/cpp/InjectModule) -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "29.0.3" 6 | defaultConfig { 7 | applicationId "com.example.androidinject" 8 | minSdkVersion 24 9 | targetSdkVersion 29 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | multiDexEnabled false 15 | 16 | externalNativeBuild { 17 | cmake { 18 | cppFlags "" 19 | // abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64" 20 | abiFilters "armeabi-v7a", "arm64-v8a" 21 | } 22 | } 23 | } 24 | 25 | buildTypes { 26 | release { 27 | minifyEnabled false 28 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 29 | } 30 | } 31 | 32 | externalNativeBuild { 33 | cmake { 34 | path "CMakeLists.txt" 35 | version "3.10.2" 36 | } 37 | } 38 | compileOptions { 39 | sourceCompatibility JavaVersion.VERSION_1_8 40 | targetCompatibility JavaVersion.VERSION_1_8 41 | } 42 | } 43 | 44 | dependencies { 45 | implementation fileTree(dir: 'libs', include: ['*.jar']) 46 | implementation 'androidx.appcompat:appcompat:1.1.0' 47 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 48 | implementation 'com.swift.sandhook:hooklib:4.2.1' 49 | implementation "org.java-websocket:Java-WebSocket:1.5.1" 50 | implementation 'com.google.code.gson:gson:2.8.1' 51 | 52 | // 不使用 Xposed API 则不需要引入 53 | implementation 'com.swift.sandhook:xposedcompat:4.2.+' 54 | testImplementation 'junit:junit:4.12' 55 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 56 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 57 | } 58 | 59 | ext { 60 | //push的路径 61 | pushPath = "/data/local/tmp/" 62 | //可执行文件的名称 63 | elfName = "SharkInject" 64 | //部署的架构 65 | abi = 'armeabi-v7a' 66 | //so名称 67 | soName = "libLoadModule.so" 68 | //注入目标包名 69 | appName = "com.ss.android.ugc.aweme" 70 | } 71 | 72 | //将相关文件push到手机 73 | task pushDevice(dependsOn: 'assembleDebug') { 74 | group 'Online-Device' 75 | description 'Debug with online device without remote server' 76 | 77 | doLast { 78 | //push到手机上 79 | def info = "adb push ${new File(project.buildDir, "/intermediates/dex/debug/mergeDexDebug/classes.dex").canonicalPath} $pushPath".execute().text 80 | println "Push dex:$info" 81 | 82 | info = "adb push ${new File(project.buildDir, "/intermediates/cmake/debug/obj/$abi/$elfName").canonicalPath} $pushPath".execute().text 83 | println "Push SharkInject:$info" 84 | 85 | info = "adb push ${new File(project.buildDir, "/intermediates/cmake/debug/obj/$abi/$soName").canonicalPath} $pushPath".execute().text 86 | println "Push libLoadModule.so:$info" 87 | } 88 | } 89 | 90 | //修改执行文件的权限 91 | task chmodDevice(dependsOn: pushDevice) { 92 | group 'Online-Device' 93 | doLast { 94 | def info = "adb shell chmod 777 $pushPath/SharkInject".execute().text 95 | println "chmod:$info" 96 | } 97 | } 98 | 99 | //执行命令 100 | task executeDevice(dependsOn: chmodDevice) { 101 | group 'Online-Device' 102 | doLast { 103 | def exec_cmd = "adb shell \"cd $pushPath;su -c $pushPath$elfName -f -n $appName\"" 104 | println exec_cmd 105 | def info = exec_cmd.execute().text 106 | 107 | println "executeDevice:$info" 108 | } 109 | } 110 | 111 | -------------------------------------------------------------------------------- /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 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/androidinject/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.androidinject; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.example.androidinject", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/cpp/InjectModule/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.4.1) 7 | 8 | # Creates and names a library, sets it as either STATIC 9 | # or SHARED, and provides the relative paths to its source code. 10 | # You can define multiple libraries, and CMake builds them for you. 11 | # Gradle automatically packages shared libraries with your APK. 12 | 13 | add_library( # Sets the name of the library. 14 | LoadModule 15 | 16 | # Sets the library as a shared library. 17 | SHARED 18 | 19 | # Provides a relative path to your source file(s). 20 | InjectModule.cpp fake_dlfcn.cpp load_dex.cpp 21 | ) 22 | 23 | # Searches for a specified prebuilt library and stores the path as a 24 | # variable. Because CMake includes system libraries in the search path by 25 | # default, you only need to specify the name of the public NDK library 26 | # you want to add. CMake verifies that the library exists before 27 | # completing its build. 28 | 29 | find_library( # Sets the name of the path variable. 30 | log-lib 31 | 32 | # Specifies the name of the NDK library that 33 | # you want CMake to locate. 34 | log android) 35 | 36 | # Specifies libraries CMake should link to your target library. You 37 | # can link multiple libraries, such as libraries you define in this 38 | # build script, prebuilt third-party libraries, or system libraries. 39 | 40 | target_link_libraries( # Specifies the target library. 41 | LoadModule 42 | 43 | # Links the target library to the log library 44 | # included in the NDK. 45 | ${log-lib}) -------------------------------------------------------------------------------- /app/src/main/cpp/InjectModule/InjectModule.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "PrintLog.h" 12 | #include "fake_dlfcn.h" 13 | #include "load_dex.h" 14 | 15 | pthread_t gThread; 16 | 17 | #if defined(__aarch64__) || defined(__x86_64__) 18 | const char *libandroid_runtime_path = "/system/lib64/libandroid_runtime.so"; 19 | #else 20 | const char *libandroid_runtime_path = "/system/lib/libandroid_runtime.so"; 21 | #endif 22 | 23 | /** 24 | * 这种方式是将我们的插件dex加载进内存后,在添加到app的ClassLoader中 25 | * 这样在app中是可以看到我们的代码的 26 | * @param env 27 | * @param jarpath 28 | */ 29 | void load_dex_and_run2(JNIEnv *env, const char *jarpath) { 30 | 31 | //使用DexFile.loadDex加载dex 32 | const char *pkgName = "com.shark.nougat"; 33 | 34 | jobject dexObject = LoadDex(env, jarpath, pkgName); 35 | 36 | //获得app的PathClassLoader 37 | jobject appPathClassLoader = getClassLoader(env); 38 | //将我们加载的dex 放入到app的PathClassLoader的pathList;的dexElements;中 39 | //如此在app中就能直接加载到我们的dex中的类了 40 | makeDexElements(env, appPathClassLoader, dexObject); 41 | 42 | //找到我们的入口类 43 | const char *targetClass = "com/app/service/Entry"; 44 | 45 | jclass Inject = myFindClass(env, targetClass, dexObject); 46 | 47 | //下面这一段就是去调用我们的入口类了 48 | jmethodID main = env->GetStaticMethodID(Inject, "onLoad", 49 | "(Ldalvik/system/PathClassLoader;Ljava/lang/String;Z)V"); 50 | 51 | if (ClearException(env)) { 52 | LOGE("find Inject class Entry jmethodId failed"); 53 | return; 54 | } 55 | 56 | //inject_flag is only used for art 57 | jboolean inject_flag = false; 58 | env->CallStaticVoidMethod(Inject, main, appPathClassLoader, env->NewStringUTF(pkgName), 59 | inject_flag); 60 | if (ClearException(env)) { 61 | LOGE("call Entry method failed"); 62 | return; 63 | } 64 | } 65 | 66 | /** 67 | * 68 | * 下面这种方式是直接自己创建一个ClassLoader 这样创建的ClassLoader在App的加载器中是找不到我们的注入代码的 69 | * @param env 70 | * @param jarpath 71 | */ 72 | void load_dex_and_run(JNIEnv *env, const char *jarpath) { 73 | jobject appPathClassLoader = getClassLoader(env); 74 | //获取当前目录 75 | char current_absolute_path[4096] ="/data/local/tmp"; 76 | 77 | jobject myClassLoader = createNewClassLoader(env, jarpath, current_absolute_path); 78 | LOGI("myClassLoader 0x%p\n", myClassLoader); 79 | if (NULL != myClassLoader) { 80 | jclass entry_class = findClassFromLoader(env, myClassLoader, "com.app.service.Entry"); 81 | 82 | if (NULL != entry_class) { 83 | LOGI("Entry Class 0x%p\n", entry_class); 84 | //"(Ldalvik/system/PathClassLoader;Ljava/lang/String;Z)V" 85 | 86 | const char *entryName = "onLoad"; 87 | jmethodID entry_method = env->GetStaticMethodID(entry_class, entryName, 88 | "(Ldalvik/system/PathClassLoader;Ljava/lang/String;Z)V"); 89 | 90 | if (NULL != entry_method) { 91 | jboolean inject_flag = false; 92 | const char *pkgName = "com.shark.initapp"; 93 | 94 | env->CallStaticVoidMethod(entry_class, entry_method, appPathClassLoader, 95 | env->NewStringUTF(pkgName), inject_flag); 96 | } 97 | } 98 | } 99 | } 100 | 101 | int _clientInit(const char *jarpath) { 102 | JNIEnv *testenv = NULL; 103 | void *handle; 104 | //依靠libandroid_runtime.so 找到JavaVM 105 | handle = dlopen_ex(libandroid_runtime_path, RTLD_NOW); 106 | LOGI("fake_dlopen for libandroid_runtime.so returned %p\n", handle); 107 | void *pVM = dlsym_ex(handle, "_ZN7android14AndroidRuntime7mJavaVME"); 108 | 109 | JavaVM *javaVM = (JavaVM *) *(void **) pVM; 110 | LOGI("use mJavaVM returned %p\n", javaVM); 111 | if (javaVM) { 112 | jint result = javaVM->AttachCurrentThread(&testenv, 0); 113 | if ((result == JNI_OK) && (testenv != NULL)) { 114 | LOGI("attach ok. clientInit JavaVM : 0x%p, JNIEnv : 0x%p\n", javaVM, testenv); 115 | load_dex_and_run(testenv, jarpath); 116 | javaVM->DetachCurrentThread(); 117 | LOGI("DetachCurrentThread all finished!"); 118 | } else { 119 | LOGE("NOTE: attach of thread failed\n"); 120 | return -1; 121 | } 122 | } 123 | 124 | return 0; 125 | } 126 | 127 | extern "C" __attribute__((visibility("default"))) int entry(char *so_parameter) { 128 | LOGE("[InjectModule] Inject_entry Func is called\n"); 129 | pthread_create(&gThread, NULL, (void *(*)(void *)) _clientInit, (void *) so_parameter); 130 | 131 | return 0; 132 | } -------------------------------------------------------------------------------- /app/src/main/cpp/InjectModule/PrintLog.h: -------------------------------------------------------------------------------- 1 | #ifndef _ANDROID_LOG_PRINT_H_ 2 | #define _ANDROID_LOG_PRINT_H_ 3 | 4 | #include 5 | 6 | #define IS_DEBUG 7 | 8 | #ifdef IS_DEBUG 9 | 10 | #define LOG_TAG ("InjectShark") 11 | 12 | #define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) 13 | 14 | #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG , LOG_TAG, __VA_ARGS__)) 15 | 16 | #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO , LOG_TAG, __VA_ARGS__)) 17 | 18 | #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN , LOG_TAG, __VA_ARGS__)) 19 | 20 | #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR , LOG_TAG, __VA_ARGS__)) 21 | 22 | #else 23 | 24 | #define LOGV(LOG_TAG, ...) NULL 25 | 26 | #define LOGD(LOG_TAG, ...) NULL 27 | 28 | #define LOGI(LOG_TAG, ...) NULL 29 | 30 | #define LOGW(LOG_TAG, ...) NULL 31 | 32 | #define LOGE(LOG_TAG, ...) NULL 33 | 34 | #endif 35 | 36 | #endif -------------------------------------------------------------------------------- /app/src/main/cpp/InjectModule/common.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMMON_H__ 2 | #define __COMMON_H__ 3 | 4 | #include 5 | #include 6 | 7 | #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) 8 | 9 | #define TAG "InjectShark" 10 | #define LOGI(FORMAT, ...) __android_log_print(ANDROID_LOG_INFO, TAG, FORMAT, ##__VA_ARGS__); 11 | #define LOGD(FORMAT, ...) __android_log_print(ANDROID_LOG_DEBUG, TAG, FORMAT, ##__VA_ARGS__); 12 | #define LOGV(FORMAT, ...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, FORMAT, ##__VA_ARGS__); 13 | #define LOGW(FORMAT, ...) __android_log_print(ANDROID_LOG_WARN, TAG, FORMAT, ##__VA_ARGS__); 14 | #define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR, TAG, FORMAT, ##__VA_ARGS__); 15 | 16 | #endif //__COMMON_H__ 17 | -------------------------------------------------------------------------------- /app/src/main/cpp/InjectModule/fake_dlfcn.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 avs333 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "common.h" 32 | 33 | 34 | #if defined(__LP64__) || defined(__aarch64__) || defined(__x86_64__) || defined(__x86_64) 35 | #define Elf_Ehdr Elf64_Ehdr 36 | #define Elf_Shdr Elf64_Shdr 37 | #define Elf_Sym Elf64_Sym 38 | #elif defined(__arm__) || defined(__i386__) 39 | #define Elf_Ehdr Elf32_Ehdr 40 | #define Elf_Shdr Elf32_Shdr 41 | #define Elf_Sym Elf32_Sym 42 | #else 43 | #error "Arch unknown, please port me" 44 | #endif 45 | 46 | struct ctx { 47 | // void *load_addr; 48 | // void *dynstr; 49 | // void *dynsym; 50 | char *load_addr; 51 | char *dynstr; 52 | char *dynsym; 53 | int nsyms; 54 | off_t bias; 55 | }; 56 | 57 | extern "C" { 58 | static int fake_dlclose(void *handle) { 59 | LOGI("fake_dlclose"); 60 | if (handle) { 61 | struct ctx *ctx = (struct ctx *) handle; 62 | if (ctx->dynsym) free(ctx->dynsym); /* we're saving dynsym and dynstr */ 63 | if (ctx->dynstr) free(ctx->dynstr); /* from library file just in case */ 64 | free(ctx); 65 | } 66 | return 0; 67 | } 68 | 69 | /* flags are ignored */ 70 | 71 | static void *fake_dlopen_with_path(const char *libpath, int flags) { 72 | LOGI("fake_dlopen"); 73 | FILE *maps; 74 | char buff[256]; 75 | struct ctx *ctx = 0; 76 | off_t load_addr, size; 77 | int k, fd = -1, found = 0; 78 | char *shoff; 79 | Elf_Ehdr *elf = (Elf_Ehdr *) MAP_FAILED; 80 | 81 | #define fatal(fmt, args...) do { LOGE(fmt,##args); goto err_exit; } while(0) 82 | 83 | maps = fopen("/proc/self/maps", "r"); 84 | if (!maps) LOGE("failed to open maps"); 85 | 86 | while (!found && fgets(buff, sizeof(buff), maps)) { 87 | if (strstr(buff, libpath) && (strstr(buff, "r-xp") || strstr(buff, "r--p"))) found = 1; 88 | } 89 | fclose(maps); 90 | 91 | if (!found) fatal("%s not found in my userspace", libpath); 92 | 93 | if (sscanf(buff, "%lx", &load_addr) != 1) 94 | LOGE("failed to read load address for %s", libpath); 95 | 96 | LOGI("%s loaded in Android at 0x%08lx", libpath, load_addr); 97 | 98 | /* Now, mmap the same library once again */ 99 | 100 | fd = open(libpath, O_RDONLY); 101 | if (fd < 0) fatal("failed to open %s", libpath); 102 | 103 | size = lseek(fd, 0, SEEK_END); 104 | if (size <= 0) fatal("lseek() failed for %s", libpath); 105 | 106 | elf = (Elf_Ehdr *) mmap(0, size, PROT_READ, MAP_SHARED, fd, 0); 107 | close(fd); 108 | fd = -1; 109 | 110 | if (elf == MAP_FAILED) fatal("mmap() failed for %s", libpath); 111 | 112 | ctx = (struct ctx *) calloc(1, sizeof(struct ctx)); 113 | if (!ctx) fatal("no memory for %s", libpath); 114 | 115 | ctx->load_addr = (char *) load_addr; 116 | shoff = ((char *) elf) + elf->e_shoff; 117 | 118 | for (k = 0; k < elf->e_shnum; k++, shoff += elf->e_shentsize) { 119 | 120 | Elf_Shdr *sh = (Elf_Shdr *) shoff; 121 | LOGI("%s: k=%d shdr=%p type=%x", __func__, k, sh, sh->sh_type); 122 | 123 | switch (sh->sh_type) { 124 | 125 | case SHT_DYNSYM: 126 | if (ctx->dynsym) fatal("%s: duplicate DYNSYM sections", libpath); /* .dynsym */ 127 | ctx->dynsym = (char*) malloc(sh->sh_size); 128 | if (!ctx->dynsym) fatal("%s: no memory for .dynsym", libpath); 129 | memcpy(ctx->dynsym, ((char *) elf) + sh->sh_offset, sh->sh_size); 130 | ctx->nsyms = (sh->sh_size / sizeof(Elf_Sym)); 131 | break; 132 | 133 | case SHT_STRTAB: 134 | if (ctx->dynstr) break; /* .dynstr is guaranteed to be the first STRTAB */ 135 | ctx->dynstr = (char*) malloc(sh->sh_size); 136 | if (!ctx->dynstr) fatal("%s: no memory for .dynstr", libpath); 137 | memcpy(ctx->dynstr, ((char *) elf) + sh->sh_offset, sh->sh_size); 138 | break; 139 | 140 | case SHT_PROGBITS: 141 | if (!ctx->dynstr || !ctx->dynsym) break; 142 | /* won't even bother checking against the section name */ 143 | ctx->bias = (off_t) sh->sh_addr - (off_t) sh->sh_offset; 144 | k = elf->e_shnum; /* exit for */ 145 | break; 146 | } 147 | } 148 | 149 | munmap(elf, size); 150 | elf = 0; 151 | 152 | if (!ctx->dynstr || !ctx->dynsym) fatal("dynamic sections not found in %s", libpath); 153 | 154 | #undef fatal 155 | 156 | LOGI("%s: ok, dynsym = %p, dynstr = %p", libpath, ctx->dynsym, ctx->dynstr); 157 | 158 | return ctx; 159 | 160 | err_exit: 161 | if (fd >= 0) close(fd); 162 | if (elf != MAP_FAILED) munmap(elf, size); 163 | fake_dlclose(ctx); 164 | return 0; 165 | } 166 | 167 | #if defined(__LP64__) 168 | static const char *const kSystemLibDir = "/system/lib64/"; 169 | static const char *const kOdmLibDir = "/odm/lib64/"; 170 | static const char *const kVendorLibDir = "/vendor/lib64/"; 171 | static const char *const kApexLibDir = "/apex/com.android.runtime/lib64/"; 172 | #else 173 | static const char *const kSystemLibDir = "/system/lib/"; 174 | static const char *const kOdmLibDir = "/odm/lib/"; 175 | static const char *const kVendorLibDir = "/vendor/lib/"; 176 | static const char *const kApexLibDir = "/apex/com.android.runtime/lib/"; 177 | #endif 178 | 179 | static void *fake_dlopen(const char *filename, int flags) { 180 | LOGI("fake_dlopen filename=%s", filename); 181 | if (strlen(filename) > 0 && filename[0] == '/') { 182 | return fake_dlopen_with_path(filename, flags); 183 | } else { 184 | char buf[512] = {0}; 185 | void *handle = NULL; 186 | //sysmtem 187 | strcpy(buf, kSystemLibDir); 188 | strcat(buf, filename); 189 | handle = fake_dlopen_with_path(buf, flags); 190 | if (handle) { 191 | return handle; 192 | } 193 | LOGI("fake_dlopen 1"); 194 | 195 | // apex 196 | memset(buf, 0, sizeof(buf)); 197 | strcpy(buf, kApexLibDir); 198 | strcat(buf, filename); 199 | handle = fake_dlopen_with_path(buf, flags); 200 | if (handle) { 201 | return handle; 202 | } 203 | LOGI("fake_dlopen 2"); 204 | 205 | //odm 206 | memset(buf, 0, sizeof(buf)); 207 | strcpy(buf, kOdmLibDir); 208 | strcat(buf, filename); 209 | handle = fake_dlopen_with_path(buf, flags); 210 | if (handle) { 211 | return handle; 212 | } 213 | LOGI("fake_dlopen 3"); 214 | 215 | //vendor 216 | memset(buf, 0, sizeof(buf)); 217 | strcpy(buf, kVendorLibDir); 218 | strcat(buf, filename); 219 | handle = fake_dlopen_with_path(buf, flags); 220 | if (handle) { 221 | return handle; 222 | } 223 | LOGI("fake_dlopen 4"); 224 | return fake_dlopen_with_path(filename, flags); 225 | } 226 | } 227 | 228 | static void *fake_dlsym(void *handle, const char *name) { 229 | int k; 230 | struct ctx *ctx = (struct ctx *) handle; 231 | Elf_Sym *sym = (Elf_Sym *) ctx->dynsym; 232 | char *strings = (char *) ctx->dynstr; 233 | 234 | for (k = 0; k < ctx->nsyms; k++, sym++) 235 | if (strcmp(strings + sym->st_name, name) == 0) { 236 | /* NB: sym->st_value is an offset into the section for relocatables, 237 | but a VMA for shared libs or exe files, so we have to subtract the bias */ 238 | void *ret = ctx->load_addr + sym->st_value - ctx->bias; 239 | LOGI("%s found at %p", name, ret); 240 | return ret; 241 | } 242 | return 0; 243 | } 244 | 245 | 246 | static const char *fake_dlerror() { 247 | return NULL; 248 | } 249 | 250 | // =============== implementation for compat ========== 251 | static int SDK_INT = -1; 252 | static int get_sdk_level() { 253 | if (SDK_INT > 0) { 254 | return SDK_INT; 255 | } 256 | char sdk[PROP_VALUE_MAX] = {0};; 257 | __system_property_get("ro.build.version.sdk", sdk); 258 | SDK_INT = atoi(sdk); 259 | return SDK_INT; 260 | } 261 | 262 | int dlclose_ex(void *handle) { 263 | if (get_sdk_level() >= 24) { 264 | return fake_dlclose(handle); 265 | } else { 266 | return dlclose(handle); 267 | } 268 | } 269 | 270 | void *dlopen_ex(const char *filename, int flags) { 271 | LOGI("dlopen: %s", filename); 272 | if (get_sdk_level() >= 24) { 273 | return fake_dlopen(filename, flags); 274 | } else { 275 | return dlopen(filename, flags); 276 | } 277 | } 278 | 279 | void *dlsym_ex(void *handle, const char *symbol) { 280 | if (get_sdk_level() >= 24) { 281 | return fake_dlsym(handle, symbol); 282 | } else { 283 | return dlsym(handle, symbol); 284 | } 285 | } 286 | 287 | const char *dlerror_ex() { 288 | if (get_sdk_level() >= 24) { 289 | return fake_dlerror(); 290 | } else { 291 | return dlerror(); 292 | } 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /app/src/main/cpp/InjectModule/fake_dlfcn.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016 avs333 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | #ifndef DEXPOSED_DLFCN_H 22 | #define DEXPOSED_DLFCN_H 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | extern "C" { 29 | 30 | void *dlopen_ex(const char *filename, int flags); 31 | 32 | void *dlsym_ex(void *handle, const char *symbol); 33 | 34 | int dlclose_ex(void *handle); 35 | 36 | const char *dlerror_ex(); 37 | 38 | }; 39 | #endif //DEXPOSED_DLFCN_H 40 | -------------------------------------------------------------------------------- /app/src/main/cpp/InjectModule/load_dex.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "android/log.h" 3 | #include "stdio.h" 4 | #include "stdlib.h" 5 | #include 6 | #include 7 | #define ANDROID_SMP 0 8 | #include 9 | #include 10 | #include "load_dex.h" 11 | #include "PrintLog.h" 12 | 13 | static jobject g_orignalDex; 14 | static jobject g_classLoader=0; 15 | 16 | 17 | int ClearException(JNIEnv *jenv){ 18 | JNIEnv * test = NULL; 19 | jthrowable exception = jenv->ExceptionOccurred(); 20 | if (exception != NULL) { 21 | jenv->ExceptionDescribe(); 22 | jenv->ExceptionClear(); 23 | return true; 24 | } 25 | return false; 26 | } 27 | 28 | 29 | 30 | int makeDexElements(JNIEnv* env, jobject classLoader, jobject dexFileobj) 31 | { 32 | jclass PathClassLoader = env->GetObjectClass(classLoader); 33 | 34 | jclass BaseDexClassLoader = env->GetSuperclass(PathClassLoader); 35 | 36 | //get pathList fieldid 37 | jfieldID pathListid = env->GetFieldID(BaseDexClassLoader, "pathList", "Ldalvik/system/DexPathList;"); 38 | jobject pathList = env->GetObjectField(classLoader, pathListid); 39 | 40 | //get DexPathList Class 41 | jclass DexPathListClass = env->GetObjectClass(pathList); 42 | //get dexElements fieldid 43 | jfieldID dexElementsid = env->GetFieldID(DexPathListClass, "dexElements", "[Ldalvik/system/DexPathList$Element;"); 44 | 45 | //获取elements数组 get dexElement array value 46 | jobjectArray dexElement = static_cast(env->GetObjectField(pathList, dexElementsid)); 47 | 48 | 49 | //获取数组的个数 get DexPathList$Element Class construction method and get a new DexPathList$Element object 50 | jint len = env->GetArrayLength(dexElement); 51 | LOGI( "original Element size:%d", len); 52 | 53 | 54 | jclass ElementClass = env->FindClass("dalvik/system/DexPathList$Element");// dalvik/system/DexPathList$Element 55 | jmethodID Elementinit = env->GetMethodID(ElementClass, "", "(Ljava/io/File;ZLjava/io/File;Ldalvik/system/DexFile;)V"); 56 | jboolean isDirectory = JNI_FALSE; 57 | 58 | /** 59 | * get origianl dex object 60 | */ 61 | jobject originalDexElement=env->GetObjectArrayElement(dexElement,0); 62 | if(originalDexElement!=0) 63 | { 64 | jfieldID tmp=env->GetFieldID(ElementClass,"dexFile","Ldalvik/system/DexFile;"); 65 | g_orignalDex=env->GetObjectField(originalDexElement,tmp); 66 | if(ClearException(env)){ 67 | LOGI("get original DexObj faield"); 68 | } 69 | } 70 | 71 | //创建一个新的dalvik/system/DexPathList$Element类 dexFileobj为新的dexFileobj 72 | jobject element_obj = env->NewObject(ElementClass, Elementinit, 0, isDirectory, 0, dexFileobj); 73 | 74 | //Get dexElement all values and add add each value to the new array 75 | jobjectArray new_dexElement = env->NewObjectArray(len + 1, ElementClass, 0); 76 | for (int i = 0; i < len; ++i) 77 | { 78 | //将以前的Elements添加到这个新的new_dexElement数组 79 | env->SetObjectArrayElement(new_dexElement, i, env->GetObjectArrayElement(dexElement, i)); 80 | } 81 | //将要加载的element_obj放在新数组的最后一个成员里 82 | env->SetObjectArrayElement(new_dexElement, len, element_obj); 83 | env->SetObjectField(pathList, dexElementsid, new_dexElement); 84 | LOGI("make complete"); 85 | env->DeleteLocalRef(element_obj); 86 | env->DeleteLocalRef(ElementClass); 87 | env->DeleteLocalRef(dexElement); 88 | env->DeleteLocalRef(DexPathListClass); 89 | env->DeleteLocalRef(pathList); 90 | env->DeleteLocalRef(BaseDexClassLoader); 91 | env->DeleteLocalRef(PathClassLoader); 92 | return 1; 93 | } 94 | 95 | 96 | 97 | jobject getClassLoader(JNIEnv *jenv){ 98 | //获取Loaders 99 | jclass clazzApplicationLoaders = jenv->FindClass("android/app/ApplicationLoaders"); 100 | jthrowable exception = jenv->ExceptionOccurred(); 101 | if (ClearException(jenv)) { 102 | LOGI("Exception","No class : %s", "android/app/ApplicationLoaders"); 103 | return NULL; 104 | } 105 | jfieldID fieldApplicationLoaders = jenv->GetStaticFieldID(clazzApplicationLoaders,"gApplicationLoaders","Landroid/app/ApplicationLoaders;"); 106 | if (ClearException(jenv)) { 107 | LOGI("Exception","No Static Field :%s","gApplicationLoaders"); 108 | return NULL; 109 | } 110 | jobject objApplicationLoaders = jenv->GetStaticObjectField(clazzApplicationLoaders,fieldApplicationLoaders); 111 | if (ClearException(jenv)) { 112 | LOGI("Exception","GetStaticObjectField is failed [%s","gApplicationLoaders"); 113 | return NULL; 114 | } 115 | // 116 | 117 | jfieldID fieldLoaders = jenv->GetFieldID(clazzApplicationLoaders,"mLoaders","Ljava/util/Map;"); 118 | if (ClearException(jenv)) { 119 | fieldLoaders = jenv->GetFieldID(clazzApplicationLoaders,"mLoaders","Landroid/util/ArrayMap;"); 120 | if(ClearException(jenv)){ 121 | LOGI("Exception","No Field :%s","mLoaders"); 122 | return NULL; 123 | } 124 | 125 | } 126 | 127 | jobject objLoaders = jenv->GetObjectField(objApplicationLoaders,fieldLoaders); 128 | if (ClearException(jenv)) { 129 | LOGI("Exception","No object :%s","mLoaders"); 130 | return NULL; 131 | } 132 | //提取map中的values 133 | jclass clazzHashMap = jenv->GetObjectClass(objLoaders); 134 | jmethodID methodValues = jenv->GetMethodID(clazzHashMap,"values","()Ljava/util/Collection;"); 135 | jobject values = jenv->CallObjectMethod(objLoaders,methodValues); 136 | 137 | jclass clazzValues = jenv->GetObjectClass(values); 138 | jmethodID methodToArray = jenv->GetMethodID(clazzValues,"toArray","()[Ljava/lang/Object;"); 139 | if (ClearException(jenv)) { 140 | LOGI("Exception","No Method:%s","toArray"); 141 | return NULL; 142 | } 143 | 144 | jobjectArray classLoaders = (jobjectArray)jenv->CallObjectMethod(values,methodToArray); 145 | if (ClearException(jenv)) { 146 | LOGI("Exception","CallObjectMethod failed :%s","toArray"); 147 | return NULL; 148 | } 149 | 150 | int size = jenv->GetArrayLength(classLoaders); 151 | 152 | //classLoaders size always is 1 ??? 153 | LOGI("classLoaders size:%d",size); 154 | 155 | for(int i = 0 ; i < size ; i ++){ 156 | jobject classLoader = jenv->GetObjectArrayElement(classLoaders,i); 157 | g_classLoader=jenv->NewGlobalRef(classLoader); 158 | if(g_classLoader==NULL){ 159 | LOGI("classLoader NewGlobalRef failed"); 160 | return NULL; 161 | } 162 | jenv->DeleteLocalRef(classLoader); 163 | return g_classLoader; 164 | } 165 | 166 | } 167 | 168 | jclass loadCLass_plan_one(JNIEnv *jenv,const char *name,jobject dexObject) 169 | { 170 | loadCLass_plan_one(jenv,name,dexObject); 171 | jclass DexFile=jenv->FindClass("dalvik/system/DexFile"); 172 | jmethodID loadClass=jenv->GetMethodID(DexFile,"loadClass","(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/Class;"); 173 | if(ClearException(jenv)) 174 | { 175 | LOGI("find loadClass methodId failed"); 176 | return 0; 177 | } 178 | //important dexObject.loadClass() 179 | jstring className=jenv->NewStringUTF(name); 180 | jclass tClazz = (jclass)jenv->CallObjectMethod(dexObject,loadClass,className,g_classLoader); 181 | if(ClearException(jenv)) 182 | { 183 | LOGI("loadClass %s failed",name); 184 | return 0; 185 | } 186 | return tClazz; 187 | 188 | } 189 | 190 | //MultiDex 191 | jclass loadCLass_plan_two(JNIEnv *jenv,const char *name) 192 | { 193 | jstring className=jenv->NewStringUTF(name); 194 | jclass clazzCL = jenv->GetObjectClass(g_classLoader); 195 | jmethodID loadClass = jenv->GetMethodID(clazzCL,"loadClass","(Ljava/lang/String;)Ljava/lang/Class;"); 196 | jclass tClazz = (jclass)jenv->CallObjectMethod(g_classLoader,loadClass,className); 197 | 198 | if(ClearException(jenv)) 199 | { 200 | LOGI("loadClass %s failed",name); 201 | return 0; 202 | } 203 | return tClazz; 204 | } 205 | 206 | jclass findAppClass_test(JNIEnv *jenv,const char *name,jobject dexObject) 207 | { 208 | //there hava 2 plan to loadClass 209 | // plan 1 210 | //jclass TargetClazz=loadCLass_plan_one(jenv,name,dexObject); 211 | 212 | // plan 2 213 | jclass TargetClazz=loadCLass_plan_two(jenv,name); 214 | if(TargetClazz!=0) 215 | LOGI("loadClass %s successful clazz:0x%x",name,TargetClazz); 216 | return TargetClazz; 217 | } 218 | 219 | 220 | jobject LoadDex(JNIEnv* jenv,const char* dexPath,const char* pKgName) 221 | { 222 | jclass DexFile=jenv->FindClass("dalvik/system/DexFile"); 223 | if(ClearException(jenv)) 224 | { 225 | LOGI("find DexFile class failed"); 226 | return 0; 227 | } 228 | jmethodID loadDex=jenv->GetStaticMethodID(DexFile,"loadDex","(Ljava/lang/String;Ljava/lang/String;I)Ldalvik/system/DexFile;"); 229 | if(ClearException(jenv)) 230 | { 231 | LOGI("find loadDex methodId failed"); 232 | return 0; 233 | } 234 | //jstring inPath=jenv->NewStringUTF("/data/data/com.example.stromhooktest/legend.dex"); 235 | jstring inPath=jenv->NewStringUTF(dexPath); 236 | 237 | char optPath[256]={0}; 238 | strcat(optPath,"/data/data/"); 239 | strcat(optPath,pKgName); 240 | strcat(optPath,"/hook.dat"); 241 | LOGI("LoadDex optFile path:%s",optPath); 242 | jstring outPath=jenv->NewStringUTF(optPath); 243 | jobject dexObject=jenv->CallStaticObjectMethod(DexFile,loadDex,inPath,outPath,0); 244 | if(ClearException(jenv)) 245 | { 246 | LOGI("call loadDex method failed"); 247 | return 0; 248 | } 249 | return dexObject; 250 | } 251 | 252 | 253 | jclass myFindClass(JNIEnv* jenv,const char* targetClassName,jobject dexObj) 254 | { 255 | //char* targetClassName="com/legend/demo/Inject"; 256 | jclass clazzTarget = jenv->FindClass(targetClassName); 257 | if (ClearException(jenv)) { 258 | LOGI("ClassMethodHook[Can't find class:%s in bootclassloader",targetClassName); 259 | clazzTarget = findAppClass_test(jenv,targetClassName,dexObj); 260 | if(clazzTarget == NULL){ 261 | LOGI("found class %s failed",targetClassName); 262 | return NULL; 263 | } 264 | } 265 | 266 | return clazzTarget; 267 | } 268 | 269 | jobject createNewClassLoader(JNIEnv* env, const char *jarpath,char * nativepath){ 270 | jclass clzPathClassLoader = env->FindClass("dalvik/system/PathClassLoader"); 271 | // LOGI("java/lang/ClassLoader 0x%p\n", clzClassLoader); 272 | jmethodID mdinitPathCL = env->GetMethodID(clzPathClassLoader, "", 273 | "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V"); 274 | 275 | LOGI("PathClassLoader loading jarpath[%s]\n", jarpath); 276 | LOGI("nativepath loading nativepath[%s]\n", nativepath); 277 | 278 | jstring jarpath_str = env->NewStringUTF(jarpath); 279 | jstring narivepath_str = env->NewStringUTF(nativepath); 280 | 281 | jobject myClassLoader = env->NewObject(clzPathClassLoader, mdinitPathCL, jarpath_str, narivepath_str, 282 | NULL); 283 | env->DeleteLocalRef(narivepath_str); 284 | env->DeleteLocalRef(jarpath_str); 285 | return myClassLoader; 286 | } 287 | 288 | jclass findClassFromLoader(JNIEnv *env, jobject class_loader, const char *class_name) { 289 | jclass clz = env->GetObjectClass(class_loader); 290 | 291 | jmethodID mid = env->GetMethodID(clz, "loadClass", 292 | "(Ljava/lang/String;)Ljava/lang/Class;"); 293 | jclass ret = nullptr; 294 | if (!mid) { 295 | mid = env->GetMethodID(clz, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;"); 296 | } 297 | jobject target = env->CallObjectMethod(class_loader, mid, 298 | env->NewStringUTF(class_name)); 299 | if (target) { 300 | return (jclass) target; 301 | } 302 | 303 | LOGE("Class %s not found", class_name); 304 | 305 | return ret; 306 | } -------------------------------------------------------------------------------- /app/src/main/cpp/InjectModule/load_dex.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Administrator on 2021/5/9. 3 | // 4 | 5 | #ifndef ANDROIDINJECT_LOAD_DEX_H 6 | #define ANDROIDINJECT_LOAD_DEX_H 7 | 8 | 9 | int ClearException(JNIEnv *jenv); 10 | 11 | int makeDexElements(JNIEnv *env, jobject classLoader, jobject dexFileobj); 12 | 13 | jobject getClassLoader(JNIEnv *jenv); 14 | 15 | jclass loadCLass_plan_one(JNIEnv *jenv, const char *name, jobject dexObject); 16 | 17 | jclass loadCLass_plan_two(JNIEnv *jenv, const char *name); 18 | 19 | jclass findAppClass_test(JNIEnv *jenv, const char *name, jobject dexObject); 20 | 21 | jobject LoadDex(JNIEnv *jenv, const char *dexPath, const char *pKgName); 22 | 23 | jclass myFindClass(JNIEnv *jenv, const char *targetClassName, jobject dexObj); 24 | 25 | jobject createNewClassLoader(JNIEnv *env, const char *jarpath, char *nativepath); 26 | 27 | jclass findClassFromLoader(JNIEnv *env, jobject class_loader, const char *class_name); 28 | 29 | #endif //ANDROIDINJECT_LOAD_DEX_H 30 | -------------------------------------------------------------------------------- /app/src/main/cpp/PtraceInject/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.1) 2 | 3 | enable_language(ASM) #支持汇编 4 | add_executable(SharkInject main.cpp PtraceInject.cpp ptrace_utils.cpp module_utils.cpp ) 5 | 6 | find_library(log-lib log) 7 | 8 | target_link_libraries(SharkInject ${log-lib} ) -------------------------------------------------------------------------------- /app/src/main/cpp/PtraceInject/PrintLog.h: -------------------------------------------------------------------------------- 1 | #ifndef _ANDROID_LOG_PRINT_H_ 2 | #define _ANDROID_LOG_PRINT_H_ 3 | 4 | #include 5 | 6 | //如果不想打印日志可以注释这行宏定义 7 | #define IS_DEBUG 8 | //如果宏定义了IS_DEBUG,那么下面就会宏定义下面这些日志打印函数 9 | #ifdef IS_DEBUG 10 | 11 | #define LOG_TAG ("InjectShark") 12 | 13 | #define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) 14 | 15 | #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG , LOG_TAG, __VA_ARGS__)) 16 | 17 | #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO , LOG_TAG, __VA_ARGS__)) 18 | 19 | #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN , LOG_TAG, __VA_ARGS__)) 20 | 21 | #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR , LOG_TAG, __VA_ARGS__)) 22 | 23 | #else 24 | 25 | #define LOGV(LOG_TAG, ...) NULL 26 | 27 | #define LOGD(LOG_TAG, ...) NULL 28 | 29 | #define LOGI(LOG_TAG, ...) NULL 30 | 31 | #define LOGW(LOG_TAG, ...) NULL 32 | 33 | #define LOGE(LOG_TAG, ...) NULL 34 | 35 | #endif 36 | 37 | #endif -------------------------------------------------------------------------------- /app/src/main/cpp/PtraceInject/PtraceInject.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "PtraceInject.h" 14 | #include "PrintLog.h" 15 | #include "ptrace_utils.h" 16 | #include "module_utils.h" 17 | 18 | /************************************************* 19 | * 通过远程直接调用dlopen\dlsym的方法ptrace注入so模块到远程进程中 20 | * Input: pid表示远程进程的ID,LibPath为被远程注入的so模块路径,FunctionName为远程注入的模块后调用的函数 21 | * FuncParameter指向被远程调用函数的参数(若传递字符串,需要先将字符串写入到远程进程空间中), 22 | * NumParameter为参数的个数 23 | * Return: 返回0表示注入成功,返回-1表示失败 24 | * ***********************************************/ 25 | int inject_remote_process(pid_t pid, char *LibPath, char *FunctionName, 26 | char *parameter, long NumParameter) { 27 | int iRet = -1; 28 | long parameters[6]; 29 | //attach到目标进程 30 | if (ptrace_attach(pid) != 0) { 31 | return iRet; 32 | } 33 | 34 | do { 35 | //CurrentRegs 当前寄存器 36 | //OriginalRegs 保存注入前寄存器 37 | struct pt_regs CurrentRegs, OriginalRegs; 38 | 39 | if (ptrace_getregs(pid, &CurrentRegs) != 0) { 40 | break; 41 | } 42 | 43 | memcpy(&OriginalRegs, &CurrentRegs, sizeof(CurrentRegs)); 44 | 45 | void *mmap_addr = get_mmap_address(pid); 46 | printf("mmap RemoteFuncAddr:0x%lx\n", (uintptr_t) mmap_addr); 47 | 48 | // mmap映射 49 | parameters[0] = 0; // 设置为NULL表示让系统自动选择分配内存的地址 50 | parameters[1] = 0x3000; // 映射内存的大小 51 | parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC; // 表示映射内存区域可读可写可执行 52 | parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // 建立匿名映射 53 | parameters[4] = 0; // 若需要映射文件到内存中,则为文件的fd 54 | parameters[5] = 0; //文件映射偏移量 55 | 56 | if (ptrace_call(pid, (uintptr_t) mmap_addr, parameters, 6, &CurrentRegs) == -1) { 57 | printf("Call Remote mmap Func Failed, err:%s\n", strerror(errno)); 58 | break; 59 | } 60 | 61 | printf("ptrace_call mmap success, return value=%lX, pc=%lX\n", ptrace_getret(&CurrentRegs), 62 | ptrace_getpc(&CurrentRegs)); 63 | 64 | // 获取mmap函数执行后的返回值,也就是内存映射的起始地址 65 | void *RemoteMapMemoryAddr = (void *) ptrace_getret(&CurrentRegs); 66 | printf("Remote Process Map Memory Addr:0x%lx\n", (uintptr_t) RemoteMapMemoryAddr); 67 | 68 | 69 | // 分别获取dlopen、dlsym、dlclose等函数的地址 70 | void *dlopen_addr, *dlsym_addr, *dlclose_addr, *dlerror_addr; 71 | dlopen_addr = get_dlopen_address(pid); 72 | dlsym_addr = get_dlsym_address(pid); 73 | dlclose_addr = get_dlclose_address(pid); 74 | dlerror_addr = get_dlerror_address(pid); 75 | // 将要加载的so库路径写入到远程进程内存空间中 76 | if (ptrace_writedata(pid, (uint8_t *) RemoteMapMemoryAddr, (uint8_t *) LibPath, 77 | strlen(LibPath) + 1) == -1) { 78 | printf("Write LibPath:%s to RemoteProcess error\n", LibPath); 79 | break; 80 | } 81 | 82 | // 设置dlopen的参数,返回值为模块加载的地址 83 | // void *dlopen(const char *filename, int flag); 84 | parameters[0] = (uintptr_t) RemoteMapMemoryAddr; 85 | parameters[1] = RTLD_NOW | RTLD_GLOBAL; 86 | 87 | if (ptrace_call(pid, (uintptr_t) dlopen_addr, parameters, 2, &CurrentRegs) == -1) { 88 | printf("Call Remote dlopen Func Failed\n"); 89 | break; 90 | } 91 | 92 | // RemoteModuleAddr为远程进程加载注入模块的地址 93 | void *RemoteModuleAddr = (void *) ptrace_getret(&CurrentRegs); 94 | printf("ptrace_call dlopen success, Remote Process load module Addr:0x%lx\n", 95 | (long) RemoteModuleAddr); 96 | 97 | if ((long) RemoteModuleAddr == 0x0) // dlopen 错误 98 | { 99 | printf("dlopen error\n"); 100 | if (ptrace_call(pid, (uintptr_t) dlerror_addr, parameters, 0, &CurrentRegs) == -1) { 101 | printf("Call Remote dlerror Func Failed\n"); 102 | break; 103 | } 104 | char *Error = (char *) ptrace_getret(&CurrentRegs); 105 | char LocalErrorInfo[1024] = {0}; 106 | ptrace_readdata(pid, (uint8_t *) Error, (uint8_t *) LocalErrorInfo, 1024); 107 | printf("dlopen error:%s\n", LocalErrorInfo); 108 | break; 109 | } 110 | 111 | // 将so库中需要调用的函数名称写入到远程进程内存空间中 112 | if (ptrace_writedata(pid, (uint8_t *) RemoteMapMemoryAddr + strlen(LibPath) + 2, 113 | (uint8_t *) FunctionName, strlen(FunctionName) + 1) == -1) { 114 | printf("Write FunctionName:%s to RemoteProcess error\n", FunctionName); 115 | break; 116 | } 117 | 118 | // 设置dlsym的参数,返回值为远程进程内函数的地址 119 | // void *dlsym(void *handle, const char *symbol); 120 | parameters[0] = (uintptr_t) RemoteModuleAddr; 121 | parameters[1] = (uintptr_t) ((uint8_t *) RemoteMapMemoryAddr + strlen(LibPath) + 2); 122 | 123 | if (ptrace_call(pid, (uintptr_t) dlsym_addr, parameters, 2, &CurrentRegs) == -1) { 124 | printf("Call Remote dlsym Func Failed\n"); 125 | break; 126 | } 127 | 128 | // RemoteModuleFuncAddr为远程进程空间内获取的函数地址 129 | void *RemoteModuleFuncAddr = (void *) ptrace_getret(&CurrentRegs); 130 | printf("ptrace_call dlsym success, Remote Process ModuleFunc Addr:0x%lx\n", 131 | (uintptr_t) RemoteModuleFuncAddr); 132 | 133 | //为调用的函数参数,拷贝字符串 (这里分配到0x200后面 一般上面的字符串不会超过) 134 | if (ptrace_writedata(pid, (uint8_t *) RemoteMapMemoryAddr + strlen(LibPath) + 2 + 135 | strlen(parameter) + 2, 136 | (uint8_t *) parameter, strlen(parameter) + 1) == -1) { 137 | printf("Write param error\n"); 138 | break; 139 | } 140 | parameters[0] = (uintptr_t) ((uint8_t *) RemoteMapMemoryAddr + strlen(LibPath) + 2 + 141 | strlen(parameter) + 2); 142 | 143 | //这里其实应该计算参数的大小 然后在分配内存空间 144 | //暂时传一个参数吧 145 | if (ptrace_call(pid, (uintptr_t) RemoteModuleFuncAddr, parameters, NumParameter, 146 | &CurrentRegs) == -1) { 147 | printf("Call Remote injected Func Failed\n"); 148 | break; 149 | } 150 | 151 | if (ptrace_setregs(pid, &OriginalRegs) == -1) { 152 | printf("Recover reges failed\n"); 153 | break; 154 | } 155 | printf("Recover Regs Success\n"); 156 | ptrace_getregs(pid, &CurrentRegs); 157 | if (memcmp(&OriginalRegs, &CurrentRegs, sizeof(CurrentRegs)) != 0) { 158 | printf("Set Regs Error\n"); 159 | } 160 | iRet = 0; 161 | } while (false); 162 | 163 | ptrace_detach(pid); 164 | return iRet; 165 | } 166 | 167 | struct process_inject { 168 | pid_t pid; 169 | char lib_name[1024]; 170 | char dex_name[1024]; 171 | char *entry_fun; 172 | } process_inject = {0, "", "", "entry"}; 173 | 174 | 175 | void init_lib() { 176 | //获取当前目录 177 | char current_absolute_path[4096] = {0}; 178 | if (realpath("./", current_absolute_path) == NULL) { 179 | perror("realpath"); 180 | exit(-1); 181 | } 182 | strcat(process_inject.lib_name, current_absolute_path); 183 | strcat(process_inject.lib_name, "/libLoadModule.so"); 184 | 185 | strcat(process_inject.dex_name, current_absolute_path); 186 | strcat(process_inject.dex_name, "/classes.dex"); 187 | 188 | //修改为默认从加载so的路径读取 189 | // cp_lib(pkgName,"libsandhook.so"); 190 | } 191 | 192 | void handle_parameter(int argc, char *argv[]) { 193 | pid_t pid = 0; 194 | int index = 0; 195 | char *pkg_name = NULL; 196 | char *file_name = NULL; 197 | char *dex_name = NULL; 198 | bool start_app_flag = false; 199 | 200 | while (index < argc) { 201 | if (strcmp("-p", argv[index]) == 0) { 202 | if (index + 1 >= argc) { 203 | printf("Missing parameter -p\n"); 204 | exit(-1); 205 | } 206 | index++; 207 | pid = atoi(argv[index]); 208 | } 209 | 210 | if (strcmp("-n", argv[index]) == 0) { 211 | if (index + 1 >= argc) { 212 | printf("Missing parameter -n\n"); 213 | exit(-1); 214 | } 215 | index++; 216 | pkg_name = argv[index]; 217 | 218 | if (start_app_flag) { 219 | start_app(pkg_name); 220 | sleep(1); 221 | } 222 | } 223 | 224 | if (strcmp("-so", argv[index]) == 0) { 225 | if (index + 1 >= argc) { 226 | printf("Missing parameter -f\n"); 227 | exit(-1); 228 | } 229 | index++; 230 | file_name = argv[index]; 231 | } 232 | 233 | if (strcmp("-f", argv[index]) == 0) { start_app_flag = true; } 234 | 235 | if (strcmp("-d", argv[index]) == 0) { 236 | if (index + 1 >= argc) { 237 | printf("Missing parameter -d\n"); 238 | exit(-1); 239 | } 240 | index++; 241 | dex_name = argv[index]; 242 | } 243 | index++; 244 | } 245 | 246 | if (pkg_name != NULL) { 247 | printf("pkg_name is %s\n", pkg_name); 248 | if (get_pid_by_name(&pid, pkg_name)) { 249 | printf("getPidByName pid is %d\n", pid); 250 | } 251 | } 252 | 253 | if (pid == 0) { 254 | printf("not found target\n"); 255 | exit(0); 256 | } 257 | 258 | process_inject.pid = pid; 259 | 260 | if (dex_name != NULL) { 261 | printf("dex_name is %s\n", dex_name); 262 | strcpy(process_inject.dex_name, strdup(dex_name)); 263 | } 264 | 265 | 266 | if (file_name != NULL) { 267 | printf("file_name is %s\n", file_name); 268 | strcpy(process_inject.lib_name, strdup(file_name)); 269 | } 270 | } 271 | 272 | int test(int argc, char *argv[]) { 273 | //初始化dex so 274 | init_lib(); 275 | //处理参数 276 | handle_parameter(argc, argv); 277 | 278 | return inject_remote_process(process_inject.pid, process_inject.lib_name, 279 | process_inject.entry_fun, process_inject.dex_name, 1); 280 | } 281 | -------------------------------------------------------------------------------- /app/src/main/cpp/PtraceInject/PtraceInject.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Rose on 2020/2/18. 3 | // 4 | 5 | #ifndef INJECT_PTRACEINJECT_H 6 | #define INJECT_PTRACEINJECT_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | int inject_remote_process(pid_t pid, char *LibPath, char *FunctionName, 15 | char * parameter, long NumParameter); 16 | int test(int argc, char *argv[]); 17 | 18 | #endif //INJECT_PTRACEINJECT_H 19 | -------------------------------------------------------------------------------- /app/src/main/cpp/PtraceInject/lib_name.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Administrator on 2021/5/3. 3 | // 4 | #include "lib_name.h" 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/cpp/PtraceInject/lib_name.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Administrator on 2021/5/3. 3 | // 4 | #pragma once 5 | 6 | #ifndef ANDROIDINJECT_LIB_NAME_H 7 | 8 | #define ANDROIDINJECT_LIB_NAME_H 9 | 10 | #if defined(__aarch64__) || defined(__x86_64__) 11 | const char *libc_path = "/system/lib64/libc.so"; 12 | const char *linker_path = "/system/bin/linker64"; 13 | const char *libdl_path = "/system/lib64/libdl.so"; 14 | 15 | #else 16 | const char *libc_path = "/system/lib/libc.so"; 17 | const char *linker_path = "/system/bin/linker"; 18 | const char *libdl_path = "/system/lib/libdl.so"; 19 | #endif 20 | 21 | #endif //ANDROIDINJECT_LIB_NAME_H 22 | -------------------------------------------------------------------------------- /app/src/main/cpp/PtraceInject/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by haoyuanli on 2020-3-24. 3 | // 4 | #include 5 | #include 6 | #include "PrintLog.h" 7 | #include "PtraceInject.h" 8 | 9 | int main(int argc, char *argv[]) { 10 | LOGI("[+] start main\n"); 11 | test(argc,argv); 12 | return 0; 13 | } -------------------------------------------------------------------------------- /app/src/main/cpp/PtraceInject/module_utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "PrintLog.h" 18 | 19 | #define MAX_PATH 0x100 20 | 21 | 22 | /************************************************* 23 | * Description: 在指定进程中搜索对应模块的基址 24 | * Input: pid表示远程进程的ID,若为-1表示自身进程,ModuleName表示要搜索的模块的名称 25 | * Return: 返回0表示获取模块基址失败,返回非0为要搜索的模块基址 26 | * **********************************************/ 27 | void *get_module_base_addr(pid_t pid, const char *ModuleName) { 28 | FILE *fp = NULL; 29 | long ModuleBaseAddr = 0; 30 | char szFileName[50] = {0}; 31 | char szMapFileLine[1024] = {0}; 32 | 33 | // 读取"/proc/pid/maps"可以获得该进程加载的模块 34 | if (pid < 0) { 35 | // 枚举自身进程模块 36 | snprintf(szFileName, sizeof(szFileName), "/proc/self/maps"); 37 | } else { 38 | snprintf(szFileName, sizeof(szFileName), "/proc/%d/maps", pid); 39 | } 40 | 41 | fp = fopen(szFileName, "r"); 42 | 43 | if (fp != NULL) { 44 | while (fgets(szMapFileLine, sizeof(szMapFileLine), fp)) { 45 | if (strstr(szMapFileLine, ModuleName)) { 46 | char *Addr = strtok(szMapFileLine, "-"); 47 | ModuleBaseAddr = strtoul(Addr, NULL, 16); 48 | 49 | if (ModuleBaseAddr == 0x8000) 50 | ModuleBaseAddr = 0; 51 | 52 | break; 53 | } 54 | } 55 | 56 | fclose(fp); 57 | } 58 | 59 | return (void *) ModuleBaseAddr; 60 | } 61 | 62 | 63 | /************************************************* 64 | * Description: 获取远程进程与本进程都加载的模块中函数的地址 65 | * Input: pid表示远程进程的ID,ModuleName表示模块名称,LocalFuncAddr表示本地进程中该函数的地址 66 | * Return: 返回远程进程中对应函数的地址 67 | * ***********************************************/ 68 | void *get_remote_func_addr(pid_t pid, const char *ModuleName, void *LocalFuncAddr) { 69 | void *LocalModuleAddr, *RemoteModuleAddr, *RemoteFuncAddr; 70 | 71 | LocalModuleAddr = get_module_base_addr(-1, ModuleName); 72 | RemoteModuleAddr = get_module_base_addr(pid, ModuleName); 73 | 74 | RemoteFuncAddr = (void *) ((uintptr_t) LocalFuncAddr - (uintptr_t) LocalModuleAddr + 75 | (uintptr_t) RemoteModuleAddr); 76 | //[get_remote_func_addr] lmod=0x752805B000, rmod=0x0, lfunc=0x752805C0C0, rfunc=0x10C0 77 | //[get_remote_func_addr] lmod=0x7CBB472000, rmod=0x0, lfunc=0x7CBB4730EC, rfunc=0x10EC 78 | //[get_remote_func_addr] lmod=0x72921C6000, rmod=0x748CD89000, lfunc=0x72921C70C0, rfunc=0x748CD8A0C0 79 | LOGI("[get_remote_func_addr] lmod=0x%lX, rmod=0x%lX, lfunc=0x%lX, rfunc=0x%lX", 80 | LocalModuleAddr, RemoteModuleAddr, LocalFuncAddr, RemoteFuncAddr); 81 | return RemoteFuncAddr; 82 | } 83 | 84 | /************************************************* 85 | Description: 通过进程名称定位到进程的PID 86 | Input: process_name为要定位的进程名称 87 | Output: 无 88 | Return: 返回定位到的进程PID,若为-1,表示定位失败 89 | Others: 无 90 | *************************************************/ 91 | pid_t find_pid_by_name(const char *process_name) { 92 | int dirid = 0; 93 | pid_t pid = -1; 94 | FILE *fp = NULL; 95 | char filename[MAX_PATH] = {0}; 96 | char cmdline[MAX_PATH] = {0}; 97 | 98 | struct dirent *entry = NULL; 99 | 100 | if (process_name == NULL) 101 | return -1; 102 | 103 | DIR *dir = opendir("/proc"); 104 | if (dir == NULL) 105 | return -1; 106 | 107 | while ((entry = readdir(dir)) != NULL) { 108 | dirid = atoi(entry->d_name); 109 | if (dirid != 0) { 110 | snprintf(filename, MAX_PATH, "/proc/%d/cmdline", dirid); 111 | fp = fopen(filename, "r"); 112 | if (fp) { 113 | fgets(cmdline, sizeof(cmdline), fp); 114 | fclose(fp); 115 | if (strncmp(process_name, cmdline, strlen(process_name)) == 0) { 116 | pid = dirid; 117 | LOGI("find cmdline: %s", cmdline); 118 | break; 119 | } 120 | } 121 | } 122 | } 123 | 124 | closedir(dir); 125 | return pid; 126 | } 127 | 128 | long freespaceaddr(pid_t pid) { 129 | FILE *fp; 130 | char filename[50]; 131 | char line[850]; 132 | long addr; 133 | char str[20]; 134 | char perms[5]; 135 | sprintf(filename, "/proc/%d/maps", pid); 136 | fp = fopen(filename, "r"); 137 | if (fp == NULL) { 138 | LOGE("open file %s failed, err:%s", filename, strerror(errno)); 139 | exit(1); 140 | } 141 | 142 | while (fgets(line, 850, fp) != NULL) { 143 | sscanf(line, "%lx-%*lx %s %*s %s %*d", &addr, perms, str); 144 | 145 | if (strstr(perms, "x") != NULL) { 146 | break; 147 | } 148 | } 149 | fclose(fp); 150 | return addr; 151 | } 152 | 153 | 154 | #define INTEL_RET_INSTRUCTION 0xc3 155 | 156 | unsigned char *findRet(void *endAddr) { 157 | unsigned char *retInstAddr = (unsigned char *) endAddr; 158 | while (*retInstAddr != INTEL_RET_INSTRUCTION) { 159 | retInstAddr--; 160 | } 161 | return retInstAddr; 162 | } 163 | 164 | bool get_pid_by_name(pid_t *pid, char *task_name) { 165 | DIR *dir; 166 | struct dirent *ptr; 167 | FILE *fp; 168 | char filepath[50]; 169 | char cur_task_name[50]; 170 | char buf[1024]; 171 | 172 | dir = opendir("/proc"); 173 | if (NULL != dir) { 174 | while ((ptr = readdir(dir)) != NULL) //循环读取/proc下的每一个文件/文件夹 175 | { 176 | //如果读取到的是"."或者".."则跳过,读取到的不是文件夹名字也跳过 177 | if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) 178 | continue; 179 | if (DT_DIR != ptr->d_type) 180 | continue; 181 | 182 | sprintf(filepath, "/proc/%s/cmdline", ptr->d_name);//生成要读取的文件的路径 183 | fp = fopen(filepath, "r"); 184 | if (NULL != fp) { 185 | if (fgets(buf, 1024 - 1, fp) == NULL) { 186 | fclose(fp); 187 | continue; 188 | } 189 | sscanf(buf, "%s", cur_task_name); 190 | //如果文件内容满足要求则打印路径的名字(即进程的PID) 191 | if (strstr(task_name, cur_task_name)) { 192 | // sscanf(ptr->d_name, "%d", pid); 193 | printf("d_name %s\n", ptr->d_name); 194 | *pid = atoi(ptr->d_name); 195 | return true; 196 | 197 | } 198 | fclose(fp); 199 | } 200 | } 201 | closedir(dir); 202 | } 203 | 204 | return false; 205 | } 206 | 207 | 208 | void copy_file(char *source_path, char *destination_path) { 209 | char buffer[1024]; 210 | FILE *in, *out; 211 | if ((in = fopen(source_path, "r")) == NULL) { 212 | printf("源文件打开失败!\n"); 213 | perror("fopen"); 214 | exit(1); 215 | } 216 | if ((out = fopen(destination_path, "w")) == NULL) { 217 | printf("目标文件创建失败!\n"); 218 | perror("fopen"); 219 | exit(1); 220 | } 221 | int len; 222 | while ((len = fread(buffer, 1, 1024, in)) > 0) { 223 | fwrite(buffer, 1, len, out); 224 | } 225 | fclose(out); 226 | fclose(in); 227 | } 228 | 229 | void find_pkg_lib_path(char *pkgName, char *pkgLibPath) { 230 | DIR *pDir; 231 | struct dirent *ent; 232 | int i = 0; 233 | char childpath[256]; 234 | pDir = opendir("/data/app"); 235 | memset(childpath, 0, sizeof(childpath)); 236 | 237 | while ((ent = readdir(pDir)) != NULL) { 238 | if (ent->d_type & DT_DIR) { 239 | if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) 240 | continue; 241 | if (strstr(ent->d_name, pkgName) != NULL) { 242 | strcat(pkgLibPath, "/data/app/"); 243 | strcat(pkgLibPath, ent->d_name); 244 | strcat(pkgLibPath, "/lib"); 245 | break; 246 | } 247 | 248 | } 249 | } 250 | } 251 | 252 | void cp_lib(char *pkgName, char *libName) { 253 | //获取目标lib目录 254 | char pkgLibPath[256] = {0}; 255 | find_pkg_lib_path(pkgName, pkgLibPath); 256 | //如果没有arm64则创建 257 | strcat(pkgLibPath, "/arm/"); 258 | printf("pkgLibPath %s \n", pkgLibPath); 259 | 260 | if ((access(pkgLibPath, F_OK)) == -1) { 261 | if (mkdir(pkgLibPath, 0755) == -1) { 262 | perror("mkdir"); 263 | exit(-1); 264 | } 265 | } 266 | strcat(pkgLibPath, libName); 267 | //获取当前目录 268 | char current_absolute_path[4096] = {0}; 269 | if (realpath("./", current_absolute_path) == NULL) { 270 | perror("realpath"); 271 | exit(-1); 272 | } 273 | //得到lib的路径 274 | char libPath[500] = {}; 275 | strcat(current_absolute_path, "/"); 276 | strcat(libPath, current_absolute_path); 277 | strcat(libPath, libName); 278 | printf("pkgLibPath %s \n", pkgLibPath); 279 | printf("libPath %s \n", libPath); 280 | copy_file(libPath, pkgLibPath); 281 | } 282 | 283 | /** 284 | * 获取应用的android.intent.action.MAIN 285 | * @param pkg_name 获取的应用包名 286 | * @param start_activity_name out 存放启动的main activity 287 | */ 288 | void get_app_start_activity(char *pkg_name, char *start_activity_name) { 289 | char cmdstring[1024] = "dumpsys package "; 290 | char cmd_string[1024] = {0}; 291 | char temp_file[] = "tmp_XXXXXX"; 292 | 293 | strcat(cmdstring, pkg_name); 294 | int fd; 295 | 296 | if ((fd = mkstemp(temp_file)) == -1) { 297 | printf("create tmp file failed.\n"); 298 | } 299 | 300 | sprintf(cmd_string, "%s > %s", cmdstring, temp_file); 301 | system(cmd_string); 302 | 303 | FILE *fp = fdopen(fd, "r"); 304 | if (fp == NULL) { 305 | printf("can not load file!"); 306 | return; 307 | } 308 | char line[1024]; 309 | while (!feof(fp)) { 310 | fgets(line, 1024, fp); 311 | if (strstr(line, "android.intent.action.MAIN")) { 312 | fgets(line, 1024, fp); 313 | char *p; 314 | //选取第二个 315 | int index = 1; 316 | p = strtok(line, " "); 317 | while (p) { 318 | if (index == 2) { 319 | strcpy(start_activity_name, p); 320 | } 321 | index++; 322 | p = strtok(NULL, " "); 323 | } 324 | break; 325 | } 326 | } 327 | fclose(fp); 328 | unlink(temp_file); 329 | return; 330 | } 331 | 332 | /** 333 | * 启动app 334 | * @param pkg_name 335 | */ 336 | void start_app(char *pkg_name) { 337 | char start_activity_name[1024] = {0}; 338 | get_app_start_activity(pkg_name, start_activity_name); 339 | printf("%s\n", start_activity_name); 340 | 341 | char start_cmd[1024] = "am start "; 342 | strcat(start_cmd, start_activity_name); 343 | printf("%s\n",start_cmd); 344 | system(start_cmd); 345 | } 346 | 347 | // SeLinux的强制实施,使得dlopen()外部的so动态库有可能会失败返回。 348 | // 关闭SeLinux setenforce 0; getenforce 349 | // SeLinux会检测so动态库的label标签与权限是否满足可加载的要求,不满足就会无情的拒绝! 350 | // 为了解决这个问题,需要调用setxattr()修改so的属性信息。 351 | // chcon -v u:object_r:system_file:s0 /data/local/tmp/libInjectModule.so 352 | // setxattr(process_inject.libname, "u:object_r:system_file:s0"); 353 | 354 | /*int setxattr(const char *path, const char *value) { 355 | if ((access("/sys/fs/selinux", F_OK)) != -1) { 356 | return 0; 357 | } 358 | return syscall(__NR_setxattr, path, "security.selinux", value, strlen(value), 0); 359 | }*/ -------------------------------------------------------------------------------- /app/src/main/cpp/PtraceInject/module_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Administrator on 2021/5/3. 3 | // 4 | 5 | #ifndef ANDROIDINJECT_MODULE_UTILS_H 6 | #define ANDROIDINJECT_MODULE_UTILS_H 7 | void *get_module_base_addr(pid_t pid, const char *ModuleName); 8 | pid_t find_pid_by_name(const char *process_name); 9 | void *get_remote_func_addr(pid_t pid, const char *ModuleName, void *LocalFuncAddr); 10 | long freespaceaddr(pid_t pid); 11 | unsigned char *findRet(void *endAddr); 12 | int setxattr(const char *path, const char *value); 13 | bool get_pid_by_name(pid_t *pid, char *task_name); 14 | void copy_file(char *source_path, char *destination_path); 15 | void find_pkg_lib_path(char *pkgName, char *pkgLibPath); 16 | void cp_lib(char * pkgName,char * libName); 17 | void get_app_start_activity(char * pkg_name, char *start_activity_name); 18 | void start_app(char * pkg_name); 19 | #endif //ANDROIDINJECT_MODULE_UTILS_H 20 | -------------------------------------------------------------------------------- /app/src/main/cpp/PtraceInject/ptrace_utils.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Administrator on 2021/5/3. 3 | // 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "PrintLog.h" 18 | #include "ptrace_utils.h" 19 | #include "lib_name.h" 20 | #include "module_utils.h" 21 | 22 | 23 | void *get_mmap_address(pid_t pid) { 24 | return get_remote_func_addr(pid, libc_path, (void *) mmap); 25 | } 26 | 27 | void *get_dlopen_address(pid_t pid) { 28 | void *dlopen_addr; 29 | char sdk_ver[32]; 30 | memset(sdk_ver, 0, sizeof(sdk_ver)); 31 | __system_property_get("ro.build.version.sdk", sdk_ver); 32 | 33 | printf("linker_path value:%s\n",linker_path); 34 | if (atoi(sdk_ver) <= 23) { 35 | dlopen_addr = get_remote_func_addr(pid, linker_path, (void *) dlopen); 36 | } else { 37 | dlopen_addr = get_remote_func_addr(pid, libdl_path, (void *) dlopen); 38 | } 39 | printf("dlopen RemoteFuncAddr:0x%lx\n", (uintptr_t) dlopen_addr); 40 | return dlopen_addr; 41 | } 42 | 43 | void *get_dlclose_address(pid_t pid) { 44 | void *dlclose_addr; 45 | char sdk_ver[32]; 46 | memset(sdk_ver, 0, sizeof(sdk_ver)); 47 | __system_property_get("ro.build.version.sdk", sdk_ver); 48 | 49 | if (atoi(sdk_ver) <= 23) { 50 | dlclose_addr = get_remote_func_addr(pid, linker_path, (void *) dlclose); 51 | } else { 52 | dlclose_addr = get_remote_func_addr(pid, libdl_path, (void *) dlclose); 53 | } 54 | printf("dlclose RemoteFuncAddr:0x%lx\n", (uintptr_t) dlclose_addr); 55 | return dlclose_addr; 56 | } 57 | 58 | void *get_dlsym_address(pid_t pid) { 59 | void *dlsym_addr; 60 | char sdk_ver[32]; 61 | memset(sdk_ver, 0, sizeof(sdk_ver)); 62 | __system_property_get("ro.build.version.sdk", sdk_ver); 63 | 64 | if (atoi(sdk_ver) <= 23) { 65 | dlsym_addr = get_remote_func_addr(pid, linker_path, (void *) dlsym); 66 | } else { 67 | dlsym_addr = get_remote_func_addr(pid, libdl_path, (void *) dlsym); 68 | } 69 | printf("dlsym RemoteFuncAddr:0x%lx\n", (uintptr_t) dlsym_addr); 70 | return dlsym_addr; 71 | } 72 | 73 | 74 | void *get_dlerror_address(pid_t pid) { 75 | void *dlerror_addr; 76 | char sdk_ver[32]; 77 | memset(sdk_ver, 0, sizeof(sdk_ver)); 78 | __system_property_get("ro.build.version.sdk", sdk_ver); 79 | 80 | if (atoi(sdk_ver) <= 23) { 81 | dlerror_addr = get_remote_func_addr(pid, linker_path, (void *) dlerror); 82 | } else { 83 | dlerror_addr = get_remote_func_addr(pid, libdl_path, (void *) dlerror); 84 | } 85 | printf("dlerror RemoteFuncAddr:0x%lx\n", (uintptr_t) dlerror_addr); 86 | return dlerror_addr; 87 | } 88 | 89 | /*************************** 90 | * Description: 使用ptrace Attach附加到指定进程,发送SIGSTOP信号给指定进程让其停止下来并对其进行跟踪。 91 | * 但是被跟踪进程(tracee)不一定会停下来,因为同时attach和传递SIGSTOP可能会将SIGSTOP丢失。 92 | * 所以需要waitpid(2)等待被跟踪进程被停下 93 | * Input: pid表示远程进程的ID 94 | * Output: 无 95 | * Return: 返回0表示attach成功,返回-1表示失败 96 | * Others: 无 97 | * ************************/ 98 | int ptrace_attach(pid_t pid) { 99 | int status = 0; 100 | if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) { 101 | printf("ptrace attach process error, pid:%d, err:%s\n", pid, strerror(errno)); 102 | return -1; 103 | } 104 | 105 | printf("attach porcess success, pid:%d\n", pid); 106 | waitpid(pid, &status, WUNTRACED); 107 | 108 | return 0; 109 | } 110 | 111 | /************************************************* 112 | * Description: 使用ptrace detach指定进程,完成对指定进程的跟踪操作后,使用该参数即可解除附加 113 | * Input: pid表示远程进程的ID 114 | * Output: 无 115 | * Return: 返回0表示detach成功,返回-1表示失败 116 | * Others: 无 117 | * ***********************************************/ 118 | int ptrace_detach(pid_t pid) { 119 | if (ptrace(PTRACE_DETACH, pid, NULL, 0) < 0) { 120 | printf("detach process error, pid:%d, err:%s\n", pid, strerror(errno)); 121 | return -1; 122 | } 123 | 124 | printf("detach process success, pid:%d\n", pid); 125 | return 0; 126 | } 127 | 128 | /************************************************* 129 | * Description: ptrace使远程进程继续运行 130 | * Input: pid表示远程进程的ID 131 | * Output: 无 132 | * Return: 返回0表示continue成功,返回-1表示失败 133 | * Others: 无 134 | * ***********************************************/ 135 | int ptrace_continue(pid_t pid) { 136 | if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) { 137 | printf("ptrace continue process error, pid:%d, err:%ss\n", pid, strerror(errno)); 138 | return -1; 139 | } 140 | 141 | printf("ptrace continue process success, pid:%d\n", pid); 142 | return 0; 143 | } 144 | 145 | 146 | /************************************************* 147 | * Description: 使用ptrace获取远程进程的寄存器值 148 | * Input: pid表示远程进程的ID,regs为pt_regs结构,存储了寄存器值 149 | * Output: 无 150 | * Return: 返回0表示获取寄存器成功,返回-1表示失败 151 | * Others: 无 152 | * ***********************************************/ 153 | int ptrace_getregs(pid_t pid, struct pt_regs *regs) { 154 | #if defined(__aarch64__) 155 | int regset = NT_PRSTATUS; 156 | struct iovec ioVec; 157 | 158 | ioVec.iov_base = regs; 159 | ioVec.iov_len = sizeof(*regs); 160 | if (ptrace(PTRACE_GETREGSET, pid, (void *) regset, &ioVec) < 0) { 161 | printf("ptrace_getregs: Can not get register values, io %llx, %d\n", ioVec.iov_base, 162 | ioVec.iov_len); 163 | return -1; 164 | } 165 | 166 | return 0; 167 | #else 168 | if (ptrace(PTRACE_GETREGS, pid, NULL, regs) < 0) { 169 | printf("Get Regs error, pid:%d, err:%s\n", pid, strerror(errno)); 170 | return -1; 171 | } 172 | #endif 173 | return 0; 174 | } 175 | 176 | 177 | /************************************************* 178 | * Description: 使用ptrace设置远程进程的寄存器值 179 | * Input: pid表示远程进程的ID,regs为pt_regs结构,存储需要修改的寄存器值 180 | * Output: 无 181 | * Return: 返回0表示设置寄存器成功,返回-1表示失败 182 | * ***********************************************/ 183 | int ptrace_setregs(pid_t pid, struct pt_regs *regs) { 184 | #if defined(__aarch64__) 185 | int regset = NT_PRSTATUS; 186 | struct iovec ioVec; 187 | 188 | ioVec.iov_base = regs; 189 | ioVec.iov_len = sizeof(*regs); 190 | if (ptrace(PTRACE_SETREGSET, pid, (void *) regset, &ioVec) < 0) { 191 | perror("ptrace_setregs: Can not get register values"); 192 | return -1; 193 | } 194 | 195 | return 0; 196 | #else 197 | if (ptrace(PTRACE_SETREGS, pid, NULL, regs) < 0) { 198 | printf("Set Regs error, pid:%d, err:%s\n", pid, strerror(errno)); 199 | return -1; 200 | } 201 | #endif 202 | return 0; 203 | } 204 | 205 | /************************************************* 206 | * Description: 获取返回值,ARM处理器中返回值存放在ARM_r0寄存器中 207 | * Input: regs存储远程进程当前的寄存器值 208 | * Return: 在ARM处理器下返回r0寄存器值 209 | * ***********************************************/ 210 | long ptrace_getret(struct pt_regs *regs) { 211 | #if defined(__i386__) || defined(__x86_64__) 212 | return regs->eax; 213 | #elif defined(__arm__) || defined(__aarch64__) 214 | return regs->ARM_r0; 215 | #else 216 | printf("Not supported Environment %s\n", __FUNCTION__); 217 | #endif 218 | } 219 | 220 | /************************************************* 221 | * Description: 获取当前执行代码的地址,ARM处理器下存放在ARM_pc中 222 | * Input: regs存储远程进程当前的寄存器值 223 | * Return: 在ARM处理器下返回pc寄存器值 224 | * **********************************************/ 225 | long ptrace_getpc(struct pt_regs *regs) { 226 | #if defined(__i386__) || defined(__x86_64__) 227 | return regs->eip; 228 | #elif defined(__arm__) || defined(__aarch64__) 229 | return regs->ARM_pc; 230 | #else 231 | printf("Not supported Environment %s\n", __FUNCTION__); 232 | #endif 233 | } 234 | 235 | /************************************************* 236 | * Description: 使用ptrace从远程进程内存中读取数据 237 | * Input: pid表示远程进程的ID,pSrcBuf表示从远程进程读取数据的内存地址 238 | * pDestBuf表示用于存储读取出数据的地址,size表示读取数据的大小 239 | * Return: 返回0表示读取数据成功 240 | * other: 这里的*_t类型是typedef定义一些基本类型的别名,用于跨平台。例如 241 | * uint8_t表示无符号8位也就是无符号的char类型 242 | * **********************************************/ 243 | int ptrace_readdata(pid_t pid, uint8_t *pSrcBuf, uint8_t *pDestBuf, size_t size) { 244 | long nReadCount = 0; 245 | long nRemainCount = 0; 246 | uint8_t *pCurSrcBuf = pSrcBuf; 247 | uint8_t *pCurDestBuf = pDestBuf; 248 | long lTmpBuf = 0; 249 | long i = 0; 250 | 251 | nReadCount = size / sizeof(long); 252 | nRemainCount = size % sizeof(long); 253 | 254 | for (i = 0; i < nReadCount; i++) { 255 | lTmpBuf = ptrace(PTRACE_PEEKTEXT, pid, pCurSrcBuf, 0); 256 | memcpy(pCurDestBuf, (char *) (&lTmpBuf), sizeof(long)); 257 | pCurSrcBuf += sizeof(long); 258 | pCurDestBuf += sizeof(long); 259 | } 260 | 261 | if (nRemainCount > 0) { 262 | lTmpBuf = ptrace(PTRACE_PEEKTEXT, pid, pCurSrcBuf, 0); 263 | memcpy(pCurDestBuf, (char *) (&lTmpBuf), nRemainCount); 264 | } 265 | 266 | return 0; 267 | } 268 | 269 | /************************************************* 270 | * Description: 使用ptrace将数据写入到远程进程空间中 271 | * Input: pid表示远程进程的ID,pWriteAddr表示写入数据到远程进程的内存地址 272 | * pWriteData用于存储写入数据的地址,size表示写入数据的大小 273 | * Return: 返回0表示写入数据成功,返回-1表示写入数据失败 274 | * ***********************************************/ 275 | int ptrace_writedata(pid_t pid, uint8_t *pWriteAddr, uint8_t *pWriteData, 276 | size_t size) { 277 | 278 | long nWriteCount = 0; 279 | long nRemainCount = 0; 280 | uint8_t *pCurSrcBuf = pWriteData; 281 | uint8_t *pCurDestBuf = pWriteAddr; 282 | long lTmpBuf = 0; 283 | long i = 0; 284 | 285 | nWriteCount = size / sizeof(long); 286 | nRemainCount = size % sizeof(long); 287 | 288 | // 先讲数据以sizeof(long)字节大小为单位写入到远程进程内存空间中 289 | for (i = 0; i < nWriteCount; i++) { 290 | memcpy((void *) (&lTmpBuf), pCurSrcBuf, sizeof(long)); 291 | if (ptrace(PTRACE_POKETEXT, pid, (void *) pCurDestBuf, (void *) lTmpBuf) < 292 | 0) // PTRACE_POKETEXT表示从远程内存空间写入一个sizeof(long)大小的数据 293 | { 294 | printf("Write Remote Memory error, MemoryAddr:0x%lx, err:%s\n", (uintptr_t) pCurDestBuf, 295 | strerror(errno)); 296 | return -1; 297 | } 298 | pCurSrcBuf += sizeof(long); 299 | pCurDestBuf += sizeof(long); 300 | } 301 | 302 | // 将剩下的数据写入到远程进程内存空间中 303 | if (nRemainCount > 0) { 304 | lTmpBuf = ptrace(PTRACE_PEEKTEXT, pid, pCurDestBuf, 305 | NULL); //先取出原内存中的数据,然后将要写入的数据以单字节形式填充到低字节处 306 | memcpy((void *) (&lTmpBuf), pCurSrcBuf, nRemainCount); 307 | if (ptrace(PTRACE_POKETEXT, pid, pCurDestBuf, lTmpBuf) < 0) { 308 | printf("Write Remote Memory error, MemoryAddr:0x%lx, err:%s\n", (uintptr_t) pCurDestBuf, 309 | strerror(errno)); 310 | return -1; 311 | } 312 | } 313 | 314 | return 0; 315 | } 316 | 317 | /************************************************* 318 | * Description: 使用ptrace远程call函数 319 | * Input: pid表示远程进程的ID,ExecuteAddr为远程进程函数的地址 320 | * parameters为函数参数的地址,regs为远程进程call函数前的寄存器环境 321 | * Return: 返回0表示call函数成功,返回-1表示失败 322 | * **********************************************/ 323 | int ptrace_call(pid_t pid, uintptr_t ExecuteAddr, long *parameters, long num_params, 324 | struct pt_regs *regs) { 325 | #if defined(__i386__) 326 | // 写入参数到堆栈 327 | regs->esp -= (num_params) * sizeof(long); // 分配栈空间,栈的方向是从高地址到低地址 328 | if (0 != ptrace_writedata(pid, (uint8_t *) regs->esp, (uint8_t *) parameters, 329 | (num_params) * sizeof(long))) { 330 | return -1; 331 | } 332 | 333 | long tmp_addr = 0x0; 334 | regs->esp -= sizeof(long); 335 | if (0 != 336 | ptrace_writedata(pid, (uint8_t *) regs->esp, (uint8_t *) &tmp_addr, sizeof(tmp_addr))) { 337 | return -1; 338 | } 339 | 340 | //设置eip寄存器为需要调用的函数地址 341 | regs->eip = ExecuteAddr; 342 | 343 | // 开始执行 344 | if (-1 == ptrace_setregs(pid, regs) || -1 == ptrace_continue(pid)) { 345 | printf("ptrace set regs or continue error, pid:%d\n", pid); 346 | return -1; 347 | } 348 | 349 | int stat = 0; 350 | // 对于使用ptrace_cont运行的子进程,它会在3种情况下进入暂停状态:①下一次系统调用;②子进程退出;③子进程的执行发生错误。 351 | // 参数WUNTRACED表示当进程进入暂停状态后,立即返回 352 | waitpid(pid, &stat, WUNTRACED); 353 | 354 | // 判断是否成功执行函数 355 | printf("ptrace call ret status is %d\n", stat); 356 | while (stat != 0xb7f) { 357 | if (ptrace_continue(pid) == -1) { 358 | printf("ptrace call error"); 359 | return -1; 360 | } 361 | waitpid(pid, &stat, WUNTRACED); 362 | } 363 | 364 | // 获取远程进程的寄存器值,方便获取返回值 365 | if (ptrace_getregs(pid, regs) == -1) { 366 | printf("After call getregs error"); 367 | return -1; 368 | } 369 | #elif defined(__x86_64__) 370 | int num_param_registers = 6; 371 | // x64处理器,函数传递参数,将整数和指针参数前6个参数从左到右保存在寄存器rdi,rsi,rdx,rcx,r8和r9 372 | // 更多的参数则按照从右到左的顺序依次压入堆栈。 373 | if (num_params > 0) regs->rdi = parameters[0]; 374 | if (num_params > 1) regs->rsi = parameters[1]; 375 | if (num_params > 2) regs->rdx = parameters[2]; 376 | if (num_params > 3) regs->rcx = parameters[3]; 377 | if (num_params > 4) regs->r8 = parameters[4]; 378 | if (num_params > 5) regs->r9 = parameters[5]; 379 | 380 | if (num_param_registers < num_params) { 381 | regs->esp -= (num_params - num_param_registers) * sizeof(long); // 分配栈空间,栈的方向是从高地址到低地址 382 | if (0 != ptrace_writedata(pid, (uint8_t *) regs->esp, 383 | (uint8_t *) ¶meters[num_param_registers], 384 | (num_params - num_param_registers) * sizeof(long))) { 385 | return -1; 386 | } 387 | } 388 | 389 | long tmp_addr = 0x0; 390 | regs->esp -= sizeof(long); 391 | if (0 != 392 | ptrace_writedata(pid, (uint8_t *) regs->esp, (uint8_t *) &tmp_addr, sizeof(tmp_addr))) { 393 | return -1; 394 | } 395 | 396 | //设置eip寄存器为需要调用的函数地址 397 | regs->eip = ExecuteAddr; 398 | 399 | // 开始执行 400 | if (-1 == ptrace_setregs(pid, regs) || -1 == ptrace_continue(pid)) { 401 | printf("ptrace set regs or continue error, pid:%d", pid); 402 | return -1; 403 | } 404 | 405 | int stat = 0; 406 | // 对于使用ptrace_cont运行的子进程,它会在3种情况下进入暂停状态:①下一次系统调用;②子进程退出;③子进程的执行发生错误。 407 | // 参数WUNTRACED表示当进程进入暂停状态后,立即返回 408 | waitpid(pid, &stat, WUNTRACED); 409 | 410 | // 判断是否成功执行函数 411 | printf("ptrace call ret status is %lX\n", stat); 412 | while (stat != 0xb7f) { 413 | if (ptrace_continue(pid) == -1) { 414 | printf("ptrace call error"); 415 | return -1; 416 | } 417 | waitpid(pid, &stat, WUNTRACED); 418 | } 419 | 420 | #elif defined(__arm__) || defined(__aarch64__) 421 | #if defined(__arm__) 422 | int num_param_registers = 4; 423 | #elif defined(__aarch64__) 424 | int num_param_registers = 8; 425 | #endif 426 | int i = 0; 427 | // ARM处理器,函数传递参数,将前四个参数放到r0-r3,剩下的参数压入栈中 428 | for (i = 0; i < num_params && i < num_param_registers; i++) { 429 | regs->uregs[i] = parameters[i]; 430 | } 431 | 432 | if (i < num_params) { 433 | regs->ARM_sp -= (num_params - i) * sizeof(long); // 分配栈空间,栈的方向是从高地址到低地址 434 | if (ptrace_writedata(pid, (uint8_t *) (regs->ARM_sp), (uint8_t *) ¶meters[i], 435 | (num_params - i) * sizeof(long)) == -1) 436 | return -1; 437 | } 438 | 439 | regs->ARM_pc = ExecuteAddr; //设置ARM_pc寄存器为需要调用的函数地址 440 | // 与BX跳转指令类似,判断跳转的地址位[0]是否为1,如果为1,则将CPST寄存器的标志T置位,解释为Thumb代码 441 | // 若为0,则将CPSR寄存器的标志T复位,解释为ARM代码 442 | if (regs->ARM_pc & 1) { 443 | /* thumb */ 444 | regs->ARM_pc &= (~1u); 445 | regs->ARM_cpsr |= CPSR_T_MASK; 446 | } else { 447 | /* arm */ 448 | regs->ARM_cpsr &= ~CPSR_T_MASK; 449 | } 450 | 451 | regs->ARM_lr = 0; 452 | 453 | // Android 7.0以上修正lr为libc.so的起始地址 getprop获取ro.build.version.sdk 454 | long lr_val = 0; 455 | char sdk_ver[32]; 456 | memset(sdk_ver, 0, sizeof(sdk_ver)); 457 | __system_property_get("ro.build.version.sdk", sdk_ver); 458 | // printf("ro.build.version.sdk: %s", sdk_ver); 459 | if (atoi(sdk_ver) <= 23) { 460 | lr_val = 0; 461 | } else { // Android 7.0 462 | static long start_ptr = 0; 463 | if (start_ptr == 0) { 464 | start_ptr = (long) get_module_base_addr(pid, libc_path); 465 | } 466 | lr_val = start_ptr; 467 | } 468 | regs->ARM_lr = lr_val; 469 | 470 | if (ptrace_setregs(pid, regs) == -1 || ptrace_continue(pid) == -1) { 471 | printf("ptrace set regs or continue error, pid:%d\n", pid); 472 | return -1; 473 | } 474 | 475 | int stat = 0; 476 | // 对于使用ptrace_cont运行的子进程,它会在3种情况下进入暂停状态:①下一次系统调用;②子进程退出;③子进程的执行发生错误。 477 | // 参数WUNTRACED表示当进程进入暂停状态后,立即返回 478 | // 将ARM_lr(存放返回地址)设置为0,会导致子进程执行发生错误,则子进程进入暂停状态 479 | waitpid(pid, &stat, WUNTRACED); 480 | 481 | // 判断是否成功执行函数 482 | printf("ptrace call ret status is %d\n", stat); 483 | while ((stat & 0xFF) != 0x7f) { 484 | if (ptrace_continue(pid) == -1) { 485 | printf("ptrace call error\n"); 486 | return -1; 487 | } 488 | waitpid(pid, &stat, WUNTRACED); 489 | } 490 | 491 | // 获取远程进程的寄存器值,方便获取返回值 492 | if (ptrace_getregs(pid, regs) == -1) { 493 | printf("After call getregs error\n"); 494 | return -1; 495 | } 496 | #else 497 | printf("Not supported Environment %s\n", __FUNCTION__); 498 | #endif 499 | return 0; 500 | } 501 | 502 | void dump_registers(struct pt_regs *regs) { 503 | #if defined(__i386__) 504 | printf("X86 eax:%08x, ebx:%08x, ecx:%08x, edx:%08x, esi:%08x, edi:%08x, ebp:%08x, xds:%08x, xes:%08x, xfs:%08x, xgs:%08x, orig_eax:%08x, eip:%08x, xcs:%08x, eflags:%08x, esp:%08x, xss:%08x,", 505 | regs->eax, regs->ebx, regs->ecx, regs->edx, regs->esi, regs->edi, regs->ebp, regs->xds, 506 | regs->xes, regs->xfs, regs->xgs, regs->orig_eax, regs->eip, regs->xcs, regs->eflags, 507 | regs->esp, regs->xss); 508 | #elif defined(__arm__) 509 | printf("ARM_r0:0x%08X, ARM_r1:0x%08X, ARM_r2:0x%08X, ARM_r3:0x%08X, ARM_r4:0x%08X, ARM_r5:0x%08X, ARM_r6:0x%08X, ARM_r7:0x%08X, ARM_r8:0x%08X, ARM_r9:0x%08X, ARM_r10:0x%08X, ARM_fp:0x%08X, ARM_ip:0x%08X, ARM_sp:0x%08X, ARM_lr:0x%08X, ARM_pc:0x%08X, ARM_cpsr:0x%08X, ARM_ORIG_r0:0x%08X", 510 | regs->ARM_r0 ,regs->ARM_r1 ,regs->ARM_r2 ,regs->ARM_r3 ,regs->ARM_r4 ,regs->ARM_r5 ,regs->ARM_r6 ,regs->ARM_r7 ,regs->ARM_r8 ,regs->ARM_r9 ,regs->ARM_r10 ,regs->ARM_fp ,regs->ARM_ip ,regs->ARM_sp ,regs->ARM_lr ,regs->ARM_pc ,regs->ARM_cpsr ,regs->ARM_ORIG_r0); 511 | #elif defined(__aarch64__) 512 | printf("x0:%016lx, x1:%016lx, x2:%016lx, x3:%016lx, x4:%016lx, x5:%016lx, x6:%016lx, x7:%016lx, x8:%016lx, x9:%016lx, x10:%016lx, x11:%016lx, x12:%016lx, x13:%016lx, x14:%016lx, x15:%016lx, x16:%016lx, x17:%016lx, x18:%016lx, x19:%016lx, x20:%016lx, x21:%016lx, x22:%016lx, x23:%016lx, x24:%016lx, x25:%016lx, x26:%016lx, x27:%016lx, x28:%016lx, x29:%016lx, x30:%016lx, sp:%016lx, pc:%016lx, pstate:%016lx\n", 513 | regs->regs[0], regs->regs[1], regs->regs[2], regs->regs[3], regs->regs[4], regs->regs[5], 514 | regs->regs[6], regs->regs[7], regs->regs[8], regs->regs[9], regs->regs[10], regs->regs[11], 515 | regs->regs[12], regs->regs[13], regs->regs[14], regs->regs[15], regs->regs[16], 516 | regs->regs[17], regs->regs[18], regs->regs[19], regs->regs[20], regs->regs[21], 517 | regs->regs[22], regs->regs[23], regs->regs[24], regs->regs[25], regs->regs[26], 518 | regs->regs[27], regs->regs[28], regs->regs[29], regs->regs[30], regs->sp, regs->pc, 519 | regs->pstate); 520 | #elif defined(__x86_64__) 521 | printf("rax:0x%lX, rbx:0x%lX, rcx:0x%lX, rdx:0x%lX, rbp:0x%lX, rdi:0x%lX, rip:0x%lX, rsi:0x%lX, rsp:0x%lX, ss:0x%lX, cs:0x%lX, eflags:0x%lX, orig_rax:0x%lX, r8:0x%lX, r9:0x%lX, r10:0x%lX, r11:0x%lX, r12:0x%lX, r13:0x%lX, r14:0x%lX, r15:0x%lX", 522 | regs->rax, regs->rbx, regs->rcx, regs->rdx, regs->rbp, regs->rdi, regs->rip, regs->rsi, 523 | regs->rsp, regs->ss, regs->cs, regs->eflags, regs->orig_rax, regs->r8, regs->r9, regs->r10, 524 | regs->r11, regs->r12, regs->r13, regs->r14, regs->r15); 525 | #else 526 | printf("Not supported Environment %s\n", __FUNCTION__); 527 | #endif 528 | } -------------------------------------------------------------------------------- /app/src/main/cpp/PtraceInject/ptrace_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Administrator on 2021/5/3. 3 | // 4 | #pragma once 5 | 6 | #ifndef ANDROIDINJECT_PTRACE_UTILS_H 7 | #define ANDROIDINJECT_PTRACE_UTILS_H 8 | 9 | #define CPSR_T_MASK ( 1u << 5 ) 10 | #define REMOTE_ADDR(addr, local_base, remote_base) ( (uint32_t)(addr) + (uint32_t)(remote_base) - (uint32_t)(local_base) ) 11 | 12 | #if defined(__aarch64__) 13 | #define pt_regs user_pt_regs 14 | #define uregs regs 15 | #define ARM_pc pc 16 | #define ARM_sp sp 17 | #define ARM_cpsr pstate 18 | #define ARM_lr regs[30] 19 | #define ARM_r0 regs[0] 20 | #define PTRACE_GETREGS PTRACE_GETREGSET 21 | #define PTRACE_SETREGS PTRACE_SETREGSET 22 | #elif defined(__x86_64__) 23 | #define pt_regs user_regs_struct 24 | #define eax rax 25 | #define esp rsp 26 | #define eip rip 27 | #endif 28 | 29 | #if defined(__aarch64__) 30 | #define pt_regs user_pt_regs 31 | #elif defined(__x86_64__) 32 | #define pt_regs user_regs_struct 33 | #endif 34 | 35 | int ptrace_attach(pid_t pid); 36 | 37 | int ptrace_detach(pid_t pid); 38 | 39 | int ptrace_continue(pid_t pid); 40 | 41 | int ptrace_getregs(pid_t pid, struct pt_regs *regs); 42 | 43 | int ptrace_setregs(pid_t pid, struct pt_regs *regs); 44 | 45 | long ptrace_getret(struct pt_regs *regs); 46 | 47 | long ptrace_getpc(struct pt_regs *regs); 48 | 49 | int ptrace_readdata(pid_t pid, uint8_t *pSrcBuf, uint8_t *pDestBuf, size_t size); 50 | 51 | int ptrace_writedata(pid_t pid, uint8_t *pWriteAddr, uint8_t *pWriteData, 52 | size_t size); 53 | 54 | int ptrace_call(pid_t pid, uintptr_t ExecuteAddr, long *parameters, long num_params, 55 | struct pt_regs *regs); 56 | 57 | void dump_registers(struct pt_regs *regs); 58 | 59 | void *get_mmap_address(pid_t pid); 60 | 61 | void *get_dlopen_address(pid_t pid); 62 | 63 | void *get_dlclose_address(pid_t pid); 64 | 65 | void *get_dlsym_address(pid_t pid); 66 | 67 | void *get_dlerror_address(pid_t pid); 68 | 69 | 70 | #endif //ANDROIDINJECT_PTRACE_UTILS_H 71 | -------------------------------------------------------------------------------- /app/src/main/cpp/native-lib/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.4.1) 7 | 8 | # Creates and names a library, sets it as either STATIC 9 | # or SHARED, and provides the relative paths to its source code. 10 | # You can define multiple libraries, and CMake builds them for you. 11 | # Gradle automatically packages shared libraries with your APK. 12 | 13 | add_library( # Sets the name of the library. 14 | native-lib 15 | 16 | # Sets the library as a shared library. 17 | SHARED 18 | 19 | # Provides a relative path to your source file(s). 20 | native-lib.cpp ) 21 | 22 | # Searches for a specified prebuilt library and stores the path as a 23 | # variable. Because CMake includes system libraries in the search path by 24 | # default, you only need to specify the name of the public NDK library 25 | # you want to add. CMake verifies that the library exists before 26 | # completing its build. 27 | 28 | find_library( # Sets the name of the path variable. 29 | log-lib 30 | 31 | # Specifies the name of the NDK library that 32 | # you want CMake to locate. 33 | log ) 34 | 35 | # Specifies libraries CMake should link to your target library. You 36 | # can link multiple libraries, such as libraries you define in this 37 | # build script, prebuilt third-party libraries, or system libraries. 38 | 39 | target_link_libraries( # Specifies the target library. 40 | native-lib 41 | 42 | # Links the target library to the log library 43 | # included in the NDK. 44 | ${log-lib} ) -------------------------------------------------------------------------------- /app/src/main/cpp/native-lib/PrintLog.h: -------------------------------------------------------------------------------- 1 | #ifndef _ANDROID_LOG_PRINT_H_ 2 | #define _ANDROID_LOG_PRINT_H_ 3 | 4 | #include 5 | 6 | #define IS_DEBUG 7 | 8 | #ifdef IS_DEBUG 9 | 10 | #define LOG_TAG ("SharkChilli") 11 | 12 | #define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) 13 | 14 | #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG , LOG_TAG, __VA_ARGS__)) 15 | 16 | #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO , LOG_TAG, __VA_ARGS__)) 17 | 18 | #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN , LOG_TAG, __VA_ARGS__)) 19 | 20 | #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR , LOG_TAG, __VA_ARGS__)) 21 | 22 | #else 23 | 24 | #define LOGV(LOG_TAG, ...) NULL 25 | 26 | #define LOGD(LOG_TAG, ...) NULL 27 | 28 | #define LOGI(LOG_TAG, ...) NULL 29 | 30 | #define LOGW(LOG_TAG, ...) NULL 31 | 32 | #define LOGE(LOG_TAG, ...) NULL 33 | 34 | #endif 35 | 36 | #endif -------------------------------------------------------------------------------- /app/src/main/cpp/native-lib/native-lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "PrintLog.h" 5 | 6 | typedef int (*FUNC_INJECT_ENTRY)(); 7 | 8 | int test_load_library() { 9 | char InjectModuleName[] = "/data/app/com.example.androidinject-waaMZzkROOdkpeuvRzkKAQ==/lib/arm64/libInjectModule.so"; // 注入模块全路径 10 | void *handle = dlopen(InjectModuleName, RTLD_LAZY); 11 | if (!handle) { 12 | LOGE("[%s](%d) dlopen %s error:%s", __FILE__, __LINE__, InjectModuleName, dlerror()); 13 | return 0; 14 | } 15 | 16 | do { 17 | FUNC_INJECT_ENTRY entry_func = (FUNC_INJECT_ENTRY) dlsym(handle, "Inject_entry"); 18 | if (NULL == entry_func) { 19 | LOGE("[%s](%d) dlsym %s error:%s", __FILE__, __LINE__, "Inject_entry", dlerror()); 20 | break; 21 | } 22 | entry_func(); 23 | } while (false); 24 | 25 | dlclose(handle); 26 | return 1; 27 | } 28 | 29 | extern "C" JNIEXPORT jstring JNICALL 30 | Java_com_example_androidinject_MainActivity_stringFromJNI( 31 | JNIEnv *env, 32 | jobject /* this */) { 33 | std::string hello = "Hello from C++"; 34 | return env->NewStringUTF(hello.c_str()); 35 | } 36 | 37 | extern "C" JNIEXPORT jint JNICALL 38 | Java_com_example_androidinject_MainActivity_testload( 39 | JNIEnv *env, 40 | jobject /* this */) { 41 | return test_load_library(); 42 | } -------------------------------------------------------------------------------- /app/src/main/java/com/app/context/ContextUtils.java: -------------------------------------------------------------------------------- 1 | package com.app.context; 2 | 3 | import android.app.Activity; 4 | import android.app.ActivityManager; 5 | import android.app.Application; 6 | import android.content.Context; 7 | import android.content.pm.ActivityInfo; 8 | import android.content.pm.PackageInfo; 9 | import android.content.pm.PackageManager; 10 | import android.util.Log; 11 | 12 | import java.lang.reflect.Field; 13 | import java.lang.reflect.InvocationTargetException; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | public class ContextUtils { 19 | private static ContextUtils mContextUtils; 20 | private ClassLoader mClassLoader; 21 | 22 | private ContextUtils() { 23 | } 24 | 25 | private ContextUtils(ClassLoader classLoader) { 26 | this.mClassLoader = classLoader; 27 | } 28 | 29 | public static synchronized ContextUtils getInstance(ClassLoader classLoader) { 30 | if (mContextUtils == null) { 31 | mContextUtils = new ContextUtils(classLoader); 32 | } 33 | return mContextUtils; 34 | } 35 | 36 | public static synchronized ContextUtils getInstance() { 37 | if (mContextUtils == null) { 38 | throw new RuntimeException("mContextUtils is null"); 39 | } 40 | return mContextUtils; 41 | } 42 | 43 | public void runOnUiThread(RunUiInterface runUiInterface) { 44 | Activity topActivity = getTopActivity(); 45 | topActivity.runOnUiThread(() -> { 46 | runUiInterface.fun(); 47 | }); 48 | } 49 | 50 | public Application geApplication() { 51 | try { 52 | Application application = 53 | (Application) mClassLoader.loadClass("android.app.ActivityThread") 54 | .getMethod("currentApplication") 55 | .invoke(null, (Object[]) null); 56 | return application; 57 | } catch (Exception e) { 58 | throw new RuntimeException(e); 59 | } 60 | } 61 | 62 | public PackageManager getPackageManager() { 63 | PackageManager packageManager = geApplication().getPackageManager(); 64 | return packageManager; 65 | } 66 | 67 | public List getAllActivityClass() { 68 | PackageManager packageManager = getPackageManager(); 69 | ArrayList list = new ArrayList<>(); 70 | 71 | Context context = geApplication(); 72 | try { 73 | PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES); 74 | 75 | for (ActivityInfo activityInfo : packageInfo.activities) { 76 | Class aClass = Class.forName(activityInfo.name); 77 | list.add(aClass); 78 | } 79 | } catch (Exception exception) { 80 | throw new RuntimeException(exception); 81 | } 82 | return list; 83 | } 84 | 85 | public String getTopActivityName() { 86 | Application application = geApplication(); 87 | ActivityManager activityManager = (ActivityManager) application.getSystemService(Application.ACTIVITY_SERVICE); 88 | List runningTasks = activityManager.getRunningTasks(1); 89 | ActivityManager.RunningTaskInfo runningTaskInfo = runningTasks.get(0); 90 | return runningTaskInfo.topActivity.getClassName(); 91 | } 92 | 93 | public Activity getTopActivity() { 94 | List runningActivitys = getRunningActivitys(); 95 | String topActivityName = getTopActivityName(); 96 | // Log.i("SharkChilli", "getTopActivity: " + topActivityName); 97 | 98 | if (runningActivitys.size() == 1) { 99 | return runningActivitys.get(0); 100 | } 101 | 102 | for (Activity activityRun : runningActivitys) { 103 | if (topActivityName.equals(activityRun.getClass().getName())) { 104 | return activityRun; 105 | } 106 | // Log.i("SharkChilli", "activityRun: " + activityRun); 107 | } 108 | return null; 109 | } 110 | 111 | public List getRunningActivitys() { 112 | List list = new ArrayList<>(); 113 | try { 114 | Class applicationClass = Application.class; 115 | Field mLoadedApkField = applicationClass.getDeclaredField("mLoadedApk"); 116 | mLoadedApkField.setAccessible(true); 117 | 118 | Application application = geApplication(); 119 | Object mLoadedApk = mLoadedApkField.get(application); 120 | Class mLoadedApkClass = mLoadedApk.getClass(); 121 | Field mActivityThreadField = mLoadedApkClass.getDeclaredField("mActivityThread"); 122 | 123 | mActivityThreadField.setAccessible(true); 124 | Object mActivityThread = mActivityThreadField.get(mLoadedApk); 125 | Class mActivityThreadClass = mActivityThread.getClass(); 126 | Field mActivitiesField = mActivityThreadClass.getDeclaredField("mActivities"); 127 | mActivitiesField.setAccessible(true); 128 | // ActivityThread.ActivityClientRecord 129 | Object mActivities = mActivitiesField.get(mActivityThread); 130 | // 注意这里一定写成Map,低版本这里用的是HashMap,高版本用的是ArrayMap 131 | if (mActivities instanceof Map) { 132 | @SuppressWarnings("unchecked") 133 | Map arrayMap = (Map) mActivities; 134 | for (Map.Entry entry : arrayMap.entrySet()) { 135 | Object value = entry.getValue(); 136 | Class activityClientRecordClass = value.getClass(); 137 | Field activityField = activityClientRecordClass.getDeclaredField("activity"); 138 | activityField.setAccessible(true); 139 | Object o = activityField.get(value); 140 | list.add((Activity) o); 141 | } 142 | } 143 | } catch (Exception e) { 144 | e.printStackTrace(); 145 | list = null; 146 | } 147 | 148 | return list; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /app/src/main/java/com/app/context/RunUiInterface.java: -------------------------------------------------------------------------------- 1 | package com.app.context; 2 | 3 | public interface RunUiInterface { 4 | public void fun(); 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/app/logic/LogicEntry.java: -------------------------------------------------------------------------------- 1 | package com.app.logic; 2 | 3 | import android.app.Activity; 4 | import android.util.Log; 5 | import android.widget.Toast; 6 | 7 | import com.app.context.ContextUtils; 8 | import com.app.service.AbstractEntry; 9 | import com.app.signal.IRecvListener; 10 | import com.app.socket.JWebSocketClient; 11 | import com.app.socket.WebSocketMessage; 12 | import com.app.tools.ScreenShot; 13 | import com.app.view.ViewInfo; 14 | import com.app.view.ViewManager; 15 | import com.google.gson.Gson; 16 | 17 | import java.net.URI; 18 | import java.util.Map; 19 | 20 | public class LogicEntry extends AbstractEntry implements IRecvListener { 21 | private static JWebSocketClient mJWebSocketClient; 22 | private ContextUtils mContextUtils; 23 | private ViewManager mViewManager; 24 | 25 | @Override 26 | public void activityOnCreate(Activity activity) { 27 | Log.i(TAG, " activityOnCreate: " + activity.getClass().getName()); 28 | } 29 | 30 | @Override 31 | public void entry() { 32 | Log.i(TAG, "entry: "); 33 | mContextUtils = ContextUtils.getInstance(this.mClassLoader); 34 | mViewManager = ViewManager.getInstance(this.mClassLoader); 35 | 36 | URI uri = URI.create("ws://192.168.124.7:9873"); 37 | mJWebSocketClient = new JWebSocketClient(uri, this); 38 | if (mJWebSocketClient != null) { 39 | try { 40 | mJWebSocketClient.connectBlocking(); 41 | } catch (Exception e) { 42 | Log.e(TAG, "onLoad: ", e); 43 | } 44 | } 45 | 46 | if (mJWebSocketClient.isOpen()) { 47 | Log.i(TAG, "连接成功"); 48 | } else 49 | Log.i(TAG, "连接失败!!!"); 50 | } 51 | 52 | @Override 53 | public void recvMessage(String message) { 54 | WebSocketMessage webSocketMessage = new Gson().fromJson(message, WebSocketMessage.class); 55 | if (WebSocketMessage.Type.GET_LAYOUT_IMG.equals(webSocketMessage.getType())) { 56 | if (mJWebSocketClient == null) { 57 | Log.i(TAG, "mJWebSocketClient == null"); 58 | return; 59 | } 60 | 61 | mContextUtils.getRunningActivitys().forEach(activity -> { 62 | byte[] activityScreenBytes = ScreenShot.getActivityScreenBytes(activity); 63 | mJWebSocketClient.send(activityScreenBytes); 64 | }); 65 | // 发送完毕 66 | WebSocketMessage textMessage = WebSocketMessage.createMessage(WebSocketMessage.Type.GET_LAYOUT_IMG_END); 67 | mJWebSocketClient.send(textMessage); 68 | } else if (WebSocketMessage.Type.GET_LAYOUT.equals(webSocketMessage.getType())) { 69 | Map activitysLayout = mViewManager.getActivitysLayout(mContextUtils.getRunningActivitys()); 70 | String activitysLayoutInfo = new Gson().toJson(activitysLayout); 71 | WebSocketMessage textMessage = WebSocketMessage.createLayoutMessage("0", activitysLayoutInfo); 72 | mJWebSocketClient.send(textMessage); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/com/app/service/AbstractEntry.java: -------------------------------------------------------------------------------- 1 | package com.app.service; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | 7 | import dalvik.system.PathClassLoader; 8 | import de.robv.android.xposed.XC_MethodHook; 9 | import de.robv.android.xposed.XposedHelpers; 10 | 11 | public abstract class AbstractEntry { 12 | protected static String TAG = "SharkChilli"; 13 | public PathClassLoader mClassLoader; 14 | private String pkgName; 15 | 16 | public AbstractEntry() { 17 | // init(); 18 | } 19 | 20 | public abstract void activityOnCreate(Activity activity); 21 | 22 | public void init() { 23 | try { 24 | Class ActivityClass = mClassLoader.loadClass("android.app.Activity"); 25 | 26 | XposedHelpers.findAndHookMethod(ActivityClass, "onCreate", Bundle.class, 27 | new XC_MethodHook() { 28 | @Override 29 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 30 | super.afterHookedMethod(param); 31 | Activity activity = (Activity) param.thisObject; 32 | activityOnCreate(activity); 33 | } 34 | }); 35 | } catch (ClassNotFoundException e) { 36 | Log.i(TAG, "init: ", e); 37 | } 38 | } 39 | 40 | public void onLoad(PathClassLoader classLoader, String pkgName, boolean flag) { 41 | this.mClassLoader = classLoader; 42 | } 43 | 44 | public abstract void entry(); 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/app/service/Entry.java: -------------------------------------------------------------------------------- 1 | package com.app.service; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.util.ArrayMap; 7 | import android.util.Log; 8 | import android.widget.Toast; 9 | 10 | import com.app.context.ContextUtils; 11 | import com.app.logic.LogicEntry; 12 | import com.app.socket.JWebSocketClient; 13 | 14 | import java.lang.reflect.Field; 15 | import java.lang.reflect.InvocationTargetException; 16 | import java.lang.reflect.Method; 17 | import java.util.Map; 18 | 19 | import dalvik.system.DexClassLoader; 20 | import dalvik.system.PathClassLoader; 21 | import de.robv.android.xposed.XC_MethodHook; 22 | import de.robv.android.xposed.XposedBridge; 23 | import de.robv.android.xposed.XposedHelpers; 24 | 25 | public class Entry { 26 | 27 | public static String TAG = "SharkChilli"; 28 | 29 | public static void onLoad(PathClassLoader classLoader, String pkgName, boolean flag) { 30 | LogicEntry logicEntry = new LogicEntry(); 31 | //添加相应的变量 32 | logicEntry.onLoad(classLoader, pkgName, flag); 33 | //初始化相关hook 34 | logicEntry.init(); 35 | //调用入口 36 | logicEntry.entry(); 37 | //获取当前activity 38 | ContextUtils contextUtils = ContextUtils.getInstance(classLoader); 39 | Activity topActivity = contextUtils.getTopActivity(); 40 | 41 | logicEntry.activityOnCreate(topActivity); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/app/signal/IRecvListener.java: -------------------------------------------------------------------------------- 1 | package com.app.signal; 2 | 3 | /** 4 | * 通信消息监听者 5 | */ 6 | public interface IRecvListener { 7 | /** 8 | * 接收消息 9 | * @param message 10 | */ 11 | void recvMessage(String message); 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/app/socket/JWebSocketClient.java: -------------------------------------------------------------------------------- 1 | package com.app.socket; 2 | 3 | import android.os.Handler; 4 | import android.util.Log; 5 | 6 | import com.app.context.ContextUtils; 7 | import com.app.signal.IRecvListener; 8 | import com.google.gson.Gson; 9 | 10 | import org.java_websocket.client.WebSocketClient; 11 | import org.java_websocket.handshake.ServerHandshake; 12 | 13 | import java.net.URI; 14 | 15 | public class JWebSocketClient extends WebSocketClient { 16 | public final static String TAG = "SharkChilli"; 17 | private static final long HEART_BEAT_RATE = 10 * 1000;//每隔10秒进行一次对长连接的心跳检测 18 | private Handler mHandler; 19 | 20 | private IRecvListener mIRecvListener; 21 | 22 | private Runnable heartBeatRunnable = new Runnable() { 23 | @Override 24 | public void run() { 25 | if (isClosed()) { 26 | // Log.i(TAG, "开启重连"); 27 | reconnectWs(); 28 | } 29 | //定时对长连接进行心跳检测 30 | mHandler.postDelayed(this, HEART_BEAT_RATE); 31 | } 32 | }; 33 | 34 | /** 35 | * 开启重连 36 | */ 37 | private void reconnectWs() { 38 | mHandler.removeCallbacks(heartBeatRunnable); 39 | new Thread() { 40 | @Override 41 | public void run() { 42 | try { 43 | //重连 44 | Log.i(TAG, "reconnectWs.................... "); 45 | reconnect(); 46 | } catch (Exception e) { 47 | Log.e(TAG, "run: ", e); 48 | } 49 | } 50 | }.start(); 51 | } 52 | 53 | public JWebSocketClient(URI serverUri, IRecvListener iRecvListener) { 54 | super(serverUri); 55 | ContextUtils contextUtils = ContextUtils.getInstance(); 56 | this.mIRecvListener = iRecvListener; 57 | 58 | contextUtils.runOnUiThread(() -> { 59 | Log.i(TAG, "runOnUiThread: "); 60 | mHandler = new Handler(); 61 | Log.i(TAG, "mHandler: " + mHandler); 62 | }); 63 | try { 64 | Thread.sleep(3 * 1000); 65 | } catch (InterruptedException e) { 66 | Log.e(TAG, "JWebSocketClient: ", e); 67 | } 68 | } 69 | 70 | public void send(WebSocketMessage webSocketMessage) { 71 | String json = new Gson().toJson(webSocketMessage); 72 | Log.i(TAG, "send: " + json); 73 | this.send(json); 74 | } 75 | 76 | @Override 77 | public void onOpen(ServerHandshake serverHandshake) { 78 | Log.e(TAG, "onOpen()"); 79 | mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);//开启心跳检测 80 | } 81 | 82 | @Override 83 | public void onMessage(String message) { 84 | Log.e(TAG, "onMessage:" + message); 85 | mIRecvListener.recvMessage(message); 86 | } 87 | 88 | @Override 89 | public void onClose(int code, String reason, boolean remote) { 90 | Log.e(TAG, "onClose()"); 91 | } 92 | 93 | public void sendMessage(String message) { 94 | if (!isClosed()) { 95 | this.send(message); 96 | } 97 | } 98 | 99 | public void closeConnect() { 100 | if (this.isOpen()) { 101 | this.close(); 102 | } 103 | } 104 | 105 | @Override 106 | public void onError(Exception ex) { 107 | Log.e(TAG, "onError()", ex); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/com/app/socket/WebSocketMessage.java: -------------------------------------------------------------------------------- 1 | package com.app.socket; 2 | 3 | public class WebSocketMessage { 4 | //发送标识 5 | private String id; 6 | 7 | //发送的数据类型 8 | private Type type; 9 | 10 | //消息内容 11 | private String message; 12 | 13 | //字节数组数据 14 | private byte[] dataBuf; 15 | 16 | public static enum Type { 17 | TEXT, 18 | LAYOUT, 19 | IMG, 20 | GET_LAYOUT, 21 | GET_LAYOUT_IMG, 22 | GET_LAYOUT_IMG_END, 23 | } 24 | 25 | private WebSocketMessage() { 26 | } 27 | 28 | public static WebSocketMessage createTextMessage(String id, String message) { 29 | WebSocketMessage webSocketMessage = new WebSocketMessage(); 30 | 31 | webSocketMessage.setId(id); 32 | webSocketMessage.setMessage(message); 33 | webSocketMessage.setType(Type.TEXT); 34 | 35 | return webSocketMessage; 36 | } 37 | 38 | public static WebSocketMessage createImgMessage(String id, byte[] dataBuf) { 39 | WebSocketMessage webSocketMessage = new WebSocketMessage(); 40 | 41 | webSocketMessage.setId(id); 42 | webSocketMessage.setDataBuf(dataBuf); 43 | webSocketMessage.setType(Type.IMG); 44 | 45 | return webSocketMessage; 46 | } 47 | 48 | public static WebSocketMessage createLayoutMessage(String id, String message) { 49 | WebSocketMessage webSocketMessage = new WebSocketMessage(); 50 | 51 | webSocketMessage.setId(id); 52 | webSocketMessage.setMessage(message); 53 | webSocketMessage.setType(Type.LAYOUT); 54 | return webSocketMessage; 55 | } 56 | 57 | public static WebSocketMessage createMessage(String id, Type type, String message, byte[] dataBuf) { 58 | WebSocketMessage webSocketMessage = new WebSocketMessage(); 59 | 60 | webSocketMessage.setId(id); 61 | webSocketMessage.setDataBuf(dataBuf); 62 | webSocketMessage.setMessage(message); 63 | webSocketMessage.setType(type); 64 | 65 | return webSocketMessage; 66 | } 67 | 68 | public static WebSocketMessage createMessage(Type type) { 69 | return createMessage("0", type, "", null); 70 | } 71 | 72 | public String getId() { 73 | return id; 74 | } 75 | 76 | public void setId(String id) { 77 | this.id = id; 78 | } 79 | 80 | public Type getType() { 81 | return type; 82 | } 83 | 84 | public void setType(Type type) { 85 | this.type = type; 86 | } 87 | 88 | public String getMessage() { 89 | return message; 90 | } 91 | 92 | public void setMessage(String message) { 93 | this.message = message; 94 | } 95 | 96 | public byte[] getDataBuf() { 97 | return dataBuf; 98 | } 99 | 100 | public void setDataBuf(byte[] dataBuf) { 101 | this.dataBuf = dataBuf; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /app/src/main/java/com/app/tools/ScreenShot.java: -------------------------------------------------------------------------------- 1 | package com.app.tools; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Bitmap; 5 | import android.graphics.Rect; 6 | import android.view.View; 7 | 8 | import java.io.ByteArrayOutputStream; 9 | import java.io.FileNotFoundException; 10 | import java.io.FileOutputStream; 11 | import java.io.IOException; 12 | 13 | public class ScreenShot { 14 | public final static String TAG = "SharkChilli"; 15 | 16 | // 获取指定Activity的截屏,保存到png文件 17 | private static Bitmap takeScreenShot(Activity activity) { 18 | // View是你需要截图的View 19 | View view = activity.getWindow().getDecorView(); 20 | view.setDrawingCacheEnabled(true); 21 | view.buildDrawingCache(); 22 | Bitmap b1 = view.getDrawingCache(); 23 | 24 | // 获取状态栏高度 25 | Rect frame = new Rect(); 26 | activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame); 27 | int statusBarHeight = frame.top; 28 | // Log.i(TAG, "" + statusBarHeight); 29 | 30 | // 获取屏幕长和高 31 | int width = activity.getWindowManager().getDefaultDisplay().getWidth(); 32 | int height = activity.getWindowManager().getDefaultDisplay() 33 | .getHeight(); 34 | // 去掉标题栏 35 | // Bitmap b = Bitmap.createBitmap(b1, 0, 25, 320, 455); 36 | Bitmap b = Bitmap.createBitmap(b1, 0, statusBarHeight, width, height 37 | - statusBarHeight); 38 | view.destroyDrawingCache(); 39 | return b; 40 | } 41 | 42 | // 保存到sdcard 43 | private static void savePic(Bitmap b, String strFileName) { 44 | FileOutputStream fos = null; 45 | try { 46 | fos = new FileOutputStream(strFileName); 47 | if (null != fos) { 48 | b.compress(Bitmap.CompressFormat.PNG, 90, fos); 49 | fos.flush(); 50 | fos.close(); 51 | } 52 | } catch (FileNotFoundException e) { 53 | e.printStackTrace(); 54 | } catch (IOException e) { 55 | e.printStackTrace(); 56 | } 57 | } 58 | 59 | /** 60 | * 获取截屏的byte数组 61 | * 62 | * @param activity 63 | * @return 64 | */ 65 | public static byte[] getActivityScreenBytes(Activity activity) { 66 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 67 | 68 | Bitmap bitmap = ScreenShot.takeScreenShot(activity); 69 | bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream); 70 | byte[] bytes = byteArrayOutputStream.toByteArray(); 71 | return bytes; 72 | } 73 | 74 | // 程序入口 75 | public static void shoot(Activity a) { 76 | ScreenShot.savePic(ScreenShot.takeScreenShot(a), "sdcard/YoupinSeckillSuccess.png"); 77 | } 78 | 79 | // 程序入口 80 | public static void shoot(Activity a, String savePath) { 81 | ScreenShot.savePic(ScreenShot.takeScreenShot(a), savePath); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/app/view/ViewInfo.java: -------------------------------------------------------------------------------- 1 | package com.app.view; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * View视图信息 7 | */ 8 | public class ViewInfo { 9 | private String className; 10 | private boolean enabled; 11 | private boolean shown; 12 | private int id; 13 | private String text; 14 | private String description; 15 | 16 | private int x; 17 | private int y; 18 | private int width; 19 | private int height; 20 | 21 | private List childList; 22 | 23 | public String getDescription() { 24 | return description; 25 | } 26 | 27 | public void setDescription(String description) { 28 | this.description = description; 29 | } 30 | 31 | public int getX() { 32 | return x; 33 | } 34 | 35 | public void setX(int x) { 36 | this.x = x; 37 | } 38 | 39 | public int getY() { 40 | return y; 41 | } 42 | 43 | public void setY(int y) { 44 | this.y = y; 45 | } 46 | 47 | public int getWidth() { 48 | return width; 49 | } 50 | 51 | public void setWidth(int width) { 52 | this.width = width; 53 | } 54 | 55 | public int getHeight() { 56 | return height; 57 | } 58 | 59 | public void setHeight(int height) { 60 | this.height = height; 61 | } 62 | 63 | public String getText() { 64 | return text; 65 | } 66 | 67 | public void setText(String text) { 68 | this.text = text; 69 | } 70 | 71 | public boolean isEnabled() { 72 | return enabled; 73 | } 74 | 75 | public void setEnabled(boolean enabled) { 76 | this.enabled = enabled; 77 | } 78 | 79 | public boolean isShown() { 80 | return shown; 81 | } 82 | 83 | public void setShown(boolean shown) { 84 | this.shown = shown; 85 | } 86 | 87 | public String getClassName() { 88 | return className; 89 | } 90 | 91 | public void setClassName(String className) { 92 | this.className = className; 93 | } 94 | 95 | public int getId() { 96 | return id; 97 | } 98 | 99 | public void setId(int id) { 100 | this.id = id; 101 | } 102 | 103 | public List getChildList() { 104 | return childList; 105 | } 106 | 107 | public void setChildList(List childList) { 108 | this.childList = childList; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /app/src/main/java/com/app/view/ViewManager.java: -------------------------------------------------------------------------------- 1 | package com.app.view; 2 | 3 | import android.app.Activity; 4 | import android.content.res.Resources; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.view.Window; 8 | import android.widget.TextView; 9 | 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | public class ViewManager { 16 | private static ViewManager mViewHelper; 17 | private ClassLoader mClassLoader; 18 | 19 | private ViewManager() { 20 | } 21 | 22 | private ViewManager(ClassLoader classLoader) { 23 | this.mClassLoader = classLoader; 24 | } 25 | 26 | public static synchronized ViewManager getInstance(ClassLoader classLoader) { 27 | if (mViewHelper == null) { 28 | mViewHelper = new ViewManager(classLoader); 29 | } 30 | return mViewHelper; 31 | } 32 | 33 | public Map getActivitysLayout(List activities) { 34 | HashMap layoutMap = new HashMap<>(); 35 | 36 | activities.forEach(activity -> { 37 | layoutMap.put(activity.getClass().getName(), getActivityViewInfo(activity)); 38 | }); 39 | 40 | return layoutMap; 41 | } 42 | 43 | public ViewInfo getActivityViewInfo(Activity activity) { 44 | Window window = activity.getWindow(); 45 | View decorView = window.getDecorView(); 46 | int statusBarHeight = getStatusBarHeight(activity); 47 | return getViewInfo(decorView.findViewById(Window.ID_ANDROID_CONTENT), statusBarHeight); 48 | } 49 | 50 | public int getStatusBarHeight(Activity activity) { 51 | Resources resources = activity.getResources(); 52 | int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android"); 53 | int height = resources.getDimensionPixelSize(resourceId); 54 | return height; 55 | } 56 | 57 | public ViewInfo getViewInfo(View view, int statusBarHeight) { 58 | ViewInfo viewInfo = new ViewInfo(); 59 | 60 | viewInfo.setId(view.getId()); 61 | viewInfo.setClassName(view.getClass().getName()); 62 | viewInfo.setEnabled(view.isEnabled()); 63 | viewInfo.setShown(view.isShown()); 64 | viewInfo.setHeight(view.getHeight()); 65 | viewInfo.setWidth(view.getWidth()); 66 | int[] viewLocation = getViewLocation(view); 67 | viewInfo.setX(viewLocation[0]); 68 | viewInfo.setY(viewLocation[1] - statusBarHeight); 69 | if (view.getContentDescription() != null) 70 | viewInfo.setDescription(view.getContentDescription().toString()); 71 | 72 | if (view instanceof TextView) 73 | viewInfo.setText(((TextView) view).getText().toString()); 74 | 75 | if (view instanceof ViewGroup) { 76 | ArrayList childList = new ArrayList<>(); 77 | 78 | ViewGroup vp = (ViewGroup) view; 79 | 80 | for (int i = 0; i < vp.getChildCount(); i++) { 81 | View viewchild = vp.getChildAt(i); 82 | ViewInfo childViewInfo = getViewInfo(viewchild, statusBarHeight); 83 | childList.add(childViewInfo); 84 | } 85 | viewInfo.setChildList(childList); 86 | } 87 | 88 | return viewInfo; 89 | } 90 | 91 | /** 92 | * 获取控件位置 93 | * 94 | * @return 95 | */ 96 | public int[] getViewLocation(View view) { 97 | int[] location = new int[2]; 98 | view.getLocationOnScreen(location); 99 | return location; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/androidinject/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.androidinject; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.widget.Button; 8 | import android.widget.TextView; 9 | import android.widget.Toast; 10 | 11 | public class MainActivity extends AppCompatActivity { 12 | 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_main); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 |