### Build Documentation Site Source: https://github.com/luckypray/dexkit/blob/master/AGENTS.md Navigate to the doc-source directory and run these commands to install dependencies and build the documentation site. ```bash cd doc-source yarn install yarn docs:build ``` -------------------------------- ### Install Windows Dependencies with MSYS2 Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/guide/run-on-desktop.md Installs GCC, CMake, and Ninja using the MSYS2 package manager on Windows. Ensure the mingw64/bin directory is added to your environment variables. ```shell pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja ``` -------------------------------- ### Install MacOS Dependencies with HomeBrew Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/guide/run-on-desktop.md Installs CMake and Ninja on macOS using HomeBrew. This is a recommended method for dependency management on Apple systems. ```shell brew install cmake ninja ``` -------------------------------- ### Example Output of PlayActivity Search Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/guide/example.md This text output shows the result of the `findPlayActivity` function, indicating the fully qualified name of the matched class. ```text org.luckypray.dexkit.demo.PlayActivity ``` -------------------------------- ### DexKit Class Matching Example Source: https://github.com/luckypray/dexkit/blob/master/README.md This snippet demonstrates how to use DexKit to match fields, methods, and annotations within a class. It specifies criteria for field modifiers, types, and names, method signatures and string usage, and annotation elements. Use this to define detailed matching rules for code analysis. ```kotlin fields { // Add a matcher for the field add { // Specify the modifiers of the field modifiers = Modifier.PRIVATE or Modifier.STATIC or Modifier.FINAL // Specify the type of the field type = "java.lang.String" // Specify the name of the field name = "TAG" } // Add a matcher for the field of the specified type addForType("android.widget.TextView") addForType("android.os.Handler") // Specify the number of fields in the class count = 3 } // MethodsMatcher Matcher for methods in a class methods { // Add a matcher for the method add { // Specify the modifiers of the method modifiers = Modifier.PROTECTED // Specify the name of the method name = "onCreate" // Specify the return type of the method returnType = "void" // Specify the parameter types of the method, if the parameter types are uncertain, // use null, and this method will implicitly declare the number of parameters paramTypes("android.os.Bundle") // Specify the strings used in the method usingStrings("onCreate") } add { paramTypes("android.view.View") // Specify the numbers used in the method, the type is Byte, Short, Int, Long, Float, Double usingNumbers(0.01, -1, 0.987, 0, 114514) } add { paramTypes("boolean") // Specify the methods called in the method invokeMethods { add { modifiers = Modifier.PUBLIC or Modifier.STATIC returnType = "int" // Specify the strings used in the method called in the method, usingStrings(listOf("getRandomDice: "), StringMatchType.Equals) } // Only need to contain the call to the above method matchType = MatchType.Contains } } count(1..10) } // AnnotationsMatcher Matcher for annotations in a class annotations { // Add a matcher for the annotation add { // Specify the type of the annotation type = "org.luckypray.dexkit.demo.annotations.Router" // The annotation needs to contain the specified element addElement { // Specify the name of the element name = "path" // Specify the value of the element stringValue("/play") } } } // Strings used by all methods in the class usingStrings("PlayActivity", "onClick", "onCreate") }.singleOrNull() ?: error("The returned result is not unique") // Print the found class: org.luckypray.dexkit.demo.PlayActivity println(classData.name) // Get the corresponding class instance val clazz = classData.getInstance(loadPackageParam.classLoader) ``` -------------------------------- ### Obfuscated Host App Method Example Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/index.md This Java code represents an obfuscated method within a host application that needs to be dynamically hooked. Method and class names may change with each version due to obfuscation. ```java public class abc { public boolean cvc() { boolean b = false; // ... Log.d("VipCheckUtil", "userInfo: xxxx"); // ... return b; } } ``` -------------------------------- ### Minimal In-Memory Cache Implementation Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/guide/cache-bridge.md Implement the Cache interface for DexKitCacheBridge. This example provides a basic in-memory storage using ConcurrentHashMap. ```kotlin import org.luckypray.dexkit.DexKitCacheBridge import java.util.concurrent.ConcurrentHashMap object MemoryCache : DexKitCacheBridge.Cache { private val values = ConcurrentHashMap() private val lists = ConcurrentHashMap>() override fun getString(key: String, default: String?): String? = values[key] ?: default override fun putString(key: String, value: String) { values[key] = value } override fun getStringList(key: String, default: List?): List? = lists[key] ?: default override fun putStringList(key: String, value: List) { lists[key] = value } override fun remove(key: String) { values.remove(key) lists.remove(key) } override fun getAllKeys(): Collection = values.keys + lists.keys override fun clearAll() { values.clear() lists.clear() } } ``` -------------------------------- ### Configure Cache Policy Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/guide/cache-bridge.md Customize the global cache behavior by setting `DexKitCacheBridge.cachePolicy`. This example configures caching for successful results only, with no caching for failures. ```kotlin DexKitCacheBridge.cachePolicy = DexKitCacheBridge.CachePolicy( cacheSuccess = true, failurePolicy = DexKitCacheBridge.CacheFailurePolicy.NONE ) ``` -------------------------------- ### Run Desktop Sample App Source: https://github.com/luckypray/dexkit/blob/master/AGENTS.md Execute the desktop sample application for a quick smoke test after native or bridge changes. ```bash ./gradlew :main:run ``` -------------------------------- ### Build Demo App Source: https://github.com/luckypray/dexkit/blob/master/AGENTS.md Assemble the release version of the demo application, used as an APK fixture. ```bash ./gradlew :demo:assembleRelease ``` -------------------------------- ### Build Core and JVM Library Source: https://github.com/luckypray/dexkit/blob/master/AGENTS.md Build the native code, JNI, query DSL, result objects, reflection wrappers, and desktop components. ```bash ./gradlew :dexkit:cmakeBuild ./gradlew :dexkit:jar ./gradlew :dexkit:test ``` -------------------------------- ### Build Ubuntu 20.04 Sysroot and Configure Build Environment Source: https://github.com/luckypray/dexkit/blob/master/build-support/linux-sysroot/README.md Builds the Ubuntu 20.04 sysroot and configures clang, ar, ranlib, and ld for C++20 compatibility. It then invokes the Gradle build for DexKit. ```bash sudo env UBUNTU_CODENAME=focal \ bash build-support/linux-sysroot/build-linux-sysroot.sh \ .dexkit-sysroot/ubuntu-20.04-x86_64 SYSROOT="$PWD/.dexkit-sysroot/ubuntu-20.04-x86_64" export CC="clang --sysroot=$SYSROOT --gcc-toolchain=$SYSROOT/usr" export CXX="clang++ --sysroot=$SYSROOT --gcc-toolchain=$SYSROOT/usr" export AR=llvm-ar export RANLIB=llvm-ranlib export LD=ld.lld ./gradlew :dexkit:cmakeBuild --console=plain ``` -------------------------------- ### DexKit Initialization and Class Finding Source: https://github.com/luckypray/dexkit/blob/master/README.md Demonstrates initializing DexKitBridge and finding a specific class using various matching criteria. Ensure proper lifecycle management for DexKitBridge to prevent memory leaks. ```java public class MainHook implements IXposedHookLoadPackage { static { System.loadLibrary("dexkit"); } private ClassLoader hostClassLoader; @Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) { String packageName = loadPackageParam.packageName; String apkPath = loadPackageParam.appInfo.sourceDir; if (!packageName.equals("org.luckypray.dexkit.demo")) { return; } this.hostClassLoader = loadPackageParam.classLoader; // DexKit creation is a time-consuming operation, please do not create the object repeatedly. // If you need to use it globally, please manage the life cycle yourself and ensure // that the .close() method is called when not needed to prevent memory leaks. // Here we use `try-with-resources` to automatically close the DexKitBridge instance. try (DexKitBridge bridge = DexKitBridge.create(apkPath)) { findPlayActivity(bridge); // Other use cases } } private void findPlayActivity(DexKitBridge bridge) { ClassData classData = bridge.findClass(FindClass.create() // Search within the specified package name range .searchPackages("org.luckypray.dexkit.demo") // Exclude the specified package name range .excludePackages("org.luckypray.dexkit.demo.annotations") .matcher(ClassMatcher.create() // ClassMatcher Matcher for classes .className("org.luckypray.dexkit.demo.PlayActivity") // FieldsMatcher Matcher for fields in a class .fields(FieldsMatcher.create() // Add a matcher for the field .add(FieldMatcher.create() // Specify the modifiers of the field .modifiers(Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL) // Specify the type of the field .type("java.lang.String") // Specify the name of the field .name("TAG") ) // Add a matcher for the field of the specified type .addForType("android.widget.TextView") .addForType("android.os.Handler") // Specify the number of fields in the class .count(3) ) // MethodsMatcher Matcher for methods in a class .methods(MethodsMatcher.create() ``` -------------------------------- ### Build Ubuntu 18.04 Sysroot and Configure Build Environment Source: https://github.com/luckypray/dexkit/blob/master/build-support/linux-sysroot/README.md Builds the Ubuntu 18.04 sysroot, fetches GCC 10 headers, and configures the build environment for C++20 features with older glibc. It then invokes the Gradle build for DexKit. ```bash sudo env UBUNTU_CODENAME=bionic \ bash build-support/linux-sysroot/build-linux-sysroot.sh \ .dexkit-sysroot/ubuntu-18.04-x86_64 bash build-support/linux-sysroot/fetch-libstdcxx-headers.sh \ .dexkit-sysroot/gcc10-headers SYSROOT="$PWD/.dexkit-sysroot/ubuntu-18.04-x86_64" GCC10_HEADERS="$PWD/.dexkit-sysroot/gcc10-headers/usr/include" COMPAT_HEADER="$PWD/.dexkit-sysroot/compat/glibc-libstdcxx10-compat.h" mkdir -p "$(dirname "$COMPAT_HEADER")" cp build-support/linux-sysroot/glibc-libstdcxx10-compat.h "$COMPAT_HEADER" export CC="clang --sysroot=$SYSROOT --gcc-toolchain=$SYSROOT/usr" export CXX="clang++ --sysroot=$SYSROOT --gcc-toolchain=$SYSROOT/usr" export AR=llvm-ar export RANLIB=llvm-ranlib export LD=ld.lld export CFLAGS= export CXXFLAGS="-nostdinc++ -isystem $GCC10_HEADERS/c++/10 -isystem $GCC10_HEADERS/x86_64-linux-gnu/c++/10 -include $COMPAT_HEADER" export LDFLAGS="-Wl,--no-as-needed -lpthread" ./gradlew :dexkit:cmakeBuild --console=plain ``` -------------------------------- ### Implement Custom Cache for DexKitCacheBridge Source: https://context7.com/luckypray/dexkit/llms.txt An example implementation of the `DexKitCacheBridge.Cache` interface using `ConcurrentHashMap` for storing strings and lists. This custom cache can be used with `DexKitCacheBridge` for persistent caching. ```kotlin @OptIn(DexKitExperimentalApi::class) object MemoryCache : DexKitCacheBridge.Cache { private val strings = java.util.concurrent.ConcurrentHashMap() private val lists = java.util.concurrent.ConcurrentHashMap>() override fun getString(key: String, default: String?) = strings[key] ?: default override fun putString(key: String, value: String) { strings[key] = value } override fun getStringList(key: String, default: List?) = lists[key] ?: default override fun putStringList(key: String, value: List) { lists[key] = value } override fun remove(key: String) { strings.remove(key); lists.remove(key) } override fun getAllKeys() = strings.keys + lists.keys override fun clearAll() { strings.clear(); lists.clear() } } ``` -------------------------------- ### Add DexKit Dependency Source: https://github.com/luckypray/dexkit/blob/master/README.md Add the dexkit dependency to your build.gradle file. Ensure you use the correct version number. Note that starting with DexKit 2.0, the ArtifactId changed from DexKit to dexkit. ```gradle repositories { mavenCentral() } dependencies { // replace with your desired version, e.g. `2.0.0` implementation 'org.luckypray:dexkit:' } ``` -------------------------------- ### Repository Structure Source: https://github.com/luckypray/dexkit/blob/master/AGENTS.md Overview of the DexKit project's directory layout. ```bash /Core/ # Shared C++ parsing/search engine and generated C++ schema headers /dexkit/ # JVM/desktop library, JNI bridge, query DSL, results, and host-side tests /dexkit-android/ # Android AAR wrapping the same sources with NDK/CMake packaging /demo/ # Android demo app; release APK is reused by dexkit tests /main/ # Desktop sample app for quick manual verification /lint-rules/ # Android lint checks shipped with the library /schema/ # FlatBuffers schema sources and code generation script /doc-source/ # VuePress documentation source /.github/workflows/ # CI workflows for native builds and docs deployment ``` -------------------------------- ### Method Descriptor Format Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/guide/knowledge.md Demonstrates the structure of a method descriptor, including class signature, method name, and return type. ```text Ljava/lang/String;->length()I ``` -------------------------------- ### Accessing Class Metadata with ClassData Source: https://context7.com/luckypray/dexkit/llms.txt Retrieve rich metadata for matched classes using `ClassData`. This includes structural information and lazy-loaded relationships, with methods to get the live `java.lang.Class` or serialize the class. ```kotlin DexKitBridge.create(apkPath).use { bridge -> val cd = bridge.findClass { matcher { className("org.luckypray.dexkit.demo.PlayActivity") } }.single() println(cd.name) // org.luckypray.dexkit.demo.PlayActivity println(cd.simpleName) // PlayActivity println(cd.descriptor) // Lorg/luckypray/dexkit/demo/PlayActivity; println(cd.modifiers) // 1 (public) println(cd.methodCount) // 5 println(cd.fieldCount) // 3 println(cd.interfaceCount) // 0 // Lazy-loaded relationships println(cd.superClass?.name) // androidx.appcompat.app.AppCompatActivity cd.methods.forEach { println(" method: ${it.descriptor}") } cd.fields.forEach { println(" field: ${it.descriptor}") } cd.annotations.forEach { println(" @${it.typeName}") } // Search within the class scope val onCreateMethod = cd.findMethod { matcher { name = "onCreate" } }.single() // Reflect val clazz: Class<*> = cd.getInstance(hostClassLoader) // Save for later use val dexClass = cd.toDexClass() val serialized: String = dexClass.serialize() // "Lorg/luckypray/dexkit/demo/PlayActivity;" } ``` -------------------------------- ### Build Ubuntu 16.04 Sysroot and Configure Toolchain Source: https://github.com/luckypray/dexkit/blob/master/build-support/linux-sysroot/README.md This script builds the sysroot for Ubuntu 16.04 and sets up the environment variables for compilation. It includes specific configurations for GCC 5 runtime compatibility, C++20 headers, and thread wrappers. ```bash sudo env UBUNTU_CODENAME=xenial \ bash build-support/linux-sysroot/build-linux-sysroot.sh \ .dexkit-sysroot/ubuntu-16.04-x86_64 bash build-support/linux-sysroot/fetch-libstdcxx-headers.sh \ .dexkit-sysroot/gcc10-headers SYSROOT="$PWD/.dexkit-sysroot/ubuntu-16.04-x86_64" GCC10_HEADERS="$PWD/.dexkit-sysroot/gcc10-headers/usr/include" COMPAT_DIR="$PWD/.dexkit-sysroot/compat" COMPAT_HEADER="$COMPAT_DIR/glibc-libstdcxx10-compat.h" WRAPPER_DIR="$COMPAT_DIR/gcc5-thread-wrapper" mkdir -p "$WRAPPER_DIR" cp build-support/linux-sysroot/glibc-libstdcxx10-compat.h "$COMPAT_HEADER" sed "s|@TARGET_SYSROOT@|$SYSROOT|g" \ build-support/linux-sysroot/gcc5-thread-wrapper/thread.in \ > "$WRAPPER_DIR/thread" sed "s|@TARGET_SYSROOT@|$SYSROOT|g" \ build-support/linux-sysroot/gcc5-thread-wrapper/future.in \ > "$WRAPPER_DIR/future" export CC="clang --sysroot=$SYSROOT --gcc-toolchain=$SYSROOT/usr" export CXX="clang++ --sysroot=$SYSROOT --gcc-toolchain=$SYSROOT/usr" export AR=llvm-ar export RANLIB=llvm-ranlib export LD=ld.lld export CFLAGS= export CXXFLAGS="-fno-exceptions -nostdinc++ -isystem $WRAPPER_DIR -isystem $GCC10_HEADERS/c++/10 -isystem $GCC10_HEADERS/x86_64-linux-gnu/c++/10 -include $COMPAT_HEADER" export LDFLAGS="-Wl,--no-as-needed -lpthread" ./gradlew :dexkit:cmakeBuild --console=plain ``` -------------------------------- ### Inspect Field Metadata with FieldData Source: https://context7.com/luckypray/dexkit/llms.txt Use FieldData to get details about a field, including its descriptor, name, type, class, and modifiers. It also shows readers and writers of the field. Reflection and serialization are demonstrated. ```kotlin DexKitBridge.create(apkPath).use { bridge -> val fd = bridge.findField { matcher { declaredClass { className("org.luckypray.dexkit.demo.PlayActivity") } type = "android.widget.TextView" } }.single() println(fd.descriptor) // private android.widget.TextView org.luckypray.dexkit.demo.PlayActivity.a println(fd.name) // a println(fd.typeName) // android.widget.TextView println(fd.className) // org.luckypray.dexkit.demo.PlayActivity println(fd.modifiers) // 2 (private) fd.annotations.forEach { println(" @${it.typeName}") } // Who reads / writes this field? fd.readers.forEach { println("reader: ${it.descriptor}") } fd.writers.forEach { println("writer: ${it.descriptor}") } // Reflect val field: Field = fd.getFieldInstance(hostClassLoader) field.isAccessible = true val value = field.get(activityInstance) // Serialize val saved: String = fd.toDexField().serialize() } ``` -------------------------------- ### Configure Legacy Packaging for JNI Libraries Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/guide/quick-start.md If your project's minSdkVersion is less than 23, use this configuration in app/build.gradle to avoid UnsatisfiedLinkError when loading libdexkit.so. ```groovy android { packagingOptions { jniLibs { useLegacyPackaging true } } } ``` -------------------------------- ### Run DexKit Main Module Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/guide/run-on-desktop.md Executes the main module of DexKit using Gradle. This command is used for testing the project on desktop platforms. ```shell gradle :main:run ``` -------------------------------- ### Create DexKitBridge Instance Source: https://context7.com/luckypray/dexkit/llms.txt Demonstrates creating a DexKitBridge from an APK path, raw DEX bytes, or a ClassLoader. Always close the bridge to free native memory. ```kotlin DexKitBridge.create(loadPackageParam.appInfo.sourceDir).use { bridge -> println("Loaded ${bridge.getDexNum()} dex files") } ``` ```kotlin val dexBytes: Array = /* load dex files manually */ DexKitBridge.create(dexBytes).use { bridge -> println("Loaded ${bridge.getDexNum()} dex files") } ``` ```kotlin DexKitBridge.create(loadPackageParam.classLoader, useMemoryDexFile = true).use { bridge -> println("Loaded ${bridge.getDexNum()} dex files") } ``` -------------------------------- ### Pack GCC 10 Headers Backup Source: https://github.com/luckypray/dexkit/blob/master/build-support/linux-sysroot/README.md Archives the GCC 10 headers directory into a compressed tarball. ```bash tar -C .dexkit-sysroot/gcc10-headers \ -cJf .dexkit-sysroot/gcc10-headers.tar.xz . ``` -------------------------------- ### Build Android AAR and Lint Rules Source: https://github.com/luckypray/dexkit/blob/master/AGENTS.md Assemble the Android AAR package and build the lint rules JAR. ```bash ./gradlew :dexkit-android:assembleRelease ``` ```bash ./gradlew :lint-rules:jar ``` -------------------------------- ### DexKitBridge.create Source: https://context7.com/luckypray/dexkit/llms.txt The entry point for all queries. Accepts an APK file path, raw DEX byte arrays, or an Android ClassLoader. The bridge is Closeable; always close it when done to free the underlying native memory. ```APIDOC ## DexKitBridge.create ### Description Creates a DexKitBridge instance to interact with DEX files. ### Method `DexKitBridge.create` ### Parameters - `apkPath` (String) - Path to the APK file. - `dexBytes` (Array) - Raw DEX byte arrays. - `classLoader` (ClassLoader) - An Android ClassLoader. - `useMemoryDexFile` (Boolean, Optional) - Whether to use memory-mapped DEX files when using a ClassLoader. ### Usage ```kotlin // From APK path DexKitBridge.create(loadPackageParam.appInfo.sourceDir).use { println("Loaded ${it.getDexNum()} dex files") } // From raw DEX bytes val dexBytes: Array = /* load dex files manually */ DexKitBridge.create(dexBytes).use { println("Loaded ${it.getDexNum()} dex files") } // From a ClassLoader (Android runtime only) DexKitBridge.create(loadPackageParam.classLoader, useMemoryDexFile = true).use { println("Loaded ${it.getDexNum()} dex files") } ``` ### Notes The bridge instance must be closed after use to free native memory. ``` -------------------------------- ### Shorthand to Method Signature Mapping Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/guide/knowledge.md Illustrates how shorthand type characters map to full method signature representations. Note that 'L' is used for all object and array types. ```text VL | `void method(Object)` ZLL | `boolean method(Object, Object)` VILFD | `void method(int, Object, long, float, double)` LL | `Object method(Object)` ILI | `int method(Object, int)` LIL | `Object method(int, Object)` ``` -------------------------------- ### Initialize and Use DexKitCacheBridge Source: https://context7.com/luckypray/dexkit/llms.txt Configures `DexKitCacheBridge` with idle timeout, cache policy, and initializes it with a custom cache implementation. Demonstrates creating a bridge with an app tag for cache namespace and reusing bridges. ```kotlin @OptIn(DexKitExperimentalApi::class) fun initDexKit() { DexKitCacheBridge.idleTimeoutMillis = 5_000L DexKitCacheBridge.cachePolicy = DexKitCacheBridge.CachePolicy( cacheSuccess = true, failurePolicy = DexKitCacheBridge.CacheFailurePolicy.NONE ) DexKitCacheBridge.init(MemoryCache) } @OptIn(DexKitExperimentalApi::class) fun findWithCache(apkPath: String) { // appTag encodes host identity; same tag = same cache namespace + bridge reuse DexKitCacheBridge.create(appTag = "com.example.app:2.1.0", path = apkPath).use { bridge -> // Explicit key: cache is reused across startups; first call queries, subsequent calls hit cache val playActivity: DexClass = bridge.getClass("play_activity") { matcher { className("org.luckypray.dexkit.demo.PlayActivity") } } println(playActivity.typeName) // Direct API when structured query is not sufficient val method: DexMethod = bridge.getMethodDirect("on_click_key") { findMethod { matcher { usingStrings("onClick: rollButton") } }.single() } println(method) // Read-only: throws NoSuchElementException if key not cached yet val cached: DexClass? = bridge.getClassOrNull("play_activity") } // Invalidate after a host upgrade DexKitCacheBridge.clearCache("com.example.app:2.0.0") } ``` -------------------------------- ### Generate Kotlin Code with Flatc Source: https://github.com/luckypray/dexkit/blob/master/schema/README.md Use the Flatc compiler to generate Kotlin source files. Specify the output directory and the FlatBuffer schema files to process. ```shell ./flatc --kotlin -o ../dexkit/src/main/java/org/luckypray \ fbs/encode_value.fbs fbs/enums.fbs fbs/matchers.fbs fbs/querys.fbs fbs/ranges.fbs fbs/results.fbs ``` -------------------------------- ### Demo Activity for Obfuscated App Analysis Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/guide/example.md This Java code defines a demo Activity that simulates an obfuscated Android application. It includes obfuscated properties and methods, demonstrating how DexKit can be used to analyze such code. Note the use of obfuscated variable names like 'a' and 'b', and method names like 'd' and 'e'. ```java package org.luckypray.dexkit.demo; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.h; import java.util.Random; import org.luckypray.dexkit.demo.annotations.Router; @Router(path = "/play") public class PlayActivity extends AppCompatActivity { private static final String TAG = "PlayActivity"; private TextView a; private Handler b; public void d(View view) { Handler handler; int i; Log.d("PlayActivity", "onClick: rollButton"); float nextFloat = new Random().nextFloat(); if (nextFloat < 0.01d) { handler = this.b; i = -1; } else if (nextFloat < 0.987f) { handler = this.b; i = 0; } else { handler = this.b; i = 114514; } handler.sendEmptyMessage(i); } public void e(boolean z) { int i; if (!z) { i = RandomUtil.a(); } else { i = 6; } String a = h.a("You rolled a ", i); this.a.setText(a); Log.d("PlayActivity", "rollDice: " + a); } protected void onCreate(Bundle bundle) { super/*androidx.fragment.app.FragmentActivity*/.onCreate(bundle); setContentView(0x7f0b001d); Log.d("PlayActivity", "onCreate"); HandlerThread handlerThread = new HandlerThread("PlayActivity"); handlerThread.start(); this.b = new PlayActivity$1(this, handlerThread.getLooper()); this.a = (TextView) findViewById(0x7f080134); ((Button) findViewById(0x7f08013a)).setOnClickListener(new a(this)); } } ``` -------------------------------- ### Generate Code with Python Script Source: https://github.com/luckypray/dexkit/blob/master/schema/README.md Execute the Python script to initiate the code generation process. This is the primary step before language-specific generation. ```shell python gen_code.py ``` -------------------------------- ### Field Descriptor Format Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/guide/knowledge.md Illustrates the format for a field descriptor, specifying the class, field name, and its type. ```text Ljava/lang/String;->count:I ``` -------------------------------- ### Find Method with Composite Matcher (Java Chaining API) Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/guide/example.md Shows how to achieve the same composite method matching logic as the Kotlin DSL but using the Java chaining API. This is useful when Kotlin DSL is not available. ```java MethodMatcher matcher = MethodMatcher.create() .declaredClass("org.luckypray.dexkit.demo.PlayActivity") .anyOf( MethodMatcher.create().name("onCreate"), MethodMatcher.create().usingStrings( List.of("onClick"), StringMatchType.Contains, false ) ) .not( MethodMatcher.create().usingStrings( List.of("rollDice: "), StringMatchType.Contains, false ) ); ``` -------------------------------- ### Clone DexKit Repository Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/guide/run-on-desktop.md Clones the DexKit project from its GitHub repository. This is the first step to accessing the project files. ```shell git clone https://github.com/LuckyPray/DexKit.git ``` -------------------------------- ### Find Class by Multi-Condition Source: https://context7.com/luckypray/dexkit/llms.txt Searches DEX files for classes matching a ClassMatcher tree, supporting package scoping, field/method/annotation matchers, and string/number constraints. Results are ClassData objects. ```kotlin DexKitBridge.create(apkPath).use { bridge -> // Find PlayActivity by structural fingerprint (all fields/methods obfuscated) val classData = bridge.findClass { searchPackages("org.luckypray.dexkit.demo") excludePackages("org.luckypray.dexkit.demo.annotations") matcher { fields { add { modifiers = Modifier.PRIVATE or Modifier.STATIC or Modifier.FINAL type = "java.lang.String" name = "TAG" } addForType("android.widget.TextView") addForType("android.os.Handler") count = 3 } methods { add { name = "onCreate" returnType = "void" paramTypes("android.os.Bundle") usingStrings("onCreate") } add { paramTypes("android.view.View") usingNumbers(0.01, -1, 0.987, 0, 114514) } count(1..10) } annotations { add { type = "org.luckypray.dexkit.demo.annotations.Router" addElement { name = "path" stringValue = "/play" } } } usingStrings("PlayActivity", "onClick", "onCreate") } }.single() println(classData.name) // org.luckypray.dexkit.demo.PlayActivity println(classData.methodCount) // 5 val clazz = classData.getInstance(hostClassLoader) // java.lang.Class } ``` -------------------------------- ### Class Descriptor Format Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/guide/knowledge.md Shows the standard format for representing a class in a Dalvik descriptor. ```text Ljava/lang/String; ``` -------------------------------- ### Xposed Hooking with DexKitBridge (Java) Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/index.md This Java code demonstrates how to use DexKitBridge to find and hook a specific method in an obfuscated Android application using the Xposed framework. It utilizes a try-with-resources statement for automatic resource management of the DexKitBridge instance. ```java class AppHooker { static { System.loadLibrary("dexkit"); } private ClassLoader hostClassLoader; public AppHooker(LoadPackageParam loadPackageParam) { this.hostClassLoader = loadPackageParam.classLoader; String apkPath = loadPackageParam.appInfo.sourceDir; // DexKit creation is a time-consuming operation, please do not create the object repeatedly. // If you need to use it globally, please manage the life cycle yourself and ensure // that the .close() method is called when not needed to prevent memory leaks. // Here we use `try-with-resources` to automatically close the DexKitBridge instance. try (DexKitBridge bridge = DexKitBridge.create(apkPath)) { isVipHook(bridge); // Other hook ... } } private void isVipHook(DexKitBridge bridge) { MethodData methodData = bridge.findMethod(FindMethod.create() .matcher(MethodMatcher.create() // All conditions are optional, you can freely combine them .modifiers(Modifier.PUBLIC) .paramCount(0) .returnType("boolean") .usingStrings("VipCheckUtil", "userInfo:") ) ).single(); Method method = methodData.getMethodInstance(hostClassLoader); XposedBridge.hookMethod(method, XC_MethodReplacement.returnConstant(true)); } // ... } ``` -------------------------------- ### Include Core Subdirectory Source: https://github.com/luckypray/dexkit/blob/master/dexkit-android/CMakeLists.txt Adds the 'Core' subdirectory from the parent directory to the build. This allows for modularity and inclusion of core functionalities. ```cmake add_subdirectory(../Core Core) ``` -------------------------------- ### Pack Ubuntu Sysroot Backup Source: https://github.com/luckypray/dexkit/blob/master/build-support/linux-sysroot/README.md Archives the specified Ubuntu sysroot directory into a compressed tarball. Excludes temporary and virtual filesystem directories. ```bash sudo tar -C .dexkit-sysroot/ubuntu-16.04-x86_64 \ --exclude=./dev \ --exclude=./proc \ --exclude=./sys \ --exclude=./run \ --exclude=./tmp \ --exclude=./var/tmp \ -cJf .dexkit-sysroot/ubuntu-16.04-x86_64.tar.xz . ``` -------------------------------- ### Find Method with Composite Matcher (Kotlin DSL) Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/guide/example.md Demonstrates using composite matchers like 'anyOf' and 'not' within the Kotlin DSL to find a specific method. This allows for complex OR and NOT conditions. ```kotlin private fun findOnCreateWithCompositeMatcher(bridge: DexKitBridge) { val method = bridge.findMethod { matcher { declaredClass("org.luckypray.dexkit.demo.PlayActivity") anyOf { match { name = "onCreate" } match { usingStrings("onClick") } } not { usingStrings("rollDice: ") } } }.single() println(method.descriptor) } ``` -------------------------------- ### Xposed Hooking with DexKitBridge (Kotlin) Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/index.md This Kotlin code demonstrates how to use DexKitBridge to find and hook a specific method in an obfuscated Android application using the Xposed framework. It utilizes `use` for automatic resource management of the DexKitBridge instance. ```kotlin class AppHooker { companion object { init { System.loadLibrary("dexkit") } } private var hostClassLoader: ClassLoader public constructor(loadPackageParam: LoadPackageParam) { this.hostClassLoader = loadPackageParam.classLoader val apkPath = loadPackageParam.appInfo.sourceDir // DexKit creation is a time-consuming operation, please do not create the object repeatedly. // If you need to use it globally, please manage the life cycle yourself and ensure // that the .close() method is called when not needed to prevent memory leaks. // Here we use `Closable.use` to automatically close the DexKitBridge instance. DexKitBridge.create(apkPath).use { bridge -> isVipHook(bridge) // Other hook ... } } private fun isVipHook(bridge: DexKitBridge) { val methodData = bridge.findMethod { matcher { // All conditions are optional, you can freely combine them modifiers = Modifier.PUBLIC returnType = "boolean" paramCount = 0 usingStrings("VipCheckUtil", "userInfo:") } }.single() val method: Method = methodData.getMethodInstance(hostClassLoader) XposedBridge.hookMethod(method, XC_MethodReplacement.returnConstant(true)) } // ... } ``` -------------------------------- ### Batch Class Search by String Groups Source: https://context7.com/luckypray/dexkit/llms.txt Optimized for finding many classes simultaneously, each identified by a characteristic set of strings. Scans the string table once and dispatches matches to all groups in parallel. ```kotlin DexKitBridge.create(apkPath).use { bridge -> val results: Map = bridge.batchFindClassUsingStrings { addSearchGroup("LoginActivity", listOf("login_success", "login_failed", "LoginActivity")) addSearchGroup("NetworkManager", listOf("connect timeout", "http_client", "NetworkManager")) addSearchGroup("ConfigManager", listOf("config_version", "config_update"), StringMatchType.Contains) } results["LoginActivity"]?.forEach { println("LoginActivity candidate: ${it.name}") } results["NetworkManager"]?.forEach { println("NetworkManager candidate: ${it.name}") } // Or via map form: val altResults = bridge.batchFindClassUsingStrings( mapOf( "EncryptUtil" to listOf("AES/CBC/PKCS5Padding", "encrypt_key"), "LogUtil" to listOf("verbose", "debug", "error") ), matchType = StringMatchType.Equals, ignoreCase = false ) } ``` -------------------------------- ### Unpack Ubuntu Sysroot Backup Source: https://github.com/luckypray/dexkit/blob/master/build-support/linux-sysroot/README.md Removes an existing sysroot directory and unpacks a new one from a tarball. ```bash rm -rf .dexkit-sysroot/ubuntu-16.04-x86_64 mkdir -p .dexkit-sysroot/ubuntu-16.04-x86_64 tar -C .dexkit-sysroot/ubuntu-16.04-x86_64 \ -xJf .dexkit-sysroot/ubuntu-16.04-x86_64.tar.xz ``` -------------------------------- ### Load DexKit Library and Find PlayActivity in Kotlin Source: https://github.com/luckypray/dexkit/blob/master/README.md This Kotlin snippet demonstrates loading the DexKit native library and using DexKitBridge to find the PlayActivity class. It includes filtering by package name and excluding specific sub-packages. The `use` block ensures proper resource management for the DexKitBridge instance. ```kotlin class MainHook : IXposedHookLoadPackage { companion object { init { System.loadLibrary("dexkit") } } private lateinit var hostClassLoader: ClassLoader override fun handleLoadPackage(loadPackageParam: XC_LoadPackage.LoadPackageParam) { val packageName = loadPackageParam.packageName val apkPath = loadPackageParam.appInfo.sourceDir if (!packageName.equals("org.luckypray.dexkit.demo")) { return } this.hostClassLoader = loadPackageParam.classLoader // DexKit creation is a time-consuming operation, please do not create the object repeatedly. // If you need to use it globally, please manage the life cycle yourself and ensure // that the .close() method is called when not needed to prevent memory leaks. // Here we use `Closable.use` to automatically close the DexKitBridge instance. DexKitBridge.create(apkPath).use { bridge -> findPlayActivity(bridge) // Other use cases } } private fun findPlayActivity(bridge: DexKitBridge) { val classData = bridge.findClass { // Search within the specified package name range searchPackages("org.luckypray.dexkit.demo") // Exclude the specified package name range excludePackages("org.luckypray.dexkit.demo.annotations") // ClassMatcher Matcher for classes matcher { ``` -------------------------------- ### Batch Find Methods Using Strings with DexKitBridge Source: https://context7.com/luckypray/dexkit/llms.txt Use `batchFindMethodUsingStrings` to search for multiple obfuscated methods simultaneously based on unique strings they emit. Ideal for identifying methods by log tags or error messages. ```kotlin DexKitBridge.create(apkPath).use { bridge -> val results: Map = bridge.batchFindMethodUsingStrings { searchPackages("com.example.app") addSearchGroup("onClickHandler", listOf("onClick: rollButton"), StringMatchType.Contains) addSearchGroup("diceRoller", listOf("rollDice: ", "You rolled a "), StringMatchType.Contains) addSearchGroup("tokenRefresh", listOf("token_refresh_success", "refresh_token")) } results["diceRoller"]?.single()?.let { method -> println("diceRoller → ${method.descriptor}") // public void org.example.a.e(boolean) // Inspect callers method.callers.forEach { println(" called by: ${it.descriptor}") } } } ``` -------------------------------- ### Opt-in to Experimental API Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/guide/cache-bridge.md Kotlin callers must explicitly opt in to use experimental APIs like DexKitCacheBridge. ```kotlin import org.luckypray.dexkit.annotations.DexKitExperimentalApi @OptIn(DexKitExperimentalApi::class) fun useCacheBridge() { // use DexKitCacheBridge here } ``` -------------------------------- ### Save Method Information Source: https://github.com/luckypray/dexkit/blob/master/doc-source/src/en/guide/example.md Serializes a found method and saves its descriptor to SharedPreferences. Ensure the method is correctly identified before serialization. ```kotlin private fun saveData(bridge: DexKitBridge) { bridge.findMethod { matcher { modifiers = Modifier.PUBLIC or Modifier.STATIC returnType = "void" paramTypes("android.view.View", null) usingStrings("onClick") } }.single().let { val dexMethod = it.toDexMethod() val serialize = dexMethod.serialize() val sp = getSharedPreferences("dexkit", Context.MODE_PRIVATE) sp.edit().putString("onClickMethod", serialize).apply() } } ``` -------------------------------- ### Find Method with Multi-Condition Search Source: https://context7.com/luckypray/dexkit/llms.txt Searches for methods across all DEX files using a MethodMatcher. Supports modifier flags, name/parameter/return-type constraints, opcode matching, string/number usage, invokeMethods (call-graph conditions), and fuzzy parameter types. ```kotlin DexKitBridge.create(apkPath).use { bridge -> // Find a static onClick handler with fuzzy (obfuscated) second parameter val methodData = bridge.findMethod { matcher { modifiers = Modifier.PUBLIC or Modifier.STATIC returnType = "void" paramTypes("android.view.View", null) // null = any type usingStrings("onClick") } }.single() println(methodData) // public static void org.example.a.onClick(android.view.View, b) // Reflect directly val method: Method = methodData.getMethodInstance(hostClassLoader) // Nested invocation condition: find method that calls a specific sub-method val encryptMethod = bridge.findMethod { matcher { returnType = "java.lang.String" invokeMethods { add { modifiers = Modifier.PUBLIC or Modifier.STATIC returnType = "int" usingStrings(listOf("getRandomDice: "), StringMatchType.Equals) } matchType = MatchType.Contains } } }.firstOrNull() println(encryptMethod?.descriptor) // Lists all strings this method uses at runtime: println(encryptMethod?.usingStrings) } ``` -------------------------------- ### Link Libraries to Target Source: https://github.com/luckypray/dexkit/blob/master/dexkit-android/CMakeLists.txt Links the main shared library with static libraries, the zlib, and the found log library. This resolves dependencies for the project. ```cmake target_link_libraries(${PROJECT_NAME} dexkit_static z ${log-lib}) ``` -------------------------------- ### Find and Link CXX Package Source: https://github.com/luckypray/dexkit/blob/master/dexkit-android/CMakeLists.txt Finds the CXX package and links it to the project. This is typically used for C++ standard library support. ```cmake find_package(cxx REQUIRED CONFIG) link_libraries(cxx::cxx) ``` -------------------------------- ### Find PlayActivity Class with DexKit Source: https://github.com/luckypray/dexkit/blob/master/README.md This Java snippet shows how to use DexKit to find a specific class (PlayActivity) based on method matchers, annotation matchers, and string usage. It demonstrates how to specify method modifiers, names, return types, parameter types, and invoked methods. It also shows how to match annotations by type and element values. Ensure DexKit is properly configured and imported. ```java // Add a matcher for the method .methods(List.of( MethodMatcher.create() // Specify the modifiers of the method .modifiers(Modifier.PROTECTED) // Specify the name of the method .name("onCreate") // Specify the return type of the method .returnType("void") // Specify the parameter type of the method .paramTypes("android.os.Bundle") // Specify the strings used by the method .usingStrings("onCreate"), MethodMatcher.create() .paramTypes("android.view.View") // Specify the numbers used in the method, the type is Byte, Short, Int, Long, Float, Double .usingNumbers(0.01, -1, 0.987, 0, 114514), MethodMatcher.create() .modifiers(Modifier.PUBLIC) .paramTypes("boolean") // Specify the method invoke the methods list .invokeMethods(MethodsMatcher.create() .add(MethodMatcher.create() .modifiers(Modifier.PUBLIC | Modifier.STATIC) .returnType("int") // be invoke method using strings .usingStrings(List.of("getRandomDice: "), StringMatchType.Equals) ) // Only need to contain the call to the above method .matchType(MatchType.Contains) ) )) // Specify the number of methods in the class, a minimum of 1, and a maximum of 10 .count(1, 10) ) // AnnotationsMatcher Matcher for annotations in a class .annotations(AnnotationsMatcher.create() // Add a matcher for the annotation .add(AnnotationMatcher.create() // Specify the type of the annotation .type("org.luckypray.dexkit.demo.annotations.Router") // The annotation needs to contain the specified element .addElement(AnnotationElementMatcher.create() // Specify the name of the element .name("path") // Specify the value of the element .stringValue("/play") ) ) ) // Strings used by all methods in the class .usingStrings("PlayActivity", "onClick", "onCreate") ) ).singleOrThrow(() -> new IllegalStateException("The returned result is not unique")); // Print the found class: org.luckypray.dexkit.demo.PlayActivity System.out.println(classData.getName()); // Get the corresponding class instance Class clazz = classData.getInstance(loadPackageParam.classLoader); ```