venerdì 1 settembre 2023

Published settembre 01, 2023 by Django Faiola with 0 comment

dfPassJSON (Carte e biglietti in Wallet)

Panoramica

Un pass è una raccolta di dati che rappresentano un biglietto, un coupon o una carta. Può essere destinato a un singolo utilizzo da parte di un individuo (e quindi contenere dettagli come un numero di volo e un'allocazione dei posti) o può essere un token di utilizzo multiplo che può essere condiviso da qualsiasi numero di utenti (ad esempio un coupon di sconto). Una descrizione dettagliata è disponibile nel documento About Pass Files di Apple.

https://learn.microsoft.com/it-it/xamarin/ios/platform/passkit

Wallet, una sorta di portafogli elettronico, è l'app di Apple che archivia e visualizza carte di credito o di debito, biglietti per i trasporti, biglietti per eventi, coupon e carte fedeltà nei propri dispositivi.

I pass vengono distribuiti all'utente come file compressi firmati digitalmente che contengono un file JSON con la descrizione e altre risorse come immagini e localizzazione delle stringhe (opzionale).

Struttura del pass sul disco

Sul disco vengono memorizzati nella cartella principale:

Backup: HomeDomain/Library/Passes/
File System: /private/var/mobile/Library/Passes/

Ogni pass ha una sua sottocartella denominata "<nome-del-pass>.pkpass".

I file presenti nella radice di questa cartella sono:

  • background.png: Immagine con il sottofondo della parte frontale del pass;
  • footer.png: Immagine visualizzata sulla parte frontale del pass vicino al codice a barre;
  • pass.json: Dizionario JSON con i dati e i metadati del pass;
  • icon.png: L'icona del pass visualizzata nelle notifiche, email e nella schermata di blocco;
  • logo.png: Immagine visualizzata sulla parte frontale del pass in alto a sinistra;
  • manifest.json: Dizionario JSON dove ogni chiave è il percorso relativo del file al top level del pacchetto e il valore della chiave è l'hash SHA-1 di questo file. Vengono esclusi dolo il file manifest.json stesso e signature;
  • pass.json: Dizionario JSON che definisce il pass (dati e metadati);
  • signature: Una firma PKCS #7 del file "manifest.json";
  • strip.png: L'immagine visualizzata dietro il campi primari sulla parte frontale del pass;
  • thumbnail.png: Immagine (miniatura) aggiuntiva visualizzata sulla parte frontale del pass utilizzata per esempio come foto del titolare in una tessera;

xx.ljproj sono le cartelle che contengono la localizzazione delle stringhe del pass memorizzate sul file "pass.strings". A volte sono presenti anche le immagini dei loghi personalizzati per la lingua o altre risorse.

Esempi di layout di un pass:

Carta d'imbarco

Coupon
Biglietto per eventi
Generico
Carte fedeltà

Per approfondimenti consulta la documentazione ufficiale Pass Design and Creation.

pass.json

Questo file JSON contiene dati e metadati che permettono sia l'identificazione del pass che la sua rappresentazione visiva. E' suddiviso a livelli di chiavi con il primo livello (Top-Level Keys) come segue:

  • Standard Keys: Informazioni di base per l'identificazione del pass;
    • description: (localizable string), required, descrizione del pass;
    • formatVersion: (integer), required, versione del formato del file, 1 (uno);
    • organizationName: (localizable string), required, nome dell'organizzazione che ha generato e firmato il pass;
    • passTypeIdentifier: (string), required, identificatore del tipo di pass;
    • serialNumber: (string), required, numero di serie univoco del pass;
    • teamIdentifier: (string), required, identificatore del team dell'organizzazione che ha generato e firmato il pass.
  • Associated App Keys: Informazioni su un'app associata a un pass;
    • appLaunchURL: (string), un URL da passare all'app associata durante l' avvio;
    • associatedStoreIdentifiers: (array of numbers), un elenco di identificatori di elementi di iTunes per le app associate.
  • Companion App Keys: Informazioni personalizzate su un pass fornito per un'app Companion da utilizzare.
    • userInfo : (any JSON data), informazioni personalizzate che non vengono visualizzate dall'utente.
  • Expiration Keys: Informazioni sulla scadenza e validità del pass;
    • expirationDate: (W3C date, as a string), data di scadenza completa di ore, minuti e facoltativamente secondi;
    • voided: (boolean), indica se il pass è stato esplicitamente contrassegnato come annullato (es. un coupon riscattato). Valore predefinito false.
  • Relevance Keys: Informazioni sul dove e quando un pass è rilevante (es. beacon, posizione o data e ora);
    • beacons: (array of beacon, dictionaries), i beacon che segnano i luoghi in cui il pass è rilevante; Il beacon è un dizionario di tipo "Beacon Dictionary Keys";
    • locations: (array of location, dictionaries), posizioni in cui il pass è rilevante; La location è un dizionario di tipo "Location Dictionary Keys".
    • maxDistance: (number), distanza massima in metri da una latitudine e longitudine rilevanti che per il pass è rilevante;
    • relevantDate: (W3C date, as a string), data e ora quando il pass diventa rilevante (es. biglietto del cinema, inizio del film).
  • Style Keys: Unica chiave che corrisponde al tipo del pass. Il valore di questa chiave è un dizionario di tipo "Pass Structure Dictionary Keys".
    • boardingPass: carta d'imbarco;
    • coupon: coupon;
    • eventTicket: biglietti per eventi;
    • generic: pass generici;
    • storeCard: carte fedeltà.
  • Visual Appearance Keys: Informazioni che definiscono l'aspetto estetico del pass (es. codici a barre, colori, sfondo, etc.);
    • barcode: (barcode dictionary), dizionario "Barcode Dictionary Keys" con le informazioni sul codice a barre;
    • barcodes: (array of barcode, dictionaries), lista di barcode;
    • backgroundColor: (color, as a string), colore di sfondo del pass, es. rgb(23, 187, 82);
    • foregroundColor: (color, as a string), colore del testo del pass, es. rgb(100, 10, 110);
    • groupingIdentifier: (string), identificatore di raggruppamento per pass correlati es. pass di gruppo. Valido solo per i biglietti per eventi e carte d'imbarco;
    • labelColor: (color, as a string), colore del testo dell'etichetta;
    • logoText: (string), testo visualizzato accanto al logo del pass;
    • suppressStripShine: (boolean), se true, l'immagine viene visualizzata senza l'effetto brillante.
  • Web Service Keys: Informazioni per l'aggiornamento del pass via web tramite URL e token di autenticazione;
    • authenticationToken: (string), il token di autorizzazione per il servizio web. Il token deve essere almeno 16 caratteri;
    • webServiceURL: (string), l'URL del servizio web conforme alle API descritte in PassKit Web Service Reference.
  • NFC-Enabled Pass Keys: Informazioni per il supporto NFC per inviare informazioni sulla carta premi come parte di una transazione Apple Pay;
    • message: (string), required, il payload da trasmettere al terminal Apple Pay. Deve essere 64 byte o meno;
    • encryptionPublicKey: (string), chiave crittografica pubblica in Base64.
  • Semantic Tags Keys: I metadati presenti nei tag semantici sono leggibili a livello macchina e aiutano il sistema a comprendere meglio i pass del portafogli e a suggerire le azioni pertinenti che l'utente può intraprendere; es. l'impostazione della modalità non disturbare per la durata di un film. Possono essere presenti a livello di root del pass o a livello di Top-Level-Keys all'interno delle chiavi dei dizionari "Pass Structure Dictionary Keys" ; auxiliaryFields, backFields, headerFields, primaryFields, secondaryField.
A un livello più profondo (diciamo secondo livello) vi sono le "Pass Structure Dictionary Keys" che definiscono la struttura del pass, dividendola in sottoparti:

  • auxiliaryFields: (array of field, dictionaries), campi aggiuntivi da visualizzare sulla parte frontale del pass;
  • backFields: (array of field, dictionaries), campi sulla parte posteriore del pass;
  • headerFields: (array of field, dictionaries), campi da visualizzare nell'intestazione della parte anteriore del pass;
  • primaryFields: (array of field, dictionaries), campi da visualizzare in modo prominente sulla parte frontale del pass;
  • secondaryFields: (array of field, dictionaries), campi da visualizzare in modo normale sulla parte frontale del pass;
  • transitType: (string), è richiesto per le carte d'imbarco e ha uno dei seguenti valori: PKTransitTypeAir, PKTransitTypeBoat, PKTransitTypeBus, PKTransitTypeGenericPKTransitTypeTrain.

Field Dictionary Keys: un campo è un dizionario che contiene le chiavi per definire diverse categorie di valori: standard, date e numeri.

  • Standard Field Dictionary Keys: Queste chiavi sono usate per tutti i dizionari che richiedono un campo;
    • attributedValue: (localizable string, ISO 8601 date as a string, or number), valore dell'attributo del campo (es. codice HTML per un link);
    • changeMessage: Specifica il formato della stringa di testo utilizzata per notificare che il pass è stato aggiornato (escape %@);
    • dataDetectorTypes: Rilevatori di dati che vengono applicati al valore del campo della parte posteriore del pass. Possibili valori:
      • PKDataDetectorTypePhoneNumber;
      • PKDataDetectorTypeLink;
      • PKDataDetectorTypeAddress;
      • PKDataDetectorTypeCalendarEvent.
    • key: (string), required, la chiave deve essere univoca all'interno dell'ambito dell'intero pass;
    • label: (localizable string), testo dell'etichetta associata;
    • textAlignment: (string), allineamento del contenuto del campo:
      • PKTextAlignmentLeft: a sinistra;
      • PKTextAlignmentCenter: al centro;
      • PKTextAlignmentRight: a destra;
      • PKTextAlignmentNatural: basato sulla direzione di scrittura.
    • value: (localizable string, ISO 8601 date as a string, or number), required, è il valore del campo.
  • Date Style Keys: Informazioni su come una data e/o ora devono essere rappresentati in visualizzazione;
    • dateStyle: (string), formato di visualizzazione delle date tra i possibili valori:
      • PKDateStyleNone: "";
      • PKDateStyleShort: "11/23/37" o "3:30 PM";
      • PKDateStyleMedium: "Nov 23, 1937" o "3:30:32 PM";
      • PKDateStyleLong: "November 23, 1937" o "3:30:32 PM PST";
      • PKDateStyleFull: "Tuesday, April 12, 1952 AD" o "3:30:42 PM Pacific Standard Time".
    • ignoresTimeZone: (boolean), visualizza sempre la data e l'ora nel fuso orario dato e il suo valore predefinito è false;
    • isRelative: (boolean), se true l'etichetta viene visualizzata come data relativa e il suo valore predefinito è false;
    • timeStyle: (string), formato di visualizzazione delle ore con i possibili valori già elencati in dateStyle;
  • Number Style Keys: Informazioni su come un numero deve essere rappresentato in visualizzazione.
    • currencyCode: (string), ISO 4217 currency code (EUR, USD);
    • numberStyle: (string), possibili valori con locale (en_US, fr_FR e  zh_CN):
      • PKNumberStyleDecimal: "1,234.568", "1 234,568", "1,234.568";
      • PKNumberStylePercent: "12%", "12 %", "12%";
      • PKNumberStyleScientific: "1.2345678E3", "1,2345678E3", "1.2345678E3";
      • PKNumberStyleSpellOut: "one hundred twenty-three", "cent vingt-trois", "一百二十三".

Location Dictionary Keys: contiene le informazioni sulla posizione.

  • altitude: (double), altitudine in metri;
  • latitude: (double), required, latitudine in gradi decimali;
  • longitude: (double), required, longitudine in gradi decimali;
  • relevantText: (string), testo visualizzato sulla schermata di blocco quando il pass è attualmente rilevante.

Beacon Dictionary Keys: contiene le informazioni dei beacon.

  • major: (16-bit unsigned integer), versione maggiore del beacon Bluetooth Low Energy;
  • minor: (16-bit unsigned integer), versione minore del beacon Bluetooth Low Energy;
  • proximityUUID: (string), required, identificatore univoco del beacon Bluetooth Low Energy;
  • relevantText: (string), testo visualizzato sulla schermata di blocco quando il pass è attualmente rilevante.
Barcode Dictionary Keys: contiene le informazioni sul codice a barre del pass.
  • altText: (string), testo visualizzato vicino al codice a barre;
  • format : (string), required, formato del codice a barre. Possibili valori;
    • PKBarcodeFormatQR;
    • PKBarcodeFormatPDF417;
    • PKBarcodeFormatAztec;
    • PKBarcodeFormatCode128
  • message: (string), required, messaggio da visualizzare come codi;
  • messageEncoding: (IANA character set name, as a string), required, il nome del set di caratteri IANA della codifica del testo da utilizzare per convertire il messaggio da una rappresentazione di stringa in una rappresentazione dei dati che il sistema rende come codice a barre, come "ISO-8859-1".
Semantic Tag Currency Amount: contiene le informazioni sulla quantità di denaro e tipo di valuta.
  • amount: (string), quantità di denaro;
  • currencyCode: (ISO 4217 currency code as a string), codice valuta es. EUR.
Semantic Tag Seat: contiene le informazioni sull'identificazione del posto per le carte d'imbarco o per i biglietti di evento.
  • seatDescription: (localizable string), descrizione del posto es. "A flat bed seat";
  • seatIdentifier: (localizable string), codice identificatore del posto;
  • seatNumber: (localizable string), numero del posto;
  • seatRow: (localizable string), fila che contiene il posto;
  • seatSection: (localizable string), sezione che contiene il posto;
  • seatType: (localizable string), tipo di posto, es. "Reserved seating".
Semantic Tag Location: contiene le informazioni sulle coordinate del posto.
  • latitude: (double), required, latitudine in gradi decimali;
  • longitude: (double), required, longitudine in gradi decimali;
Semantic Tag WiFi Network: contiene le informazioni richieste per la connessione alla rete WiFi.
  • password: (string), required, password della rete WiFi;
  • ssid: (string), required, nome della rete WiFi.
Semantic Tag Person Name Component: contiene le informazioni che compongono il nome di una persona.
  • familyName: (string), nome di famiglia o cognome;
  • givenName: (string), nome;
  • middleName: (string), secondo nome;
  • namePrefix: (string), prefisso per il nome es. "Dr";
  • nameSuffix: (string), suffisso per il nome es. "Junior";
  • nickname: (string), nickname;
  • phoneticRepresentation: (string), pronuncia del nome.

Per completare il quadro dei pass bisogna tenere conto anche della lingua se disponibile ovvero la traduzione locale di tutti i campi di tipo localizable string.

Questo è un esempio di "pass.strings" della lingua italiana presente nella sottocartella "it.lproj" del pass.

"departure_time_label" = "Ora";
"departure_date_label" = "Data";
"gate_close_time_label" = "Il gate chiude";
"queue_name_label" = "Fila";
"seat_number_label" = "Posto";
"seat_paid_number_label" = "Posto*";
"passenger_name_label" = "Passeggero";
"flight_number_label" = "N. volo";
"sequence_number_label" = "Sequenza";
"recordlocator_label" = "Riferimento";
"departure_station_label" = "Da";
"arrival_station_label" = "A";
"baggage_label" = "Bagagli";
"discount_label" = "Sconto";
"fast_track" = "Fast Track";
"business_plus" = "Flexi Plus";
"leisure_plus" = "Plus";
"infant" = "Neonato";
"queue_other" = "Non Prioritario";
"queue_priority" = "Priorità";
"boarding_pass" = "Carta D’Imbarco";
"ssr_label" = "Extra per il viaggio";
"bags_collected_at_label" = "Bagagli ritirati a";
"connection_flight_label" = "Volo in coincidenza - Finale";
"operated_by_label" = "Operato da";
"smallbag_label" = "1 borsa piccola a bordo (40 cm x 20 cm x 25 cm) che entra sotto il sedile";
"twocabinbags_label" = "2 x Bagagli a mano";
"10kg_label" = "Bagaglio da stiva da 10 kg";
"departure_station" = "Roma (Ciampino)";
"arrival_station" = "Francoforte (Hahn)";
"ssr_names" = "1xBagaglio da stiva da 10 kg"; 
Come si procede? semplice, per ogni valore del campo di tipo localizable string si cerca la corrispondente traduzione. Supponiamo di avere un campo del tipo  "label": "departure_time_label", a "departure_time_label" corrisponde nel dizionario "it.lproj" il nuovo valore localizzato "Ora", quindi "label": "Ora".

Per maggiori informazioni sui Wallet Passes fare riferimento alla documentazione ufficiale Wallet Passes.

Esempio di decodifica

Per fare un po' di chiarezza a tutto questo "pastrocchio" analizziamo un caso concreto di una carta d'imbarco (cartella principale del pass 48LkoTqneTAeJW5kDN3H7f9CvQk=.pkpass).

pass.json
{
   "serialNumber":"465829952",
   "formatVersion":1,
   "passTypeIdentifier":"pass.booking.swiss.com",
   "teamIdentifier":"6K643MP477",
   "authenticationToken": "daeb1f05-XXXX-YYYY-ZZZZ-883ffd57146b",
   "webServiceURL": "https://prod.bc.ncrwebhost.mobi/mobiqa/servlet/Passbook",
   "description":"SWISS Boarding pass",
   "locations": [
         {
           "latitude" : 49.6233,
           "longitude" : 6.2044,
           "relevantText" : "LX759 Boarding 18:35"
         }
        ],
   "expirationDate":"2016-09-26T05:46:30+00:00",
   "barcode":{
      "message":"M1FAIOLA/D      E8LO2H7 LUXZRHLX 0759 267Y011A0047 148>5180 M267 BLX              2A7242301648536N                           N",
      "format":"PKBarcodeFormatAztec",
      "messageEncoding":"iso-8859-1"
   },
   "organizationName":"SWISS",
   "logoText":"",
   "foregroundColor":"rgb(0, 0, 0)",
   "backgroundColor":"rgb(255, 255, 255)",
   "labelColor":"rgb(0, 0, 0)",
   "boardingPass":{
      "transitType":"PKTransitTypeAir",
      "headerFields":[
         {
            "key":"seat",
            "label":"Seat",
            "value":"11A",
            "changeMessage":"Seat number changed to %@"
         },
{
            "key":"gate",
            "label":"Gate",
            "value":"A09",
            "changeMessage":"Gate changed to %@."
         }
      ],
      "primaryFields":[
         {
            "key":"depart",
            "label":"Departure",
            "value":"LUX",
            "changeMessage":"Departure airport changed to %@."
         },
         {
            "key":"destination",
            "label":"Arrival",
            "value":"ZRH",
            "changeMessage":"Destination airport changed to %@."
         }
      ],
      "secondaryFields":[         
         {
            "key":"passenger",
            "label":"Passenger",
            "value":"FAIOLA/D",
            "changeMessage":"Passenger changed to %@."
         },
{
            "key":"ff_status",
            "label":"Status",
            "value":"",
            "changeMessage":"Status number changed to %@"
         }],
              
      "auxiliaryFields":[
         {
            "key":"flightNumber",
            "label":"Flight",
            "value":"LX759",
            "changeMessage":"Flight number changed to %@"
         },
         {
            "key":"date",
            "label":"Date",
            "value":"23September",
            "changeMessage":"Date number changed to %@"
         },
         {
            "key":"boarding",
            "label":"Boarding",
            "value":"18:35",
            "changeMessage":"Boarding number changed to %@"
         },
         {
            "key":"bookingClass",
            "label":"Class",
            "value":"Y",
            "changeMessage":"Class number changed to %@"
         }
      ],
"backFields" : [
{
"key":"eTicket",
"label":"Your ticket number:",
"value":"724 2301 648 536"
},
{
"key":"SWISSwishes",
"label":"SWISS wishes you a pleasant flight.",
"value":"Before departure please read the detailed information regarding  baggage regulations carefully.\r\rNo  dangerous goods are allowed in either checked or carry-on baggage. \r\rTo change your seat or if you plan not to travel, please go to <a href='https://checkin.swiss.com/ck.fly?locale=EN&amp;first_name1=D&amp;last_name1=FAIOLA&amp;departure_port=LUX&amp;departure_date=2016/09/23&amp;flight_number=759&amp;carrier=LX&amp;group=NO&amp;etkt=7242301648536#'>swiss.com</a>.\r\rSWISS.COM"
}
    ]
    
   }
}

I risultati dei campi sono formattati come (key) label=value

  • Standard Keys
    • description: SWISS Boarding pass
    • formatVersion: 1
    • organizationName: SWISS
    • passTypeIdentifier: pass.booking.swiss.com
    • serialNumber: 465829952
    • teamIdentifier: 6K643MP477
  • Expiration Keys
    • expirationDate: 2016-09-26T05:46:30+00:00
  • locations (posizione[0])
    • latitude: 49.6233
    • longitude: 6.2044
    • relevantText: LX759 Boarding 18:35
  • Style Keys: boardingPass "Pass Structure Dictionary Keys"
    • transitType: PKTransitTypeAir (aereo)
    • headerFields (campi di intestazione[0]): (seat) Seat=11A
      • key: seat
      • label: Seat
      • value: 11A
    • headerFields (campi di intestazione[1]): (gate) Gate=A09
      • key: gate
      • label: Gate
      • value: A09
    • primaryFields (campi primari[0]): (depart) Departure=LUX
      • key: depart
      • label: Departure
      • value: LUX
    • primaryFields (campi primari[1]): (destination) Arrival=ZRH
      • key: destination
      • label: Arrival
      • value: ZRH
    • secondaryFields (campi secondari[0]): (passenger) Passenger=FAIOLA/D
      • key: passenger
      • label: Passenger
      • value: FAIOLA/D
    • secondaryFields (campi secondari[1]): (ff_status) Status=
      • key: ff_status
      • label: Status
      • value
    • auxiliaryFields (campi ausiliari[0]): (flightNumber) Flight=LX759
      • key: flightNumber
      • label: Flight
      • value: LX759
    • auxiliaryFields (campi ausiliari[1]): (date) Date=23September
      • key: date
      • label: Date
      • value: 23September
    • auxiliaryFields (campi ausiliari[2]): (boarding) Boarding=18:35
      • key: boarding
      • label: Boarding
      • value: 18:35
    • auxiliaryFields (campi ausiliari[3]): (bookingClass) Class=Y
      • key: bookingClass
      • label: Class
      • value: Y
    • backFields (campi retro[0]): (eTicket) Your ticket number:=724 2301 648 536
      • key: eTicket
      • label: Your ticket number:
      • value: 724 2301 648 536
    • backFields (campi retro[1]): (SWISSwishes) SWISS wishes you a pleasant flight.=Before departure please read the detailed information regarding  baggage regulations carefully.\r\rNo  dangerous goods are allowed in either checked or carry-on baggage. \r\rTo change your seat or if you plan not to travel, please go to <a href='https://checkin.swiss.com/ck.fly?locale=EN&amp;first_name1=C&amp;last_name1=FAIOLA&amp;departure_port=LUX&amp;departure_date=2016/09/23&amp;flight_number=759&amp;carrier=LX&amp;group=NO&amp;etkt=7242301648536#'>swiss.com</a>.\r\rSWISS.COM
      • key: SWISSwishes
      • label: SWISS wishes you a pleasant flight.
      • value: Before departure please read the detailed information regarding  baggage regulations carefully.\r\rNo  dangerous goods are allowed in either checked or carry-on baggage. \r\rTo change your seat or if you plan not to travel, please go to <a href='https://checkin.swiss.com/ck.fly?locale=EN&amp;first_name1=C&amp;last_name1=FAIOLA&amp;departure_port=LUX&amp;departure_date=2016/09/23&amp;flight_number=759&amp;carrier=LX&amp;group=NO&amp;etkt=7242301648536#'>swiss.com</a>.\r\rSWISS.COM

Questo è un semplice esempio di come decodificare la struttura di un pass, le cose possono complicarsi un po' con la presenza di numeri formattati, valute, beacon, tag semantici, etc. Un processo alquanto laborioso da svolgere a mano per un singolo pass, immaginate di ripeterlo per 100 ed ecco che viene in aiuto dfPassJSON, strumento creato ad hoc per i pass in grado di fare in automatico tutto questo e generare i report nei formati di testo semplice (.txt), testo separato da virgola (.csv) e foglio di calcolo Excel (.xls e .xlsx).

dfPassJSON

dfPassJSON, è se così si può definire uno "spin-off" del progetto WhatsAppIZZA nato nel gennaio del 2021 come parser autonomo per i pass del Wallet di Apple.

Windows | Linux

E' uno strumento user-friendly, bastano pochi click e l'analisi dei pass è pronta.


Seleziona la lingua di localizzazione, per esempio it per l'italiano, spunta il checkbox "Full details" per ottenere più dettagli nel report e/o semplicemente seleziona il percorso della cartella principale "Passes/" dei pass.

Per finire, fai click sul pulsante "Export as" per generare il report finale e seleziona il formato, per esempio Excel 2007.

Questo è un piccolo esempio di report in formato Excel:


Read More

giovedì 24 agosto 2023

Published agosto 24, 2023 by Django Faiola with 0 comment

Come correggere l'encoding nei JSON di Facebook

Facebook ha una bellissima funzione che permette di scaricare su richiesta l'archivio dei tuoi dati personali.

Dal menu Impostazioni seleziona "Le tue informazioni su Facebook" e infine seleziona "Scarica le tue informazioni". Ora puoi richiedere un download.

Non è lo scopo di questo post elencare tutte le opzioni disponibili nella fase di creazione della copia di backup di Facebook; in sintesi è possibile filtrare la quantità di dati per periodo, categorie, qualità dei contenuti multimediali e per finire il formato. Due formati di presentazione dei dati sono disponibili: HTML e JSON. 

Il formato JSON è il nostro caso di studio e a mio avviso il formato consigliato da scegliere per un'analisi più completa (basti pensare ai timestamp che nel formato JSON sono di valore numerico UTC mentre nell'HTML sono rappresentazioni localizzate di stringhe di difficile interpretazione).

