- JDK environment at least version 1.8 (Recommend JDK version 17)
- python3
- use
jadx/jadx-gui
to load the target file, such as.apk
or.dex
- use
frida
orunidbg
to run the decryption server - load the jadx-script and decrypt
Attention!!
Currently, the jadx
script has dependency bugs. When developing scripts, you can develop them as usual, and then add file dependencies to depend_config.json
. To download the corresponding dependencies, you need to run envsetup.py
. The script will check the files for dependencies listed in config, and replace them with the absolute path of the library if there is a match.
/**
* Replace method call with calculated result.
* Useful for custom string deobfuscation.
*
* Example for sample from issue https://github.com/skylot/jadx/issues/1251
*/
import jadx.core.dex.instructions.ConstStringNode
import jadx.core.dex.instructions.InvokeNode
import jadx.core.dex.instructions.args.InsnArg
import jadx.core.dex.instructions.args.InsnWrapArg
import jadx.core.dex.instructions.args.RegisterArg
val jadx = getJadxInstance()
val mthSignature = "com.xshield.aa.iIiIiiiiII(Ljava/lang/String;)Ljava/lang/String;"
jadx.replace.insns { mth, insn ->
if (insn is InvokeNode && insn.callMth.rawFullId == mthSignature) {
val str = getConstStr(insn.getArg(0))
if (str != null) {
val resultStr = decode(str)
log.info { "Decode '$str' to '$resultStr' in $mth" }
return@insns ConstStringNode(resultStr)
}
}
null
}
fun getConstStr(arg: InsnArg): String? {
val insn = when (arg) {
is InsnWrapArg -> arg.wrapInsn
is RegisterArg -> arg.assignInsn
else -> null
}
if (insn is ConstStringNode) {
return insn.string
}
return null
}
/**
* Decompiled method, automatically converted to Kotlin by IntelliJ Idea
*/
fun decode(str: String): String {
val length = str.length
val cArr = CharArray(length)
var i = length - 1
while (i >= 0) {
val i2 = i - 1
cArr[i] = (str[i].code xor 'z'.code).toChar()
if (i2 < 0) {
break
}
i = i2 - 1
cArr[i2] = (str[i2].code xor '\u000c'.code).toChar()
}
return String(cArr)
}
/**
* Replace method call with requested result.
* Useful for custom string deobfuscation.
*
*/
// That is the path relative to the jadx/bin execution directory, or it can be changed to an absolute path.
@file:DependsOn("com.squareup.okhttp3:okhttp:4.11.0")
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import jadx.core.dex.instructions.ConstStringNode
import jadx.core.dex.instructions.InvokeNode
import jadx.core.dex.instructions.args.InsnArg
import jadx.core.dex.instructions.args.InsnWrapArg
import jadx.core.dex.instructions.args.RegisterArg
val jadx = getJadxInstance()
val mthSignature = "kotlinx.android.extensionss.qz.b(Ljava/lang/String;)Ljava/lang/String;"
jadx.replace.insns { mth, insn ->
if (insn is InvokeNode && insn.callMth.rawFullId == mthSignature) {
val str = getConstStr(insn.getArg(0))
if (str != null) {
val resultStr = decrypt(mthSignature, str)
log.info { "Decrypt '$str' to '$resultStr' in $mth" }
return@insns ConstStringNode(resultStr)
}
}
null
}
fun getConstStr(arg: InsnArg): String? {
val insn = when (arg) {
is InsnWrapArg -> arg.wrapInsn
is RegisterArg -> arg.assignInsn
else -> null
}
if (insn is ConstStringNode) {
return insn.string
}
return null
}
fun decrypt(mthSignature: String, param: String): String?{
val client = OkHttpClient()
val json = """
{
"method": "${mthSignature}",
"param": "${param}"
}
""".trimIndent()
val requestBody = json.toRequestBody("application/json; charset=utf-8".toMediaType())
val request = Request.Builder()
.url("http://127.0.0.1:5000/decrypt")
.post(requestBody)
.build()
val response = client.newCall(request).execute()
return response.body?.string().toString()
}