building split apks / single apk
jeromew opened this issue · 18 comments
Hello,
I tried to pull a splitted apk and apk.sh built a file.single.apk.
This file.single.apk did not work as expected after installation (android complained about missing components and proposed to re-download the app via the app store)
After some research, I found how to install splitted apks (avoiding the need for the single apk hack)
- first you need to push the apks to the device in /data/local/tmp
- then you need to execute a sh script in an adb shell
TOTALSIZE=`wc -c /data/local/tmp/*.apk | tail -n 1 | cut -f1 -d " "`
SESSIONID=`pm install-create -S $TOTALSIZE | tr -dc '0-9'`
INDEX=0
for f in /data/local/tmp/*.apk
do
let SIZE=`wc -c ${f} | cut -f1 -d " "`
echo streaming ${f}
pm install-write -S ${SIZE} ${SESSIONID} ${INDEX} ${f}
let INDEX=${INDEX}+1
done
pm install-commit ${SESSIONID}
rm /data/local/tmp/*.apk
this creates an install session on the devices and then writes the different apks in this session.
Using this the installed app starts correctly
Which apk?
Which Android version?
Thanks for sharing!
the single apk feature was mis-behaving with deezer 7.1.4.88 installed yesterday from the google play store. device: samsung galaxy s5 running android 6.0.1 ; I agree this is very old by 2023 standards ;-)
Also note that inside the AndroidManifest there is
<meta-data android:name="com.android.vending.splits.required" android:value="true"/>
<meta-data android:name="com.android.stamp.source" android:value="https://play.google.com/store"/>
<meta-data android:name="com.android.stamp.type" android:value="STAMP_TYPE_DISTRIBUTION_APK"/>
<meta-data android:name="com.android.vending.splits" android:resource="@xml/splits0"/>
<meta-data android:name="com.android.vending.derived.apk.id" android:value="3"/>
so that is maybe another way that android has to require the splits.
apk.sh currently only patches "android:isSplitRequired" which is also present in the manifest.
I can confirm that after setting the "com.android.vending.splits.required" to false, the single apk works
After some more testing, there seem to be another issue.
the app starts but after a specific click I get in adb logcat
10-17 20:26:37.774 5557 5557 I AndroidRuntime: VM exiting with result code 0, cleanup skipped.
10-17 20:27:00.274 6377 6377 D AndroidRuntime: Shutting down VM
10-17 20:27:00.894 6377 6377 E AndroidRuntime: FATAL EXCEPTION: main
10-17 20:27:00.894 6377 6377 E AndroidRuntime: Process: xxx.android.app, PID: 6377
10-17 20:27:00.894 6377 6377 E AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{xxx.android.app/com.xxx.feature.UnkActivity}: android.view.InflateException: Binary XML file line #2: Binary XML file line #2: Error inflating class xxx.android.masthead.MastheadCoordinatorLayout
10-17 20:27:00.894 6377 6377 E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3253)
10-17 20:27:00.894 6377 6377 E AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3349)
10-17 20:27:00.894 6377 6377 E AndroidRuntime: at android.app.ActivityThread.access$1100(ActivityThread.java:221)
10-17 20:27:00.894 6377 6377 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1794)
10-17 20:27:00.894 6377 6377 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:102)
Thanks for sharing jeromew!
Is the app still not working?
Do you figured out why?
After some more debugging, I don't think this Exception trace is related to apk.sh.
It is related to a problem in the decode/build cycle of apktool that misses some @drawable resources references. I don't know exactly what is the problem but it is not with apk.sh. This is probably a problem specific to the application I was testing.
I fixed my problem in the split-apk case by using 'apktool d -r base.apk', the -r
option avoiding the resource decoding step.
I don't think it would work in the single apk case because there are resources that would need to be merged and if the resource decoding step is lossy that would create the Exception issue.
Thanks for your work @jeromew! PR merged.
After all, do you managed to correctly merge the apks?
No I worked with the apktool -r
flag that does not decode the resources but then all the resource files (including AndroidManifest.xml) are binary files which makes it hard patching them or even detecting the MainActivity to inject frida.
the script I gave when I created the issue works for re-installing base.apk & splits after smali modifications (even adding frida manually). I found a way to execute it on the device from a .sh script if you think it could be a good addition to apk.sh, but you'll have to decide on the apk.sh API to decide how to handle the single/splitted in parallel
# sign apks
ls -1 *.apk | xargs -I{} ~/.apk.sh/sdk_root/build-tools/33.0.1/apksigner sign --ks-pass pass:keykey --ks ../my-key.keystore {}
# push signed apks to android device
ls -1 *.apk | xargs -I{} ~/.apk.sh/sdk_root/platform-tools/adb push {} /data/local/tmp/{}
REMOTECODE=$(cat <<"EOF"
TOTALSIZE=`wc -c /data/local/tmp/*.apk | tail -n 1 | cut -f1 -d " "`
SESSIONID=`pm install-create -S $TOTALSIZE | tr -dc '0-9'`
INDEX=0
for f in /data/local/tmp/*.apk
do
let SIZE=`wc -c ${f} | cut -f1 -d " "`
echo streaming ${f}
pm install-write -S ${SIZE} ${SESSIONID} ${INDEX} ${f}
let INDEX=${INDEX}+1
done
pm install-commit ${SESSIONID}
rm /data/local/tmp/*.apk
exit
EOF
)
echo "$REMOTECODE"
# install app on android device
~/.apk.sh/sdk_root/platform-tools/adb shell "$REMOTECODE"
I digged a little bit further on the single apk problem.
it seems that the resources exist in one of the split apk, but apktool does not replace them with a DUMMY tag. It simply puts drawable="@null"
instead so the DUMMY replacement mechanism does not recover the resource id and those resource links go missing in the final apk.
ok I may have found the issue (I need to re-do a whole decode / build round trip)
According to the changelog of apktool - https://github.com/iBotPeaches/Apktool/releases/tag/v2.9.0 the 2.9.0 version introduced a new option called "res-mode" which has an impact on how missing ressources are handled.
The default res-mode is "remove" (DECODE_RES_RESOLVE_REMOVE) which does what I observe, setting missing resources to @null.
but when calling "apktool_2.9.0.jar d -resm dummy base.apk", the DUMMY placeholders are appearing !
hopefully the apk.sh DUMMY replacement sequence should do the correct thing now.
Note that the options for the res-mode are
switch (mode) {
case "remove":
case "delete":
config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_REMOVE);
break;
case "dummy":
case "dummies":
config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_DUMMY);
break;
case "keep":
case "preserve":
config.setDecodeResolveMode(Config.DECODE_RES_RESOLVE_RETAIN);
break;
default:
System.err.println("Unknown resolve resources mode: " + mode);
System.err.println("Expect: 'remove', 'dummy' or 'keep'.");
System.exit(1);
}
It now works with PR#27.
Note that I added "--resource-mode dummy" without checking that the apktool version is >= 2.9.0 so we could say this is not compatible with apktool < 2.9.0. This seems ok to me as working on an older apktool version should not be recommended.
apktool cli doc - https://apktool.org/docs/cli-parameters
Also note that I had to change the DUMMY replacement strategy because the one-by-one strategy was simply too slow on my setup and the app I used for testing.
@ax @jeromew
I experience the same issue for APK that have split file. I got this exception
Process: com.package, PID: 8459
04-13 14:59:53.979 8459 8459 E AndroidRuntime: at com.package.ui.MainActivity.m0(Unknown Source:41)
04-13 14:59:53.979 8459 8459 E AndroidRuntime: Caused by: android.content.res.Resources$NotFoundException: Drawable com.package:drawable/APKTOOL_DUMMY_3ed with resource ID #0x7f0803ed
I try couple of things:
- I copy all the drawable from the split file and paste them in the drawable of the base.apk #Does not work
- I try to delete the APKTOOL_DUMMY_3ed from the drawable and properties but still the app search it by ID and didn't work again
Is there any solution for this, or I need to search for alternative to pull APK that have split.
Are you using the latest version?
Can u open a new issue?