Tuttavia se le stringhe contengono caratteri Unicode (es. le lettere accentate), i parser JSON non sono in grado di ricostruire le stringhe originali.  

HTML

JSON

A prima vista sembra che Facebook rappresenti i caratteri in UTF-8 in modo non standard. Infatti, se prendiamo come riferimento il testo "può" nel formato HTML, il corrispondente testo in JSON è "può". Cosa c'è di sbagliato? Questa è la rappresentazione del testo in esame del file JSON in HxD Hex Editor:

Il carattere "ò" è rappresentato dalla sequenza "\u00c3\u00b2" una coppia di caratteri Unicode che il parser interpreta come Latin-1 (che mappa esattamente i 256 caratteri ai 256 possibili valori di byte) "ò", invece dovrebbe essere un singolo carattere "\uc3b2". Questo significa che ogni carattere non ASCII è codificato due volte usando la sequenza di escape "\u00HH". Nel nostro caso il carattere "\uc3b2" è scritto come "\u00c3" (Ã) e "\u00b2" (²).

Soluzione

E' possibile correggere questa "anomalia" in due modi:

  1. Decodifica del JSON, codifica della stringa in Latin-1 e infine decodifica in UTF-8;
  2. Leggere il file come binario, individuare e sostituire tutte le doppie sequenze "\u00HH\u00HH" con l'unica sequenza "\uHHHH" e infine decodifica  come JSON.

