[Scena Switch] Cage The Unicorn, il nuovo emulatore di debug per Nintendo Switch

Cage The Unicorn è un nuovo emulatore di debug per Nintendo Switch, è possibile eseguire per intero sysmodule o applicazioni di debug, eseguire il tracciamento e il debug del codice, test exploit, fuzz e altro ancora.

Non si potranno eseguire backup di giochi, questo è scontato, anche perché non presenta alcun supporto per la grafica, il suono, l’input o qualsiasi tipo di elaborazione anche remota. Tutto questo è stato fatto per il solo design.

Installazione

  • Installare Unicorn da git e assicurarsi di aver installato Python 2.7.x.
  • Creare una directory denominata SwitchFS/archives e copiare tutti i file .bin dall’archivio di sistema, se un sysmodule richiede l’esecuzione.

Utilizzo

Il caso più semplice è quello di eseguire un sysmodule. Per impostarlo, eseguire:

./addtitle.sh <fonte di destinazione>

Dove <fonte di destinazione> è il nome della directory (si consiglia di includere il nome del sistema e la versione del sistema, ad esempio pctl30), la sorgente è la directory contenente i binari.

La directory di destinazione verrà creata insieme a un file run.py e load.yaml che verranno copiati insieme ai binari del source. Per eseguirlo, è sufficiente digitare da Python target/run.py.

È possibile visualizzare uno scheletro vuoto nella directory skeletonSample.

load.yaml

Il file load.yaml definisce tutto quello che deve essere caricato durante un processo, è un dizionario con le seguenti chiavi ammissibili:

  • nxo/nro/nso — Filename singolo (stringa) o array di nomi di file da caricare. Non include l’estensione dei file.
  • mod — Filename singolo (stringa) da caricare.
  • bundle — Filename di un bundle di memoria da caricare. Se il filename.gz esiste e il nome del file non viene eseguito, CTU decomprime automaticamente il file al primo caricamento.
  • maps — Dizionario degli indirizzi di classe -> file di mappa.
    • Ogni voce deve essere la versione di Titlecase del binario che la mappa è disponibile, ad es. main becomes Main. Il valore della voce assume la forma [0xf00b, "something.map"], dove l’indirizzo è la base del caricamento della mappa.

API

