Come da titolo, parliamo del debugging su windows e del memory patching; poi raggiriamo asprotect ;)
CloneDVD 2.5.0.0
Debugging su Windows,memory patching,fooling asprotect,protezione a tempo Written by bender0
Introduzione
ci troviamo di fronte a una
versione trial di CloneDVD (h**p://www.clonedvd.net), con scadenza a 14
giorni dall'installazione. anche stavolta crackato per un amico... non
ce l'ho neanche il masterizzatore dvd :)
Microsoft Visual C++ 6.0 (o qualsiasi altro compilatore di qualsiasi linguaggio)
una buona guida delle API, preferibilmente il pdk (Platform Software Development Kit) di windows.
se siete su linea lenta come me, scordatevi di scaricarlo con
l'interfaccia che offre il sito, perchè non permette il resume al di
fuori del giorno stesso. come si scarica allora? scaricate i singoli
file da qui:
h**p://download.microsoft.com/download/platformsdk/sdk/update/win98mexp/en-us/3790.0/
ecco la lista dei file necessari per avere l'ambiente di sviluppo e la documentazione:
---lista---
PSDK-x86.cab
PSDK-common.0.0.cab
PSDK-x86.0.0.cab
PSDK-common.1.0.cab
PSDK-common.2.0.cab
PSDK-x86.2.0.cab
PSDK-common.4.0.cab
CoreSDK-x86.cab
CoreSDK-common.0.0.cab
CoreSDK-common.1.0.cab
CoreSDK-common.1.1.cab
CoreSDK-common.1.2.cab
CoreSDK-common.1.3.cab
CoreSDK-common.1.4.cab
CoreSDK-common.2.0.cab
CoreSDK-common.2.1.cab
CoreSDK-common.2.10.cab
CoreSDK-common.2.11.cab
CoreSDK-common.2.12.cab
CoreSDK-x86.2.0.cab
CoreSDK-common.3.0.cab
CoreSDK-x86.3.0.cab
CoreSDK-common.0.0.cab
---fine lista---
per un totale di 134mb: tanto, ma ne vale la pena.
caricate CloneDVD.EXE nel
PeID, e leggete il responso: "ASProtect 1.23 RC4 - 1.3.08.24 ->
Alexey Solodovnikov". buone notizie, non vi pare?
prima ancora di spiegare che metodo di attacco ho usato per rimuovere
la protezione di questo programma è bene fare una bella digressione sul
debugging sotto Win, che a quanto pare è la parte fatta meglio
dell'intero sistema di API ;)
in effetti scrivere un debugger elementare (ai livelli di ntsd) è quasi una passeggiata: vediamo.
Il debugging su windows:
1.debugging APIs
2.creazione\attaching di un processo
3.main loop e debug events
4.struttura di un debugger
5.interagire con il debuggee [breakpoints, code injection e contexts]
ecco l'elenco delle API (principali) relative al debugging, con breve commento; poi parlerò in dettaglio delle più importanti.
nb: con "debuggee" si intende il processo debuggato dal debugger. che frase oscena...
ContinueDebugEvent
> in risposta a un evento di debug permette di far ripartire il thread che ha generato tale evento.
DebugActiveProcess
> permette di debuggare un processo già in corso.
DebugActiveProcessStop
> "libera" il debuggee dal debugger.
DbgBreakPoint
> genera un eccezione di breakpoint (int3). ecco il codice della funzione:
int 3
ret
ore e ore di coding...
DebugBreakProcess
> genera un eccezione di breakpoint (int3) nel processo specificato e di conseguenza passa il controllo al debugger.
GetThreadContext
> preleva il context di un thread.
IsDebuggerPresent
> chiamata da un processo (e dall'asprotect ;) per sapere se è debuggato.
OutputDebugString
> il processo può mandare una stringa al debugger che la mostrerà
all'utente. se il processo non è debuggato questa funzione non ha
nessun effetto.
ReadProcessMemory
> permette di leggere nell'address space del debuggee.
SetThreadContext
> imposta il context di un thread.
WaitForDebugEvent
> aspetta che il programma sollevi un'eccezione e dà il controllo al debugger.
WriteProcessMemory
> permette di scrivere nell'address space del debuggee.
**************** 2.creazione\attaching di un processo ****************
prima di iniziare con il debugging vero e proprio (o con i nostri
loschi propositi ;) è necessario segnalare al sistema operativo che il
nostro programma deve agire da debugger sul processo target. si può
fare in due modi, sta a voi scegliere il migliore di volta in volta:
Creazione del processo:
permette di debuggare il processo target dalla prima all'ultima
istruzione, "creandolo" alle dipendenze del nostro debugger. lo
svantaggio è che il debugger non può terminare la sua esecuzione senza
che si chiuda anche il debuggee. per creare un processo si usa la API
CreateProcess, che non ho citato prima perchè non è propriamente una
API di debug. ecco la sintassi (questa volta spiego in breve i
parametri):
lpApplicationName
[in] Stringa che specifica percorso e nome file del target.
lpCommandLine
[in, out] Un NULL andrà benissimo, windows usa il parametro precedente per individuare il file.
lpProcessAttributes
[in] Un NULL andrà benissimo, il processo avrà un descrittore di sicurezza standard.
lpThreadAttributes
[in] Un NULL andrà benissimo, il thread principale avrà un descrittore di sicurezza standard.
bInheritHandles
[in] Decide se ereditare le handle del debuggee per usarle nel debugger; male non fanno, passiamo true.
dwCreationFlags
[in] Specificando i valori DEBUG_PROCESS o DEBUG_ONLY_THIS_PROCESS (non
debugga i processi creati dal debuggee), il nostro processo diventerà
un debugger.
lpEnvironment
[in] Un NULL andrà benissimo.
lpCurrentDirectory
[in] Stringa che specifica la cartella di lavoro del debuggee. se il
debugger è nella stessa cartella passando NULL il debuggee userà la sua
cartella.
lpStartupInfo
[in] Puntatore a una struttura STARTUPINFO che specifica dei parametri di avvio (vedi sotto).
lpProcessInformation
[out] Puntatore a una struttura PROCESS_INFORMATION che riceve dei parametri di identificazione del novo processo (vedi sotto).
CreateProcess ritorna zero se fallisce e nonzero altrimenti.
questa API si serve di due strutture, STARTUPINFO e PROCESS_INFORMATION.
la prima è inutile ai nostri scopi e conviene passarla azzerata in questo modo:
STARTUPINFO si;
ZeroMemory(&si,sizeof(si));
si.cb = sizeof(si);
la seconda invece è molto importante; ecco come è definita:
int ret = CreateProcess("nome del file qui",NULL,NULL,NULL,true,DEBUG_ONLY_THIS_PROCESS,NULL,NULL,&si,π);
Attaching:
in ogni momento si può iniziare a debuggare un processo.
con EnumProcesses si possono ottenere gli id di tutti i processi
attivi; scelto il processo a cui "attaccarsi" si passa il suo id a
DebugActiveProcess.
terminata la sessione di debug si usa DebugActiveProcessStop per "liberare" il processo target.
**************** 3.main loop e debug events ****************
i debugger in user-mode (vedi ollydbg :) si basano su un loop di
WaitForDebugEvent e ContinueDebugEvent. ecco la sintassi delle
funzioni, presa dalla documentazione del pdk (e tradotta):
lpDebugEvent
[out] Puntatore a una struttura DEBUG_EVENT che riceve informazioni sull'evento di debug.
dwMilliseconds
[in] Tempo di attesa per un evento di debug in millisecondi. Se questo
parametro è zero, la funzione controlla se c'è un evento di debug e
ritorna immediatamente. Se il parametro è INFINITE (-1), la funzione
non ritorna finchè non avviene un evento di debug.
dwProcessId
[in] Id del processo da continuare.
dwThreadId
[in] Id del thread da continuare. La combinazione di id del processo e
id del thread deve identificare un thread che ha provocato un evento di
debug in precedenza.
dwContinueStatus
[in] Opzioni per continuare il thread che ha provocato un evento di debug.
Le flag specificabili per questo parametro sono due:
DBG_CONTINUE termina l'exception handling (assumendo che sia stato compiuto dal debugger) e continua il thread.
DBG_EXCEPTION_NOT_HANDLED procede con l'exception handling del processo e continua il thread.
detto questo, il main loop di un debugger può apparire così:
ora la domanda è: "cos'è un evento di debug?"
in un certo senso è il modo in cui il debuggee comunica (spesso a sua
insaputa) con il debugger. tutte le eccezioni sollevate dal debugee
passano al debugger sotto forma di eventi di debug. lo stesso fanno
eventi come la creazione e la distruzione di un processo. anche i
breakpoints (che sono comunque un tipo di eccezione) subiscono lo
stesso processo.
il sistema da parte sua si occupa di raccogliere le eccezioni e gli
altri eventi e di passarli per prima cosa al debugger (se ce n'è
uno...); se il debugger afferma di aver risolto il problema (nel caso
di un'eccezione), il processo continua; se il debugger non risolve il
problema l'eccezione viene passata al seh del processo. se neanche lì
l'eccezione viene risolta il sistema procede con la chiusura del
processo.
il debugger riceve questi eventi dal sistema chiamando
WaitForDebugEvent; il thread che ha generato l'evento si ferma
automaticamente e il debugger ha campo libero e accesso completo sul
debuggee per risolvere il problema o rispondere ad altri eventi.
il sistema passa l'evento al debugger attraverso una struttura complessa, ovvero DEBUG_EVENT, così definita:
dwProcessId e dwThreadId ci danno rispettivamente l'id del processo e l'id del thread dove è avvenuto l'evento.
il membro dwDebugEventCode ci informa del tipo di evento segnalato; può assumere i seguenti valori:
EXCEPTION_DEBUG_EVENT [eccezione]
CREATE_THREAD_DEBUG_EVENT [creazione di un thread]
CREATE_PROCESS_DEBUG_EVENT [creazione di un processo]
EXIT_THREAD_DEBUG_EVENT [distruzione di un thread]
EXIT_PROCESS_DEBUG_EVENT [distruzione di un processo]
LOAD_DLL_DEBUG_EVENT [caricamento di una dll]
UNLOAD_DLL_DEBUG_EVENT [liberazione di una dll]
OUTPUT_DEBUG_STRING_EVENT [stringa di debug]
RIP_EVENT [system debugging error]
il membro u è un unione; quindi i suoi membri occupano tutti la stessa
porzione di memoria. per scegliere il membro giusto dobbiamo basarci
sul valore di dwDebugEventCode.
ora, per un debugger che può servire ai nostri scopi è sufficiente
rispondere a due eventi: EXIT_PROCESS_DEBUG_EVENT (per terminarsi
quando termina il debuggee) e soprattutto EXCEPTION_DEBUG_EVENT. il
membro di u relativo a questo evento è Exeption, che punta a una
struttura di tipo EXCEPTION_DEBUG_INFO, definita così:
il membro che ci interessa è ExceptionRecord, che punta a una struttura
EXCEPTION_RECORD, che contiene informazioni essenziali sull'eccezione,
definita così:
il membro ExceptionCode indica il tipo di eccezione. assume valori
interessanti come EXCEPTION_ACCESS_VIOLATION, EXCEPTION_BREAKPOINT,
EXCEPTION_INT_DIVIDE_BY_ZERO, EXCEPTION_SINGLE_STEP,
EXCEPTION_STACK_OVERFLOW e via dicendo.
il membro ExceptionFlags indica con uno zero che l'eccezione è continuabile e con EXCEPTION_NONCONTINUABLE il contrario.
il membro ExceptionAddress indica l'indirizzo dove è avvenuta l'eccezione nell'address space del debuggee.
**************** 4.struttura di un debugger ****************
per completare il nostro debugger general purpose, dobbiamo rispondere agli eventi di debug. ecco una possibile implementazione:
 case EXIT_PROCESS_DEBUG_EVENT:
// chiama ContinueDebugEvent per non bloccare il debuggee e esci.
 break;
}
--- pseudo-code fine ---
**************** 5.interagire con il debuggee [breakpoints, code injection e contexts] ****************
sottotitolo: come far diventare il debuggee una marionetta nelle nostre mani.
breakpoints
sappiamo già che un breakpoint è un'eccezione posta appositamente in un
certo punto dell'esecuzione del target per dare il controllo al
debugger. nel concreto l'eccezione in questione viene sollevata quando
viene eseguita l'istruzione int 3, il cui opcode è un singolo byte,
ovvero CCh.
se solo potessimo sovrascrivere il primo byte di un'istruzione del
debuggee con un CCh potremmo quindi breakkare dove vogliamo nel target,
e nel punto giusto avremmo molte possibilità di azione. ovviamente
possiamo ;)
per scrivere nell'address space del debuggee il sistema di debugging
del buon vecchio win ci propone la API WriteProcessMemory, che funziona
più o meno come WriteFile. eccone la sintassi:
hProcess
[in] Handle del processo di cui vogliamo sovrascrivere la memoria.
lpBaseAddress
[in] Puntatore al blocco di memoria da sovrascrivere.
lpBuffer
[in] Puntatore al buffer che contiene i byte che verranno scritti nella memoria del target.
nSize
[in] Numero di bytes da scrivere.
lpNumberOfBytesWritten
[out] Puntatore che riceve il numero di bytes effettivamente scritti.
Diversamente da WriteFile se non vi interessa sapere quanti byte sono
stati scritti potete passare NULL.
quindi se vogliamo settare un breakpoint all'indirizzo 0040123c, chiameremo WriteProcessMemory così:
non preoccupatevi del valore di ritorno; anche se la funzione fallisce
(ritornando 0) probabilmente l'errore è del tipo "la richiesta non è
stata completata del tutto", ma in quel caso il(i) byte(s) vengono
scritti ugualmente.
ovviamente quando il debugger risponderà all'eccezione dovrà sistemare
il tutto prima di far ripartire il processo; questo include
rispristinare il byte originale e risistemare il registro eip (per la
modifica dei registri vedi contexts).
code injection
la code injection quando si hanno privilegi da debugger è
semplicissima. reversatevi il target e segnatevi gli offset dei byte da
modificare; chiamate poi WriteProcessMemory tante volte quante sono le
modifiche da effettuare, passandogli di volta in volta l'indirizzo, il
puntatore al buffer e la grandezza di quest'ultimo appropriati.
il procedimento è analogo a quello per settare breakpoints appena spiegato.
contexts
questa è la parte più ostica ma anche la più interessante (imo) del discorso.
domanda: "cos'è un thread context?"
sappiamo che windows e un sistema operativo che supporta il
multitasking e il multithreading. ogni processo ha il suo address space
e i suoi threads; i diversi threads e i diversi processi vengono spesso
eseguiti in contemporanea, o almeno questo è quello che accade in
apparenza. in realtà il processore non può eseguire più istruzioni di
diversi thread simultaneamente. inoltre, ogni thread ha i suoi registri
che devono essere preservati.
il modo in cui windows amministra l'esecuzione dei thread è detto
"scheduling". i thread attivi compaiono in una lista e vengono detti
"ready threads", a significare che sono pronti a essere eseguiti in
ogni momento. lo scheduling consiste nel passare al processore questo o
quest'altro thread per l'esecuzione, a seconda della loro priorità;
questi cambi vengono detti "thread switch". ma come ho già detto ogni
thread ha i registri da preservare; infatti quando si effettua un
thread switch queste informazioni vengono salvate in una struttura per
poi essere rispristinate quando riprende l'esecuzione di quel
particolare thread. questa struttura è la struttura CONTEXT.
ora fai un respiro profondo e rileggi questa parte ;)
la definizione della struttura CONTEXT varia a seconda
dell'architettura del processore ed è quindi hardware-dependent. la
seguente definizione si applica ai processori x86:
typedef struct _CONTEXT {
// flags, le spiego più avanti.
 DWORD ContextFlags;
per modificare il context di un thread (e quindi le sue flags e i suoi
registri, eip incluso), win ci fornisce due API: GetThreadContext e
SetThreadContext (che fantasia alla Microsoft!). ecco le sintassi:
hThread
[in] Handle del thread di cui si vuole modificare il context.
lpContext
[in] Puntatore a una struttura CONTEXT che contiene il nuovo context del thread.
prima di prelevare o impostare il context bisogna settare il membro
ContextFlags della struttura CONTEXT. tale membro specifica quali
valori del context devono essere prelevati\impostati. può avere diversi
valori, ma per andare sul sicuro conviene settare CONTEXT_ALL. se avete
degli headers vecchi è possibile che manchi questa costante; in tal
caso passate direttamente il suo valore, 0x1003F.
con questo procedimento potete modificare a piacimento i registri di un
thread. la possibilità di modificare anche i registri di debug del
target ci permette di settare degli hardware breakpoints. infatti se
scriviamo degli indirizzi nei primi quattro registri di debug (Dr0 ..
Dr3), se e quando verranno eseguite delle istruzioni a quegli indirizzi
verrà generata un'eccezione di breakpoint che verrà ovviamente passata
al debugger.
Attacchiamo CloneDVD:
bene, finita questa (enorme!) digressione, possiamo cominciare a
studiare il target, CloneDVD.EXE, che come abbiamo visto prima è
packato con asprotect.
il mio obiettivo non è ottenere un dump funzionante; se vi interessa
unpackare asprotect leggetevi il tutorial di AndreaGeddon (bel tute ;).
lo carichiamo nel debugger (io ho usato ntsd per il test, ma qualsiasi
debugger fungerà allo scopo). eseguiamo CloneDVD... dopo esserci
sorbiti le numerose eccezioni generate da asprotect per disturbarci,
ecco il nag che ci informa che non dovremmo usare il debugger. sembra
una messagebox... proviamo a breakkare su MessageBoxA. bingo. steppiamo
fino al ret e poi steppiamoci sopra, per vedere da dove viene chiamata
questa messagebox. niente da fare: prima di chiamare MessageBox il
return address della call viene modificato, e il risultato è che ci
troviamo su ExitProcess.
questo è il momento di usare un debugger un pò più serio ;) carichiamo
il target in Olly, mettiamo un bp su IsDebuggerPresent e eseguiamo con
f9.
nota: anche se di solito quando si lavora con i packer è meglio usare
gli hardware breakpoints per non modificare l'area di memoria
eseguibile, asprotect spazza via i vostri hw breakpoints ogni volta che
genera un eccezione, quindi usiamo (per quanto possibile) i breakpoint
"tradizionali".
bene, breakka, e siamo @00A1169C (quindi il check del debugger lo fa
asprotect, non CloneDVD). riavviamo e attiviamo il plugin
IsDebuggerPresent che rende il nostro debugger invisibile a questa
funzione, poi eseguiamo. funziona e il programma parte, ma dato che
siamo ancora entro i limiti del trial period non vediamo il nag che ci
dice che il prog è expired - quindi tiriamo avanti l'orologio di
windows di un anno (anche meno se volete ;) e lo rilanciamo.
ecco un'altra messagebox, breakkiamo anche su questa, la chiamata è
fatta @00403342. guardiamoci intorno: qualche chiamata interna, un pò
di salti condizionali, la messagebox di prima... dopo sembra continuare
l'esecuzione normalmente, anche perchè chiama CreateDVDCloner, l'unico
export della dll che lo accompagna. vediamo cosa succede se non viene
eseguito quel salto @00403365. steppiamo fino al salto, poi click
destro sull'istruzione successiva, "new origin here". così abbiamo
cambiato eip, come se quel salto non ci fosse mai stato. eseguiamo e,
come previsto, il programma parte anche se è scaduto il trial period.
segnatevi l'offset del salto da eliminare e chiudete il debugger. fine
della parte noiosa.
ora viene il bello: studiamo l'attacco. l'idea è di creare un loader
che si comporti da debugger su CloneDVD, che ignori completamente
asprotect e che patchi la memoria per togliere quel jmp. ecco che ci si
pongono davanti diversi problemi:
1.ignorare asprotect non è immediato dato che genera parecchie eccezioni
2.abbiamo visto che asprotect cerca il debugger, dobbiamo nasconderci
3.dobbiamo patchare la memoria al momento giusto, ovvero DOPO essere stata decriptata e PRIMA di essere eseguita.
risolviamoli, nell'ordine :)
1.è vero che asprotect genera molte
eccezioni ma è anche vero che il programma non crasha se lanciato
normalmente. questo perchè le eccezioni vengono riparate dal seh del
processo. l'unica cosa da fare è dire al sistema che noi (il debugger)
non siamo riusciti a riparare l'eccezione, così ci penserà il processo.
per far questo, è sufficiente chiamare ContinueDebugEvent così:
ContinueDebugEvent(
 DebugEv.dwProcessId, // id del processo
 DebugEv.dwThreadId, // id del thread
 DBG_EXCEPTION_NOT_HANDLED // passa l'exception handling al processo
);
2.asprotect cerca il debugger con
IsDebuggerPresent. prima per evitare questo check con Olly abbiamo
usato quel plugin. come funziona il plugin? come fa IsDebuggerPresent a
capire se c'è un debugger o meno? andiamo a reversarci questa API.
avete l'ultima versione di OllyDbg? con la 1.10 si possono caricare dll
a sè stanti e chiamare gli exports "fingendo" dei parametri.
IsDebuggerPresent non ha parametri, comunque possiamo caricare
kernel32.dll (che esporta questa API) in Olly e guardarcela. andate su
debug->call dll export, scegliete IsDebuggerPresent e clickate
Follow In Disassembler. ecco IsDebuggerPresent in tutto il suo
splendore:
ben tre mov, anche qui ore e ore di coding... ad ogni modo vediamo che
con le prime due istruzioni si calcola un'indirizzo, e con la terza
muove in eax (con estensione di zero) il byte che si trova 2 byte dopo
quell'indirizzo, probabilmente un valore booleano. quindi pare che a
quell'indirizzo venga settato il byte se il processo è debuggato.
allora andiamo a scriverci uno zero noi ;) che è quello che fa anche il
plugin.
questo è il codice:
bool HideDebugger(HANDLE hProc,HANDLE hT)
{
// legge il thread context
 CONTEXT tc;
 tc.ContextFlags = CONTEXT_ALL;
 if(!GetThreadContext(hT,&tc)) return false;
// calcola l'indirizzo del byte in modo analogo a IsDebuggerPresent
 DWORD addr,buf;
 ReadProcessMemory(hProc,(void*)(tc.SegFs+0x18),&addr,4,NULL);
 ReadProcessMemory(hProc,(void*)(addr+0x30),&addr,4,NULL);
 ReadProcessMemory(hProc,(void*)addr,&buf,4,NULL);
 addr = buf+2;
bene, questo codice ci nasconderà da IsDebuggerPresent, ma quando lo
eseguiamo? dato che il nostro loader creerà il processo con
CreateProcess, ho pensato che la scelta migliore fosse patchare il byte
al system breakpoint (poco prima di cominciare l'esecuzione di un
processo il sistema chiama DbgBreakPoint, che è per certo il primo
EXCEPTION_BREAKPOINT che il nostro programma registra, e quindi
facilmente individuabile).
if (DebugEv.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT)
{
 if (exceptioncounter == 0 )
{
 if (debug) printf("exception (%u) at [%.8X] (DbgBreakPoint)\n",
 exceptioncounter,DebugEv.u.Exception.ExceptionRecord.ExceptionAddress);
 HideDebugger((DWORD)hProc,(DWORD)hT);
}
}
3.sono stato a lungo indeciso su questo
punto. in realtà la mia idea iniziale era di patchare i jumps
precedenti la messagebox, per avere un'esecuzione perfetta. si creava
però il problema del timing: come trovare il momento giusto per
patchare la memoria? il programma non genera eccezioni dopo aver
decriptato la memoria, e nessuno mi garantisce che mentre cerco di
patcharla le istruzioni non vengano eseguite. per aggirare l'ostacolo
(in modo poco ortodosso...) ho sfruttato il fatto che la messagebox è
eseguita in stato modale; avviso l'utente di non chiudere la
messagebox, creo un thread che prova a patchare ogni mezzo secondo la
memoria (ovviamente non patchandola se è ancora criptata), e una volta
patchata avviso l'utente di procedere.
ecco il mio thread (il resto mi sembra superfluo):
DWORD WINAPI PatchWait(HANDLE hProc)
{
 patchloop:
// timeout: 0,5s
 Sleep(500);
// legge i byte da patchare
 ReadProcessMemory(hProc,(void*)patchaddr,&patchbuffer,patchsize,NULL);
// li confronta con quelli originali decriptati
 for (unsigned int i=0;i<patchsize;i++)
{
 if (patchbuffer[i] != origpatchbytes[i]) goto patchloop;
}
// se sono uguali, patcha e avvisa l'utente
 WriteProcessMemory(hProc,(void*)patchaddr,&patchbytes,patchsize,NULL);
 printf("wait 2 seconds then click ok to launch CloneDVD");
 return 0;
}
per rendere il loader più generico il thread usa queste variabili globali, facili da reimpostare:
DWORD patchaddr = 0x00403365;
DWORD patchsize = 5;
BYTE patchbuffer[5]; // vuoto, è solo un buffer
BYTE patchbytes[] = {0x90,0x90,0x90,0x90,0x90}; // qualche nop..
BYTE origpatchbytes[] = {0xE9,0x4A,0x01,0x00,0x00}; // i bytes originali del salto, per il confronto
ecco fatto... fatevi il vostro loader personalizzato (se non ne avete
già uno). questo è un loader generico per asprotect, ma si può fare
molto di meglio ;)
per concludere, pasto qui il sorgente completo del mio loader:
--- code ---
#include <windows.h>
#include <stdio.h>
// patch info
char filename[] = "CloneDVD.EXE";
DWORD patchaddr = 0x00403365;
DWORD patchsize = 5;
BYTE patchbytes[] = {0x90,0x90,0x90,0x90,0x90};
BYTE origpatchbytes[] = {0xE9,0x4A,0x01,0x00,0x00};
BYTE patchbuffer[5];
// fine info
DWORD WINAPI PatchWait(HANDLE hProc)
{
 patchloop:
// timeout: 0,5s
 Sleep(500);
// legge i byte da patchare
 ReadProcessMemory(hProc,(void*)patchaddr,&patchbuffer,patchsize,NULL);
// li confronta con quelli originali decriptati
 for (unsigned int i=0;i<patchsize;i++)
{
 if (patchbuffer[i] != origpatchbytes[i]) goto patchloop;
}
// se sono uguali, patcha e avvisa l'utente
 WriteProcessMemory(hProc,(void*)patchaddr,&patchbytes,patchsize,NULL);
 printf("click ok to launch CloneDVD\n");
 return 0;
}
bool HideDebugger(HANDLE hProc,HANDLE hT)
{
// legge il thread context
 CONTEXT tc;
 tc.ContextFlags = CONTEXT_ALL;
 if(!GetThreadContext(hT,&tc)) return false;
// calcola l'indirizzo del byte in modo analogo a IsDebuggerPresent
 DWORD addr,buf;
 ReadProcessMemory(hProc,(void*)(tc.SegFs+0x18),&addr,4,NULL);
 ReadProcessMemory(hProc,(void*)(addr+0x30),&addr,4,NULL);
 ReadProcessMemory(hProc,(void*)addr,&buf,4,NULL);
 addr = buf+2;
questa volta ho deciso di scrivere un
essay di livello più elevato perchè ho trovato un target che mi ha
stuzzicato la fantasia; come al solito giocare con i packer è molto
divertente ;)
se poi vi ho anche insegnato qualcosa, sono ben contento :D
ovviamente per chiarimenti, migliorie o correzioni mandatemi una mail.
ci vediamo al prossimo essay...bye!
bender0
Disclaimer
Vorrei ricordare che il software va comprato
e non rubato, dovete registrare il vostro prodotto dopo il periodo di
valutazione. Non mi ritengo responsabile per eventuali danni causati al vostro
computer determinati dall'uso improprio di questo tutorial. Questo documento è
stato scritto per invogliare il consumatore a registrare legalmente i propri
programmi, e non a fargli fare uso dei tantissimi file crack presenti in rete,
infatti tale documento aiuta a comprendere lo sforzo immane che ogni singolo
programmatore ha dovuto portare avanti per fornire ai rispettivi consumatori i
migliori prodotti possibili.
Noi reversiamo al solo scopo informativo e di
miglioramento del linguaggio Assembly.