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.





