Scrivere un plugin per Ollydbg

Data

by ZaiRoN

 

14-12-2oo5

UIC's Home Page

Published by Quequero

Vidi cacare e mi venne voglia!

Grazie Zai in effetti un tute sui plugin di olly proprio ci mancava, ottima idea

W Quequero :ppppppp (hihihi NdQue)

....

E-mail: [email protected]
Nick, UIN, canale IRC/EFnet frequentato: ZaiRoN su irc/efnet

....

Difficoltà

(Z)NewBies ( )Intermedio ( )Avanzato ( )Master

 
Allegato

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 usati

Ollydbg, un compilatore C

URL o FTP del programma

Debugger: www.ollydbg.de
Pdk: http://www.ollydbg.de/pdk.htm

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 pò 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. E' 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

E' tutto vero e tutto legale!!!