Reversing di MSI
From UIC
Reversing di MSI
Contents |
| Infos | |
|---|---|
| Author: | DrWatson |
| Email: | alex_dsoft@yahoo.it |
| Website: | None |
| Date: | 12/08/2007 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| Comments: |
None |
Introduzione
In questo tutorial vediamo rapidamente la struttura di un file MSI (Microsoft Installer) per passare poi alle tecniche di debugging, decisamente più interessanti.
Tools
Ci serviranno
Essay
Il formato MSI è nato per supplire alla carenza di un sistema centralizzato e valido di gestione dei pacchetti software su sistemi windows, in grado di gestire correttamente tutte le fasi di installazione / rimozione del software, con qualche funzionalità in più. Prima erano gli installer InstallShield, Wise e compagnia, più o meno a pagamento, che generavano eseguibili che installavano i programmi. Alcuni non impostavano correttamente le chiavi di registro per la rimozione, altri lasciavano in giro file, altri ancora sovrascrivevano incautamente DLL vitali di sistema, causando fenomeni noti come il DLL-Hell. MSI è un formato di database, come recita wikipedia: "loosely relational", infatti il file MSI altro non è che una collezione di tabelle che contengono la descrizione delle procedure di installazione e rimozione del pacchetto software, ben dettagliate e contenenti la descrizione della GUI da presentare, il tree dei pacchetti o le condizioni sotto le quali l'installazione può/non può avvenire o deve seguire altri percorsi, oltre che file binari. Alcune tabelle possono contenere infatti file binari che possono venir invocati dalle azioni contenute nelle tabelle. Un esempio è proprio la richiesta di un serial all'installazione del pacchetto, una funzione che MSI non supporta nativamente (per tante buone ragioni) e che ogni autore di pacchetti deve implementare in una DLL che MSI caricherà al momento opportuno. In quanto a momenti... Il funzionamento di un'installazione MSI si divide in "fasi", in cui il server MSI installato sul computer (msiexec.exe, servizio msiserver) carica il file MSI, la presentazione all'utente delle opzioni d'installazione e il lavoro sui file vero e proprio. Finché l'utente non ha specificato tutte le opzioni che il setup prevede, nessuna azione viene intrapresa sul sistema.
Le azioni più comuni
Per editare un MSI consiglio Microsoft Orca, un MSI editor free di microsoft che è il punto di partenza di ogni reversing di MSI. Aperto l'MSI con Orca ci si presenta la lista delle tabelle, segnalo come importanti per i nostri scopi le tabelle CustomActions e Binary. In una buona parte dei casi in cui lavoreremo con MSI ci sarà semplicemente da saltare alcuni controlli iniziali, tipo la versione di windows in uso. Ad esempio l'SDK per DirectX9.0, che mi serviva per un cliente, non voleva installarsi su Windows 2000 (dove ho Visual Studio 2005). La modifica da farsi va cercata nella tabella LaunchConditions, probabilmente ci sarà una riga che vincola l'installazione con una condition tipo
che verifica che il sistema sia windows xp (5.01) o superiore, o magari qualcosa di più complesso, tipo
che si assicura che windows sia Xp o superiore, o anche windows 2000 (5.0) solo se ha il service pack 4 (o superiori). Alcune conditions potremmo doverle togliere per facilitarci il lavoro, o per distribuire a terzi pacchetti non pensati per certe configurazioni.
Le CustomActions
La tabella CustomActions contiene, in ordine, le azioni che MSI eseguirà oltre a mostrare le solite finestre. Un esempio di una CustomAction che possiamo trovare può portare il nome suggestivo di Keyvalidate o magari RequireSerial, ma probabilmente il programmatore accorto avrà usato un nome più idiota (e mascherabile) tipo CustomAction5F7B o simili. Le CustomActions possono essere dei riferimenti ad altre tabelle, a vbscript/jscript contenuti in tabelle, a messaggi da mostrare, a eseguibili contenuti nella tabella Binary da eseguire o a funzioni di DLL (sempre contenute in Binary) da chiamare. Per una lista dei tipi di CustomActions potete provare su MSDN, ma non è completa. Per i nostri scopi c'è da sapere che, oltre il tipo 63, i bit restanti sono flag. Capita così di trovare la custom action di tipo 65, che altro non è che la customaction di tipo 1 (chiamata a DLL) con il flag 64 (esecuzione sincrona, attendi il ritorno dalla DLL) settato. Nella colonna Source c'è il riferimento ad una riga della tabella Binary (il "nome" della DLL), mentre in Target c'è il nome dell'export da invocare. In alcuni casi per proseguire nell'installazione è sufficiente rimuovere fisicamente la richiesta del serial dalla tabella (basta cancellare la riga dell'Action corrispondente alla richiesta), tanto poi il programma al primo avvio probabilmente richiederà il serial, al massimo si tratterà di togliere anche una verifica più oltre sul formato (MSI offre alcune primitive per il controllo del seriale, che ruotano attorno alla Property PIDKEY (nella tabella Properties), che possono fermare l'installazione durante la copia dei file). In altri casi invece la richiesta del codice durante il setup deve essere soddisfatta perché il programma non si occupa della registrazione ma cerca informazioni che solo il setup MSI può impostare sul sistema, caso questo di Visual Studio 2005, che durante l'installazione chiama una DLL esterna a validare il codice. Questa però lo memorizza anche, crittato, in una chiave del registry che l'IDE cerca al suo avvio. Se la chiave non è presente l'IDE non si avvia nemmeno, quindi non è possibile semplicemente "saltare" la CustomAction del seriale nel setup di Visual Studio. Sarà uno dei casi in cui andrà debuggata la DLL.
Le Custom DLL
Queste DLL vengono aggiunte al MSI dall'autore del pacchetto software nel caso in cui voglia aggiungere funzionalità all'installazione oltre alle classiche verifiche sulla versione di windows, sono delle vere e proprie DLL che esportano funzioni che verranno chiamate direttamente da un'istanza di MSIexec.exe. Le troviamo elencate nella tabella Binary, per esportarle da Orca un semplice doppio click sulla colonna [Binary Data]. Scegliamo di scrivere un file, e salviamo con un nome decente la Dll. Tentando di aprirla con IDA, nella maggior parte delle volte IDA stesso c'informerà che il file contiene le informazioni di debug, quindi scaricherà da Microsoft Symbol Server le informazioni sulle chiamate e le caricherà nel database aperto. Ci concentriamo subito sulle export. Se cerchiamo un nome noto (trovato sulla tabella delle CustomActions) e lo apriamo, vediamo che molte delle chiamate sono a MSI stesso. In particolare ci interessano MsiGetProperty, MsiSetProperty e MsiGetMode. Infatti la DLL può leggere e scrivere le variabili globali dello "script" contenute nelle tabelle (principalmente nella tabella Properties) tramite queste funzioni, e tramite MsiGetMode saprà se la chiamata corrente è dovuta a un'installazione, a una rimozione o a una modifica delle opzioni. Anche se non è un'ipotesi da scartare a priori, raramente viene usato il valore di ritorno della funzione.
Primi passi nell'attacco a MSI
Per iniziare a mettere le mani su MSI bisogna sapere che MSIexec.exe offre all'autore una manciata di utili strutture di debug, che useremo anche noi nel nostro lavoro. Se ancora non fosse chiaro dalla lista delle CustomActions, dobbiamo capire su quale funzione di quale DLL andare a mettere le mani. Ci possono essere più DLL in un pacchetto, o ce ne può essere una con tutte le exports di cui il setup ha bisogno. Per avere un'idea dettagliata di cosa fa il setup, qual cosa migliore di un bel log super-verbose... Bene, msiexec consente di salvare un log dettagliatissimo di ogni operazione, la funzione non è attiva di default ma la attiviamo creando la chiave di registro HKLM\Software\Policies\Microsoft\Windows\Installer, e inserendoci la dword "debug" impostata al valore 7 e la stringa "logging" impostata al valore "voicewarmup"
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Installer]
"logging"="voicewarmup"
"debug"=dword:00000007
poi dobbiamo riavviare il servizio msiserver, in modo che MSI prenda le nuove opzioni
Il servizio Windows Installer sta per essere arrestato.
Servizio Windows Installer arrestato.
<b>C:\net start msiserver</b>
Servizio Windows Installer in fase di avvio .
Avvio del servizio Windows Installer riuscito.
Svuotiamo il contenuto della directory Temp dell'utente corrente e lanciamo l'installazione. Al punto che vogliamo attaccare (esempio, appena comparsa la messagebox di seriale errato) lasciamo in pausa il setup e andiamo nella directory temp dell'utente. Ci troveremo un bel file di log, che copiamo da qualche altra parte per comodità, e scorriamo fino alla fine. Troveremo, se è stata chiamata una dll esterna, qualcosa come
Operazione 22.06.44: CheckScriptHost.9CED35E4_C9E6_11D3_9833_00A0C9DA4FE9.
Inizio dell'operazione 22.06.44: CheckScriptHost.9CED35E4_C9E6_11D3_9833_00A0C9DA4FE9.
MSI (c) (A4:D0) [22:06:44:140]: Transforming table Binary.
MSI (c) (A4:9C) [22:06:44:140]: Invoking remote custom action.
DLL: E:\Users\ADMINI~1\IMPOST~1\Temp\MSI55.tmp, Entrypoint: CheckScriptHost
che ci dice che, dato che era da eseguire l'azione CheckScriptHost, è stata trasformata un'entry della tabella Binary in una dll nella folder Temp, ed è stato invocato l'export CheckScriptHost. La chiamata dello snippet qui sopra viene dal setup di un componente per Visual Studio, in particolare questa funzione si occupa di assicurarsi che il sistema abbia installato Windows Script Host.
Passi un po' più evoluti
Ora che sappiamo chi viene invocato, e sappiamo qual è la DLL che ci interessa (anche se ha un nome strano o casuale), la esportiamo con Orca dal database. La apriamo con IDA, se siamo fortunati lasciamo scaricare le informazioni di debug, e cerchiamo l'export incriminato. Da qui possiamo già farci un'idea di come funzioni il codice della funzione, probabilmente prenderà qualche valore dalle tabelle, macinerà qualcosa e finirà con lo scrivere il risultato su qualche altra tabella, o mostrerà una messagebox :D Se IDA (com'è facile) non sarà sufficiente beh... dovremo debuggare la dll! Ma come si debugga una dll così persa in mezzo al mondo (leggesi: in uno dei subprocessi del servizio msiserver)? Semplice, usando un'altra delle strutture di debug che MSIexec.exe offre! In questo caso la comodità sta nel farsi dire da msiexec stesso il processo a cui "attacheremo" ollydbg, mettendo pure preventivamente un breakpoint prima della chiamata all'export della dll. Nulla di più facile: se abbiamo settato la dword "debug" nel registro, impostiamo una variabile d'ambiente (sì, quelle nelle proprietà del sistema -> avanzate -> variabili d'ambiente) chiamata MsiBreak al valore stringa corrispondente al nome della custom action su cui interrompere. Nello snippet qua sotto, l'output del comando "set" in una finestra di cmd.exe dopo che ho settato MsiBreak per interrompere alla chiamata della custom action CA_SetPidProps (una del setup di Visual Studio 2005, quella che si occupa proprio di memorizzare le informazioni della licenza nel registro)
NUMBER_OF_PROCESSORS=2
OS=Windows_NT
Path=C:\WINNT\system32;C:\WINNT;C:\;E:\Users\Administrator\Desktop\Strumenti
MsiBreak=CA_SetPidProps
Ora semplicemente riavviamo l'installazione (anche non direttamente dal MSI, se richiede l'avvio da un qualche setup.exe facciamolo), seguiamo i passaggi et voilà, non appena è stata caricata la DLL in memoria (ma prima che venga fatta la CALL all'export) comparirà una tattica MessageBox che ci informerà di attaccare il debugger ad un tal processo, e premere OK quando pronto.
A questo punto carichiamo Olly e da File->Attach scegliamo il processo che msiexec ci ha indicato e premiamo F9 per avviare l'esecuzione. Diamo OK sulla messagebox, olly andrà in pausa ad ogni DebugBreak. Se il Break che ha interrotto Olly non fosse al punto che ci interessa (es. anche al caricamento della DLL) semplicemente premiamo F9. Dal DebugBreak che ci interessa andiamo avanti con F8 fino a che giungeremo nel codice della DLL, intorno al solito indirizzo 100xxxxx. A quel punto il codice che vedremo sarà lo stesso che abbiamo visto con IDA all'inizio dell'export, e potremo divertirci a debuggare la nostra particolare applicazione.
Note Finali
Il lavoro sui file MSI, come si è visto, non è particolarmente complesso. Il servizio MSIServer ci viene in aiuto con strutture per il debug che facilitano la risoluzione di problemi nelle nostre custom DLL (o, perché no, nelle DLL custom di altri). Non dimenticate di disattivare il logging e la modalità di debug dopo aver lavorato con un package, altrimenti tutte le successive installazioni saranno appesantite dall'environment di debug!
DrWatson
Disclaimer
I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.
Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevoli e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.
