REAndroid / ARSCLib

Android binary resources read/write library
Apache License 2.0
227 stars 48 forks source link

Error encountered while compiling APK #71

Open cgapk123 opened 3 months ago

cgapk123 commented 3 months ago

I tried to modify the APK, but most of them gave an error: Attempt to invoke virtual method 'boolean com.reandroid.arsc.chunk.xml.ResXmIStartNamespace.removelfNoReference()' on a nullobject reference

`package com.nekolaska.apk

import android.app.Activity import com.luajava.LuaFunction import com.reandroid.apkeditor.Util import com.reandroid.apkeditor.compile.Builder import com.reandroid.apkeditor.decompile.Decompiler import java.io.File import kotlin.concurrent.thread

class Editor(private val context: Activity) { val cacheDir: String = File(context.cacheDir, "editor").absolutePath

fun start(
    inApkPath: String,
    outApkPath: String,
    newPackage: String,
    callback: LuaFunction<*>,
    onError: LuaFunction<*>
) {
    if (!File(inApkPath).exists()) {
        onError.call("APK file does not exist")
        return
    }
    thread {
        decompile(inApkPath, newPackage, callback, onError)
        build(outApkPath, callback, onError)
        Util.deleteDir(File(cacheDir))
        context.runOnUiThread {
            callback.call("Task completed")
        }
    }
}

// Command line equivalent:
// java -jar C:\\Users\\NekoLaska\\Downloads\\APKEditor-1.3.9.jar d -i C:\\Users\\NekoLaska\\Downloads\\example.apk -o test_json
fun decompile(
    apkPath: String,
    newPackage: String,
    callback: LuaFunction<*>,
    onError: LuaFunction<*>
) = try {
    decode(apkPath, newPackage, callback)
} catch (e: Exception) {
    context.runOnUiThread {
        callback.call("An exception occurred: " + e.message)
    }
    try {
        decode(apkPath, newPackage, callback)
    } catch (e: Exception) {
        context.runOnUiThread {
            onError.call("An exception occurred: " + e.message)
        }
    }
}

private fun decode(
    apkPath: String,
    newPackage: String,
    callback: LuaFunction<*>,
) {
    val outFile = File(cacheDir)
    Util.deleteDir(outFile)
    outFile.mkdirs()
    Decompiler.execute(callback, "-i", apkPath, "-o", cacheDir)
    modifyJson(newPackage)
    modifyManifest(newPackage)
}

// Command line equivalent:
// java -jar C:\\Users\\NekoLaska\\Downloads\\APKEditor-1.3.9.jar b -i test_json -o test_edit.apk
fun build(apkPath: String, callback: LuaFunction<*>, onError: LuaFunction<*>) =
    try {
        File(apkPath).delete()
        Builder.execute(callback, "-i", cacheDir, "-o", apkPath)
    } catch (e: Exception) {
        context.runOnUiThread {
            callback.call("An exception occurred: " + e.message)
        }
        try {
            File(apkPath).delete()
            Builder.execute(callback, "-i", cacheDir, "-o", apkPath)
        } catch (e: Exception) {
            context.runOnUiThread {
                onError.call("An exception occurred: " + e.message)
            }
        }
    }

fun modifyJson(newPackage: String) {
    val jsonPath = findJson()
    var json = readString(jsonPath)
    json = replacePackageText2(json!!, newPackage)
    writeString(json, jsonPath)
}

fun modifyManifest(newPackage: String) {
    val manifestPath = findManifest()
    var manifest = readString(manifestPath)
    manifest = replacePackageText(manifest!!, newPackage)
    writeString(manifest, manifestPath)
}

fun writeString(str: String, path: String?) {
    try {
        if (path == null) {
            return
        }
        val file = File(path)
        file.writeText(str)
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

fun readString(path: String?): String? {
    try {
        if (path == null) {
            return null
        }
        val file = File(path)
        return file.readText()
    } catch (e: Exception) {
        e.printStackTrace()
    }
    return null
}

private fun replacePackageText(inputText: String, replacementText: String): String {
    val pattern = "package=\\"([^\\"]*)\\""
    return inputText.replace(pattern.toRegex(), "package=\\"$replacementText\\"")
}

private fun replacePackageText2(inputText: String, replacementText: String): String {
    val pattern = "\\"package_name\\": \\"([^\\"]*)\\""
    return inputText.replace(pattern.toRegex(), "\\"package_name\\": \\"$replacementText\\"")
}

private fun findFile(dirPath: String, fileName: String): String? {
    val dir = File(dirPath)  // Create a File object representing the specified directory path
    if (!dir.exists()) {  // If the directory doesn't exist, return null
        return null
    }

    val files = dir.listFiles() ?: return null  // Get list of all files in the directory, return null if empty
    for (file in files) {  // Iterate through the file list
        if (file.isDirectory) {
            val result = findFile(file.absolutePath, fileName)
            if (result != null) {  // If the file is found, return its path
                return result
            }
        } else {  // If it's a file
            if (file.name.equals(fileName)) {  // If the file name matches fileName, return its absolute path
                return file.absolutePath
            }
        }
    }
    return null  // If the file is not found, return null
}

fun findJson(): String? = findFile(cacheDir, "package.json")
fun findManifest(): String? = findFile(cacheDir, "AndroidManifest.xml")

}`