WhatsAppIZZA utilizza il metodo 2: usa una semplice regular expression per eseguire il match e poi la sostituzione dei valori.

(?i)\\u00([a-f0-9]{2})

Nel nostro caso "\u00c3\u00b2", dai match c3 e b2 ottiene "\uc3b2" che in UTF-8 è proprio il carattere "ò".

Read More

giovedì 10 agosto 2023

Published agosto 10, 2023 by Django Faiola with 0 comment

Identificazione App in multiutente

dfAPKdngrader come identifica le app in multiutente? e quelle in modalità clone con la tecnologia nativa?

La logica di base è molto semplice. In primo luogo chi sono gli utenti configurati sul dispositivo?

adb shell pm list users
Users:
        UserInfo{0:Owner:13} running
        UserInfo{10:New user:10}

in questo caso sono presenti 2 utenti:

  1. id=0; name=Owner; flags=0x13 
  2. id=10; name=New user; flags=0x10

per ogni utente viene eseguito il comando per ottenere la lista di tutti i package installati:

adb shell pm list package -3 --user 0
package:com.whatsapp
package:fr.vinted
package:com.google.ar.core
package:com.rebelvox.voxer
package:it.subito

adb shell pm list package -3 --user 10
package:fr.vinted
package:com.kia.eu.mykia
package:com.wallapop

