Created
April 16, 2021 13:35
-
-
Save obfusk/f9b68f58ef7afcd41ad5b0b51d82274b to your computer and use it in GitHub Desktop.
fast way to get the minSdkVersion from an APK
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
# encoding: utf-8 | |
import os | |
import struct | |
import sys | |
# import zipfile | |
import zlib | |
from collections import namedtuple | |
from androguard.core.bytecodes import axml | |
ZipData = namedtuple("ZipData", ("cd_offset", "eocd_offset", "cd_and_eocd")) | |
class ZipError(Exception): | |
pass | |
# def get_android_manifest(apkfile): | |
# with zipfile.ZipFile(apkfile, "r") as apk: | |
# with apk.open("AndroidManifest.xml") as manifest: | |
# return manifest.read() | |
def get_android_manifest(apkfile): | |
zdata = zip_data(apkfile) | |
with open(apkfile, "rb") as fhi: | |
fhi.seek(zdata.cd_offset) | |
while True: | |
hdr = fhi.read(46) | |
if hdr[:4] != b"\x50\x4b\x01\x02": | |
break | |
n, m, k = struct.unpack("<HHH", hdr[28:34]) | |
hdr += fhi.read(n + m + k) | |
name = hdr[46:46 + n] | |
if name == b"AndroidManifest.xml": | |
header_offset = int.from_bytes(hdr[42:46], "little") | |
compress_size = int.from_bytes(hdr[20:24], "little") | |
compress_type = int.from_bytes(hdr[10:12], "little") | |
if compress_type != 8: | |
raise ZipError("Expected deflate compression") | |
fhi.seek(header_offset) | |
lhr = fhi.read(30) | |
if lhr[:4] != b"\x50\x4b\x03\x04": | |
raise ZipError("Expected local file header signature") | |
ln, lm = struct.unpack("<HH", lhr[26:30]) | |
lhr += fhi.read(ln + lm) | |
return zlib.decompress(fhi.read(compress_size), -15) | |
raise RuntimeError("AndroidManifest.xml not found") | |
def zip_data(apkfile, count=1024): | |
with open(apkfile, "rb") as fh: | |
fh.seek(-count, os.SEEK_END) | |
data = fh.read() | |
pos = data.rfind(b"\x50\x4b\x05\x06") | |
if pos == -1: | |
raise ZipError("Expected end of central directory record (EOCD)") | |
fh.seek(pos - len(data), os.SEEK_CUR) | |
eocd_offset = fh.tell() | |
fh.seek(16, os.SEEK_CUR) | |
cd_offset = int.from_bytes(fh.read(4), "little") | |
fh.seek(cd_offset) | |
cd_and_eocd = fh.read() | |
return ZipData(cd_offset, eocd_offset, cd_and_eocd) | |
def get_minsdkversion_from_binary_android_manifest(data): | |
result = 1 | |
parser = axml.AXMLParser(data) | |
while parser.is_valid(): | |
event = next(parser) | |
if event == axml.END_DOCUMENT: | |
break | |
# FIXME: depth == 2 ?! | |
if event == axml.START_TAG and parser.name == "uses-sdk" \ | |
and parser.namespace == "": | |
for i in range(parser.getAttributeCount()): | |
if parser.getAttributeName(i) == "minSdkVersion": | |
# FIXME: VALUE_TYPE_STRING -> getMinSdkVersionForCodename() | |
val = parser.getAttributeValueData(i) | |
result = max(result, val) | |
# return result | |
return result | |
if __name__ == "__main__": | |
# from androguard.core.bytecodes.apk import APK | |
for apkfile in sys.argv[1:]: | |
data = get_android_manifest(apkfile) | |
print(get_minsdkversion_from_binary_android_manifest(data)) | |
# apk = APK(apkfile) | |
# print(apk.get_min_sdk_version()) | |
# vim: set tw=80 sw=4 sts=4 et fdm=marker : |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment