Compago

...free knowledge

 
  • Increase font size
  • Default font size
  • Decrease font size
Home Manuali Programmazione Codice auto modificante

Codice auto modificante

E-mail Stampa PDF

In questo articolo tratteremo l'argomento del codice auto modificante (self modifying code).

Il primo problema da superare quando il codice deve modificare se stesso è sono i diritti si accesso della sezione di memoria in cui la parte da modificare risiede. Infatti a differenza dell'ambiente DOS, sotto windows la memoria virtuale riservata per il processo è suddivisa in sezioni ognuna delle quali può avere i propri diritti di accesso:

  • PAGE_NOACCESS = 1
  • PAGE_READONLY = 2
  • PAGE_READWRITE = 4
  • PAGE_WRITECOPY = 8
  • PAGE_EXECUTE = 16
  • PAGE_EXECUTE_READ = 32

Quindi ci sono fondamentalmente due modi per fare in modo che il codice da modificare sia in una area scrivibile:

  • Fare in modo che il codice risieda in una area con quelle caratteristiche ( ad esempio con l'allocazione della codice nell' heap )
  • Modificare manualmente i diritti della area di memoria dove risiede il codice, affinché esso possa essere modificato.

Nell'esempio seguente vi propongo la seconda soluzione, che, secondo me è anche la più intuitiva:

program AutoMod;

{$APPTYPE CONSOLE}

uses
  SysUtils,windows;

function strana_somma(a,b:integer):integer;
begin
  asm
    mov eax,a
    mov edx,b

    call @1              // procedura recupero indirizzo attuale
   @1:                   //
    pop esi              //
    xor [esi+5],$28      //  Auto modifica
    add eax,edx          //  funzione modificata

    mov result,eax
  end;
end;

var
  oldmod,i:cardinal;
begin
  //Modifico le modalità di accesso alla pagina di memoria
  VirtualProtect(@strana_somma,$20,PAGE_READWRITE,oldmod);

  for i := 0 to 9 do
    writeln(strana_somma(5,2));

  //Ripristino le modalità di accesso alla pagina di memoria
  VirtualProtect(@strana_somma,$20,oldmod,oldmod);
  readln;

end.

Analizziamo prima di tutto la chiamata della "strana"  funzione. Come prima cosa faccio in modo di rendere scrivibile la parte di memoria dove risiede la funzione, che inizialmente ha solo i diritti di lettura ed esecuzione.
La funzione usata a questo scopo è la VirtualProtect, la quale prende come parametri l'indirizzo iniziale del blocco da modificare, che nel nostro caso è dato dall'indirizzo della funzione, la dimensione del blocco, che io ho arbitrariamente imposto essere 20h byte, la nuova modalità di accesso e una variabile a cui verrà assegnato il valore della vecchia modalità di accesso.

Ho scelto di 20h byte perché sono sufficienti a coprire l'area della minuscola funzione con cui abbiamo a che fare. In teoria si dovrebbe utilizzare solo l'indirizzo del codice da modificare, oppure calcolare l'esatta dimensione del codice dalla funzione, ma ad ogni modo una approssimazione è accettabile, basta che funzioni.

Dopo aver utilizzato la funzione ripristinerò con la stessa funzione la modalità che c'era in precedenza.
In questo modo potremo fare a meno di dover tenere conto di dove si trova il codice in memoria.

Ora proviamo ad analizzare il meccanismo con cui il codice si modifica:

Indirizzo Codice      Istruzione             
00408C21 E800000000  call $00408C26 //call @1
00408C26 5E          pop esi
00408C27 83760528    xor dword ptr [esi+$05],$28 //xor [esi+5],$28
00408C2B 01D0        add eax,edx          

Con le prima due istruzioni non faccio altro che trovare l'indirizzo corrente del codice in esecuzione. Infatti con la CALL viene inserito nello stack l'indirizzo successivo (408C26h), l'istruzione successiva estrae dallo stack quest'ultimo valore e lo memorizza nel registro ESI.
Ora che so l'indirizzo attuale del codice lo posso usare per modificarlo. L'istruzione successiva fa proprio questo, cioè modifica il codice desiderato prendendo come riferimento l'indirizzo che avevamo trovato precedentemente.

ESI + 5 = 408C26h + 5 = 408C2Bh

Quello che verrà modificato è la double word (4 byte) a partire dall'indirizzo 408C2Bh, ed in particolare a questo indirizzo troviamo l'istruzione di somma "add eax,edx".

                      D001
Add eax,edx = 01D0 => xor => 29D0 = Sub eax,edx
0028
D029

In questo modo ogni volta che viene chiamata la funzione il codice si auto modifica diventando di volta in volta una addizione oppure una sottrazione.

Un consiglio, per chi volesse addentrarsi in questo tipo di codice, è quello di studiarsi bene gli opcode in modo da comprendere meglio tutti i tipi di istruzioni.

 

Ultimo aggiornamento ( Sabato 19 Giugno 2010 17:14 )