├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── test │ │ └── fgum │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── hook.js │ ├── cpp │ │ ├── CMakeLists.txt │ │ ├── frida-gumjs-example.c │ │ ├── frida-gumjs.h │ │ ├── libfrida-gumjs.zip │ │ ├── logging.h │ │ ├── native-lib.cpp │ │ └── native-lib.h │ ├── java │ │ └── com │ │ │ └── test │ │ │ └── fgum │ │ │ └── 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-anydpi-v33 │ │ └── ic_launcher.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values-night │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── test │ └── fgum │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.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 | local.properties 16 | .idea 17 | app/release -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 说明 2 | 3 | 这是[精简并换个姿势用frida](https://blog.seeflower.dev/archives/214/)的演示demo 4 | 5 | `app/src/main/cpp/libfrida-gumjs.a`本身仍然比较大,已压缩为zip,请解压使用 6 | 7 | 参考: 8 | 9 | - [Frida持久化方案(Xcube)之方案二——基于xposed](https://bbs.kanxue.com/thread-266784.htm) -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | namespace 'com.test.fgum' 7 | compileSdk 33 8 | 9 | defaultConfig { 10 | applicationId "com.test.fgum" 11 | minSdk 24 12 | targetSdk 33 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | 18 | externalNativeBuild { 19 | cmake { 20 | abiFilters "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 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | externalNativeBuild { 36 | cmake { 37 | path file('src/main/cpp/CMakeLists.txt') 38 | version '3.22.1' 39 | } 40 | } 41 | buildFeatures { 42 | viewBinding true 43 | } 44 | } 45 | 46 | dependencies { 47 | 48 | implementation 'androidx.appcompat:appcompat:1.6.1' 49 | implementation 'com.google.android.material:material:1.8.0' 50 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 51 | testImplementation 'junit:junit:4.13.2' 52 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 53 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 54 | } -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /app/src/androidTest/java/com/test/fgum/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.test.fgum; 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 | assertEquals("com.test.fgum", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/assets/hook.js: -------------------------------------------------------------------------------- 1 | function main() { 2 | Java.perform(function () { 3 | Java.enumerateClassLoaders({ 4 | onMatch: function(loader) { 5 | let msg = `[loader]:${loader}`; 6 | console.log(msg); 7 | Java.use("com.test.fgum.MainActivity").update_text(msg); 8 | }, 9 | onComplete: function() { 10 | let msg = `find loader end`; 11 | console.log(msg); 12 | Java.use("com.test.fgum.MainActivity").update_text(msg); 13 | } 14 | }); 15 | }) 16 | } 17 | 18 | setImmediate(main); -------------------------------------------------------------------------------- /app/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about using CMake with Android Studio, read the 2 | # documentation: https://d.android.com/studio/projects/add-native-code.html 3 | 4 | # Sets the minimum version of CMake required to build the native library. 5 | 6 | cmake_minimum_required(VERSION 3.22.1) 7 | 8 | # Declares and names the project. 9 | 10 | project("fgum") 11 | 12 | add_library(gumjs STATIC IMPORTED) 13 | set_target_properties(gumjs PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libfrida-gumjs.a) 14 | 15 | # Creates and names a library, sets it as either STATIC 16 | # or SHARED, and provides the relative paths to its source code. 17 | # You can define multiple libraries, and CMake builds them for you. 18 | # Gradle automatically packages shared libraries with your APK. 19 | 20 | add_library( # Sets the name of the library. 21 | fgum 22 | 23 | # Sets the library as a shared library. 24 | SHARED 25 | 26 | # Provides a relative path to your source file(s). 27 | native-lib.cpp) 28 | 29 | # Searches for a specified prebuilt library and stores the path as a 30 | # variable. Because CMake includes system libraries in the search path by 31 | # default, you only need to specify the name of the public NDK library 32 | # you want to add. CMake verifies that the library exists before 33 | # completing its build. 34 | 35 | find_library( # Sets the name of the path variable. 36 | log-lib 37 | 38 | # Specifies the name of the NDK library that 39 | # you want CMake to locate. 40 | log) 41 | 42 | # Specifies libraries CMake should link to your target library. You 43 | # can link multiple libraries, such as libraries you define in this 44 | # build script, prebuilt third-party libraries, or system libraries. 45 | 46 | target_link_libraries( # Specifies the target library. 47 | fgum 48 | gumjs 49 | 50 | # Links the target library to the log library 51 | # included in the NDK. 52 | ${log-lib}) -------------------------------------------------------------------------------- /app/src/main/cpp/frida-gumjs-example.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Compile with: 3 | * 4 | * clang++ -DANDROID -ffunction-sections -fdata-sections frida-gumjs-example.c -o frida-gumjs-example -L. -lfrida-gumjs -llog -ldl -lm -pthread -Wl,-z,relro,-z,noexecstack,--gc-sections 5 | * 6 | * Visit https://frida.re to learn more about Frida. 7 | */ 8 | 9 | #include "frida-gumjs.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | static void on_message (const gchar * message, GBytes * data, gpointer user_data); 16 | 17 | int 18 | main (int argc, 19 | char * argv[]) 20 | { 21 | GumScriptBackend * backend; 22 | GCancellable * cancellable = NULL; 23 | GError * error = NULL; 24 | GumScript * script; 25 | GMainContext * context; 26 | 27 | gum_init_embedded (); 28 | 29 | backend = gum_script_backend_obtain_qjs (); 30 | 31 | script = gum_script_backend_create_sync (backend, "example", 32 | "Interceptor.attach(Module.getExportByName(null, 'open'), {\n" 33 | " onEnter(args) {\n" 34 | " console.log(`[*] open(\"${args[0].readUtf8String()}\")`);\n" 35 | " }\n" 36 | "});\n" 37 | "Interceptor.attach(Module.getExportByName(null, 'close'), {\n" 38 | " onEnter(args) {\n" 39 | " console.log(`[*] close(${args[0].toInt32()})`);\n" 40 | " }\n" 41 | "});", 42 | NULL, cancellable, &error); 43 | g_assert (error == NULL); 44 | 45 | gum_script_set_message_handler (script, on_message, NULL, NULL); 46 | 47 | gum_script_load_sync (script, cancellable); 48 | 49 | close (open ("/etc/hosts", O_RDONLY)); 50 | close (open ("/etc/fstab", O_RDONLY)); 51 | 52 | context = g_main_context_get_thread_default (); 53 | while (g_main_context_pending (context)) 54 | g_main_context_iteration (context, FALSE); 55 | 56 | gum_script_unload_sync (script, cancellable); 57 | 58 | g_object_unref (script); 59 | 60 | gum_deinit_embedded (); 61 | 62 | return 0; 63 | } 64 | 65 | static void 66 | on_message (const gchar * message, 67 | GBytes * data, 68 | gpointer user_data) 69 | { 70 | JsonParser * parser; 71 | JsonObject * root; 72 | const gchar * type; 73 | 74 | parser = json_parser_new (); 75 | json_parser_load_from_data (parser, message, -1, NULL); 76 | root = json_node_get_object (json_parser_get_root (parser)); 77 | 78 | type = json_object_get_string_member (root, "type"); 79 | if (strcmp (type, "log") == 0) 80 | { 81 | const gchar * log_message; 82 | 83 | log_message = json_object_get_string_member (root, "payload"); 84 | g_print ("%s\n", log_message); 85 | } 86 | else 87 | { 88 | g_print ("on_message: %s\n", message); 89 | } 90 | 91 | g_object_unref (parser); 92 | } 93 | -------------------------------------------------------------------------------- /app/src/main/cpp/libfrida-gumjs.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SeeFlowerX/FGum/1d125d56fc67ac723e7e78c6b42b4c6f57456258/app/src/main/cpp/libfrida-gumjs.zip -------------------------------------------------------------------------------- /app/src/main/cpp/logging.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by weimo on 2023/4/1. 3 | // 4 | 5 | #ifndef FGUM_LOGGING_H 6 | #define FGUM_LOGGING_H 7 | 8 | #include 9 | 10 | #define LOG_TAG "gum" 11 | 12 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) 13 | #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) 14 | #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__) 15 | #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) 16 | 17 | #endif //FGUM_LOGGING_H 18 | -------------------------------------------------------------------------------- /app/src/main/cpp/native-lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "logging.h" 9 | #include "native-lib.h" 10 | 11 | static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; 12 | static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 13 | static GumScriptBackend *backend; 14 | static GCancellable *cancellable = NULL; 15 | static GError *error = NULL; 16 | static GumScript *script; 17 | static GMainContext *context; 18 | static GMainLoop *loop; 19 | 20 | char *readfile(const char *filepath) { 21 | FILE *file = fopen(filepath, "r"); 22 | if (file == NULL) { 23 | LOGE("file open failed : %s " ,filepath); 24 | return NULL; 25 | } 26 | 27 | struct stat statbuf{}; 28 | stat(filepath, &statbuf); 29 | int filesize = statbuf.st_size; 30 | 31 | void *buffer = malloc(filesize + 1); 32 | memset(buffer, 0, filesize + 1); 33 | int count = 0; 34 | int total = 0; 35 | while ((count = fread((char *) buffer + total, sizeof(char), 1024, file)) != 0) { 36 | total += count; 37 | } 38 | if (file != NULL) { 39 | fclose(file); 40 | } 41 | return (char *) buffer; 42 | } 43 | 44 | int hookFunc(const char *scriptpath) { 45 | LOGD ("[*] gumjsHook()"); 46 | gum_init_embedded(); 47 | backend = gum_script_backend_obtain_qjs(); 48 | char *js = readfile(scriptpath); 49 | if (!js) { 50 | return 1; 51 | } 52 | 53 | script = gum_script_backend_create_sync(backend, "example", js, NULL, cancellable, &error); 54 | g_assert (error == NULL); 55 | gum_script_set_message_handler(script, on_message, NULL, NULL); 56 | gum_script_load_sync(script, cancellable); 57 | //下面这段代码会执行一下已有的事件 58 | context = g_main_context_get_thread_default(); 59 | while (g_main_context_pending(context)) 60 | g_main_context_iteration(context, FALSE); 61 | //到这里说明脚本已经加载完成,通知主线程继续执行 62 | pthread_mutex_lock(&mtx); 63 | pthread_cond_signal(&cond); 64 | pthread_mutex_unlock(&mtx); 65 | 66 | loop = g_main_loop_new(g_main_context_get_thread_default(), FALSE); 67 | g_main_loop_run(loop);//block here 68 | 69 | return 0; 70 | } 71 | 72 | int gumjsHook(const char *scriptpath) { 73 | pthread_t pthread; 74 | int result = pthread_create(&pthread, NULL, (void *(*)(void *)) (hookFunc), 75 | (void *) scriptpath); 76 | struct timeval now; 77 | struct timespec outtime; 78 | pthread_mutex_lock(&mtx); 79 | gettimeofday(&now, NULL); 80 | outtime.tv_sec = now.tv_sec + 5; 81 | outtime.tv_nsec = now.tv_usec * 1000; 82 | pthread_cond_timedwait(&cond, &mtx, &outtime); 83 | pthread_mutex_unlock(&mtx); 84 | if (result != 0) { 85 | LOGD("create thread failed"); 86 | } else { 87 | LOGD("create thread success"); 88 | } 89 | return result; 90 | } 91 | 92 | static void 93 | on_message(const gchar *message, GBytes *data, gpointer user_data) { 94 | JsonParser *parser; 95 | JsonObject *root; 96 | const gchar *type; 97 | 98 | parser = json_parser_new(); 99 | json_parser_load_from_data(parser, message, -1, NULL); 100 | root = json_node_get_object(json_parser_get_root(parser)); 101 | 102 | type = json_object_get_string_member(root, "type"); 103 | if (strcmp(type, "log") == 0) { 104 | const gchar *log_message; 105 | log_message = json_object_get_string_member(root, "payload"); 106 | LOGD ("[*] log : %s ", log_message); 107 | } else { 108 | LOGD ("[*] %s ", message); 109 | } 110 | 111 | g_object_unref(parser); 112 | } 113 | 114 | extern "C" JNIEXPORT jstring JNICALL 115 | Java_com_test_fgum_MainActivity_stringFromJNI( 116 | JNIEnv* env, 117 | jobject /* this */) { 118 | std::string hello = "Hello from C++"; 119 | return env->NewStringUTF(hello.c_str()); 120 | } 121 | 122 | extern "C" 123 | JNIEXPORT jboolean JNICALL 124 | Java_com_test_fgum_MainActivity_loadJS(JNIEnv *env, jobject thiz, jstring jspath) { 125 | const char *scriptpath = env->GetStringUTFChars(jspath, 0); 126 | bool status = gumjsHook(scriptpath); 127 | env->ReleaseStringUTFChars(jspath, scriptpath); 128 | return status; 129 | } -------------------------------------------------------------------------------- /app/src/main/cpp/native-lib.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by weimo on 2023/4/1. 3 | // 4 | 5 | #ifndef FGUM_NATIVE_LIB_H 6 | #define FGUM_NATIVE_LIB_H 7 | 8 | #include "frida-gumjs.h" 9 | 10 | static void on_message( const gchar *message, GBytes *data, gpointer user_data); 11 | 12 | int gumjsHook(const char *scriptpath); 13 | 14 | char *readfile(const char *filepath); 15 | 16 | int hookFunc(const char *scriptpath); 17 | 18 | #endif //FGUM_NATIVE_LIB_H 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/test/fgum/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.test.fgum; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.os.Bundle; 6 | import android.text.method.ScrollingMovementMethod; 7 | import android.view.View; 8 | import android.widget.TextView; 9 | 10 | import com.test.fgum.databinding.ActivityMainBinding; 11 | 12 | import java.io.FileOutputStream; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | 16 | public class MainActivity extends AppCompatActivity { 17 | 18 | // Used to load the 'fgum' library on application startup. 19 | static { 20 | System.loadLibrary("fgum"); 21 | } 22 | 23 | private ActivityMainBinding binding; 24 | static public MainActivity INSTANCE; 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | 30 | binding = ActivityMainBinding.inflate(getLayoutInflater()); 31 | setContentView(binding.getRoot()); 32 | INSTANCE = this; 33 | 34 | // Example of a call to a native method 35 | TextView tv = binding.sampleText; 36 | tv.setMovementMethod(ScrollingMovementMethod.getInstance()); 37 | tv.setText(stringFromJNI()); 38 | } 39 | 40 | static public void update_text(String msg) { 41 | MainActivity.INSTANCE.runOnUiThread(new Runnable() { 42 | @Override 43 | public void run() { 44 | TextView tv = MainActivity.INSTANCE.binding.sampleText; 45 | tv.append("\n" + msg + "\n"); 46 | } 47 | }); 48 | } 49 | 50 | public boolean load_so_flag = false; 51 | 52 | public void tryfridagumjs(View view) throws IOException { 53 | // 演示项目 只加载一次 54 | if (load_so_flag) return; 55 | load_so_flag = true; 56 | // 从 assets 中释放js 57 | String jsPath = getApplicationInfo().dataDir + "/hook.js"; 58 | InputStream is = getAssets().open("hook.js"); 59 | FileOutputStream fos = new FileOutputStream(jsPath); 60 | byte[] buffer = new byte[1024]; 61 | int byteCount = 0; 62 | while ((byteCount = is.read(buffer)) != -1) { 63 | fos.write(buffer, 0, byteCount); 64 | } 65 | fos.flush();// 刷新缓冲区 66 | is.close(); 67 | fos.close(); 68 | 69 | loadJS(jsPath); 70 | } 71 | 72 | /** 73 | * A native method that is implemented by the 'fgum' native library, 74 | * which is packaged with this application. 75 | */ 76 | public native String stringFromJNI(); 77 | 78 | public native boolean loadJS(String jspath); 79 | } -------------------------------------------------------------------------------- /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 | 14 | 15 | 23 | 24 | 30 | 31 |