come è possibile notare il comando --user 10 mette in evidenza che i package com.kia.eu.mykiacom.wallapop sono stati installati solo per utente 10, mentre fr.vinted è presente su entrambi gli account.

dfAPKdngrader quindi associa a ogni package la lista degli id utenti.

Per quanto riguarda la tecnologia di clonazione nativa (Samsung Dual Messenger, Samsung Secure Folder, Huawei/Honor App Twin, Huawei PrivateSpace, Xiaomi Dual Apps, Oppo Clone Apps, Vivo App Clone, OnePlus Parallel Apps, Asus Twin Apps, LG Dual App, realme App Cloner, etc.) la questione si complica un pò e varie tecniche sono state implementate per cercare di identificare quella in uso e non per tutti i brand. dfAPKdngrader non è in grado di capire al 100% se l'utente è realmente un utente clone e l'identificazione è del tutto sperimentale.

Huawei/Honor App Twin

Sempre con la mia cavia Huawei Android 7 (Nougat), ho attivato la clonazione "App Gemella (App Twin)" di WhatsApp. Per poter identificare l'utente associato all'app gemella, in primo luogo dfAPKdngrader identifica il costruttore.

adb shell getprop ro.product.manufacturer
HUAWEI

Lista degli utenti

adb shell pm list users
Users:
        UserInfo{0:Proprietario:13} running
        UserInfo{10:Gemella:4000010} running

