skylot / jadx

Dex to Java decompiler
Apache License 2.0
40.86k stars 4.8k forks source link

Custom Batch Renaming for Class Names, Fields, and Methods #2126

Closed Lohita9 closed 3 months ago

Lohita9 commented 5 months ago

Describe your idea

I propose adding a feature to JADX that enables users to perform batch renaming of class names, fields, and methods. This feature would allow users to change class names, fields, and method names to custom names of their choice.

For instance, some applications utilize obfuscation techniques like using 'O' and '0' in class, field, and method names, such as "O0000OO000OO0O00OO0O" for a class name, "O00O0000000000000000" for a field name, and "O0000000000000000000" for a method name. Screenshot_20240317-193714.png

With this feature, users could rename class names to consist of 5 or 4 different English letters like "Ahhsb.java" or "Akjs.java," rename field names to consist of 6 English letters like "Ishkkw," and rename method names to consist of 6 English letters like "Ikbajj."

skylot commented 5 months ago

@ArjunaKumarMohanta to support custom renaming scenarios, jadx now support scripting. Please try latest unstable build with deobf.jadx.kts script, it allows change regexp from preferences. For details of using scripts, check this readme. If you have any questions, feel free to ask :slightly_smiling_face:

Lohita9 commented 5 months ago

@skylot Firstly, I want to express my gratitude for your reply. Could you please provide the CLI commands necessary to accomplish this task? Additionally, could you briefly explain the deobf.jadx.kts code?

skylot commented 5 months ago

Could you please provide the CLI commands

jadx app.apk deobf.jadx.kts -Pjadx-script.deobf.regex="[Oo0]+" --log-level info

Here regex option is optional and can be changed directly in script. Log level adjusted to check script messages.

could you briefly explain the deobf.jadx.kts code?

/**
 * Custom regexp deobfuscator
 */

// get jadx decompiler instance
val jadx = getJadxInstance()

// disable build in deobfuscation and all renames (optional)
jadx.args.isDeobfuscationOn = false
jadx.args.renameFlags = emptySet()

// register custom option to simplify regexp value change, usage:
// - in cli add '-Pjadx-script.deobf.regex="[Oo0]+"' argument
// - in gui go to Preferences -> Plugins -> Scripts -> deobf
val regexOpt = jadx.options.registerString(
    name = "regex",
    desc = "Apply rename for names matches regex",
    defaultValue = "[Oo0]+",
)

// get option value and build regex instance
val regex = regexOpt.value.toRegex()
var n = 0

// check names of all nodes (classes, fields, methods)
// if name match regex rename node to 'NodeTypeN', for class it will become 'class2'
jadx.rename.all { name, node ->
    when {
        name matches regex -> {
            val newName = "${node.typeName()}${n++}"
            log.info { "renaming ${node.typeName()} '$node' to '$newName'" }
            newName
        }

        else -> null
    }
}

jadx.afterLoad {
    // this will be printed after all renames done
    log.info { "Renames count: $n" }
}

Actually, custom option register here is mostly for example, regex value can be set in script directly:

val regex = "[Oo0]+".toRegex()

Hope, this will be helpful :smile:

Lohita9 commented 5 months ago

Thanks

Lohita9 commented 5 months ago

@skylot Add a script to deobfuscate *.xml files comprises names with characters such as 0, o, and O.

skylot commented 5 months ago

@ArjunaKumarMohanta I commit example script for resources rename here, but I also change core classes, so you need to download latest unstable build once again.

Lohita9 commented 5 months ago

Thanks

Lohita9 commented 5 months ago

@skylot Combine the deobf.jadx.kts and deobf-resources.jadx.kts code into a separate file named deobf-OZero.jadx.kts . This file should facilitate the renaming of classes, fields, methods, and *.XML files containing 0oO obfuscation for easier deobfuscation.

skylot commented 5 months ago

@ArjunaKumarMohanta It feels like I am ChatGPT :rofl: Anyway, here combined script deobf-OZero.jadx.kts:

import jadx.api.plugins.options.OptionFlag.PER_PROJECT

/**
 * Custom regexp deobfuscator
 */

val jadx = getJadxInstance()
jadx.args.isDeobfuscationOn = false
jadx.args.renameFlags = emptySet()

val regexOpt = jadx.options.registerString(
        name = "regex",
        desc = "Apply rename for nodes with names matches regex",
        defaultValue = "[Oo0]+",
).flags(PER_PROJECT)

val resRegexOpt = jadx.options.registerString(
        name = "resRegex",
        desc = "Rename resource files with names matches regex",
        defaultValue = """[Oo0]+\.xml""",
).flags(PER_PROJECT)

var n = 0

val regex = regexOpt.value.toRegex()
jadx.rename.all { name, node ->
        when {
                name matches regex -> {
                        val newName = "${node.typeName()}${n++}"
                        log.info { "renaming ${node.typeName()}: '$node' to '$newName'" }
                        newName
                }

                else -> null
        }
}

val resRegex = resRegexOpt.value.toRegex()
jadx.stages.prepare {
        for (resFile in jadx.internalDecompiler.resources) {
                val fullName = resFile.originalName
                val name = fullName.substringAfterLast('/')
                if (name matches resRegex) {
                        val path = fullName.substringBeforeLast('/') // TODO: path also may be obfuscated
                        val ext = name.substringAfterLast('.')
                        val newName = "$path/res-${n++}.$ext"
                        log.info { "renaming resource: '$fullName' to '$newName'" }
                        resFile.deobfName = newName
                }
        }
}

jadx.afterLoad {
        log.info { "Renames count: $n" }
}
Lohita9 commented 5 months ago

@ArjunaKumarMohanta It feels like I am ChatGPT :rofl: Anyway, here combined script deobf-OZero.jadx.kts:

import jadx.api.plugins.options.OptionFlag.PER_PROJECT

/**
 * Custom regexp deobfuscator
 */

val jadx = getJadxInstance()
jadx.args.isDeobfuscationOn = false
jadx.args.renameFlags = emptySet()

val regexOpt = jadx.options.registerString(
        name = "regex",
        desc = "Apply rename for nodes with names matches regex",
        defaultValue = "[Oo0]+",
).flags(PER_PROJECT)

val resRegexOpt = jadx.options.registerString(
        name = "resRegex",
        desc = "Rename resource files with names matches regex",
        defaultValue = """[Oo0]+\.xml""",
).flags(PER_PROJECT)

var n = 0

val regex = regexOpt.value.toRegex()
jadx.rename.all { name, node ->
        when {
                name matches regex -> {
                        val newName = "${node.typeName()}${n++}"
                        log.info { "renaming ${node.typeName()}: '$node' to '$newName'" }
                        newName
                }

                else -> null
        }
}

val resRegex = resRegexOpt.value.toRegex()
jadx.stages.prepare {
        for (resFile in jadx.internalDecompiler.resources) {
                val fullName = resFile.originalName
                val name = fullName.substringAfterLast('/')
                if (name matches resRegex) {
                        val path = fullName.substringBeforeLast('/') // TODO: path also may be obfuscated
                        val ext = name.substringAfterLast('.')
                        val newName = "$path/res-${n++}.$ext"
                        log.info { "renaming resource: '$fullName' to '$newName'" }
                        resFile.deobfName = newName
                }
        }
}

jadx.afterLoad {
        log.info { "Renames count: $n" }
}

Thanks

Lohita9 commented 5 months ago

Screenshot_20240322-143102.png

@skylot Fix the jadx script to rename R.layout. references, which do not exist. Replace R.layout. with the appropriate references from deobf-resources.jadx.kts.