Zoom Icon

Plugin per OllyDbg

From UIC Archive

Scrivere un plugin per Ollydbg

Contents


Plugin per OllyDbg
Author: ZaiRoN
Email: zaironcrk (at) hotmail (dot) com
Website: ...
Date: 14/12/2005 (dd/mm/yyyy)
Level: Some skills are required
Language: Italian Flag Italian.gif
Comments: ...


Introduzione

Introduzione allo sviluppo di un plugin per Ollydbg, un semplice esempio introduttivo all'argomento. Questo tutorial è nato per puro caso e scritto per passare una serata fino a quel momento noiosa per cui non aspettatevi di trovare tutto sui plugin di Ollydbg perché rimarrete delusi. Tratterò soltanto le cose usate nel plugin in questione e poco altro.


Tools

Allegato


Essay

Il trick anti-attach
L'idea del plugin prende spunto da un anti-attach trick pubblicato sul sito http://www.kakeeware.com/i_antiattach.php Il trick funziona in questo modo: ogni volta che un debugger user-mode esegue un attach su un qualsiasi processo il sistema crea un nuovo thread che parte chiamando la funzione ntdll!DbgUiRemoteBreakin; patchando il codice della funzione DbgUiRemoteBreakin è ovviamente possibile fare ciò che vogliamo; il codice di prova che trovate sul sito di kakeeware patcha la funzione in modo che venga mostrato un box con un messaggio di avviso. Sul sito si legge che questo trick funziona sia su wXP che su W2k, io l'ho provato su wXP ed effettivamente funziona però non sono sicurissimo che funzioni altrettanto bene su w2K. Perché? Beh, Kayaker risponde a questa domanda nel seguente thread: http://207.218.156.34/forum/showthread.php?t=7586

L'anti-anti-attach
Come possiamo evitare questo trick? L'idea è quella di controllare che il codice della funzione DbgUiRemoteBreakin non sia stato cambiato; per far ciò devo salvare i byte non modificati della funzione DbgUiRemoteBreakin e compararli con quelli di una eventuale funzione modificata. Non occorre salvare tutti i byte della funzione, ne basteranno soltanto alcuni. I byte verranno presi a runtime e non direttamente da ntdll.dll. Una volta che i byte sono stati salvati posso procedere con il controllo che viene fatto per ogni processo in esecuzione sul sistema, questo perché la modifica al codice di DbgUiRemoteBreakin è relativa all'address space del singolo processo. Certamente, sapendo a priori quale sia il programma in grado di modificare la funzione DbgUiRemoteBreakin avrei potuto organizzare il plugin in un altro modo; per il momento va più che bene così.

Due parole sui plugin di Ollydbg
Un plugin non è altro che una dll che estende le capacità del debugger. I plugin vengono caricati in automatico all'avvio del debugger; ogni dll presente nella directory dei plugin di Ollydbg viene caricata e riconosciuta come plugin se contiene due procedure, chiamate ODBG_Plugindata e ODBG_Plugininit. La directory la potete settare tramite il percorso 'Options-Appearence-Directories'. Lo scheletro di un comune plugin è composto da una serie di funzioni di callback, queste funzioni vengono usate da Ollydbg per eseguire le operazioni di inizializzazione/apertura/chiusura/mantenimento/etcetc del plugin stesso; un po' come accade per qualsiasi entità che utilizza callback. Tra tutte le funzioni di callback utilizzabili soltanto due sono indispensabili: - ODBG_Plugindata: usata per definire il nome del plugin deve necessariamente ritornare la costante PLUGIN_VERSION - ODBG_Plugininit: usata per definire tutte le inizializzazioni del plugin; se qualcosa va storto si deve ritornare -1 (e il plugin non verrà caricato) altrimenti se non ci sono problemi si ritorna 0. Ci sono altre funzioni di callback ma io -come vi ho detto nell'introduzione- vi parlerò soltanto delle funzioni che ho effettivamente utilizzato per scrivere il plugin.