Da test effettuati (non è sicuro al 100%), sembra che l'utente clone è sempre identificato con l'id 10. Il nome dell'utente è una stringa localizzata "Gemella" per l'italiano "App Twin" per l'inglese.

Come si può vedere il programma mostra il badge (giallo) per indicare che è un app in multiutente e il badge (lavanda) per l'app clonata.

Samsung

Identificazione del costruttore

adb shell getprop ro.product.manufacturer
SAMSUNG

Samsung permette di clonare un'app con due tecnologie: "Dual Messenger" e "Secure Folder".

Lista degli utenti "Dual Messenger"

adb shell pm list users
Users:
        UserInfo{0:Pippo:13} running
        UserInfo{95:DUAL_APP:20000030} running

Il nome dell'utente è una stringa fissa "DUAL_APP", mentre per l'id è 95, variabile? Per i due dispositivi testati ho riscontrato sempre 95. Per ora l'identificazione Dual Messenger è basata solo sul nome fisso "DUAL_APP".

Lista degli utenti "Secure Folder" 

adb shell pm list users
Users:
        UserInfo{0:Pippo:13} running
        UserInfo{150:Secure Folder:10021030} running

Entrambi, nome dell'utente "Secure Folder" e id 150 sono fissi.

LG Dual App

Lista degli utenti
adb shell pm list users
Users:
        UserInfo{0:Pippo:13} running
        UserInfo{97:dual:1030} running

Da ricerche basate sul web, il nome dell'utente è fisso "dual" mentre l'id è variabile in un range 90..99. Valori più frequenti dell'id sono 97 e 98.

Tutto il resto...

Chi vuole dare un contributo per comprendere meglio l'identificazione delle app clonate native è libero di postare i suoi dati. Grazie in anticipo.

Riferimenti

Read More

sabato 5 agosto 2023

Published agosto 05, 2023 by Django Faiola with 0 comment

Aggiungere un'app a dfAPKdngrader

Vuoi aggiungere una tua app a dfAPKdngrader?

Molto semplice, pochi passi e l'app è disponibile nel programma. Vediamo come inserire per esempio WhatsApp:

  1. Rinomina il file della tua app "nuova app.apk" in "com.whatapp.apk" (packageName.apk);
  2. Sposta il file "com.whatapp.apk" nella cartella "APKs";
  3. Scarica l'icona in formato PNG e rinomina in "com.whatsapp.png" (packageName.png);
  4. Sposta il file "com.whatsapp.png" nella cartella "APKs";
  5. Apri il file "APKs.json" con un editor di testo (es: Notepad++);
  6. Aggiungi un nuovo oggetto alla fine dell'array packages e compila tutte le coppie proprietà/valori come segue (es: WhatsApp Messenger):
    },
    {
    "packageName": "com.whatsapp",
    "marketName": "WhatsApp Messenger",
    "versionCode": 450146,
    "versionName": "2.11.431",
    "minSdk": 7,
    "abiList": "armeabi, armeabi-v7a, x86, mips",
    "isCustom": true
    }
  7. Salva il file ed esci;
  8. Esegui dfAPKdngrader.

APK Info

Andrea Lazzarotto ha sviluppato uno script in python "APK Info" che analizza un file .apk, ne verifica l'autorizzazione per il backup e infine genera il codice da inserire nel file di configurazione "APKs.json". Stupendo un po' di lavoro manuale in meno!

Note:

La coppia proprietà/valore "isCustom": true è obbligatoria per indicare che l'applicazione è di tipo personale.

I possibili valori di "abiList" sono: noarch, armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips, mips64.

Se non desideri scaricare e associare l'immagine alla tua app puoi saltare i punti 3 e 4, verrà assegnata l'icona di default.


