Device & systemSecurity & malware
Installed Apps: packages.xml and packages.list
Android's package manager state: packages.xml install and update times, installer and initiator, UIDs, signatures and permissions, packages.list and ABX.
- Location
- /data/system/packages.xml
- Proves
- Which apps are installed, when each was first installed and last updated, which app installed it (store, package installer, browser, adb), its UID, signing certificate and install-time permissions
- Timestamps
- Attributes ft, it and ut are hexadecimal Unix milliseconds (UTC): APK timestamp, first install, last update
- Access
- Full file system (root, exploit or agent); system device-encrypted storage, readable in BFU by a privileged extraction. Logical and bugreport: the same facts via pm and dumpsys package. Not in ADB backup.
- Retention
- Current state only: an uninstalled app disappears from packages.xml; traces remain in usagestats, Play Store databases and other logs
- Collection
- Full file system extraction, adb shell pm list packages -f -i -U, adb shell dumpsys package, adb bugreport, androidqf
Tools
Compare all tools- Disk Image ParserIn browser
- ALEAPPCLI · open source
- MVTCLI · open source
- abx2xmlCLI · on the device
- ccl_abxLibrary · open source
- xmllintCLI · open source
- pmCLI · on the device
- dumpsysCLI · on the device
- jadxGUI · open source
- ApktoolCLI · open source
- Cellebrite UFEDPlatform · commercial
- Magnet AXIOMPlatform · commercial
- Oxygen Forensic DetectivePlatform · commercial
- Disk Image Parser: Reads ext4 only: not F2FS (used for userdata on many recent phones) and not Android sparse images. An image of an FBE userdata partition shows encrypted names and contents unless it was decrypted during extraction.
What it is
The Android package manager keeps its persistent state in /data/system/packages.xml: one <package> element per installed app (system and user), with its code path, version, UID, install source, timestamps, signing certificates and install-time permissions. A companion text file, packages.list, maps each package to its UID and data directory. Together they are the authoritative inventory of what can run on the phone, and the starting point of any malware or stalkerware triage.
Where it lives
| Item | Path | Notes |
|---|---|---|
| Package state | /data/system/packages.xml | Android Binary XML (ABX) from Android 12 on most builds |
| Package list | /data/system/packages.list | Plain text |
| Per-user state | /data/system/users/<user>/package-restrictions.xml | Installed-for-user, enabled, stopped, hidden, suspended flags |
| Runtime permissions | /data/system/users/<user>/runtime-permissions.xml (8 to 10), /data/misc_de/<user>/apexdata/com.android.permission/runtime-permissions.xml (11+) | Dangerous permissions granted at runtime |
| Install sessions | /data/system/install_sessions.xml | Pending and recent install sessions |
| APKs | /data/app/<pkg>-<random>/ (8 to 10), /data/app/~~<random>/<pkg>-<random>/ (11+) | base.apk and split APKs |
| Transient backup | /data/system/packages-backup.xml | Present only if the system was interrupted while writing |
What it proves
- The full list of installed packages, including disabled system apps and apps installed for other users.
- First install time (
it) and last update time (ut) of each package. - The installer (
installer):com.android.vendingfor Google Play,com.sec.android.app.samsungappsfor Galaxy Store,com.google.android.packageinstallerorcom.android.packageinstallerfor a sideloaded APK opened from a file or browser, empty or the shell foradb install. - On Android 11 and later, the initiating app (
installInitiator), for example the browser or file manager the APK was opened from, and the originating app. - The app's UID (matches
packages.list,usagestatsand file ownership), shared UID, signing certificate and install-time permissions.
It does not prove the user launched the app (usagestats does that) or chose to install it: an app with install permission can install others.
Key fields
| Attribute or element | Meaning |
|---|---|
name, codePath | Package name and APK folder |
version, userId / sharedUserId | versionCode, UID or shared UID |
ft, it, ut | APK file timestamp, first install time, last update time (hex milliseconds) |
installer, installInitiator, installOriginator | Installing package, initiating package, originating package (the latter two on Android 11+) |
packageSource | On newer releases, a code for where the install came from (store, local file, downloaded file) |
<sigs> / <cert> | Signing certificate(s) |
<perms> / <item name granted> | Install-time permissions |
packages.list line | <package> <uid> <debuggable> <dataDir> <seinfo> <gids> |
Timestamps
ft, it and ut are Unix epoch milliseconds written in hexadecimal. it survives updates; ut changes on every update, including silent Play Store updates.
# Android 12+: convert ABX first (on a device: abx2xml in out; offline: ccl_abx, see its README)
python3 ccl_abx.py packages.xml > packages_plain.xml
python3 - <<'PY'
import datetime as dt, xml.etree.ElementTree as ET
ts = lambda h: dt.datetime.fromtimestamp(int(h, 16) / 1000, dt.timezone.utc).isoformat() if h else None
for p in ET.parse("packages_plain.xml").getroot().iter("package"):
print(p.get("name"), "installed", ts(p.get("it")), "updated", ts(p.get("ut")),
"installer", p.get("installer"), "initiator", p.get("installInitiator"))
PY
Retention
packages.xml is rewritten with the current state after every change, so an uninstalled app vanishes from it. Evidence of a removed app survives elsewhere: usagestats events, the Play Store's own databases (library.db, localappstate.db in com.android.vending), raw contacts or accounts it created in contacts2.db, Wi-Fi networks it configured in WifiConfigStore.xml, and leftover folders in shared storage.
Collection
- Full file system extraction: copy
/data/system/(packages files,users/) and ideally the APKs of suspicious packages. The files are in system device-encrypted storage. - Logical (no root): the package manager exposes the same facts.
adb shell pm list packages -f -i -U > packages.txt # path, installer, UID
adb shell dumpsys package > dumpsys_package.txt # install times, initiator, permissions
adb shell pm path com.suspicious.app # then adb pull the APK
- Bugreport:
dumpsys packageis part of it. androidqf collects packages, their APKs and dumpsys output in one run.
Parsing
- ALEAPP reports installed apps from
packages.xml(ABX included) andpackages.list. - MVT (
mvt-android check-androidqf,check-bugreportorcheck-adb) flags sideloaded apps, apps with dangerous permissions and matches against spyware indicators. - jadx and Apktool decompile a suspicious APK to review its manifest, permissions and code.
- Disk Image Parser can browse
/data/systemin an ext4 image of a decrypteduserdatapartition to export these files; it does not read F2FS.
Investigator tips
- Sort by
itand look at everything installed shortly before the incident; a sideload installer orinstallInitiatorset to a browser, file manager or messaging app deserves a look. - Cross-check a sideloaded APK with its download in Chrome history and the link that led there in SMS.
- Stalkerware often uses innocuous names ("System Service", "Sync"), hides its launcher icon, and holds accessibility, notification listener or device admin rights: review runtime permissions and special access next to the package list.
- System apps updated through the Play Store appear with a
/data/appcode path and an update time: that is normal, not a replaced system binary. - A package present in
packages.listand absent frompackages.xml(or the reverse) points to an interrupted write or tampering on a rooted device.
See also
Related artifacts
- Usagestats: Android App Usage Events
- Chrome on Android: the History Database
- Wi-Fi Networks on Android: WifiConfigStore.xml
- SMS and MMS on Android: mmssms.db
- Android Contacts: contacts2.db
- Runtime Permissions and App Ops on Android
- Accessibility Services and Device Admin Abuse
- ADB Backup Format: backup.ab Files