Codice standard del plugin
Potete scrivere il plugin nel linguaggio che preferite, io ho usato il c. Ecco le funzioni presenti nel mio file .c: BOOL WINAPI DllEntryPoint(HINSTANCE hi,DWORD reason,LPVOID reserved) {

  if (reason==DLL_PROCESS_ATTACH)
     hInst=hi;
  return 1;

} Stiamo scrivendo una dll per cui mi sembra inutile commentare la procedura qua sopra... extc int _export cdecl ODBG_Plugindata(char shortname[32]) {

  strcpy(shortname,"antiAttach_DbgUiRemoteBreakin");
  return PLUGIN_VERSION;

} Ecco una delle due funzioni di callback necessarie. Viene definito un nome da associare al plugin e ritornato il numero di versione del plugin stesso, proprio come vi avevo detto in precedenza. Questa è una funzione standard, niente di complicato. extc int _export cdecl ODBG_Plugininit(int ollydbgversion,HWND hw,ulong *features) {

  if (ollydbgversion<PLUGIN_VERSION) // La versione del plugin è corretta?
     return -1; // In caso negativo il plugin non può essere caricato
  hwmain=hw; // Salva l'handle della finestra principale di Ollydbg
  Addtolist(0,0,"antiAttach_DbgUiRemoteBreakin sample plugin, a not

professional plugin made just for fun... by ZaiRoN");

  return 0;

} La seconda funzione necessaria. Qua dentro dovete mettere tutte le inizializzazioni del caso. Come potete vedere per questo semplice plugin non servono particolari inizializzazioni; controllo che la versione sia quella giusta, salvo un handle e scrivo una frase nel log. Il controllo sulla versione mi consente di capire se il plugin verrà effettivamente caricato mentre il savataggio dell'handle è importante perché necessario ogni qual volta dovrete avere a che fare con menu e dialog vari. La funzione Addtolist invece è puramente opzionale e serve soltanto per visualizzare un messaggio nel log di Ollydbg (ALT-L); è un modo per loggare il fatto che il plugin è stato caricato con successo. L'uso del log può essere un comodo strumento in fase di sviluppo di un plugin.

Callback opzionali usate extc int _export cdecl ODBG_Pluginmenu(int origin,char data[4096],void *item) {

  switch (origin)
  {
  case PM_MAIN:
     strcpy(data,"0 Save bytes,1 antiAttach DbgUiRemoteBreakin|2 &About");
     return 1;
  default: break;
  };
  return 0;

} Questa è la callback usata per definire qualsiasi tipo di menu usato dal plugin, sia esso il menu contenente le varie opzioni del plugin che un qualsiasi popup menu. Dato che è possibilie definire varie tipologie di menu la callback utilizza un costrutto di tipo switch per capire cosa si vuole definire. Nel mio caso è presente soltanto PM_MAIN perché viene definito un solo menu nella finestra principale (per capirsi dentro il menu 'Plugins'), niente altro. Se avete bisogno di qualsiasi altro menu vi basta definirlo qua; nella guida fornita con il PDK trovate tutte le altre possibili keyword usate per definire un particolare menu. Il menu viene definito inserendo tutte le informazioni nel buffer chiamato 'data'. Ho definito 3 item e un separatore; il primo item contiene il testo 'Save bytes', il secondo 'antiAttach DbgUiRemoteBreakin' mentre il terzo item è il classico 'About'. Da notare che tra il secondo e il terzo item ho inserito una linea di separazione, definita dal carattere '|'; se usate la ',' non verrà inserito nessun separatore tra le voci di menu. È importante, quando si definiscono degli item menu, ritornare il valore 1. Si, i plugin usano molto i valori di ritorno. extc void _export cdecl ODBG_Pluginaction(int origin,int action,void *item) {

if (origin==PM_MAIN)
{
 switch (action)
 {
 case 0: // Save bytes
  // Ottengo l'indirizzo della funzione DbgUiRemoteBreakin e salvo 0x2F byte
  mhNtdll = GetModuleHandle("ntdll.dll");
  dbguiremotebreakinAdd = (DWORD)GetProcAddress(mhNtdll, "DbgUiRemoteBreakin");
  memcpy(_DbgUiRemoteBreakin, dbguiremotebreakinAdd, 0x2F);
  break;
 case 1: // antiAttach DbgUiRemoteBreakin
  if (_DbgUiRemoteBreakin[0] == 0x00)
  { // Per poter effettuare il test è necessario aver salvato i byte...
   MessageBox(hwmain, "Save bytes first", "Attention!", MB_OK);
   break;
  }
  // Ottengo la lista dei processi in running
  hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  pEntry.dwSize = sizeof(PROCESSENTRY32);
  if (Process32First(hSnap, &pEntry))
  {
   do
   {
    // Per ogni processo eseguo il controllo sui byte
    HANDLE hProcess;
    hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pEntry.th32ProcessID);
    ReadProcessMemory(hProcess,dbguiremotebreakinAdd, buffer,0x2F,rBytes);
    if (memcmp(_DbgUiRemoteBreakin, buffer, 0x2F) != 0)
     // Funzione patchata! Ripristino i byte originali
     WriteProcessMemory(hProcess,dbguiremotebreakinAdd, _DbgUiRemoteBreakin, 0x2F,rBytes);
    CloseHandle(hProcess);
   }
   while (Process32Next(hSnap, &pEntry));
  }
  CloseHandle (hSnap);
  MessageBox(hwmain, "Attach now", "OK",MB_OK);
  break;
 case 2: // About...
  MessageBox(hwmain, "antiAttach_DbgUiRemoteBreakin sample plugin, 

a not professional plugin made just for fun...\nby ZaiRoN","Info",MB_OK|MB_ICONINFORMATION);

  break;
 default: break;
 };
}

} Ecco la callback che viene chiamata ogni volta che viene selezionato uno degli item del menu definito in precedenza. Qua dentro metteremo, per ogni item, il codice che gestisce la particolare funzione. In questo caso ci sono tre item per cui 3 funzioni. - Item 0: Save bytes. Lo scopo di questa funzione è quello di salvare alcuni byte (0x2F) della funzione DbgUiRemoteBreakin. Questa operazione va ovviamente effettuata prima di lanciare il programma che sfrutta l'anti-attach. - Item 1: antiAttach DbgUiRemoteBreakin. Funzione principale di tutto il plugin, quella che ha il compito di neutralizzare il trick. Usando le tre classiche funzioni di Tlhelp32.h ottengo tutti i processi in running e per ognuno di essi controllo se hanno modificato parte del codice della funzione DbgUiRemoteBreakin. Se la funzione è stata modificata ripristino i byte originali altrimenti passo a controllare il prossimo processo in running. - Item 2: About. Il più classico degli about...

Non mi sembra ci sia altro da dire, anche perché il tutto è mooooolto semplice. Ah, se trovate errori correggeteli, grazzzzzie :p

Bona, ZaiRoN


Note Finali

Note finali: nessuna nota finale. Ringraziamenti: nessun ringraziamento. Saluti: un saluto a tutti.


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 malevole 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.