Read More
Published agosto 05, 2023 by Django Faiola with 0 comment

dfAPKdngrader (Forensic extraction tool for Android Backup APK Downgrade method)

 Introduzione

Progetto nato il 27 ottobre 2021 con l'idea di creare un nuovo strumento di semplice utilizzo e robusto allo stesso tempo come supporto valido per la comunità forense open-source/freeware in grado di eseguire il metodo del downgrade delle app su dispositivi Android.

Di recente ho ripreso il progetto iniziale con l'intento di potenziarlo, non dico al pari di quelli commerciali, ma non meno valido di questi. Dopo aver speso tanto, tanto tempo, sono riuscito a portarlo a un buon livello.

dfAPKdngrader è compatibile con Android OS 5.0 o superiore, supporta sia le app a singolo APK che i Bundle/Split, avvisa se l'app è installata in multiutente, esegue il ripristino automatico o manuale delle app in caso di downgrade fallito e per concludere solo le app che sono eleggibili per il downgrade, ovvero che hanno superato tutti i test di verifica, sono mostrate nella scheda APPLICAZIONI INSTALLATE DOWNGRADABILI.

Ultima versione

Windows | Linux

Supporta il mio progetto e considera una piccola donazione su PayPal.

Elenco delle app supportate

Attualmente supporta 200+ applicazioni di terze parti.
  1. adidas Running
  2. AfreecaTV
  3. Airbnb
  4. Algosa
  5. AliExpress
  6. Alipay
  7. Amazon Alexa
  8. Amazon Music
  9. Amazon Prime Video
  10. Amazon Seller
  11. Amazon Shopping
  12. Amberfog for VK
  13. American Airlines
  14. Aruba PEC
  15. ASKfm
  16. Audiomack
  17. AutoScout24
  18. Azar
  19. Badoo
  20. Baidu Browser
  21. Baidu Netdisk
  22. BAND
  23. BBC News
  24. Bigo Live
  25. BiP
  26. BlackBerry Messenger
  27. Bolt
  28. bonprix
  29. Box
  30. BoyAhoy
  31. Bumble
  32. Burger King Italia
  33. CamScanner
  34. Careem
  35. Chat & Date
  36. ChatON
  37. Coffee Meets Bagel
  38. Cookpad
  39. CSL
  40. Daum
  41. Deliveroo
  42. Dolphin Browser
  43. Dropbox
  44. DuckDuckGo Privacy Browser
  45. Duolingo
  46. ebay
  47. ebay Annunci
  48. Endomondo
  49. eToro
  50. Evernote
  51. Expedia
  52. Facebook
  53. Facebook Messenger
  54. FireChat
  55. Firefox Browser
  56. Fitbit
  57. Flipboard
  58. FM WhatsApp
  59. foodpanda
  60. FreeTone
  61. GB WhatsApp
  62. Geocaching®
  63. Glovo
  64. Gmail
  65. GO SMS Pro
  66. Goibibo
  67. Gojek
  68. Google Chrome
  69. Google Drive
  70. Google Maps
  71. Google Meet
  72. Google Talk
  73. Grindr
  74. GroupMe
  75. Groupon
  76. happn
  77. Hike News & Content
  78. Huawei Backup
  79. ICQ New
  80. idealista
  81. imo
  82. Indeed
  83. Instagram
  84. Intesa Sanpaolo Mobile
  85. Italo Treno
  86. Jack'd
  87. Joongna
  88. KakaoMetro
  89. KakaoNavi
  90. KakaoStory
  91. KakaoTalk
  92. Kaktus
  93. KAYAK
  94. Keepsafe Photo Vault
  95. kik
  96. Libero Mail
  97. Lidl
  98. Lightning
  99. LINE
  100. LinkedIn
  101. LOVOO
  102. MakeMyTrip Hotels, Flight, Bus
  103. Maxthon browser
  104. McDonald's
  105. Meetic
  106. Meetup
  107. MeWe
  108. MOMO
  109. Mooney App
  110. Moovit
  111. MYPEUGEOT APP
  112. NateOn UC
  113. NAVER Blog
  114. NAVER Cafe
  115. NAVER Mail
  116. NAVER Map
  117. NAVER MYBOX
  118. NAVER Search
  119. Nike Run Club
  120. OK
  121. OkCupid
  122. Once
  123. ONE NAVI
  124. ooVoo
  125. Opera Mini (Android)
  126. Opera Mini (Native)
  127. PayPal
  128. Pinterest
  129. Pocket
  130. Plenty of Fish
  131. Postepay
  132. Prezzi Benzina
  133. Puffin Browser
  134. Pulse for Booking.com partners
  135. QQ
  136. QQ (old)
  137. QQ International
  138. Radarbot
  139. ROMEO
  140. Ryanair
  141. Samsung Health
  142. SATUSEHAT Mobile
  143. Secondamano
  144. SHEIN
  145. Showroomprive
  146. Signal Private Messenger
  147. SinglesAroundMe
  148. SKOUT
  149. SkyDrive/OneDrive
  150. Skype
  151. Skype for Business
  152. Skyscanner
  153. Slack
  154. Snapchat
  155. Sogou Map
  156. Speedtest by Ookla
  157. Steam
  158. Subito.it
  159. Sygic GPS Navigation
  160. TamTam
  161. Tango
  162. TaoBao
  163. Telegram
  164. Telegram X
  165. Telepass
  166. Text Free
  167. Text Me
  168. TextNow
  169. textPlus
  170. Textra SMS
  171. TheFork
  172. The Weather Channel
  173. Threema
  174. TigerConnect (TigerText)
  175. TikTok
  176. TikTok (Asia)
  177. Tinder
  178. Tiscali Mail
  179. Todoist
  180. Too Good To Go
  181. Trenitalia
  182. Trip.com
  183. Tripadvisor
  184. Trivago
  185. Truecaller
  186. Tumblr
  187. Tumile
  188. Twitch
  189. Twitter
  190. UC Browser
  191. Unicorn HTTPS
  192. Unlimited Texting
  193. Vault - Hide Pics
  194. Venmo
  195. Viber Messenger
  196. Vimeo
  197. Vine Camera
  198. Vinted
  199. VK
  200. Voxer Walkie Talkie Messenger
  201. Wallapop
  202. Waze
  203. WeChat
  204. Weibo
  205. WhatsApp Messenger
  206. Whisper
  207. Wickr Me
  208. Wire
  209. Wish
  210. WowApp
  211. Xabber
  212. Yahoo Mail
  213. Yahoo Messenger
  214. Yandex Mail
  215. Yandex Maps
  216. Yandex Navigator
  217. Yelp
  218. Yr
  219. Yuka
  220. Zalando
  221. Zalo
  222. Zangi Messenger
  223. Zello
  224. Zepp Life (Mi Fit)
  225. Zoiper
  226. Zomato
  227. ZOOM Cloud Meetings
  228. zooplus