Questa è l’API principale dell’oggetto CTU, utilizzabile da run.py.

  • call(pc, [X0, [X1, ...]], _start=False) — Chiama una determinata funzione nativa su un thread appena creato, è possibile passare qualsiasi numero di argomenti; Il valore di ritorno è X0 all’uscita. Se _start è True, X0 è 0 e X1 è l’handle del thread appena creato.
  • debugbreak() — Interrompe il debugger.
  • malloc(size) — Assegna la size dei byte all’interno dello spazio indirizzo dell’ospite.
  • free(addr) — No-op!
  • reg(i, [val]) — Legge o assegna un registro per numero o nome (X0X31LR, and SP).
  • pc — Proprietà che consente di leggere/scrivere il valore del PC per il thread corrente.
  • dumpregs() — Mostra tutti i registri.
  • dumpmem(addr, size, check=False) — Hexdump un blocco di memoria. Secheck è True e memcheck è attivato, sarai avvisato per le letture di memoria non modificate.
  • readmem(addr, size) e writemem(addr, value) — legge o scrive la memoria come stringa di byte.
  • read8/16/32/64(addr) e write8/16/32/64(addr, value) — Legge o scrive la memoria come un intero senza segno di una determinata dimensione.
  • readS8/16/32/64(addr) — legge la memoria come un numero intero firmato di una determinata dimensione.
  • readstring(addr) — Legge una stringa fino al terminatore null o fino alla memoria non rimovibile.
  • newHandle(obj) — Assegna un nuovo handle a obj e restituisce tale handle ID.
  • closeHandle(handle) — Chiude un handle.
  • map(base, size) — Mappa un blocco di memoria. Se non si trova allineato con la pagina, questo avverrà automaticamente. La memoria verrà annullata se l’esecuzione viene riavviata.
  • unmap(base, size) — Libera un blocco di memoria.
  • getmap(addr) — Restituisce la base, size di tuple er un determinato indirizzo, o -1, -1 se la memoria non è stata mappata.
  • memregions() — Restituisce un generatore che fornisce le tuple di (begin, end, perms) per tutte le regioni mappate e non mappate; Le regioni non definite hanno perms == -1.
  • hookinsn(insn) — Decoratore che consente di associare un dato valore d’istruzione (come un intero). La funzione decorata dovrebbe avere argomenti ctu, addr dove addr è l’indirizzo dell’istruzione. Il gancio accade prima dell’esecuzione.
  • hookfetch(addr) — Decoratore che consente di associare un dato indirizzo di istruzione prima dell’esecuzione. La funzione decorata non assume argomenti.
  • hookread(addr) e hookwrite(addr) — Decoratori che consentono di agganciare la memoria in lettura/scrittura ad un indirizzo. I decoratori dovrebbero avere argomenti ctu, size o ctu, addr, size, value. I ganci di lettura possono restituire un valore di sostituzione. Se il writehook restituisce non-None/False, viene eliminato.
  • replaceFunction(addr) — Decoratore che consente di sostituire una funzione nel codice nativo nell’indirizzo indicato. La funzione decorata ottiene ctu come primo argomento, ma può prendere qualsiasi numero di argomenti al di là di esso (mappati automaticamente da X0 … X30) e restituire un numero qualsiasi di valori (mappato automaticamente a X0 … X30). La funzione originale non verrà eseguita.

IPC Client

Sysmodules espone l’interfaccia IPC sulla rete. I dettagli del protocollo wire sono documentate nel file wireprotocol.txt, ma è incluso un client Python.

L’utilizzo di esempio è in skeletonSample/client.py. Si crea un oggetto Client e si connette a un’istanza CTU, quindi crea e invia IPCMessages avanti e indietro.

Debugger Reference

Il debugger in CTU è approssimativamente basato su gdb ma ha alcune differenze chiave che veramente irritano i fan di GDB.

  • exit — Esci.
  • s/start — Avvia o riavvia l’esecuzione
  • t/trace (i/instruction | b/block | f/function | m/memory) — Consente di attivare il tracciamento.
  • mc/memcheck — Consente di attivare il monitoraggio delle violazioni di accesso alla memoria.
  • b/break [name] — Senza name, lista breakpoint; Altrimenti commuta il breakpoint per il nome o l’indirizzo del simbolo.
  • bt — Stampa lo stack di chiamata.
  • sym <name> — Stampa l’indirizzo di un dato simbolo.
  • c/continue — Continua l’esecuzione.
  • n/next — Singolo step.
  • r/reg/regs [reg [value]]
    • No parameters: Visualizza tutti i registri.
    • Reg parameter: Visualizza un registro.
    • Reg and value: Assegna un valore (sempre hex, o un nome di simbolo) a un registro.
  • x/exec <code> — Valuta una determinata riga in C.
  • dump <address> [size=0x100] — Dumpa size byte della memoria in un indirizzo. Se l’indirizzo ha la forma *register (es. *X1), verrà utilizzato il valore di quel registro.
  • save <address> <size> <fn> — Scrivi size byte della memoria in un file. Se l’indirizzo o la dimensione assumono il modulo *register (es. *X1), verrà utilizzato il valore di quel registro.
  • ad — Attiva la specializzazione di visualizzazione degli indirizzi.
  • w/watch [expression] — Si interrompe quando l’espressione viene valutata come true. Senza un’espressione, elenca i watchpoint esistenti.
  • mr/memregions — Visualizza le regioni di memoria mappate.

Source code: Cage The Unicorn

(Visited 147 times, 1 visits today)

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *