Last week I had an Android application that I wanted to test in the Android emulator (the official one included in the SDK). I had the application installed from Play Store on a physical device, and as I’ve done many times, I just grabbed it using Drozer and issued the usual ADB command to install it on the emulator. (The sizes and package names have been altered to protect the innocent.)
$ adb install hu.silentsignal.blogpost.apk
1337 KB/s (27313378 bytes in 22.233s)
pkg: /data/local/tmp/hu.silentsignal.blogpost.apk
Failure [INSTALL_FAILED_CONTAINER_ERROR]
A quick search on the web revealed that the application most probably had the installLocation
parameter set to preferExternal
in the manifest file. Latter is an XML file called AndroidManifest.xml
that contains important metadata about Android applications and is transformed into a binary representation upon compilation to reduce the size and processing power required on resource-constrained devics. Running android-apktool converted it back to text format, and revealed that it was indeed the cause.
<?xml version="1.0" encoding="utf-8"?>
<manifest android:versionCode="133744042" android:versionName="4.13.37"
android:installLocation="preferExternal" package="hu.silentsignal.blogpost"
xmlns:android="http://schemas.android.com/apk/res/android">
Most results of the web search agreed that the emulator (although capable of emulating SD cards) is incompatible with this setting, some suggested increasing the memory of the emulated device, others said the same about the SD card, but unfortunately none of these worked for us. The majority of accepted answers solved the problem by changing the preferExternal
parameter to auto
, which is the default.
Changing an Android application and repackaging it is also a breeze usually, apktool supports this natively, I just have to sign the resulting APK with a key of my own. However, this application used some features that apktool (and other tools invoked in the process, including AAPT) didn’t like. I’ve met this situation before, and there are usually two solutions.
However in this case, even beta versions couldn’t handle the task, I’ve even written some wrapper scripts around the dependencies to tweak with parameters like minimum and targeted API levels, but got nowhere. Then I realized, binary XML files still have to store the attribute names somewhere, so I fired up a hex editor and hoped that the Android runtime won’t complain about unknown attributes and will use the default value. It seemed that the format uses UTF-16 to store strings, and at offset 0x112, I changed the i
to o
, resulting in installLocatoon
.
00f0 3a080000 82080000 0f006900 6e007300 |:.........i.n.s.|
0100 74006100 6c006c00 4c006f00 63006100 |t.a.l.l.L.o.c.a.|
0110 74006f00 6f006e00 00000b00 76006500 |t.o.o.n.....v.e.|
0120 72007300 69006f00 6e004300 6f006400 |r.s.i.o.n.C.o.d.|
0130 65000000 0b007600 65007200 73006900 |e.....v.e.r.s.i.|
Then, I simply updated the manifest in the APK file using a ZIP tool since APK files are just ZIP archives with specific naming conventions, just like JAR, DOCX and ODT files. Since the digital signature of the APK is now corrupted, I had to resign it with jarsigner, but installation still failed.
$ 7z -tzip a hu.silentsignal.blogpost.apk AndroidManifest.xml
7-Zip [64] 9.20 Copyright (c) 1999-2010 Igor Pavlov 2010-11-18
p7zip Version 9.20 (locale=hu_HU.UTF-8,Utf16=on,HugeFiles=on,4 CPUs)
Scanning
Updating archive hu.silentsignal.blogpost.apk
Compressing AndroidManifest.xml
Everything is Ok
$ jarsigner -sigalg SHA1withRSA -digestalg SHA1 \
-keystore s2.jks hu.silentsignal.blogpost.apk s2
Enter Passphrase for keystore:
$ adb install hu.silentsignal.blogpost.apk
1337 KB/s (27313378 bytes in 22.233s)
pkg: /data/local/tmp/hu.silentsignal.blogpost.apk
Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES]
How can there be no certificates? Let’s list the contents with a ZIP tool, and look for the META-INF
, an old friend from the Java world (JAR files) that contains a list of files with name and (in case of Android, SHA-1) hash (MANIFEST.MF
), a public key (*.RSA
in case of RSA), and a signature of the former with the latter (*.SF
). The name of the public key and signature files are the same as their alias in the keystore converted to uppercase; see the last parameter of jarsigner in the above commands (s2
).
Date Time Attr Size Compressed Name
------------------- ----- -------- ------------ --------------------
2014-04-03 14:44:42 ..... 362941 110461 META-INF/MANIFEST.MF
2014-04-03 14:44:42 ..... 369105 113933 META-INF/S2.SF
2014-04-03 14:44:42 ..... 2046 1865 META-INF/S2.RSA
2014-02-27 13:37:16 ..... 928 637 META-INF/CERT.RSA
2014-02-27 13:37:16 ..... 361020 113942 META-INF/CERT.SF
See? It’s there! Also, another certificate (CERT.*
), since without apktool (which rebuilds APK archives from scratch) everything except AndroidManifest.xml
is included from the original file. Let’s delete the META-INF
directory, and try again.
$ 7z -tzip d hu.silentsignal.blogpost.apk META-INF
7-Zip [64] 9.20 Copyright (c) 1999-2010 Igor Pavlov 2010-11-18
p7zip Version 9.20 (locale=hu_HU.UTF-8,Utf16=on,HugeFiles=on,4 CPUs)
Updating archive hu.silentsignal.blogpost.apk
Everything is Ok
$ jarsigner -sigalg SHA1withRSA -digestalg SHA1 \
-keystore s2.jks hu.silentsignal.blogpost.apk s2
Enter Passphrase for keystore:
$ adb install hu.silentsignal.blogpost.apk
1337 KB/s (27313378 bytes in 22.233s)
pkg: /data/local/tmp/hu.silentsignal.blogpost.apk
Success
With this last modification, it worked, and I was able to explore the application within the emulator that makes it much more easier to set a global proxy and manipulate certificates than a physical device, and I haven’t even mentioned faking sensors and GSM information, or taking snapshots. The lesson here was that
META-INF
directory should be removed before signing an APK file, otherwise the runtime refuses it.Thanks to Etamme for the featured image Androids, licensed under CC-BY-3.0