Read More

venerdì 4 agosto 2023

Published agosto 04, 2023 by Django Faiola with 0 comment

APK Downgrade Manuale - Parte 2

 

Introduzione

Nel post APK Downgrade Manuale - Parte 1 è stata mostrata la procedura su come eseguire il downgrade delle applicazioni di terze parti nel caso di singolo package installato sul dispositivo. Come procedere nel caso in cui il package è diviso in più file?

Procedura manuale del downgrade (Bundle/Split)

Dal mio vecchio smartphone (la mia cavia) con Android Nougat proviamo a scalare l'applicazione Deliveroo: consegna di cibo che so per certo è un pacchetto diviso. Vecchia versione utilizzata 2.54.0 precedentemente scaricata e salvata sul computer con il nome del file "com.deliveroo.orderapp.old.apk".

Eseguire il prompt dei comandi.

Lista dei package installati di terze parti

adb shell pm list package -3
package:pedometer.steptracker.calorieburner.stepcounter
package:com.shazam.android
package:com.whatsapp
package:fr.vinted
package:uk.co.novaware.telepass.android
package:com.deliveroo.orderapp
package:com.instagram.android
package:com.twitter.android
package:io.yuka.android
package:com.zhiliaoapp.musically
package:com.topjohnwu.magisk
package:com.glovo
package:com.wallapop
package:com.snapchat.android

Lista path del package com.deliveroo.orderapp

adb shell pm path com.deliveroo.orderapp
package:/data/app/com.deliveroo.orderapp-2/base.apk
package:/data/app/com.deliveroo.orderapp-2/split_config.arm64_v8a.apk
package:/data/app/com.deliveroo.orderapp-2/split_config.it.apk
package:/data/app/com.deliveroo.orderapp-2/split_config.xxhdpi.apk
Come si può vedere l'applicazione deliveroo è divisa in 4 file.

Copia dell'apk originale sulla workstation

adb pull -a /data/app/com.deliveroo.orderapp-2/base.apk
adb pull -a /data/app/com.deliveroo.orderapp-2/split_config.arm64_v8a.apk
adb pull -a /data/app/com.deliveroo.orderapp-2/split_config.it.apk
adb pull -a /data/app/com.deliveroo.orderapp-2/split_config.xxhdpi.apk

Se viene riscontrato un errore del tipo

adb: error: remote object '/data/app/com.deliveroo.orderapp/base.apk' does not exist

allora bisogna creare una copia dei file prima del pull nella cartella tmp.

