reandroid / arsclib Goto Github PK
View Code? Open in Web Editor NEWAndroid binary resources read/write library
License: Apache License 2.0
Android binary resources read/write library
License: Apache License 2.0
Currently your decompiler added "s" for all resource types on when making resource dir
This is just what I've felt while using the library and only my expert opinion. I can also help you implementing this if you choose to do this.
The use of xmlpull
-style parser and serialiser is quite common in Android ecosystem. The framework itself uses this library for parsing both normal and binary XML (newly introduced in Android 12) files. So, it allows greater control such as easy to convert between one XML format to another by simultaneous use of parser and serialiser. This also make it easier to separate reading and writing the XML contents. So, I propose, possibly, creating a wrapper around the XMLDocument
class.
Hello good time
I want to change the package of an application for testing
and install the original and edited version side by side
For this, I changed the manifest and a series of elements such as the provider and receiver and meta data
and package element
Using your library, I also changed the resource table (resources.arsc) of the package with this code
PackageBlock packageBlock = tableBlock.getPackageArray().get(0);
packageBlock.setPackageName("com.example.app);
But again, when installing the edited version next to the original application
The following error is seen
App not installed as package conflicts with an existing package
Thank you for your guidance
Merge apk error use the latest commit d8ef9db
W java.lang.IllegalArgumentException: java.io.EOFException: Finished reading: 0
W at com.reandroid.apk.ApkModule.getTableBlock(ApkModule.java:523)
W at com.reandroid.apk.ApkModule.getTableBlock(ApkModule.java:530)
W at com.reandroid.apk.ApkModule.getVolatileTableStringPool(ApkModule.java:597)
W at com.reandroid.apk.ApkBundle.mergeStringPools(ApkBundle.java:81)
W at com.reandroid.apk.ApkBundle.mergeModules(ApkBundle.java:44)
W at com.xiaoyv.manager.tool.apk.editor.merge.ApkMerger.run(ApkMerger.kt:62)
W at com.xiaoyv.manager.tool.apk.editor.merge.ApkMerger$Companion.merge(ApkMerger.kt:245)
W at com.xiaoyv.manager.tool.apk.editor.merge.ApkMerger$Companion.merge(ApkMerger.kt:214)
W at com.xiaoyv.manager.ui.file.FileSelectorViewModel$mergeApksFile$2$1.invokeSuspend(FileSelectorViewModel.kt:300)
W at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
W at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
W at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
W at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
W at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
W at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
W at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
W at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
W Caused by: java.io.EOFException: Finished reading: 0
W at com.reandroid.arsc.io.BlockReader.readFully(BlockReader.java:204)
W at com.reandroid.arsc.io.BlockReader.readFully(BlockReader.java:187)
W at com.reandroid.arsc.item.BlockItem.onReadBytes(BlockItem.java:100)
W at com.reandroid.arsc.base.Block.readBytes(Block.java:48)
W at com.reandroid.arsc.base.BlockContainer.onReadBytes(BlockContainer.java:149)
W at com.reandroid.arsc.header.HeaderBlock.onReadBytes(HeaderBlock.java:151)
W at com.reandroid.arsc.base.Block.readBytes(Block.java:48)
W at com.reandroid.arsc.base.BlockContainer.onReadBytes(BlockContainer.java:149)
W at com.reandroid.arsc.chunk.Chunk.onReadBytes(Chunk.java:59)
W at com.reandroid.arsc.base.Block.readBytes(Block.java:48)
W at com.reandroid.arsc.chunk.TableBlock.onReadBytes(TableBlock.java:147)
W at com.reandroid.arsc.base.Block.readBytes(Block.java:48)
W at com.reandroid.arsc.chunk.TableBlock.readBytes(TableBlock.java:161)
W at com.reandroid.arsc.chunk.TableBlock.load(TableBlock.java:326)
W at com.reandroid.apk.ApkModule.loadTableBlock(ApkModule.java:615)
W at com.reandroid.apk.ApkModule.getTableBlock(ApkModule.java:517)
W ... 16 more
java.lang.NullPointerException: Cannot invoke "String.indexOf(int)" because "value" is null
at com.android.org.kxml2.io.KXmlSerializer.attribute(KXmlSerializer.java:432)
at com.reandroid.apk.xmldecoder.EntryWriterSerializer.attribute(EntryWriterSerializer.java:52)
at com.reandroid.apk.xmldecoder.EntryWriterSerializer.attribute(EntryWriterSerializer.java:23)
at com.reandroid.apk.xmldecoder.BagDecoderCommon.decode(BagDecoderCommon.java:52)
at com.reandroid.apk.xmldecoder.BagDecoderCommon.decode(BagDecoderCommon.java:24)
at com.reandroid.apk.xmldecoder.DecoderResTableEntryMap.decode(DecoderResTableEntryMap.java:39)
at com.reandroid.apk.xmldecoder.XMLEntryDecoder.decode(XMLEntryDecoder.java:61)
at com.reandroid.apk.xmldecoder.XMLEntryDecoder.decode(XMLEntryDecoder.java:87)
at com.reandroid.apk.xmldecoder.XMLEntryDecoderSerializer.decode(XMLEntryDecoderSerializer.java:56)
at com.reandroid.apk.xmldecoder.XMLEntryDecoderSerializer.decodeUniqueConfigs(XMLEntryDecoderSerializer.java:50)
at com.reandroid.apk.xmldecoder.XMLEntryDecoderSerializer.decode(XMLEntryDecoderSerializer.java:42)
at com.reandroid.apk.ApkModuleXmlDecoder.decodeValues(ApkModuleXmlDecoder.java:298)
at com.reandroid.apk.ApkModuleXmlDecoder.decodeValues(ApkModuleXmlDecoder.java:294)
at com.reandroid.apk.ApkModuleXmlDecoder.decodeValues(ApkModuleXmlDecoder.java:282)
at com.reandroid.apk.ApkModuleXmlDecoder.decodeResourceTable(ApkModuleXmlDecoder.java:63)
at com.reandroid.apk.ApkModuleDecoder.decode(ApkModuleDecoder.java:48)
There is an option to set indentation for child tags in XMLDocument
but no option to set indentation for new line attributes and they are at present calculated at XMLElement#appendAttributesIndentText(Writer)
. It might be better to provide an option to set indentations instead of calculating them. For example, instead of providing an argument boolean newLineAttributes
, you can provide int attributeIndentation
whose negative value (e.g. -1
) would imply no new line and any positive number would imply the amount of indentation.
Thanks.
Test the library using unit tests. This will help the maintainers as well as developers:
App: QQ version 8.9.50(9+res packages)
Link:
https://apkcombo.com/ru/qq/com.tencent.mobileqq/download/phone-8.9.50-apk
In the first versions compilation worked successfully.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0"
android:compileSdkVersion="28"
android:compileSdkVersionCodename="9"
package="com.web2apk"
platformBuildVersionCode="28"
platformBuildVersionName="9"
>
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="27"
>
</uses-sdk>
<uses-permission
android:name="android.permission.INTERNET"
>
</uses-permission>
<application
android:label="@7F040000"
android:icon="@7F030000"
android:allowBackup="true"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
>
<activity
android:name="com.web2apk.MainActivity"
android:screenOrientation="6"
>
<intent-filter
>
<action
android:name="android.intent.action.MAIN"
>
</action>
<category
android:name="android.intent.category.LAUNCHER"
>
</category>
</intent-filter>
</activity>
</application>
</manifest>
i want to set packagename = "com.web2apk.main"
AndroidManifestBlock manifestBlock = AndroidManifestBlock.load(new File("AndroidManifest.xml"));
String oldpack = manifestBlock.getPackageName();
System.out.println( manifestBlock.getPackageName());
manifestBlock.setPackageName("com.web2apk.main");
manifestBlock.refresh();
manifestBlock.writeBytes(new File("out.xml"));;
output use xml2axml
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0"
android:compileSdkVersion="28"
android:compileSdkVersionCodename="9"
package=""
platformBuildVersionCode="28"
platformBuildVersionName="9"
>
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="27"
>
</uses-sdk>
<uses-permission
android:name="android.permission.INTERNET"
>
</uses-permission>
<application
android:label="@7F040000"
android:icon="@7F030000"
android:debuggable="true"
android:allowBackup="true"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
>
<activity
android:name="com.web2apk.MainActivity"
android:screenOrientation="6"
>
<intent-filter
>
<action
android:name="android.intent.action.MAIN"
>
</action>
<category
android:name="android.intent.category.LAUNCHER"
>
</category>
</intent-filter>
</activity>
</application>
</manifest>
After the changes in the resources.arsc file (adding a new string or a new XML)
After compiling the Android application, the application encounters a force close error
the edited application loses its name and its icon after editing and compiling with the new resources.arsc file.
In the logs section, the error is as follows
java.lang.RuntimeException: Unable to get provider androidx.startup.InitializationProvider: android.content.res.Resources$NotFoundException: String resource ID #0x7f0f001b
at android.app.ActivityThread.installProvider(ActivityThread.java:5858)
Orginal Apk
https://dl.dropboxusercontent.com/s/y72kyqk8alakz0u/app-release_original.apk?dl=0
Modified Apk
https://dl.dropboxusercontent.com/s/3fyhod67lgvfv1v/app-release-modified.apk?dl=0
Hi, maybe migrate your dex lib to another project or another dependency, currently project only for apk resources.
gradle 7.0 or above, enable D8 proguard apk cannot be installed
what are the commands make the readme.me good
Hi, thank you for your great library and your good support.
I am using ARSCLib v1.2.0 to add an XML file, edit resources.arsc, edit manifest of multiple APKs. there is no problem in all APKs except one of them, when I repack this specific APK, even without any edit, just decompile and build, the app crash on runtime as follows:
E FATAL EXCEPTION: main
Process: a.a.myapplication, PID: 2683
java.lang.RuntimeException: Unable to instantiate application android.support.multidex.MultiDexApplication package a.a.myapplication: java.lang.ClassNotFoundException: Didn't find class "android.support.multidex.MultiDexApplication" on path: DexPathList[[zip file "/data/app/~~jiNlAldBI9zriYWq2RGs0w==/a.a.myapplication-t3lmIin-UCSOuRv7-T9tfQ==/base.apk"],nativeLibraryDirectories=[/data/app/~~jiNlAldBI9zriYWq2RGs0w==/a.a.myapplication-t3lmIin-UCSOuRv7-T9tfQ==/lib/arm64, /system/lib64, /system/system_ext/lib64]]
at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1573)
at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1502)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7553)
at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2400)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8757)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
Caused by: java.lang.ClassNotFoundException: Didn't find class "android.support.multidex.MultiDexApplication" on path: DexPathList[[zip file "/data/app/~~jiNlAldBI9zriYWq2RGs0w==/a.a.myapplication-t3lmIin-UCSOuRv7-T9tfQ==/base.apk"],nativeLibraryDirectories=[/data/app/~~jiNlAldBI9zriYWq2RGs0w==/a.a.myapplication-t3lmIin-UCSOuRv7-T9tfQ==/lib/arm64, /system/lib64, /system/system_ext/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:259)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
at android.app.AppComponentFactory.instantiateApplication(AppComponentFactory.java:76)
at androidx.core.app.CoreComponentFactory.instantiateApplication(CoreComponentFactory.java:52)
at android.app.Instrumentation.newApplication(Instrumentation.java:1232)
at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1565)
at android.app.LoadedApk.makeApplicationInner(LoadedApk.java:1502)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7553)
at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2400)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8757)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
There is no difference in dex files, and the class "android.support.multidex.MultiDexApplication" not exists in the original APK too, the resources.arsc binary files have some small differences, but I don't find any difference when opening them in android studio.
The class "android.support.multidex.MultiDexApplication" is in the manifest application tag:
<application
android:theme="@ref/0x7f1101d2"
android:label="@ref/0x7f10001c"
android:icon="@ref/0x7f0d0000"
android:name="android.support.multidex.MultiDexApplication"
android:debuggable="true"
android:allowBackup="true"
android:largeHeap="true"
android:supportsRtl="true"
android:fullBackupContent="@ref/0x7f130000"
android:usesCleartextTraffic="true"
android:roundIcon="@ref/0x7f0d0001"
android:appComponentFactory="androidx.core.app.CoreComponentFactory"
android:dataExtractionRules="@ref/0x7f130001"
android:requestLegacyExternalStorage="true">
In the merging process, StyleArray of BaseStringPool is populated from the base package only.
The other modules should participate.
Duplicate class org.xmlpull.v1.XmlPullParser found in modules ARSCLib-1.2.3 (io.github.reandroid:ARSCLib:1.2.3) and xpp3-1.1.4c (xpp3:xpp3:1.1.4c)
Please use dependencies or rename package name
Hi,
I've discovered this library while trying to find a solution for this issue: MuntashirAkon/AppManager#951
However, it appears that it is unable to parse the AndroidManifest.xml
mentioned in the issue just like any other libraries (apksig, jadx, apk-parser, etc.), and throws the following error:
java.io.IOException: Not ResXmlBlock: NULL(0x0000){Header=8, Chunk=15756}
at com.reandroid.lib.arsc.chunk.xml.ResXmlBlock.onReadBytes(ResXmlBlock.java:95)
at com.reandroid.lib.arsc.base.Block.readBytes(Block.java:38)
If it were strictly based on aapt2
source (which includes the same parser that is included in modern Android OS), it should've been able to parse the file.
Here's the file for reference: AndroidManifest.xml.pdf (remove .pdf
extension)
Thanks.
PS: This is how it was fixed in LSPatch: LSPosed/LSPatch@c41be02
It would be very easy to use if the library could be published to maven.
Thank you :)
Caused by: java.util.regex.PatternSyntaxException: Syntax error in regexp pattern near index 8:
^\s*(?<A>https?://[^:\s]+)(:(?<B>([^:/\s]+)))?\s*$
^
at java.util.regex.Pattern.compileImpl(Native Method)
at java.util.regex.Pattern.compile(Pattern.java:411)
at java.util.regex.Pattern.<init>(Pattern.java:394)
at java.util.regex.Pattern.compile(Pattern.java:381)
at com.reandroid.xml.SchemaAttr.<clinit>(SchemaAttr.java:123)
... 17 more
PS: This appears to be a Android/ICU4C limitation
When trying to decompile in xml mode (in json mode, everything is ok), the attached apk crashes with an error (stack trace from apkeditor)
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.indexOf(int)" because "value" is null
at com.android.org.kxml2.io.KXmlSerializer.attribute(KXmlSerializer.java:432)
at com.reandroid.arsc.coder.xml.XmlCoder$BagChild.decodeStyle(XmlCoder.java:439)
at com.reandroid.arsc.coder.xml.XmlCoder$BagChild.decode(XmlCoder.java:384)
at com.reandroid.arsc.coder.xml.XmlCoder$ValuesXml.decodeBag(XmlCoder.java:157)
at com.reandroid.arsc.coder.xml.XmlCoder$ValuesXml.decode(XmlCoder.java:146)
at com.reandroid.arsc.coder.xml.XmlCoder$ValuesXml.decode(XmlCoder.java:127)
at com.reandroid.arsc.coder.xml.XmlCoder$ValuesXml.decode(XmlCoder.java:118)
at com.reandroid.arsc.coder.xml.XmlCoder$ValuesXml.decodePackage(XmlCoder.java:108)
at com.reandroid.arsc.coder.xml.XmlCoder$ValuesXml.decodeTable(XmlCoder.java:90)
at com.reandroid.arsc.coder.xml.XmlCoder$ValuesXml.decodeTable(XmlCoder.java:83)
at com.reandroid.apk.ApkModuleXmlDecoder.decodeValues(ApkModuleXmlDecoder.java:273)
at com.reandroid.apk.ApkModuleXmlDecoder.decodeResourceTable(ApkModuleXmlDecoder.java:60)
at com.reandroid.apk.ApkModuleDecoder.decode(ApkModuleDecoder.java:48)
at com.reandroid.apkeditor.decompile.Decompiler.run(Decompiler.java:62)
at com.reandroid.apkeditor.decompile.Decompiler.execute(Decompiler.java:127)
at com.reandroid.apkeditor.Main.execute(Main.java:81)
at com.reandroid.apkeditor.Main.execute(Main.java:64)
at com.reandroid.apkeditor.Main.main(Main.java:36)
APK: https://mega.nz/file/Ax82ESYQ#yhKTPKpplRqhqQcmGPKmlKO2lPR4AY3R3L1fOlOIfqE
For example attrs.getAttributeIntValue("http://schemas.android.com/apk/res/android", "orientation", 1)
should normally return 1 if the attribute is not set. Currently it always returns 0 no matter what default value is given.
Other methods with defaultValue options are affected as well.
would it be possible to add (read-only) support for the binary version of .9.png?
for an interface, could probably use something as simple as a function that returns an RGB byte array for a .9.png file and width+height of the result, though would probably want some intermediary type that would reduce the amount of operations that need to be performed when re-scaling.
EDIT: got no use for this anymore, closing
I've used XML decompile type. Entry @attr/cornerSize
original value was 192 but after rebuild from xml
the value is 65472
ResXmlDocument xmlBlock = new ResXmlDocument();
try (BlockReader reader = new BlockReader(byteBuffer.array())) {
xmlBlock.readBytes(reader);
xmlBlock.setPackageBlock(getFrameworkPackageBlock());
return xmlBlock.decodeToXml();
}
For Google Play Services, it's throwing an IllegalArgumentException
.
java.lang.IllegalArgumentException: Namespace not found for prefix: ns2
at com.reandroid.xml.XMLElement.setName(XMLElement.java:397)
at com.reandroid.xml.XMLElement.setName(XMLElement.java:377)
at com.reandroid.xml.XMLElement.<init>(XMLElement.java:42)
at com.reandroid.arsc.chunk.xml.ResXmlElement.decodeToXml(ResXmlElement.java:1430)
at com.reandroid.arsc.chunk.xml.ResXmlElement.decodeToXml(ResXmlElement.java:1452)
at com.reandroid.arsc.chunk.xml.ResXmlDocument.decodeToXml(ResXmlDocument.java:488)
at io.github.muntashirakon.AppManager.apk.parser.AndroidBinXmlDecoder.decodeToXml(AndroidBinXmlDecoder.java:5)
Don't use targetSdkVersion for select version framework. Need use compileSdkVersion
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.reandroid.arsc.item.StyleItem.addStylePiece(int, int, int)" because "styleItem" is null
at com.reandroid.apk.xmlencoder.ValuesStringPoolBuilder.buildWithStyles(ValuesStringPoolBuilder.java:72)
at com.reandroid.apk.xmlencoder.ValuesStringPoolBuilder.addTo(ValuesStringPoolBuilder.java:40)
at com.reandroid.apk.xmlencoder.RESEncoder.preloadStringPool(RESEncoder.java:152)
at com.reandroid.apk.xmlencoder.RESEncoder.scanResourceFiles(RESEncoder.java:69)
at com.reandroid.apk.xmlencoder.RESEncoder.scanDirectory(RESEncoder.java:61)
at com.reandroid.apk.ApkModuleXmlEncoder.scanDirectory(ApkModuleXmlEncoder.java:38)
at com.reandroid.apkeditor.compile.Builder.buildXml(Builder.java:74)
at com.reandroid.apkeditor.compile.Builder.run(Builder.java:42)
at com.reandroid.apkeditor.compile.Builder.execute(Builder.java:144)
at com.reandroid.apkeditor.Main.execute(Main.java:60)
at com.reandroid.apkeditor.Main.main(Main.java:38)
This issue can be reproduced by using APKEditor:
d -t xml -i YouTube_18.03.36.apk -o YouTube
b -i YouTube -o Build
修改了 manifest.xml 和 resource.asrc 之后打包。
Failed to parse /data/app/vmdl479469645.tmp/0_jdh________________
java.lang.ArrayIndexOutOfBoundsException: length=31; index=31
at android.content.res.StringBlock.getSequence(StringBlock.java:107)
at android.content.res.XmlBlock$Parser.getAttributeValue(XmlBlock.java:233)
at android.content.res.XmlBlock$Parser.getAttributeValue(XmlBlock.java:262)
at android.content.pm.parsing.ApkLiteParseUtils.parsePackageSplitNames(ApkLiteParseUtils.java:668)
at android.content.pm.parsing.ApkLiteParseUtils.parseApkLite(ApkLiteParseUtils.java:405)
at android.content.pm.parsing.ApkLiteParseUtils.parseApkLiteInner(ApkLiteParseUtils.java:385)
at android.content.pm.parsing.ApkLiteParseUtils.parseApkLite(ApkLiteParseUtils.java:329)
at com.android.server.pm.PackageInstallerSession.validateApkInstallLocked(PackageInstallerSession.java:2730)
at com.android.server.pm.PackageInstallerSession.streamValidateAndCommit(PackageInstallerSession.java:1848)
at com.android.server.pm.PackageInstallerSession.handleStreamValidateAndCommit(PackageInstallerSession.java:1696)
at com.android.server.pm.PackageInstallerSession.-$$Nest$mhandleStreamValidateAndCommit(Unknown Source:0)
at com.android.server.pm.PackageInstallerSession$4.handleMessage(PackageInstallerSession.java:727)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.os.HandlerThread.run(HandlerThread.java:67)
Hi, thank you for your great library.
I want to add a new XML file to APK without decompiling the whole APK XML files.
just copy the XML to APK (we have default and v22 version for this resource), index the XML files in resources.arsc, and also modify AndroidManifest and reference this XML in meta-data tag.
I tried doing this as follows:
ApkModule module=ApkModule.loadApkFile(inputFile);
TableBlock tableBlock = module.getTableBlock();
PackageBlock packageBlock = tableBlock.getPackageBlockById(0x7f);
Entry newEntry = packageBlock.getOrCreate("", "xml", "config");
newEntry.setValueAsString("res/xml/"+ newEntry.getName() +".xml");
Entry newV22Entry = packageBlock.getOrCreate("-v22", "xml", "config");
newV22Entry.setValueAsString("res/xml-v22/"+ newV22Entry.getName() +".xml");
EntryGroup newEntryGroup = new EntryGroup(newEntry.getResourceId());
newEntryGroup.add(newEntry);
newEntryGroup.add(newV22Entry);
packageBlock.getEntriesGroupMap().put(newEntry.getResourceId(), newEntryGroup);
AndroidManifestBlock manifest = module.getAndroidManifestBlock();
ResXmlElement applicationTag = manifest.getApplicationElement();
List<ResXmlElement> appElements = applicationTag.listElements();
for (ResXmlElement element: appElements) {
if (element.getAttributeAt(0).getValueString().equals("MyService")) {
ResXmlElement metaData = element.getElementByTagName("meta-data");
for (ResXmlAttribute att: metaData.listAttributes()) {
if (att.getName().equals(AndroidManifestBlock.NAME_resource)) {
att.setData(newEntry.getResourceId());
}
}
}
}
Util.addApkEditorInfo(module, getClass().getSimpleName());
String message = module.refreshTable();
if(message != null){
log(message);
}
message = module.refreshManifest();
if(message != null){
log(message);
}
log("Writing apk ...");
module.writeApk(outputFile, this);
log("Saved to: "+outputFile);
log("Done");
Because there is no document, I wrote this code by trial and error and I know there are a lot of problems there. and finally, the generated APK looks ok but my config for MyService is not applied. probably there are problems in encoding and obfuscation. also, I don't know what to do if the APK is obfuscated?!
Also, I tried to solve my problem using this issue and it works. but I want to prevent decoding the whole of APK resources.
Originally posted by sourvodka2023 January 8, 2023
Is it possible the original signature scheme (APK Sig Block 42PK) hidden in the APK can be extracted for later use and put it back in APK again? just to make a modded APK with original signature (Modders usually call it an unsigned APK), in order to be able to login with Google for rooted devices with CorePatch module?
This is how it looks like in hex editor, it's at near the bottom, but I don't understand much how it works in hex
For example with preserving original signature:
The problem with unsigned APK:
When making changes in an APK using ordinary zip utility like Winrar, it removes the (APK Sig Block 42PK) completely, breaking Google login on Android 9 and above because Android 9 expect signature scheme v2, v3, v4 (APK Sig Block 42PK). Android 8 and below ignores it and read signature scheme v1 (RSA, SF and MF files in META-INF). Also signature scheme v1 is getting deprecated, some APK doesn't come with v1 anymore
To install unsigned APK:
Install Xposed and CorePatch https://github.com/LSPosed/CorePatch
When the resource.arsc file of base.apk is very large, such as 10-20MB.
Merge apks will take a significant amount of time, especially when running on Android devices.
I found that the following area can be optimized. If the baseModule exists, it can be directly used as the root node of Merge.
Instead of creating a new module and merging the baseModule.
public class ApkBundle {
...
public ApkModule mergeModules() throws IOException {
List<ApkModule> moduleList=getApkModuleList();
if(moduleList.size()==0){
throw new FileNotFoundException("Nothing to merge, empty modules");
}
// This place can be optimized, Use the baseMode when it present in aps dir as the mergeRoot module instead of create a new module.
ApkModule result=new ApkModule(generateMergedModuleName(), new APKArchive());
result.setAPKLogger(apkLogger);
mergeStringPools(result);
ApkModule base=getBaseModule();
if(base==null){
base=getLargestTableModule();
}
result.merge(base);
for(ApkModule module:moduleList){
if(module==base){
continue;
}
result.merge(module);
}
if(result.hasTableBlock()){
TableBlock tableBlock=result.getTableBlock();
tableBlock.sortPackages();
tableBlock.refresh();
}
result.getApkArchive().sortApkFiles();
return result;
}
...
}
I have made some rough modifications, there may be issues
public ApkModule mergeModules() throws IOException {
List<ApkModule> moduleList = getApkModuleList();
if (moduleList.size() == 0) {
throw new FileNotFoundException("Nothing to merge, empty modules");
}
ApkModule result = getBaseModule();
if (result == null) {
result = getLargestTableModule();
}
result.setModuleName(generateMergedModuleName());
result.setAPKLogger(apkLogger);
mergeStringPools(result);
for (ApkModule module : moduleList) {
if (module == result) {
continue;
}
result.merge(module);
}
if (result.hasTableBlock()) {
TableBlock tableBlock = result.getTableBlock();
tableBlock.sortPackages();
tableBlock.refresh();
}
result.getApkArchive().sortApkFiles();
return result;
}
1 rename androidmanifest all provider authority
2 AndroidManifestBlock rename setPackageName
3 zipalign apk and resign apk then run crash log:
ms.supervision, role: android.app.role.SYSTEM_SUPERVISION
07-02 15:07:42.917 31505 31505 E com.test.dol: Not starting debugger since process cannot load the jdwp agent.
07-02 15:07:43.184 31505 1706 E OpenGLRenderer: Unable to match the desired swap behavior.
07-02 15:07:43.192 31505 1926 E ion : ioctl c0044901 failed with code -1: Invalid argument
07-02 15:07:43.662 31505 31505 E AndroidRuntime: FATAL EXCEPTION: main
07-02 15:07:43.662 31505 31505 E AndroidRuntime: Process: com.test.dol, PID: 31505
07-02 15:07:43.662 31505 31505 E AndroidRuntime: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
07-02 15:07:43.662 31505 31505 E AndroidRuntime: at android.preference.PreferenceManager.getDefaultSharedPreferencesName(PreferenceManager.java:555)
07-02 15:07:43.662 31505 31505 E AndroidRuntime: at android.preference.PreferenceManager.getDefaultSharedPreferences(PreferenceManager.java:545)
07-02 15:07:43.662 31505 31505 E AndroidRuntime: at com.common.lib.utils.PrefUtil.getString(PrefUtil.java:21)
07-02 15:07:43.662 31505 31505 E AndroidRuntime: at com.common.lib.manager.DataManager.getToken(DataManager.kt:90)
07-02 15:07:43.662 31505 31505 E AndroidRuntime: at com.xingchat.android.activity.SplashActivity.login(SplashActivity.java:155)
07-02 15:07:43.662 31505 31505 E AndroidRuntime: at com.xingchat.android.activity.SplashActivity.lambda$onCreated$0$com-xingchat-android-activity-SplashActivity(SplashActivity.java:60)
07-02 15:07:43.662 31505 31505 E AndroidRuntime: at com.xingchat.android.activity.SplashActivity$$ExternalSyntheticLambda1.run(Unknown Source:2)
07-02 15:07:43.662 31505 31505 E AndroidRuntime: at android.os.Handler.handleCallback(Handler.java:1013)
07-02 15:07:43.662 31505 31505 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:101)
07-02 15:07:43.662 31505 31505 E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:226)
07-02 15:07:43.662 31505 31505 E AndroidRuntime: at android.os.Looper.loop(Looper.java:328)
07-02 15:07:43.662 31505 31505 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:9224)
07-02 15:07:43.662 31505 31505 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
07-02 15:07:43.662 31505 31505 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:586)
07-02 15:07:43.662 31505 31505 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
07-02 15:07:43.732 4380 5386 E OpenGLRenderer: Unable to match the desired swap behavior.
07-02 15:07:55.364 4192 19614 E Auth : [TokenResponseCallback] TokenResponse failed with exception [CONTEXT service_id=343 ]
i add new function renamePkgForProvider in AndroidManifestBlock
code:
@test
public void testRenamePkg() throws IOException {
ApkModule module = null;
try {
module = ApkModule.loadApkFile(new File("./a.apk"));
AndroidManifestBlock manifestBlock = module.getAndroidManifestBlock();
manifestBlock.renamePkgForProvider(manifestBlock.getPackageName(),"com.test.dol");
manifestBlock.setPackageName("com.test.dol");
module.writeApk(new File("./a_rename.apk"));
} catch (Exception e) {
e.printStackTrace();
} finally {
if (module != null) module.destroy();
}
}
@Override
public void renamePkgForProvider(String originalPkg,String newpkg) {
System.out.println("originalPkg=" + originalPkg + " newpkg=" + newpkg);
List<ResXmlElement> providerElementList = listProviders();
for(ResXmlElement providerXmlElement:providerElementList) {
ResXmlAttribute attribute = providerXmlElement.getOrCreateAttribute(NAME_authorities,ID_authorities);
attribute.setValueAsString(attribute.getValueAsString().replace(originalPkg,newpkg));
}
}
how can we debug by exporting an apk file?
Original attr type is integer-array but after rebuilding string-array
Files exist in #11
Example input
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="50001204"
android:versionName="5.0.1.204"
android:compileSdkVersion="28"
android:compileSdkVersionCodename="9"
package="com.android.activity.test"
platformBuildVersionCode="50001204"
platformBuildVersionName="5.0.1.204">
<application>
<activity
android:label=" "
android:name="com.android.activity.EmptyActivity"
android:exported="false"
android:screenOrientation="portrait"
android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenLayout|screenSize|smallestScreenSize">
<meta-data
android:name="hwc-theme"
android:value="androidhwext:style/Theme.Emui">
</meta-data>
</activity>
</application>
</manifest>
Example output (after converting the above into binary XML and then converted back)
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="50001204"
android:versionName="5.0.1.204"
android:compileSdkVersion="28"
android:compileSdkVersionCodename="9"
package="com.android.activity.test"
platformBuildVersionCode="50001204"
platformBuildVersionName="5.0.1.204">
<application>
<activity
android:label=""
android:name="com.android.activity.EmptyActivity"
android:exported="false"
android:screenOrientation="portrait"
android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenLayout|screenSize|smallestScreenSize">
<meta-data
android:name="hwc-theme"
android:value="androidhwext:style/Theme.Emui">
</meta-data>
</activity>
</application>
</manifest>
What to look for
Look at the line 13 of each XML file above. The first one had an space but the second one didn't.
Code for converting binary XML
private static byte[] encode(@NonNull XMLSource xmlSource) throws IOException {
EncodeMaterials encodeMaterials = new EncodeMaterials();
FrameworkApk frameworkApk = AndroidFrameworks.getLatest();
encodeMaterials.addFramework(frameworkApk);
Collection<PackageBlock> packageBlocks = frameworkApk.getTableBlock().listPackages();
if (packageBlocks.size() < 1) {
throw new IOException("Framework apk does not contain any packages!");
}
PackageBlock packageBlock = packageBlocks.iterator().next();
encodeMaterials.setCurrentPackage(packageBlock);
XMLEncodeSource xmlEncodeSource = new XMLEncodeSource(encodeMaterials, xmlSource);
return xmlEncodeSource.getResXmlBlock().getBytes();
}
When decompiling the attached apk, ARSCLib crashes with NPE :(
From what I was able to understand - ARSCLib uses BagDecoderCommon instead of BarDecoderArray when processing arrays.xml
Stacktrace:
java.lang.NullPointerException: Cannot invoke "String.indexOf(int)" because "value" is null
at com.android.org.kxml2.io.KXmlSerializer.attribute(KXmlSerializer.java:432)
at com.reandroid.apk.xmldecoder.EntryWriterSerializer.attribute(EntryWriterSerializer.java:52)
at com.reandroid.apk.xmldecoder.EntryWriterSerializer.attribute(EntryWriterSerializer.java:23)
at com.reandroid.apk.xmldecoder.BagDecoderCommon.decode(BagDecoderCommon.java:52)
at com.reandroid.apk.xmldecoder.BagDecoderCommon.decode(BagDecoderCommon.java:24)
at com.reandroid.apk.xmldecoder.DecoderResTableEntryMap.decode(DecoderResTableEntryMap.java:39)
at com.reandroid.apk.xmldecoder.XMLEntryDecoder.decode(XMLEntryDecoder.java:61)
at com.reandroid.apk.xmldecoder.XMLEntryDecoder.decode(XMLEntryDecoder.java:87)
at com.reandroid.apk.xmldecoder.XMLEntryDecoderSerializer.decode(XMLEntryDecoderSerializer.java:56)
at com.reandroid.apk.xmldecoder.XMLEntryDecoderSerializer.decodeUniqueConfigs(XMLEntryDecoderSerializer.java:50)
at com.reandroid.apk.xmldecoder.XMLEntryDecoderSerializer.decode(XMLEntryDecoderSerializer.java:42)
at com.reandroid.apk.ApkModuleXmlDecoder.decodeValues(ApkModuleXmlDecoder.java:298)
at com.reandroid.apk.ApkModuleXmlDecoder.decodeValues(ApkModuleXmlDecoder.java:294)
at com.reandroid.apk.ApkModuleXmlDecoder.decodeValues(ApkModuleXmlDecoder.java:282)
at com.reandroid.apk.ApkModuleXmlDecoder.decodeResourceTable(ApkModuleXmlDecoder.java:63)
at com.reandroid.apk.ApkModuleDecoder.decode(ApkModuleDecoder.java:48)
Version: 1.2.0
APK: https://drive.google.com/file/d/1Osi6k_DRKK7D8a_X9v5ZzHRespn2UDPt/view?usp=sharing
Title says it all. In case you're wondering why it's necessary, we don't know if an APK is aligned or not in first place. So, we need to do that for both aligned and unaligned APKs.
This apk file contains 1,441,646 resource entries, and fails to load to ApkModule
class with exception OutOfMemory
Apk File:
huge_resource.zip
NB: this apk file is generated by ARSCLib for test purpose
com.reandroid.arsc.decoder.ComplexUtil$Radix.formatFloat(ComplexUtil.java:156)
try { value = Float.parseFloat(result); } catch (NumberFormatException ignored) { }
Example log:
java.lang.NumberFormatException: For input string: "115,199982"
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.