cgapk123 commented 3 months ago

Attempt to invoke virtual method 'boolean com.reandroid.arsc.chunk.xml.ResXmlStartNamespace.removeIfNoReference()' on a null object reference java.lang.NullPointerException: Attempt to invoke virtual method 'boolean com.reandroid.arsc.chunk.xml.ResXmlStartNamespace.removeIfNoReference()' on a null object reference at com.reandroid.arsc.chunk.xml.ResXmlElement.removeUnusedNamespaces(ResXmlElement.java:306) at com.reandroid.arsc.chunk.xml.ResXmlDocument.removeUnusedNamespaces(ResXmlDocument.java:150) at com.reandroid.arsc.chunk.xml.ResXmlDocument.refreshFull(ResXmlDocument.java:170) at com.reandroid.arsc.chunk.xml.ResXmlDocument.parse(ResXmlDocument.java:481) at com.reandroid.apk.xmlencoder.XMLEncodeSource.encode(XMLEncodeSource.java:77) at com.reandroid.apk.xmlencoder.XMLEncodeSource.getArray(XMLEncodeSource.java:65) at com.reandroid.apk.xmlencoder.XMLEncodeSource.write(XMLEncodeSource.java:41) at com.reandroid.archive.writer.OutputSource.writeBuffer(OutputSource.java:58) at com.reandroid.archive.writer.FileOutputSource.writeBuffer(FileOutputSource.java:46) at com.reandroid.archive.writer.FileOutputSource.makeBuffer(FileOutputSource.java:42) at com.reandroid.archive.writer.ApkFileWriter.writeBuffer(ApkFileWriter.java:81) at com.reandroid.archive.writer.ApkFileWriter.prepareOutputs(ApkFileWriter.java:43) at com.reandroid.archive.writer.ApkFileWriter.prepareOutputs(ApkFileWriter.java:27) at com.reandroid.archive.writer.ApkWriter.write(ApkWriter.java:51) at com.reandroid.apk.ApkModule.writeApk(ApkModule.java:481) at com.reandroid.apkeditor.compile.Builder.buildXml(Builder.java:127) at com.reandroid.apkeditor.compile.Builder.run(Builder.java:52) at com.reandroid.apkeditor.compile.Builder.execute(Builder.java:191) at com.nekolaska.apk.Editor.build-0E7RQCE(Editor.kt:79) at com.nekolaska.apk.Editor.modify$lambda$2(Editor.kt:29) at com.nekolaska.apk.Editor.$r8$lambda$JaMRkojg8dzJ9SyjMfogwP69MsM(Unknown Source:0) at com.nekolaska.apk.Editor$$ExternalSyntheticLambda0.invoke(Unknown Source:12) at kotlin.concurrent.ThreadsKt$thread$thread$1.run(Thread.kt:30)

REAndroid commented 3 months ago

Null at removeUnusedNamespaces could not happen, the base collection is BlockList so this doesn't store null items at all.

FYI: If your end goal is to edit resources/xml, you don't have to decompile at all. There are all the methods to edit binary files directly.

cgapk123 commented 3 months ago

Null at removeUnusedNamespaces could not happen, the base collection is BlockList so this doesn't store null items at all.

  • Kotlin stripped stack trace (most likely), change to java to confirm
  • Maybe you are running in multiple threads (ARSCLib is not thread safe)

FYI: If your end goal is to edit resources/xml, you don't have to decompile at all. There are all the methods to edit binary files directly.

Hi, I need to modify the resource/xml code, how can I edit the binary file directly? Is there any example usage?

cgapk123 commented 2 months ago

Null at removeUnusedNamespaces could not happen, the base collection is BlockList so this doesn't store null items at all.

  • Kotlin stripped stack trace (most likely), change to java to confirm
  • Maybe you are running in multiple threads (ARSCLib is not thread safe)

FYI: If your end goal is to edit resources/xml, you don't have to decompile at all. There are all the methods to edit binary files directly.

Sorry, I need to change the package name, but I don't know how to directly edit binary file resources and xml. Please provide the following sample code

REAndroid commented 2 months ago

@cgapk123 Confirm :https://github.com/REAndroid/ARSCLib/commit/c8002afd48cc613b3f74a8d3f5a92b4d6c945259 fixes this issue

cgapk123 commented 2 months ago

@cgapk123 Confirm :c8002af fixes this issue

Can you compile and update jar files?