The definitive open-source DFIR toolkit for PhonePe iOS evidence extraction, cross-database correlation, and UPI fraud investigation
"What Chitragupt records in mythology, PhonePe records in SQLite. This tool reads both."
- π― What Is This?
- π‘ Why It Matters
- ποΈ Architecture
- β‘ Quick Start
- π¦ Installation
- π§ Usage
- π Evidence Modules
- ποΈ Supported Artifacts β The Full Map
- π§ͺ The Three Storage Domains
- π PPQL β PhonePe Query Language
- πΈοΈ Cross-Database Correlation Engine
- β±οΈ Unified Timeline & Suspicious Signal Detection
- π§© Timestamp Semantics β The Three Epochs
- π€ Export Formats
- π¬ Research Reference
- ποΈ Project Structure
- π€ Contributing
- βοΈ Legal & Ethics
- π References & Further Reading
- π€ Author
PhonePe iOS Forensics is a full-spectrum digital forensics workstation β packaged as a local Flask web application β purpose-built for extracting, correlating, and presenting evidence from PhonePe iOS app acquisitions.
It turns a raw iOS backup or filesystem extraction into a structured, investigator-ready case:
Raw Acquisition Folder
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PhonePe iOS Forensics Engine β
β β
β 16 Extraction Modules βββΊ Correlation Engine β
β 50+ SQLite Databases βββΊ Unified Timeline β
β 15+ Plist Files βββΊ Social Graph β
β Binary Cookies βββΊ PPQL Hunt Interface β
β NSKeyedArchiver blobs βββΊ Suspicious Signals β
β WebKit data βββΊ Multi-format Reports β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
Structured Evidence + Exportable Reports
This is not a wrapper around commercial tools. Every parser is written from scratch, specifically for PhonePe's internal SDK architecture β handling the real column names, real BLOB encodings, real timestamp epochs, and real database schemas that published forensic write-ups consistently get wrong.
Virtually every publicly available PhonePe forensic reference makes the same category of mistakes:
| Common Mistake | Reality |
|---|---|
References a ZTRANSACTION table with columns like ZAMOUNT, ZSENDERUPIID |
That table does not exist. The real table is ZTRANSACTIONENTITY |
| Reads amount directly from a column | Amounts are packed inside a zlib-compressed JSON BLOB in ZDATA, in paise (not rupees) |
| Uses Unix epoch for all timestamps | PhonePe iOS uses three different epochs interchangeably |
| Only reads the main AppDomain | Critical databases (chat, contacts, P2P) live in AppDomainGroup β invisible to AppDomain-only extraction |
| Ignores WAL files | Deleted records survive in SQLite WAL pages β critical for anti-tampering analysis |
This tool fixes all of the above.
Unlike server-side forensics (which requires legal process and is often delayed), the PhonePe iOS app locally preserves:
- π Every transaction in a local SQLite ledger β including failed and abandoned payment attempts
- π₯ Every contact with their UPI IDs and cached profile photos
- π¬ In-app chat messages with bidirectional links to financial transactions
- π² Sub-second behavioral logs of every tap, screen view, and keyboard entry
- π Push notification payloads that survive even after transaction deletion
βοΈ Physical travel data including PNR numbers, passenger identities, and co-traveler names- π€ ML-inferred financial patterns that persist even after transaction deletion
- π¦ AutoPay mandates (active and revoked) invisible in the current app UI
phonepe_forensics/
βββ core.py β Parsing primitives (SQLite/WAL, plist, binarycookies,
β NSKeyedArchiver, timestamp normalization, hashing)
β
βββ extractors.py β 16 forensic extraction modules
β (identity, transactions, contacts, chat, notifications,
β analytics, financial, travel, payment_infra, config_state,
β recommendations, media, search, webkit, audit, app_state)
β
βββ correlator.py β Cross-database fusion engine
β (unified_timeline, social_graph, counterparty_profiles,
β corroboration_index, suspicious_signal_detection)
β
βββ hunt.py β PPQL β PhonePe Query Language parser & executor
β (SPL-inspired query engine over merged forensic indexes)
β
βββ case.py β Case orchestrator
β (coordinates extraction, caches results, exposes to UI)
β
βββ case_manager.py β Multi-case registry
β (JSON-manifest backed, persist across restarts)
β
βββ reports.py β Report exporter
β (CSV, JSON, HTML β self-contained, no external deps)
β
βββ research_data.py β In-app reference documentation
β (every artifact, schema, and query β built-in)
β
βββ webapp.py β Flask web UI (35+ routes, multi-case, full UI)
β
βββ templates/ β Jinja2 HTML templates
βββ static/ β CSS + JS
- Defensive parsing everywhere β all parsers degrade gracefully on partial corruption, because forensic acquisitions frequently contain truncated or partially checkpointed databases
- WAL-aware by default β SQLite opened in
mode=ro&cache=private, WAL contents automatically applied on open, uncommitted-but-checkpointed transactions are visible - Zero writes to evidence β never touches the source folder; all processing is read-only
- Multi-epoch timestamp normalization β every timestamp column is automatically detected and normalized across Unix-ms, Unix-s, and Apple CoreData epochs
- BLOB auto-detection β
ZDATAblobs are tried as raw JSON, then zlib-compressed JSON, then NSKeyedArchiver plist, before graceful fallback
# 1. Clone
git clone https://github.com/thelocalh0st/phonepe-ios-forensics.git
cd phonepe-ios-forensics
# 2. Install
pip install flask
# 3. Launch
python run.py
# Opens http://127.0.0.1:5000 in your browser automaticallyThat's it. No config files. No API keys. No database setup. Open the Case Manager, point it at your acquisition folder, and click Process Case.
| Component | Minimum |
|---|---|
| Python | 3.9+ |
| Flask | 2.x |
| OS | Windows Β· macOS Β· Linux |
| Disk | Enough for your acquisition + ~50MB working space |
pip install flaskFlask is the only hard dependency. The tool uses Python's stdlib for everything else: sqlite3, plistlib, hashlib, struct, zlib, re, json, csv.
pip install flask werkzeugpython -m venv venv
source venv/bin/activate # Linux / macOS
venv\Scripts\activate.bat # Windows
pip install flask
python run.pyOn Windows, the entry point auto-configures UTF-8 encoding for stdout/stderr β no manual chcp 65001 needed:
# run.py handles this automatically
os.environ.setdefault("PYTHONIOENCODING", "utf-8")
sys.stdout.reconfigure(encoding="utf-8")# Default (127.0.0.1:5000)
python run.py
# Custom host:port
python run.py 0.0.0.0:8080
# Or as a module
python -m phonepe_forensics.webapp 127.0.0.1:5000π Security Note: The server binds to
127.0.0.1by default. Do not expose to a network interface in a multi-user environment β this is a single-investigator local workstation tool.
Navigate to Case Manager β New Case. Two input modes are supported:
Mode 1 β Single Root Folder (most common)
Point to the parent directory that contains all three AppDomain* containers:
/path/to/extraction/
βββ AppDomain-com.phonepe.PhonePeApp/
βββ AppDomainGroup-group.com.phonepe.PhonePeApp/
βββ AppDomainGroup-group.com.phonepe.shared/
Mode 2 β Three-Folder Mode (split exports)
Explicitly point each of the three containers at a separate path β useful when an investigator has received partial exports from different acquisition sources.
After creation, click Process Case to run all 16 extraction modules sequentially. Processing is fully parallelism-safe β each module reads independently. Typical processing time on a full acquisition: 5β30 seconds depending on database sizes.
Results are cached in memory and available immediately across all views.
The left sidebar provides one-click access to every evidence category:
| View | What You See |
|---|---|
| π Dashboard | Summary cards β transaction count, contact count, chat groups, findings |
| π€ Identity | Registered name, UPI ID, device fingerprints, session tokens, location hints |
| πΈ Transactions | Full ledger with filtering by date, direction, state, amount, counterparty |
| π₯ Contacts | Cyclops-verified contacts + phonebook with UPI ID resolution |
| π Social Graph | Contact β transaction β chat linkage visualization |
| π¬ Chat | Burble group messages with linked transaction cards |
| π Notifications | PubSubCore push topic archive |
| π Analytics | Foxtrot + KN + Dash behavioral event batches |
| π° Financial | Rewards, mutual funds, vouchers, donations |
| Yatra booking PNRs, passenger names, co-travelers | |
| π¦ Payment Infra | Linked banks, UPI VPAs, payment instruments, AutoPay mandates |
| βοΈ Config State | Chimera remote config, A/B test assignments, feature flags |
| π― Recommendations | Maximus offers engine + Athena ML signals |
| πΌοΈ Media | QR code artifacts, transaction backgrounds, profile photos |
| π Search | AppSearch FTS query history + NexusCore sitemap |
| π WebKit | ResourceLoadStatistics + binary cookies |
| π΅οΈ Audit | Chitragupt + Chronicle + Samsara + sync gap analysis |
| β±οΈ Timeline | Unified chronological event stream across all databases |
| π¨ Findings | Heuristic suspicious signal flags |
| ποΈ DB Browser | Raw SQL interface against any parsed database |
| π Hunt (PPQL) | PhonePe Query Language search interface |
| π Research | Built-in artifact reference documentation |
Each module is a self-contained extractor that reads from specific databases and plists within the three storage domains.
Pins the ownership of the device + UPI account. Sources:
com.phonepe.widgetxii.datacache.plistβ primary UPI IDcom.phonepe.help.customDataStore.plistβuserName+refreshTokencom.phonepe.ads.sdk.plistβ GPS coordinates, pincode, statecom.apple.AdSupport.plistβ IDFA enforcement statecom.firebase.FIRInstallations.plistβ Firebase install ID__gads__.plistβ Google Ads signalscom.phonepe.PhonePeApp.plist(222 keys) β session IDs, BoltV2 token preview, Chimera quick-flags
Forensic Output:
registered_name β KYC-verified legal name
upi_id β Primary UPI VPA
phones_seen β All phone numbers extracted from any source
device_identifiers β AppsFlyer, Firebase, GADS, AdSupport IDs
location_hints β [{ lat, lng, pincode, state, source }]
tokens β Help refresh token, optimus token, BoltV2 preview
sessions β Token expiry, fetch timestamps
feature_flags β Active Chimera quick-flags
Master ledger parser β the highest-value module. Reads TransactionsStore.sqlite with full ZDATA blob decoding.
TransactionsStore.sqlite
βββ ZTRANSACTIONENTITY β one row per transaction
β βββ ZDATA β zlib-compressed JSON with the actual payload
βββ ZTRANSACTIONTAGENTITY β ~4 tags per transaction (payment_source, utr, etc.)
βββ ZVIEWENTITY β display-optimized mirror (survives ZDATA corruption)
βββ ZUSER β device owner (1 row)
βββ ZTRANSACTIONSEARCHRECENTS β user's own search queries in transaction history
The ZDATA blob is automatically decoded through a waterfall:
- Raw JSON
zlib.decompress()β JSON- NSKeyedArchiver plist
- Graceful hex preview fallback
Amount unit detection handles the paise trap automatically: "value": 50000, "unitType": "PAISA" β βΉ500.00.
Transaction ID Embedded Timestamp Decoding:
PhonePe transaction IDs embed a timestamp in their structure (e.g., T2412190843728...). The tool decodes this as an independent timestamp source β useful for detecting clock manipulation.
Reads SamparkV2.sqlite (group container) β PhonePe's proprietary contact intelligence system.
SCONTACT β Phone β UPI ID β name mapping (financial social graph)
SCONTACT_UPI_MAPPING β Historical phone-to-VPA associations with timestamps
ZPHONEBOOKCONTACT β Raw device address book (with deletion flags)
Key forensic outputs:
- Name divergence detection β
SDISPLAY_NAME(PhonePe's view) vsSDEVICE_CONTACT_NAME(phonebook) β reveals contact renaming used to obscure payment recipients - Transaction frequency scores β
SLAST_TRANSACTED_AT+STRANSACTION_COUNTindependent ofTransactionsStore - Profile photo recovery from
.SamparkV2_SUPPORT/_EXTERNAL_DATA/β photos persist after contact deletion
Parses BurbleNotificationStore.sqlite β the in-app P2P chat + payment notification system.
ZBURBLEGROUP β Group containers with subscriber lists
ZBURLBEMESSAGE β Individual messages (TEXT, PAYMENT_INFO_CARD, REWARD_CARD)
ZBANKACCOUNTCONTACT β Shared bank account disclosures (account + IFSC + VPA)
Every PAYMENT_INFO_CARD message embeds: amount, UPI reference, instrument, UTR, payment state β creating a second independent transaction record inside the chat layer.
Reads PubSubCoreBullhornDataStore.sqlite β the most tamper-resistant evidence source in the corpus.
Push notification payloads arrive from PhonePe's servers before any user interaction is possible. A user can delete a transaction from TransactionsStore. They cannot retroactively delete the push notification. The payload:
{
"transactionId": "T241119182327",
"amount": "500",
"sender": "rahul@phonepe",
"status": "SUCCESS",
"bankRefNo": "401234567890",
"timestamp": "2024-11-19T18:23:27+05:30"
}...is a server-pushed attestation of a successful transaction, logged independently of any user-controlled record.
Three sub-sources:
Foxtrot (FoxtrotEventsDB.sqlite β group container):
PhonePe's analytics batching pipeline. The critical distinction:
FUPLOAD_STATUS = 'UPLOADED' β Server-side records exist β obtainable via legal process
FUPLOAD_STATUS = 'PENDING' β EXCLUSIVELY local evidence β cannot be obtained via legal process
Events inside pending batches include geolocation coordinates: {"latitude": 17.385, "longitude": 78.486} β location data tied to transactions even when no explicit location column exists.
KN Analytics (kn_analytics_db.sqlite): Content interaction log β proves which merchants/offers a user was exposed to and clicked on before any payment.
Dash (Dash-Events.sqlite): Screen load latencies β implicitly prove specific screens were rendered at specific timestamps.
Parses rewards, mutual funds, vouchers, and donations:
RewardsDataStore.sqliteβ scratch cards withZLINKED_TXN_IDβ independent transaction corroborationMFDataStore.sqliteβ portfolio holdings + SIP mandatesBrandVouchersDataStore.sqliteβ merchant-specific spending patterns (Swiggy, Zomato, Amazon...)DonationsDataStore.sqliteβ NGO payments, PM-CARES contributionsOffersDataStore.sqliteβ merchant offers shown and clicked, timestamped
Scratch card forensic significance: A scratch card is server-issued only on transaction success. Its existence independently proves the transaction completed β even if the ZTRANSACTIONENTITY row was deleted.
Reads YatraDataModel.sqlite β a physical movement artifact.
ZBOOKING β Route, journey date, PNR, booking timestamp, amount
ZPASSENGER β Legal name, age, ID proof type, ID proof number (Aadhaar/Passport)
ZID_PROOF_NUMBER is a direct Aadhaar or passport number. Co-passenger records prove physical co-location with named associates. PNR cross-reference with IRCTC/airline records builds a travel timeline that corroborates or contradicts alibi claims.
Transaction Background Date Decoding: Folder names in P2P/TransactionBackgrounds/ embed the download date. The oldest folder = earliest PhonePe activity on this device. This survives complete transaction history deletion.
Comprehensive payment infrastructure mapping:
AccountSharedDataModel.sqlite
βββ ZUSERPROFILE β KYC identity, Aadhaar/PAN link status
βββ ZLINKEDBANKACCOUNT β All banks ever linked (including delinked with ZDELINKED_AT)
βββ ZUPIID β All VPAs (active + deregistered)
PaymentDataStore.sqlite
βββ ZPAYMENTINTENTENTITY β "Ghost transactions" β initiated but never completed
βββ ZPAYMENTGATEWAYRESPONSE β Raw bank gateway responses with NPCI trace IDs
βββ ZUPILINKEDACCOUNT β VPA-to-bank-account binding records
βββ ZAUTOPAYMANDATE β AutoPay mandates β including REVOKED with revoked_at
Delinked bank accounts (ZDELINKED_AT not null) are invisible in the current UI but fully preserved β historically linked financial instruments that may have been removed shortly before investigation.
Ghost transactions β ZPAYMENTINTENTENTITY rows created the instant a user initiates a payment flow, before any UPI call is made. Even if the user cancels after entering the amount, this record exists. Destroys "I never tried to send that amount" claims.
Reads Chimera's remote config cache and A/B test assignments:
ChimeraCoreResponseStore.sqlite β Exact UI specs served to this device at each timestamp
ExperimentationCoreStore.sqlite β Which A/B test variant the user was actually shown
ConfigManagerKeyStore.sqlite β API endpoints, transaction limits, fraud detection toggles
If a defense argument is "the payment screen was confusing/misleading," Chimera's cache is the evidentiary record of the exact UI the user saw at that time β pinned to a specific server deployment version.
MaximusDataModel.sqliteβ promotions and offer engine stateAthenaStore.sqliteβ on-device ML recommendation model outputs and signals
- QR code artifacts β downloaded QR images with merchant embeddings
- Transaction backgrounds β PNG card assets with encoded download dates (first-use dating)
- Profile photos β contact display pictures from
_EXTERNAL_DATA/
AppSearch FTS recentsβ user's in-app search queriesNXCoreDataStore.sqliteβ NexusCore mini-app sitemap
ResourceLoadStatisticsβ website interaction historyCookies.binarycookiesβ Apple binary cookie format, fully decoded:
class BinaryCookieReader:
# Parses Apple's proprietary binary cookie format
# Header: "cook" magic + page count
# Per-page: offset table β cookies with flags, domain, name, value, path, expiryMulti-source audit reconstruction:
- Chitragupt β sub-second UI behavioral log (tap β keyboard β screen β mPIN confirm)
- Chronicle β app timeline feed (orphaned references prove deleted transactions existed)
- Samsara β UPI payment state machine transitions (
INITIATED β PROCESSING β SUCCESS) - BGFramework β background task execution log (proves device was active at specific times)
- CentralSyncManager β sync gap analysis (uniform gap = offline period, seizure, or tampering)
- AuthDataModel β login history with success/failure flags and failure reasons
KEYBOARD events in Chitragupt prove an amount was typed manually (not auto-filled). The mpin_confirm TAP event proves the user explicitly authorized a payment. Together, these are the most powerful anti-repudiation evidence in the corpus.
| Database | SDK | Forensic Role |
|---|---|---|
TransactionsStore.sqlite |
Core Financial | Master UPI transaction ledger |
PaymentDataStore.sqlite |
Payment Engine | In-flight payment state, ghost transactions, AutoPay mandates |
TransferDataStore.sqlite |
Transfer SDK | Recent payees, collect requests, payment frequency map |
AccountSharedDataModel.sqlite |
Account SDK | KYC identity, linked banks (including delinked), UPI VPAs |
AuthDataModel.sqlite |
Auth SDK | Session tokens, device binding, login history |
Consent.sqlite |
Privacy SDK | Consent grants and revocations with timestamps |
CustodianPrivacy.sqlite |
Privacy SDK | Data protection policy acceptance log |
ChatPlatform.sqlite |
Chat SDK | P2P conversation threads with transaction links |
PubSubCoreBullhornDataStore.sqlite |
PubSub SDK | Push notification payload archive (tamper-resistant) |
ChimeraCoreResponseStore.sqlite |
Chimera / LiquidUI | Remote UI config + feature flag cache |
ExperimentationCoreStore.sqlite |
Experimentation | A/B test variant assignments |
ConfigManagerKeyStore.sqlite |
ConfigManager | API endpoints, limits, fraud toggle state |
ChimeraCoreResponseStore.sqlite (LiquidUI path) |
LiquidUI | Screen definition cache |
Chitragupt.sqlite |
Chitragupt | Full behavioral audit ledger (sub-second) |
Chronicle.sqlite |
Chronicle | App timeline + notification history |
Dash-Events.sqlite |
Dash | Performance metrics (screen render timestamps) |
kn_analytics_db.sqlite |
KN Analytics | Content/merchant interaction log |
BGFrameworkDataModel.sqlite |
BGFramework | Background task execution log |
CentralSyncManager.sqlite |
Sync | Cross-module sync gap analysis |
SamsaraDataStore.sqlite |
Samsara | UPI payment state machine transitions |
AthenaStore.sqlite |
Athena | On-device ML recommendation engine |
Cassini.sqlite |
Cassini | Document classification (KYC/QR) |
MaximusDataModel.sqlite |
Maximus | Promotions and offer engine |
NXCoreDataStore.sqlite |
NexusCore | Mini-app catalogue + sitemap |
MFDataStore.sqlite |
Mutual Funds | Portfolio holdings + SIP mandates |
RewardsDataStore.sqlite |
Rewards | Scratch cards β independent transaction proof |
BrandVouchersDataStore.sqlite |
Brand Vouchers | Merchant-specific spending patterns |
DonationsDataStore.sqlite |
Donations | NGO/charity payment records |
OffersDataStore.sqlite |
Offers | Merchant offer exposure + click timestamps |
YatraDataModel.sqlite |
Yatra | Travel bookings + passenger PII + PNR |
PrepaidRechargeDataStore.sqlite |
Recharge | Mobile/DTH recharge history + saved numbers |
CRMDataModel.sqlite |
CRM | Support ticket transcripts + user-authored dispute text |
Pratikriya.sqlite |
Pratikriya | User ratings + free-text feedback linked to transaction IDs |
Gravity.sqlite |
Gravity | Feed/discovery ranking state |
| Database | Forensic Role |
|---|---|
P2P.sqlite |
Split-bill groups, expenses, money requests, payment backgrounds |
SamparkV2.sqlite |
Financial social graph β phone β UPI ID β name β profile photo |
FoxtrotEventsDB.sqlite |
Analytics upload queue (PENDING = exclusively local evidence) |
| Artifact | What It Contains |
|---|---|
Preferences/com.phonepe.PhonePeApp.plist |
222 keys β session IDs, BoltV2 token, Chimera flags |
Preferences/com.phonepe.widgetxii.datacache.plist |
Primary UPI ID (home page cache) |
Preferences/com.phonepe.help.customDataStore.plist |
Registered name + auth tokens |
Preferences/com.phonepe.ads.sdk.plist |
GPS coordinates, pincode, state |
Preferences/com.phonepe.account.plist |
Token expiry + fetch timestamps |
Preferences/com.apple.AdSupport.plist |
IDFA enforcement |
Preferences/com.firebase.FIRInstallations.plist |
Firebase install ID |
Preferences/__gads__.plist |
Google Ads signals |
Cookies/Cookies.binarycookies |
Apple binary cookie format β decoded session cookies |
WebKit/ResourceLoadStatistics/ |
Website interaction history |
PhonePe iOS sandboxes evidence across three containers with different trust boundaries. Missing any one domain means missing evidence.
iOS Filesystem
β
βββ AppDomain-com.phonepe.PhonePeApp/
β βββ Documents/ β Core app databases (Transactions, Auth, Chat...)
β βββ Library/
β βββ Preferences/ β 15+ plist files (identity, tokens, location)
β βββ Cookies/ β Cookies.binarycookies
β βββ WebKit/ β ResourceLoadStatistics, offline storage
β
βββ AppDomainGroup-group.com.phonepe.PhonePeApp/
β βββ com.phonepe.PhonePeApp/
β βββ P2P/ β P2P.sqlite (split bills, money requests)
β βββ SamparkV2/ β SamparkV2.sqlite + profile photo BLOBs β¬
MOST CRITICAL
β βββ FoxtrotEventsStore/ β FoxtrotEventsDB.sqlite
β
βββ AppDomainGroup-group.com.phonepe.shared/
βββ Library/Preferences/ β Cross-process shared state
β οΈ Critical: Many investigations only includeAppDomain. Without the group containers, chat history, contact graph, and recent UPI events are unrecoverable β they are physically stored in the group sandbox. Always verify all three containers are present before drawing conclusions.
1. Full filesystem (jailbroken / GrayKey / Cellebrite Premium)
β All three containers + WAL files + free pages + temp files
2. Advanced Logical / AFC2 (jailbroken)
β AppDomain + AppDomainGroup accessible
3. iTunes Encrypted Backup
β AppDomain manifest-based; requires backup password
4. iTunes Unencrypted Backup
β Limited; some fields protected by iOS Data Protection
TransactionsStore.sqlite β committed, checkpointed data
TransactionsStore.sqlite-wal β recent uncommitted changes β CRITICAL
TransactionsStore.sqlite-shm β shared memory WAL index
SQLite in WAL mode writes changes to the -wal file first. Deleted records are not immediately removed β they remain in WAL or free pages until SQLite reuses storage. The tool opens all databases in mode=ro&cache=private β WAL contents are automatically applied.
β οΈ Never runPRAGMA wal_checkpoint(TRUNCATE)on evidence. It destroys WAL contents and with them any recoverable deleted records.
PPQL is a small, deterministic SPL-inspired query language built into the Hunt interface. Issue fast filters and aggregations across merged forensic indexes without writing SQL.
QUERY := SOURCE PIPE_OP*
SOURCE := "search" STRING -- full-text across the merged index
| "from" INDEX -- use a specific index
| INDEX -- alias for "from <index>"
PIPE_OP := "|" CMD ARG*
CMD := "where" CONDITION
| "search" STRING -- second-stage full-text filter
| "sort" FIELD ["asc"|"desc"]
| "head" N | "tail" N | "limit" N
| "table" FIELD ("," FIELD)*
| "top" N FIELD
| "rare" N FIELD
| "stats" AGG ("by" FIELD)?
| "dedup" FIELD
| "rename" FIELD "as" FIELD
AGG := "count" | "sum(" FIELD ")" | "avg(" FIELD ")"
| "min(" FIELD ")" | "max(" FIELD ")"
| Index | Description |
|---|---|
transactions |
Master ledger β ZTRANSACTIONENTITY |
contacts |
PhonePe-verified Cyclops contacts |
phonebook |
Raw device address book |
chat_groups |
Burble group containers |
chat_messages |
Individual chat messages |
notifications |
PubSub push topic archive |
timeline |
Unified chronological event stream |
Find all outgoing transactions over βΉ5,000 and sort by amount:
transactions
| where direction = "OUT" and amount_inr > 5000
| sort amount_inr desc
| head 50
| table created_at, counterparty, amount_inr, utr
Reconstruct a specific phone number's payment history:
transactions
| where counterparty_phone = "9876543210"
| stats count by state
Find all senders in chat who match a partial masked number:
chat_messages
| where sender_phone_masked like "*6259"
| stats count by sender_name
Top 10 regions of PhonePe contacts:
contacts
| where on_phonepe = true
| top 10 region
Regex match β find transactions to merchants containing "Bharath":
transactions
| where counterparty matches "[Bb]harath.*"
| stats sum(amount_inr) by counterparty
Timeline after a specific date from Burble:
timeline
| where source = "Burble" and when_iso > "2025-01-01"
| head 200
Full-text search across all indexes:
search "UTR401234567890"
search "UTR" | where amount_inr > 1000
Find failed transactions in a date range:
transactions
| where state = "FAILED"
| where created_at_iso > "2024-01-01" and created_at_iso < "2024-12-31"
| sort created_at_iso desc
| table created_at_iso, counterparty, amount_inr, response_code
correlator.py fuses evidence from all 16 modules into investigation-grade artifacts.
Merges every timestamped event across every module into one chronological stream. Sources fused:
TransactionsStore β UPI transaction events
Burble β Chat messages + payment cards
PubSubCore β Push notification arrivals
Foxtrot β Analytics batch uploads
Chitragupt β UI behavioral events (taps, keyboard, screen views)
Chronicle β App timeline + notification history
Samsara β UPI state machine transitions
Yatra β Travel booking timestamps
Analytics β KN content interactions
Recommendations β ML signal timestamps
Each event carries: { when_ms, when_iso, source, kind, title, detail, link_id?, amount_inr? }
Builds a counterparty-centric social graph by fusing:
- Contacts (
SamparkV2) β who is in the phonebook + UPI IDs - Transactions (
TransactionsStore) β who money moved to/from - Chat (
Burble) β who messages were exchanged with
Output: per-counterparty summary with transaction count, total amount, chat message count, shared groups, and last interaction timestamp.
Given any identifier (phone, VPA, or name), produces a comprehensive dossier:
- All transactions (in + out)
- All chat interactions
- Contact record details
- Financial relationship metrics
- Timeline of interactions
For each transaction ID, maps every database that references it:
Transaction T241119182327:
β TransactionsStore β ZTRANSACTIONENTITY row
β RewardsStore β ZSCRATCH_CARD with ZLINKED_TXN_ID
β PubSubCore β Push notification payload
β Chronicle β Timeline item (even if TransactionsStore row deleted)
β Burble β Chat PAYMENT_INFO_CARD
Corroboration Score: 5/5 β cannot be disputed as non-existent
A score of 1 with the single source being outside TransactionsStore is a red flag β it means a transaction ID exists in satellite evidence but the master ledger row is missing (possible deletion).
The detect_suspicious_signals function produces investigator flags:
| Signal | Severity | Trigger |
|---|---|---|
| High deletion intensity | π΄ High | freelist_count / page_count > 0.20 in transactions/contacts/chat DB |
| Failed/pending transactions | π‘ Medium | Any FAILED, PENDING, or REJECTED transactions |
| High-value transactions | π΅ Info | Any success transaction β₯ βΉ50,000 |
| Analytics upload failures | π΅ Info | Foxtrot events with β₯ 3 failed upload retries |
| Uncorroborated transaction IDs | π‘ Medium | TXN IDs visible in satellite DBs but absent from TransactionsStore |
| Wallet balance present | π΅ Info | eGV wallet balance > 0 (relevant to investigation scope) |
-- Quick deletion check on any database
PRAGMA freelist_count; -- Pages on freelist (deleted rows)
PRAGMA page_count; -- Total pages
-- > 20% = active deletion β flag in reportThe tool automatically runs this check and flags databases where deletion intensity suggests evidence tampering.
The most common error in published PhonePe forensic write-ups. PhonePe iOS uses three timestamp formats interchangeably.
| Value Range | Epoch | Example | Conversion |
|---|---|---|---|
~400M β 950M |
Apple CoreData (+ 978,307,200) | 721,692,800 β Nov 14, 2023 |
ts + 978307200 |
~1.4B β 1.8B |
Unix seconds | 1,700,000,000 β Nov 14, 2023 |
Direct |
~1.4T β 1.8T |
Unix milliseconds (Γ· 1000) | 1,700,000,000,000 β Nov 14, 2023 |
ts / 1000 |
The tool's normalize_timestamp() function auto-detects the epoch by value range:
def normalize_timestamp(value: Any) -> Optional[Dict[str, Any]]:
v = float(value)
if v > 1e12: # Unix milliseconds
epoch_s = v / 1000.0
elif v > 1e9: # Unix seconds
epoch_s = v
elif NSDATE_REASONABLE_MIN < v < NSDATE_REASONABLE_MAX:
epoch_s = v + APPLE_EPOCH_OFFSET # CoreData
# Returns { epoch_ms, iso, display, source }Every extracted timestamp is returned as { epoch_ms, iso, display, source } β the source field tells you which epoch was detected, so you can audit the conversion.
β οΈ A single miscategorized epoch shifts every date in the case by 31 years. Always verify the epoch before reporting dates.
All exports are available from the Exports page or per-view download buttons.
| File | Contents |
|---|---|
transactions.csv |
Full ledger β all decoded fields including counterparty, UTR, amount |
contacts_phonepe.csv |
Cyclops contacts with UPI IDs |
contacts_phonebook.csv |
Raw device phonebook |
chat_groups.csv |
Burble group metadata |
chat_messages.csv |
All messages with linked transaction IDs |
chat_shared_contacts.csv |
Bank account disclosures shared in chat |
linked_accounts.csv |
All bank accounts ever linked |
linked_cards.csv |
All payment cards |
timeline.csv |
Full unified timeline |
findings.csv |
Suspicious signal flags |
social_graph.csv |
Contact β transaction relationship map |
Structured master JSON of the complete case β all modules, all correlation outputs, all metadata β suitable for integration with other DFIR tooling.
Self-contained, single-file HTML evidence report with no external dependencies β fully renderable offline. Suitable for court submission or sharing with legal teams.
The Research tab inside the tool contains built-in reference documentation for every artifact β no internet required:
1. Architecture Overview β Storage domains, acquisition hierarchy, SDK map
2. Core Financial DBs β TransactionsStore, PaymentDataStore, P2P
3. Identity & Auth DBs β AccountSharedDataModel, AuthDataModel
4. Social Graph β SamparkV2 schema, profile photo recovery
5. Chat & Notifications β ChatPlatform, PubSubCore
6. Behavioral Analytics β Chitragupt, Foxtrot, Dash, Chronicle
7. Server Config & A/B Testing β Chimera, ExperimentationCore, ConfigManager
8. Financial Services β MF, Rewards, Vouchers, Donations, Offers
9. Travel & Recharge β Yatra, PrepaidRecharge
10. Infrastructure β BGFramework, CentralSyncManager, Samsara
11. Specialty DBs β Pratikriya, CRM, Gravity, Cassini
12. Plist Files β All 15+ plists with field-level documentation
13. WebKit & Cookies β Binary cookie format, ResourceLoadStatistics
14. Timestamp Reference β Three epochs, conversion formulas, detection guide
15. Corroboration Framework β Cross-DB evidence matrix
16. Query Arsenal β 30+ ready-to-run PPQL queries
phonepe-ios-forensics/
β
βββ run.py β Entry point
β
βββ phonepe_forensics/
β βββ __init__.py β Package init, version
β βββ core.py β Parsing primitives
β βββ extractors.py β 16 extraction modules (~2,200 lines)
β βββ correlator.py β Correlation engine
β βββ hunt.py β PPQL parser & executor
β βββ case.py β Case orchestrator
β βββ case_manager.py β Multi-case registry
β βββ reports.py β Report exporter
β βββ research_data.py β Built-in reference docs
β βββ webapp.py β Flask web UI (35+ routes)
β β
β βββ templates/
β β βββ base.html β Navigation + layout
β β βββ dashboard.html
β β βββ transactions.html β Filterable ledger
β β βββ transaction_detail.html
β β βββ contacts.html
β β βββ social_graph.html
β β βββ chat.html
β β βββ chat_group.html
β β βββ identity.html
β β βββ analytics.html
β β βββ financial.html
β β βββ travel.html
β β βββ payment_infra.html
β β βββ audit.html
β β βββ timeline.html
β β βββ hunt.html β PPQL query interface
β β βββ counterparty.html β Per-counterparty dossier
β β βββ database_browser.html β Raw SQL interface
β β βββ database_sql.html
β β βββ findings.html
β β βββ research.html β Built-in docs index
β β βββ research_section.html
β β βββ exports.html
β β βββ cases_list.html
β β βββ case_new.html
β β βββ case_detail.html
β β βββ ...
β β
β βββ static/
β βββ css/app.css
β βββ js/app.js
β
βββ .pp_forensics/ β Auto-created β case registry
β βββ cases.json
β
βββ exports/ β Auto-created β export output
βββ <case_name>/
βββ transactions.csv
βββ contacts_phonepe.csv
βββ timeline.csv
βββ master.json
βββ report.html
Contributions are warmly welcomed. PhonePe's app is actively developed β new databases and schema changes appear with each app update.
- π New SDK parsers β new databases added in recent PhonePe versions
- π Schema corrections β if you've found a real schema difference from your extraction
- π PPQL extensions β new operators, aggregations, index definitions
- π Correlation heuristics β new suspicious signal categories
- π Export formats β JSONL, XLSX, court-ready PDF templates
git clone https://github.com/thelocalh0st/phonepe-ios-forensics.git
cd phonepe-ios-forensics
python -m venv venv && source venv/bin/activate
pip install flask
# Run with auto-reload for development
FLASK_DEBUG=1 python run.py- Fork β feature branch β PR against
main - New parsers go in
extractors.pyfollowing the existingextract_*pattern - All parsers must handle
None, empty tables, and partial corruption without raising exceptions - Include the specific SQLite path and table/column names for any new artifact
- If correcting a schema error, cite the real column name and how you verified it
This tool is intended exclusively for:
- β Law enforcement β criminal investigations with appropriate legal authority
- β Licensed digital forensic examiners β working under professional mandate
- β Cybercrime investigators β authorized UPI fraud case analysis
- β Legal/compliance professionals β internal investigation with device owner consent
- β Security researchers β academic/responsible disclosure contexts
- β Device owners β analyzing your own device's data
Always ensure you have legal authorization before examining any device. Unauthorized access to a person's device data may violate the Computer Fraud and Abuse Act (CFAA), the IT Act 2000 (India), and equivalent legislation in your jurisdiction.
This tool is a read-only forensic viewer. It never writes to, modifies, or deletes any file in the evidence folder.
| Resource | Link |
|---|---|
| π Original Research Blog Post | PhonePe Forensics in iOS β thelocalh0st.com |
| π Apple Core Data SQLite Internals | Apple Developer Documentation |
| ποΈ SQLite WAL Mode | SQLite WAL Documentation |
| π iOS App Sandbox Domains | iOS Security Guide β Apple |
| π³ NPCI UPI Technical Specs | NPCI β Unified Payments Interface |
| π NSKeyedArchiver Format | Apple plist Format Reference |
| πͺ Binary Cookie Format | Satishb3 β Safari Binary Cookie Reader |
| π± iOS DFIR Fundamentals | SANS FOR585 β Smartphone Forensic Analysis |




