Extract App Information from APK and IPA Package

3 minute read

During my mobile security journey, I often get stuck when searching for the publicly known app information that I am testing. We are talking about app name, app main package, version number and build. This information is important for reporting: I usually fire up my MobsF Docker instance and get this information from there. This process is often too slow, especially for big applications: I often forget to note down this data before turning off the Docker instance:(

This mini guide serves as a reminder for me and, hopefully, a precious tip for you. I will explain an easier way to extract that information via Linux command line and a bit of Python!

Android (APK)

Android applications contains this information in a file called AndroidManifest.xml[1]. This file contains details about the application structure, the entry point (MainActivity), the activities, what hardware and software the app will use, service provider and broadcast receiver, …

We can extract all the information we need by installing apkinfo from the pyaxmlparser[2] python package (pip3 install pyaxmlparser) and run

apkinfo app.apk

It will give an output like this one:

APK: proxydroid.apk
App name: ProxyDroid
Package: org.proxydroid
Version name: 3.2.0
Version code: 72
Is it Signed: True
Is it Signed with v1 Signatures: True
Is it Signed with v2 Signatures: True
Is it Signed with v3 Signatures: False

Before that tool, I was using the technique described below, which requires a bit more parsing (I will probably remove this part for conciseness).

We can use aapt to dumb the badging information, and extract the package information using Linux string manipulation magic tool sed:

aapt dump badging app.apk | grep package | sed -e "s/package: //" -e "s/'//g" -e "s/ /\\n/g"

This will give an output like the one below:

name=com.example.com
versionCode=1
versionName=1.0
compileSdkVersion=33
compileSdkVersionCodename=13

iOS (IPA)

We can say that the equivalent for AndroidManifest in IPA package is Info.plist[3].

While I was writing this article, I found out that LaurieWired published the perfect video about this topic. She suggests using plistlib[4] Python library, which is perfect for parsing binary and xml .plist files.

Here is my python script to extract the information I usually add in my reports.

import plistlib
import sys


def parse_plist(plist_file_name):

    with open(plist_file_name,'rb') as fp:
        plist = plistlib.load(fp)

        print('App name: ' + plist['CFBundleDisplayName'])
        print('Package: ' + plist['CFBundleIdentifier'])
        print('Version code: '  + plist['CFBundleShortVersionString'])
        print('Version bundle: ' + plist['CFBundleVersion'])
        print('Version platform: ' + plist['DTPlatformVersion'])

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Usage: python parse_plist.py <Info.plist>")
        sys.exit(1)

    plist_file_name = sys.argv[1]

    parse_plist(plist_file_name)

(I will probably remove this part for conciseness) My previous technique was a bit more complex and not perfect, but it was getting the job done. I used plistutil to convert Info.plist into a readable format (sometimes this file is stored in XML, sometimes in binary, we want the XML version and this tool helps us with that), and manually extract the tags (gosh the code is quite messy).

import sys
import xml.etree.ElementTree as ET


def parse_xml_from_file(file_path):
    # Parse the XML file
    tree = ET.parse(file_path)
    # we need to add [0] or it will get <plist> empty tag
    root = tree.getroot()[0]

    # Save all the top level string tags in a dictionary
    plist = {}
    i=0
    while i < len(root):
        if root[i].tag == 'key':
            if root[i+1].tag == 'string':
                plist[root[i].text] = root[i+1].text
            i+=2


    print('CFBundleDisplayName = ' + plist['CFBundleDisplayName'])
    print('CFBundleIdentifier = ',plist['CFBundleIdentifier'])
    print('CFBundleShortVersionString = ' + plist['CFBundleShortVersionString'])
    print('CFBundleVersion = ' + plist['CFBundleVersion'])
    print('DTPlatformVersion = ' + plist['DTPlatformVersion'])
            

if __name__ == "__main__":

    if len(sys.argv) != 2:
        print("Usage: python file.py <xml_file>")
        sys.exit(1)

    xml_file_path = sys.argv[1]

    parse_xml_from_file(xml_file_path)

Conclusion

That’s all for today! I hope you found something useful. Feel free to get in touch with me if you have any more questions:)

References