adb shell cp /data/app/com.deliveroo.orderapp-2/*.apk /data/local/tmp/
adb pull /data/local/tmp/base.apk
adb pull /data/local/tmp/split_config.arm64_v8a.apk
adb pull /data/local/tmp/split_config.it.apk
adb pull /data/local/tmp/split_config.xxhdpi.apk

Disinstallazione dell'app originale dal dispositivo preservando i dati dell'utente

adb shell pm uninstall -k com.deliveroo.orderapp

Riavvio del dispositivo

adb reboot

Downgrade

adb push com.deliveroo.orderapp.old.apk /data/local/tmp/
adb shell pm install /data/local/tmp/com.deliveroo.orderapp.old.apk
adb shell rm /data/local/tmp/com.deliveroo.orderapp.old.apk

Backup

adb backup -f deliveroo.ab -apk -obb -keyvalue com.deliveroo.orderapp

Inserire la password per esempio "1234". Il backup può essere eseguito anche (consigliato) utilizzando direttamente l'utility di backup bu.

Ripristino dell'applicazione originale

Nel caso in cui la copia degli originali sulla workstation è stata eseguita direttamente senza l'ausilio della cartella tmp, ovvero solo tramite una serie di pull,  bisogna creare una copia di questi sul dispositivo.

adb push base.apk /data/local/tmp/
adb push split_config.arm64_v8a.apk /data/local/tmp/
adb push split_config.it.apk /data/local/tmp/
adb push split_config.xxhdpi.apk /data/local/tmp/

Utilizzando i file originali precedentemente copiati nella cartella tmp, per ripristinare l'app originale bisogna creare un nuovo package di installazione come segue:

adb shell pm install-create -r
Success: created install session [800467548]

adb shell pm install-write 800467548 base.apk /data/local/tmp/base.apk
Success: streamed 29701113 bytes

adb shell pm install-write 800467548 split_config.arm64_v8a.apk /data/local/tmp/split_config.arm64_v8a.apk
Success: streamed 21107947 bytes

adb shell pm install-write 800467548 split_config.it.apk /data/local/tmp/split_config.it.apk
Success: streamed 147865 bytes

adb shell pm install-write 800467548 split_config.xxhdpi.apk /data/local/tmp/split_config.xxhdpi.apk
Success: streamed 418214 bytes

adb shell pm install-commit 800467548
Success

Android Backup (.ab)

Il file di backup .ab (Android Backup) è proprietario con magic numbers "ANDROID BACKUP", dati compressi con algoritmo deflate e cifrati con AES CBC se impostata la password di backup. Esistono vari tool per convertire il file .ab in .tar, uno di questi è Android Backup Extractor oppure con il tool integrato in dfAPKdngrader.

Read More
Published agosto 04, 2023 by Django Faiola with 0 comment

APK Downgrade Manuale - Parte 1

Introduzione

Oggi è sempre più difficile estrarre prove da smartphone con Android 7 o superiore e non sempre è così facile ottenere i privilegi di root per eseguire un'estrazione filesystem o meglio eseguire un'estrazione fisica decriptata del dispositivo. 

Fare un'estrazione logica con gli strumenti di backup forniti dal costruttore, Samsung Kies/Smart Switch, Huawei HiSuite, Xiaomi Mi Backup, etc. o semplicemente sfruttare il backup nativo Android via ADB può portare a un risultato che non è quello tanto "sperato", l'amato/odiato WhatsApp per esempio è escluso dal backup nativo.

Non resta che tentare di scalare le app di interesse ed eseguire il backup nativo, metodo noto come APK downgrade.

Procedura manuale del downgrade (singolo package)

Prima di procedere al downgrade manuale bisogna verificare se il dispositivo è sbloccato o quantomeno essere in possesso del PIN e/o password di sblocco, attivare le opzioni sviluppatore, nello specifico abilitare Debug USB, modificare tutte le opzioni che permettono di rendere possibile l'estrazione dei dati senza intoppi (modalità aereo, resta attivo etc.)...già fatto? vero! hai provato già gli altri metodi di estrazione.

Proviamo a scalare l'applicazione Vinted: vendi e compra vestiti utilizzando la vecchia versione 6.19.2.0 precedentemente scaricata e salvata sul computer con il nome del file "com.vinted.old.apk".

Eseguire il prompt dei comandi.

Lista dei package installati di terze parti

adb shell pm list packages -3
package:com.whatsapp
package:fr.vinted

Lista path del package fr.vinted

adb shell pm path fr.vinted
package:/data/app/fr.vinted-vui9dpo9lDF-F3aDKrvrSA==/base.apk

Copia dell'apk originale sulla workstation

adb pull -a /data/app/fr.vinted-vui9dpo9lDF-F3aDKrvrSA==/base.apk
/data/app/fr.vinted-vui9dpo9lDF-F3aDKrvrSA==/base.apk: 1 file pulled, 0 skipped. 110.4 MB/s (33533699 bytes in 0.290s)

Disinstallazione dell'app originale dal dispositivo preservando i dati dell'utente

adb shell pm uninstall -k fr.vinted
Success

Riavvio del dispositivo

adb reboot

Questo permette di bypassare il seguente messaggio di errore "Failure [INSTALL_FAILED_VERSION_DOWNGRADE]" generato quando si tenta di installare la vecchia versione senza riavviare.

Downgrade

adb push fr.vinted.old.apk /data/local/tmp/
fr.vinted.old.apk: 1 file pushed, 0 skipped. 518.3 MB/s (14644009 bytes in 0.027s)

adb shell pm install /data/local/tmp/fr.vinted.old.apk
Success

adb shell rm /data/local/tmp/fr.vinted.old.apk

Backup

adb backup -f vinted.ab -apk -obb -keyvalue fr.vinted
WARNING: adb backup is deprecated and may be removed in a future release
Now unlock your device and confirm the backup operation...

Inserire la password per esempio "1234". Il backup può essere eseguito anche (consigliato) utilizzando direttamente l'utility di backup bu.

Ripristino dell'applicazione originale

adb push base.apk /data/local/tmp/
base.apk: 1 file pushed, 0 skipped. 240.2 MB/s (33533699 bytes in 0.133s)

adb shell pm install -r /data/local/tmp/base.apk
Success
adb shell rm /data/local/tmp/base.apk

Android Backup (.ab)

Il file di backup .ab (Android Backup) è proprietario con magic numbers "ANDROID BACKUP", dati compressi con algoritmo deflate e cifrati con AES CBC se impostata la password di backup. Esistono vari tool per convertire il file .ab in .tar, uno di questi è Android Backup Extractor oppure con il tool integrato in dfAPKdngrader.

Read More

martedì 1 agosto 2023

Published agosto 01, 2023 by Django Faiola

Android Backup APK Downgrade

Introduzione

Il backup nativo di Android è stato per anni uno dei metodi più comuni di raccolta dei dati delle app utilizzato dagli esperti forensi. Ad oggi e più precisamente dalla versione 5.0 (Lollipop) tale metodo produce una raccolta molto limitata dei possibili dati acquisibili in quanto la maggior parte degli sviluppatori di applicazioni di terze parti, tenuto conto della sicurezza dei dati personali, hanno disabilitato l'autorizzazione di backup; vedi per esempio WhatsApp, Facebook, etc.

Cos'è l'APK downgrade?

Gli investigatori ed esaminatori per poter bypassare questo ostacolo hanno messo a punto un metodo detto APK downgrade che consiste, detto in parole semplici, nello scalare l'app con una vecchia versione che ha la proprietà di backup abilitataallowBackup='true' nel file AndroidManifest.xml.

Il metodo può essere schematizzato nei seguenti passi:

  1. copia dell'app originale sulla workstation;
  2. disinstallazione dell'app originale dal dispositivo preservando i dati dell'utente;
  3. installazione temporanea della vecchia versione compatibile che consente il backup;
  4. backup;
  5. ripristino dell'app originale sul dispositivo.

Il passaggio chiave di questo metodo forense è quello di preservare i dati utente durante la disinstallazione dell'app originale. Nel caso di whatsapp per esempio, la riga comando è: 

adb shell pm uninstall -k com.whatsapp

dove -k è il parametro che permette di preservare i dati e com.whatsapp è il package che si vuole disinstallare. 

Purtroppo il downgrade delle applicazioni installate di terze parti non è sempre possibile e soprattutto non è infallibile. Esistono molte variabili in gioco, le forti personalizzazioni dell'Android stock, incompatibilità delle ABIs (Application Binary Interface), cambio del "package name" nel tempo, app con permesso di backup disabilitato sin dalla prima versione, etc. sono solo alcuni dei problemi che non permettono l'utilizzo di questo metodo.

Come ha ben illustrato Heather Mahalik (Senior Director of Digital Intelligence and Forensics at Cellebrite) nel suo blog When to Use the Android APK Downgrade Feature in Cellebrite UFED il downgrade deve essere utilizzato come una sorta di "ultima spiaggia" dopo aver esaurito tutti gli altri metodi di estrazione con scarsi risultati.

L'APK downgrade permette di ottenere dati rilevanti da svariate applicazioni senza avere i privilegi di root, ma come ho detto sopra ha i suoi limiti: 

  • Bisogna essere in possesso di un dispositivo sbloccato o quantomeno con PIN e/o password di sblocco noti (richiesti durante la fase di riavvio);
  • Possibile perdita delle autenticazioni su alcune app (è richiesto di nuovo il login anche dopo aver ripristinato l'originale);
  • Perdita delle posizioni delle app nella schermata home (rimosse durante la disinstallazione);
  • Alcuni dati potrebbero andare persi (non dell'utente).

Conclusioni

Il downgrade delle app è un metodo fantastico, la sua utilità non si discute e le migliori case software del settore forense lo hanno inserito nei loro prodotti con svariati nomi: Belkasoft X (APK downgrade), Cellebrite UFED (Android Backup APK Downgrade), Magnet Acquire/Axiom (ADB con l'opzione "Try do downgrade it to..."), Oxygen Forensic Detective (APK downgrade), MOBILedit Forensic (App downgrade), GMDSOFT MD Next (Android App Downgrade) e XRY (App downgrade).

Nella comunità forense, open-source e freeware, vi sono alcuni software/script che "vantano" di eseguire il downgrade delle app ma il mio "piccolo" strumento forense user-friendly dfAPKdngrader (freeware) fa davvero il suo dovere. 

Read More
Published agosto 01, 2023 by Django Faiola

Welcome

Appunti di Informatica Forense

Benvenuti, questo blog nasce in primo luogo come punto comune di raccolta per la distribuzione di programmi (Freeware) da me creati per la comunità forense, approfondimenti e supporto agli stessi; in secondo luogo per la condivisione degli appunti sulle ricerche relative all'analisi di artefatti, reverse engineering e tutto ciò che è connesso alla Digital Forensics con particolare attenzione alla Mobile Forensics.

Non sono un blogger. Ho impostato una semplice struttura di base, il giusto per poter condividere i miei appunti e scrivere qualche articolo, quindi perdonatemi se non è così bello 😄 

I commenti sono sempre benvenuti e apprezzati, la condivisione della conoscenza di più! 

Django Faiola

Read More