diff --git a/README.md b/README.md index d71e047e..590bee52 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ -
+ 🇬🇧 English | 🇪🇸 Español | 🇨🇳 简体中文 +
-A high-performance runtime parsing library for dex implemented in C++, used for lookup of obfuscated classes, methods, or properties.
--- @@ -32,7 +32,8 @@ Basic Features: - [x] Batch search of classes using strings - [x] Batch search of methods using strings -> Note: Optimizations have been implemented for string search scenarios, significantly enhancing +> [!NOTE] +> Optimizations have been implemented for string search scenarios, significantly enhancing > search speed. Increasing query groups will not lead to a linear increase in time consumption. ### Documentation @@ -53,7 +54,7 @@ dependencies { } ``` -> **Note** +> [!IMPORTANT] > Starting with **DexKit 2.0**, the new ArtifactId has been changed from `DexKit` to `dexkit`. DexKit current version: [](https://central.sonatype.com/search?q=dexkit&namespace=org.luckypray) @@ -133,6 +134,7 @@ public class PlayActivity extends AppCompatActivity { At this point, to obtain this class, you can use the following code: +> [!NOTE] > This is just an example, in actual usage, there's no need for such an extensive set of matching > conditions. Choose and use as needed to avoid unnecessary complexity in matching due to an > excessive number of conditions. diff --git a/README_es.md b/README_es.md new file mode 100644 index 00000000..2d0aef55 --- /dev/null +++ b/README_es.md @@ -0,0 +1,401 @@ ++ 🇬🇧 English | 🇪🇸 Español | 🇨🇳 简体中文 +
+ +Una biblioteca de alto rendimiento para el análisis en tiempo de ejecución de archivos dex implementada en C++, usada para localizar clases, métodos o propiedades ofuscadas.
+ +--- + +# DexKit 2.0 + +Actualmente la versión 2.0 ha sido lanzada oficialmente; consulta las **Notas de la versión** para ver las mejoras relacionadas. + +## APIs compatibles + +Funciones básicas: + +- [x] Búsqueda de clases con múltiples condiciones +- [x] Búsqueda de métodos con múltiples condiciones +- [x] Búsqueda de campos con múltiples condiciones +- [x] Proporciona varias APIs de metadatos para obtener datos relacionados con campos/métodos/clases + +⭐️ Funciones distintivas (recomendadas): + +- [x] Búsqueda por lotes de clases usando cadenas +- [x] Búsqueda por lotes de métodos usando cadenas + +> [!NOTE] +> Se han implementado optimizaciones para escenarios de búsqueda por cadenas, mejorando significativamente la velocidad de búsqueda. +> Aumentar el número de grupos de consulta no supondrá un incremento lineal en el tiempo de ejecución. + +### Documentación + +- [Haz click aquí](https://luckypray.org/DexKit/en/) para ir a la página de documentación y ver tutoriales más detallados. + +### Dependencias + +Agrega la dependencia `dexkit` en tu `build.gradle`: + +```gradle +repositories { + mavenCentral() +} +dependencies { + // reemplaza+ +```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)); + } +} +``` +
+ +```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; + // La creación de un objeto DexKit es una operación que consume mucho tiempo; por favor no crees el objeto de forma repetida. + // Si necesitas usarlo de manera global, gestiona su ciclo de vida tú mismo + // y asegúrate de llamar al método .close() cuando ya no lo necesites, para evitar fugas de memoria. + // Aquí utilizamos `try-with-resources` para cerrar automáticamente la instancia de DexKitBridge. + try (DexKitBridge bridge = DexKitBridge.create(apkPath)) { + findPlayActivity(bridge); + // Otros casos de uso + } + } + + private void findPlayActivity(DexKitBridge bridge) { + ClassData classData = bridge.findClass(FindClass.create() + // Incluir paquetes donde buscar + .searchPackages("org.luckypray.dexkit.demo") + // Excluir paquetes no relevantes + .excludePackages("org.luckypray.dexkit.demo.annotations") + .matcher(ClassMatcher.create() + // ClassMatcher: Matcher para clases + .className("org.luckypray.dexkit.demo.PlayActivity") + // FieldsMatcher: Matcher para los campos de una clase + .fields(FieldsMatcher.create() + // FieldMatcher: Agregar un matcher para un campo + .add(FieldMatcher.create() + // Especificar los modificadores del campo + .modifiers(Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL) + // Especificar el tipo del campo + .type("java.lang.String") + // Especificar el nombre del campo + .name("TAG") + ) + // Agregar un matcher para campos del tipo especificado + .addForType("android.widget.TextView") + .addForType("android.os.Handler") + // Especificar la cantidad de campos en la clase + .count(3) + ) + // MethodsMatcher: Matcher para métodos de una clase + .methods(MethodsMatcher.create() + // Agregar un matcher para el método + .methods(List.of( + MethodMatcher.create() + // Especificar los modificadores del método + .modifiers(Modifier.PROTECTED) + // Especificar el nombre del método + .name("onCreate") + // Especificar el tipo de retorno del método + .returnType("void") + // Especificar los tipos de parámetros del método + .paramTypes("android.os.Bundle") + // Especificar los strings utilizados en el método + .usingStrings("onCreate"), + MethodMatcher.create() + .paramTypes("android.view.View") + // Especificar los números utilizados en el método; los tipos pueden ser Byte, Short, Int, Long, Float o Double + .usingNumbers(0.01, -1, 0.987, 0, 114514), + MethodMatcher.create() + .modifiers(Modifier.PUBLIC) + .paramTypes("boolean") + // Especificar que el método invoque una lista de métodos + .invokeMethods(MethodsMatcher.create() + .add(MethodMatcher.create() + .modifiers(Modifier.PUBLIC | Modifier.STATIC) + .returnType("int") + // Método invocado que utiliza los siguientes strings + .usingStrings(List.of("getRandomDice: "), StringMatchType.Equals) + ) + // Solo necesita contener la llamada al método anterior + .matchType(MatchType.Contains) + ) + )) + // Especificar la cantidad de métodos en la clase; mínimo 1 y máximo 10 + .count(1, 10) + ) + // AnnotationsMatcher: Matcher para anotaciones en una clase + .annotations(AnnotationsMatcher.create() + // Agregar un matcher para la anotación + .add(AnnotationMatcher.create() + // Especificar el tipo de la anotación + .type("org.luckypray.dexkit.demo.annotations.Router") + // La anotación debe contener el elemento especificado + .addElement(AnnotationElementMatcher.create() + // Especificar el nombre del elemento + .name("path") + // Especificar el valor del elemento + .stringValue("/play") + ) + ) + ) + // Strings utilizados por todos los métodos de la clase + .usingStrings("PlayActivity", "onClick", "onCreate") + ) + ).singleOrThrow(() -> new IllegalStateException("El resultado devuelto no es único")); + // Imprimir la clase encontrada: org.luckypray.dexkit.demo.PlayActivity + System.out.println(classData.getName()); + // Obtener la instancia correspondiente de la clase + Class> clazz = classData.getInstance(loadPackageParam.classLoader); + } +} +``` + +
+ +```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 + // La creación de un objeto DexKit es una operación que consume mucho tiempo; por favor no crees el objeto de forma repetida. + // Si necesitas usarlo de manera global, gestiona su ciclo de vida tú mismo + // y asegúrate de llamar al método .close() cuando ya no lo necesites, para evitar fugas de memoria. + // Aquí utilizamos `Closable.use` para cerrar automáticamente la instancia de DexKitBridge. + DexKitBridge.create(apkPath).use { bridge -> + findPlayActivity(bridge) + // Otros casos de uso + } + } + + private fun findPlayActivity(bridge: DexKitBridge) { + val classData = bridge.findClass { + // Incluir paquetes donde buscar + searchPackages("org.luckypray.dexkit.demo") + // Excluir paquetes no relevantes + excludePackages("org.luckypray.dexkit.demo.annotations") + // ClassMatcher: Matcher para clases + matcher { + // FieldsMatcher: Matcher para los campos de una clase + fields { + // FieldMatcher: Agregar un matcher para un campo + add { + // Especificar los modificadores del campo + modifiers = Modifier.PRIVATE or Modifier.STATIC or Modifier.FINAL + // Especificar el tipo del campo + type = "java.lang.String" + // Especificar el nombre del campo + name = "TAG" + } + // Agregar un matcher para campos del tipo especificado + addForType("android.widget.TextView") + addForType("android.os.Handler") + // Especificar la cantidad de campos en la clase + count = 3 + } + // MethodsMatcher: Matcher para métodos de una clase + methods { + // Agregar un matcher para el método + add { + // Especificar los modificadores del método + modifiers = Modifier.PROTECTED + // Especificar el nombre del método + name = "onCreate" + // Especificar el tipo de retorno del método + returnType = "void" + // Especificar los tipos de parámetros del método; si son inciertos, usa null; + // en ese caso, se inferirá implícitamente la cantidad de parámetros + paramTypes("android.os.Bundle") + // Especificar los strings utilizados en el método + usingStrings("onCreate") + } + add { + paramTypes("android.view.View") + // Especificar los números utilizados en el método; los tipos pueden ser Byte, Short, Int, Long, Float o Double + usingNumbers(0.01, -1, 0.987, 0, 114514) + } + add { + paramTypes("boolean") + // Especificar que el método invoque una lista de métodos + invokeMethods { + add { + modifiers = Modifier.PUBLIC or Modifier.STATIC + returnType = "int" + // Método invocado que utiliza los siguientes strings + usingStrings(listOf("getRandomDice: "), StringMatchType.Equals) + } + // Solo necesita contener la llamada al método anterior + matchType = MatchType.Contains + } + } + // Especificar la cantidad de métodos en la clase; mínimo 1 y máximo 10 + count(1..10) + } + // AnnotationsMatcher: Matcher para anotaciones en una clase + annotations { + // Agregar un matcher para la anotación + add { + // Especificar el tipo de la anotación + type = "org.luckypray.dexkit.demo.annotations.Router" + // La anotación debe contener el elemento especificado + addElement { + // Especificar el nombre del elemento + name = "path" + // Especificar el valor del elemento + stringValue("/play") + } + } + } + // Strings utilizados por todos los métodos de la clase + usingStrings("PlayActivity", "onClick", "onCreate") + } + }.singleOrNull() ?: error("El resultado devuelto no es único") + // Imprimir la clase encontrada: org.luckypray.dexkit.demo.PlayActivity + println(classData.name) + // Obtener la instancia correspondiente de la clase + val clazz = classData.getInstance(loadPackageParam.classLoader) + } +} +``` + +
+ 🇬🇧 English | 🇪🇸 Español | 🇨🇳 简体中文 +
-一个使用 C++ 实现的 dex 高性能运行时解析库,用于查找被混淆的类、方法或者属性。
--- @@ -31,7 +32,8 @@ - [x] 批量查找使用字符串的类 - [x] 批量查找使用字符串的方法 -> 备注:对于字符串搜索场景进行了优化,可以大幅度提升搜索速度,增加查询分组不会导致耗时成倍增长 +> [!NOTE] +> 对于字符串搜索场景进行了优化,可以大幅度提升搜索速度,增加查询分组不会导致耗时成倍增长 ### 使用文档 @@ -51,7 +53,7 @@ dependencies { } ``` -> **Note** +> [!IMPORTANT] > 从 **DexKit 2.0** 开始,新的 ArtifactId 已从 `DexKit` 更改为 `dexkit`。 DexKit 当前版本: [](https://central.sonatype.com/search?q=dexkit&namespace=org.luckypray) @@ -130,6 +132,7 @@ public class PlayActivity extends AppCompatActivity { 此时我们想得到这个类可以使用如下代码: +> [!NOTE] > 这仅仅是个样例,实际使用中并不需要这么多条件进行匹配,按需选用即可,避免条件过多带来的匹配复杂度增长