A file structure is a whole fascinating world with its own history, mysteries and a home-grown circus of freaks, where workarounds are applied liberally. If you dig deeper into it, you can discover loads of interesting stuff.
In our digging we came across a particular feature of APK files — a special signature with a specific block of metadata, i.e. frosting. It allows you to determine unambiguously if a file was distributed via Google Play. This signature would be useful to antivirus vendors and sandboxes when analyzing malware. It can also help forensic investigators pinpoint the source of a file.
There’s hardly any information out there regarding this topic. The only reference appears to be in Security Metadata in Early 2018 on the Android Developers Blog, and there is also an Avast utility that allows this signature to be validated. I decided to explore the feature and check the Avast developers’ assumptions about the contents of the frosting block and share my findings.
Frosting and APK Signing Block
Google uses a special signature for APK files when publishing apps on Google Play. This signature is stored in the APK Signing Block, which precedes the central directory of ZIP files and follows its primary contents:
The magic APK Sig Block 42 can be used to identify the APK Signing Block. The signing block may contain other blocks, whose application can be determined by the 4-byte ID. Thus, we get a ZIP format extension with backward compatibility. If you are interested in reading more or seeing the source code, you can check out the description of the method ApkSigningBlockUtils.findSignature
here.
Let us take some file as an example, for instance 2124948e2b7897cd6fbbe5fbd655c26d. You can use androguard to view the block identifiers within the APK Signing Block:
There are several types of blocks with various identifiers, which are officially described in the documentation:
0x7109871a (APK_SIGNATURE_SCHEME_V2_BLOCK_ID
) — APK signature scheme version 20xf05368c0 (APK_SIGNATURE_SCHEME_V3_BLOCK_ID)
— APK signature scheme version 3
Some of the blocks can be found in Android source codes:
0x42726577 (VERITY_PADDING_BLOCK_ID)
— verity padding block0x6dff800d (SOURCE_STAMP_BLOCK_ID)
— a relatively new type of blocks
Other types of blocks that may come up:
0x504b4453 (DEPENDENCY_INFO_BLOCK_ID
) — a block that apparently contains dependency metadata, which is saved by the Android Gradle plugin to identify any issues related to dependencies0x71777777 (APK_CHANNEL_BLOCK_ID)
— a Walle (Chinese gizmo) assembler block, which contains JSON with a channel ID0xff3b5998
— a zero block, which I ran into in the file — I couldn't find any information on that0x2146444e
— a block with the necessary metadata from Google Play
Frosting and Play Market
Let us get back to analyzing the 0x2146444e
block. First off, we should explore the innards of the Play Market application.
The identifier of our interest is found in two locations. As we delve deeper, we quite quickly spot the class responsible for block parsing. This is the first time that the name of a frosting block pops up among the constants:
Having compared different versions of the Play Market application, I have made the following observation: the code responsible for the parsing of this type of signature appeared around January 2018 together with the release of the 8.6.X version. While the frosting metadata block already existed, it was during this period that it took on its current form.
In order to parse the data, we need a primitive for reading 4-byte numbers. The scheme is a standard varint without any tricks involving negative numbers.
Though simple, the block parsing function is fairly large. It allows you to gain an understanding of the data structure:
To validate the signature, the first-field hash and key from validation_sequence
are used, where validation_strategy
equals zero. The signature itself is taken from signature_sequence
with the same ordinal number as the validation_sequence
entry. The figure below presents the explanatory pseudocode:
The signing_key_index
value indicates the index in the array finsky.peer_app_sharing_api.frosting_public_keys
, which contains only one key so far as shown below:
The size_signed_data
is signed with the ECDSA_SHA256
algorithm starting with the size_frosting
variable. Note that the signed data contains SHA-256 of the file data:
1) data from the beginning of the file to the signing block
2) data from the central directory to the end of the central directory, with the field value ‘offset of start of central with respect to the starting disk number’ at the end of the central directory replaced with a signing block offset
The signature scheme version 2 block (if any) is inserted between the data from the above items 1 and 2 with APK_SIGNATURE_SCHEME_V2_BLOCK_ID
preceding it.
The hash calculation function in the Play Market application is represented as follows:
Frosting and ProtoBuf
This information is sufficient for signature validation. Alas, I failed to figure out what is hidden in the frosting block data. The only thing I was able to discover was the data has a ProtoBuf format and varies greatly in size and the number of fields depending on the file.
Typical representation of decoded data without a scheme (4b005c9e9ea0731330a757fcf3abeb6e):
But you can come across some instances (471c589acc800135eb318057c43a8068) with around five hundred fields.
The data occasionally contains such curious strings as: android.hardware.ram.low, com.samsung.feature.SAMSUNG_EXPERIENCE, com.google.android.apps.photos.PIXEL_2018_PRELOAD
. These strings are not explicitly declared feature names, which a device may have.
You can find the description of available features in the files on the device — in the /etc/sysconfig/
folder:
If we were to give an example of a declared feature, this could be checking the camera availability by calling the android.hardware.camera
function through the method PackageManager hasSystemFeature
. However, the function of these strings within this context is vague.
I could not guess, find or recover the data scheme for the Play Market APK classes. It would be great if anyone could tell us what is out there and how they managed to figure it out. Meanwhile, all we have now are the assumptions of the Avast utility developers about the ProtoBuf structure and the string com.google.android.apps.photos.PIXEL_2018_PRELOAD
indicating a system or pre-installed app:
I would like to share some of my comments with respect to the above.
1. When it comes to the string com.google.android.apps.photos.PIXEL_2018_PRELOAD
: you can easily prove that this assumption is incorrect. If we download a few Google factory images, we will realize that they have neither such strings nor a single app with a frosting block.
We can look into it in more detail using the image walleye for Pixel 2 9.0.0 (PQ3A.190801.002, Aug 2019). Having installed the image, we are not able to spot a single file with a frosting block among a total of 187 APK files. If we update all the apps, 33 out of the 264 APK files will acquire a frosting block. However, only 5 of them will contain these strings:
- com.google.android.feature.DPS
- com.google.android.feature.PIXEL_EXPERIENCE
- com.google.android.feature.PIXEL_2017_EXPERIENCE
- com.google.android.feature.PIXEL_2019_EXPERIENCE
- com.google.android.feature.ANDROID_ONE_EXPERIENCE
- com.google.android.feature.PIXEL_2018_EXPERIENCE
- com.google.android.feature.PIXEL_2020_EXPERIENCE
google.android.inputmethod.latin:
- android.hardware.ram.low
- com.google.android.apps.dialer.GO_EXPERIENCE
- com.google.android.feature.PIXEL_2020_EXPERIENCE
- android.hardware.camera.level.full
- com.google.android.feature.PIXEL_2020_EXPERIENCE
We can assume that these strings show the relevance of features to the device where the app is installed. However, requesting a full list of features on the updated device proves that the assumption is wrong.
2. I would disagree with the ‘frosting versions’ as you can find similar data, but with values other than 1. The maximum value of this field that I have come across so far is 26.
3. I would disagree with the ‘С timestamp of the frosting creation’: I have been monitoring a specific app and noticed that this field value does not necessarily increase with every new version. It tends to be unstable and can become negative.
4. MinSdkLevel and VersionCode appear plausible.
Conclusion
In summary, a frosting block in the signature helps to precisely ascertain if a file has been distributed through an official store. I wasn’t able to derive any other benefit from this signature.
For the finale, here is an illustration from the ApkLab mobile sandbox report of how this information is applied: