Curiosando nel Sistema Operativo, ovvero: |
|
|
23/10/2000 |
by Alga |
|
|
Published by Quequero |
|
Nessun pensiero, nessuna riflessione, nessuna analisi, nessuna
preparazione, nessuna intenzione; lasciate che si stabilisca da sé Tilopa |
Trovo sempre molta difficoltà a commentare Olga, sicuramente non ne sono all'altezza, infatti sarebbe lei a dover commentare me e non io a dover commentare lei, cmq il tute, come nel suo stile, è molto molto simpatico, leggero e soprattutto ottimamente spiegato, è curato nei minimi dettagli nonostante il soggetto della sua mail fosse: "Orribile!", mentre il tute non è affato orribile :), prendete tutti esempio da Olga che è un'ottima reverser, una bravissima professoressa (come potete vedere :) e molto altro, tante grazie ed ancora complimenti Olga :) Cmq anche tu il forum lo potresti aggiornare di tanto in tanto :) |
Ma come avremo fatto a perdere, agli Europei... Dino Zoff |
UIC's form | UIC's form | |
Difficoltà |
(X)So accendere il computer ( )NewBies ( )Intermedio ( )Avanzato ( )Master |
Questa è la versione "modulo UIC" della risposta (mai inviata) ad un post comparso giorni addietro sul forum UIC. Il post è analogo a centinaia di altri che compaiono mensilmente su tutti i forum "cracking related" del pianeta. Così, poichè la domanda è frequente e la risposta è lunga, ed inoltre parte delle informazioni contenute sono state ottenute curiosando (con i mezzi più disparati) all'interno del sistema operativo, mi sono decisa ad inviarla qui.
Introduzione |
Tools usati |
URL o FTP del programma |
Notizie sul programma |
Essay |
void hmemcpy(hpvDest, hpvSource, cbCopy) void _huge* hpvDest; /* address of destination buffer*/ const void _huge* hpvSource; /* address of source buffer */ long cbCopy; /* number of bytes to copy */
Essa è assolutamente analoga, come si vede, alla memcpy; cioè, veramente non
si vede, ma la documentazione dell' SDK dichiara esplicitamente che i risultati sono
imprevedibili se le aree di memoria sorgente e destinazione si sovrappongono parzialmente.
Una cosa da notare è che i puntatori alle aree sorgente e destinazione sono di tipo huge,
(cioè gli oggetti cui essi fanno riferimento possono essere più grandi di 64K , e quindi
trovarsi in due segmenti diversi); questo è appunto il significato della "h"
davanti al nome della funzione.
Per quanto io ne sappia (non ho verificato in Windows ME, ma sono ragionevolmente sicura
che le cose stiano realmente così), tutte le versioni di KRNL386.EXE da Windows 3.1 in
poi esportano la funzione hmemcpy, compreso quelle contenute nei sistemi NT. Le
versioni presenti nel corso del tempo sono state riscritte, ricompilate, linkate con
versioni diverse del linker, ma la funzione è sempre lì (come è facilmente verificabile
con Quick View); se così non fosse, i programmi a 16 bit che ne fanno uso non potrebbero
girare sui sistemi che "non la supportano". Tutt'al più, essa potrebbe essere
implementata in maniera completamente diversa; ma se vogliamo toglierci la curiosità
disassemblandola, ci accorgeremo che la versione contenuta in KRNL386.EXE di Windows 3.1
è assolutamente identica a quella di Windows 2000: essa non è mai stata riscritta.
Quindi, risulta chiaro il nonsenso dell'affermazione "Windows NT non supporta
hmemcpy": la funzione è lì, e se non fosse lì il sottosistema WOW (Windows On
Windows) non potrebbe, almeno in certi casi, funzionare.
Probabilmente, l'affermazione è generata dal fatto che, con i file di configurazione
standard, il tentativo di dare in pasto al SoftICE un BPX su questa funzione si risolve
sempre, su Windows NT, in una rispostaccia dello stesso; invece in Win 9x il SoftICE
accetta il comando senza fiatare. Ciò è tuttavia soltanto in relazione a quanto scritto
nel file di configurazione; aggiungendo
EXP %SystemRoot%\system32\krnl386.exe
alla lista degli export in Winice.dat, SoftICE accetta il BPX
hmemcpy anche in Win NT, e se viene eseguito un programma a 16 bit su Windows NT
che chiami direttamente hmemcpy, SoftICE interromperà l'esecuzione nel corso della
chiamata.
Il fatto che hmemcopy possa venire utilizzato come avviene in Windows 9x è invece
tutt'altro discorso; ed esso si interseca con la seconda delle affermazioni, secondo la
quale in Windows NT dovrebbe venire usata memcpy.
Come ho già accennato prima, questa è una parziale verità. Infatti, questa affermazione
è parzialmente vera nell'ambito della programmazione, ancor meno vera nell'ambito del
reversing "puro" (ma che sarebbe mai poi il reversing "puro"? Non
cominciate a fare domande delle quali non so la risposta perché questo LO ODIO! J), per niente vera nell'ambito del cracking.
Discutiamo inizialmente il primo punto.
IL PUNTO DI VISTA DEL/LA PROGRAMMATORE/TRICE
Il passaggio a Win32 ha reso privi di significato i puntatori huge; tutto avviene
all'interno di un unico segmento da 4Gb, ed un puntatore a 32 bit può indirizzare solo in
questo intervallo. La funzione hmemcpy è stata così sostituita dalle funzioni CopyMemory
:
VOID CopyMemory ( PVOID Destination, // address of copy destination CONST VOID *Source, // address of block to copy DWORD Length // size, in bytes, of block to copy );
e MoveMemory:
VOID MoveMemory ( PVOID Destination, // address of move destination CONST VOID *Source, // address of block to move DWORD Length // size, in bytes, of block to move );
le quali sono esattamente analoghe alle funzioni standard ANSI C memcopy e memmove,
e cioè CopyMemory fornisce risultati inattendibili in caso di sovrapposizione dei
blocchi di memoria, caso in cui andrebbe utilizzata MoveMemory. Ciò varrebbe anche
per le Win32s, che supportano queste due funzioni. Ci si potrebbe domandare: chi esporta
queste funzioni?
In teoria, possedendo una copia degli help file dei compilatori Borland relativa alle API
di Windows, tutto è molto semplice; quel gran gentiluomo di Frank Borland (o chi per lui J) infatti, oltre a copiare pedissequamente il contenuto dei file
di help dell'SDK relativo alle API, mette a disposizione per ogni funzione i riferimenti
relativi ai sistemi che la supportano, la libreria che la esporta e l'header che definisce
le costanti mnemoniche: basta premere il pulsante "Quick Info", et voilà!
Se però proviamo a premere il fatidico pulsantino nel caso delle suddette funzioni, in
corrispondenza della voce "Import Library" vediamo un
trattino! Chi
esporta le funzioni?
Nessuno, sembrerebbe. Tant'è che, anche inserendo nel file di configurazione di SoftICE
tutti gli EXP del mondo, la risposta di SoftICE al tentativo di BPX su una di queste
funzioni è sempre quella che è impossibile settarlo in quanto non riconosce la funzione
(almeno sul SoftICE di Soucer, che sembra molto loquace; il mio, decisamente più
scontroso, si limita ad un laconico "Symbol not defined" J).
Però...qualcosa l'help di Borland ci dice: l'header di CopyMemory e di MoveMemory
è winbase.h; ed in quello troviamo una definizione in una
sezione commentata come...Compatibility Macros. Sono delle macro! Anzi, sono degli alias
per:
#define MoveMemory RtlMoveMemory #define CopyMemory RtlCopyMemory
Se proviamo ad inserire in SoftICE BPX rtlmovememory, il
SoftICE si prenderà il breakpoint senza fiatare. Ma al BPX
rtlcopymemory seguirà comunque il "Symbol not defined".
Bene, dove sono le funzioni, allora? Intanto, se, dopo aver inserito i breakpoint in
SoftICE, diamo il comando bl, SoftICE mostrerà:
00) BPX Kernel32!RtlMoveMemory
in Win 9x, e
00) BPX ntdll!RtlMoveMemory
in Windows NT.
Se andiamo a cercare nelle export table di, rispettivamente, KERNEL32.DLL e NTDLL.DLL con
QuickView o con Dumpbin (se l'interfaccia a linea di comando non vi garba, vi consiglio
caldamente di usarlo con Process Tree), RtlMoveMemory si troverà effettivamente
tra le funzioni esportate, ma nessuna traccia di RtlCopyMemory o similari sarà
rilevabile. Potremmo (solo se volessimo farci del male - SoftICE lo può fare per noi)
cercare in tutti i file dei sistemi operativi, non troveremmo mai alcuna traccia di questa
funzione. Eppure, se avviamo un compilatore C/C++ qualunque ed inseriamo ad esempio
char *pippo, *pluto; _________________________________________ pluto="Walt Disney"; pippo=" " ; RtlCopyMemory(pippo,pluto,12);
il compilatore compilerà allegramente, ed alla fine pippo sarà Walt Disney (!).
Quindi RtlCopyMemory è dichiarata da qualche parte, e qualcosa viene esportato che
possa consentire al compilatore di generare il programma.
Cercando, tramite Find
le occorrenze di RtlCopyMemory, due file vengono
elencati. Uno è winbase.h, come si sapeva già; l'altro è winnt.h.
Una ricerca all'interno del file ci porta a:
#define RtlMoveMemory(Destination,Source,Length) memmove((Destination),(Source),(Length)) #define RtlCopyMemory(Destination,Source,Length) memcpy((Destination),(Source),(Length))
inseriti in una sezione commentata come "for move macros". Anche RtlMoveMemory
ed RtlCopyMemory sono alias; tuttavia, mentre RtlMoveMemory ha la sua
implementazione all'interno di KERNEL32.DLL in Win 9x, ed all'interno di NTDLL.DLL ed
NTOSKRNL.EXE, RtlCopyMemory non la possiede.
Inoltre, le due implementazioni di RtlMoveMemory in Win NT sono assolutamente
identiche.
Così, ancora una volta le funzioni non sono altro che quelle della libreira standard ANSI
C, né più, né meno.
A questo punto, si vede che inserendo BPX memcpy il SoftICE
non si lamenta più, ed il comando bl mostra
00) BPX CRTDLL!memcpy 01) BPX CRTDLL!memmove
in Win 9x, mentre mostra
00) BPX ntoskrnl!memcpy 01) BPX ntoskrnl!memmove
in Win NT.
Ciò vuol dire che, mentre le due funzioni vengono esportate dalla libreria di Runtime C
di Microsoft in W9x, esse hanno invece un'implementazione "apposita" sui sistemi
NT; questa è una delle (molteplici) differenze tra i due sistemi. Inoltre,
l'implementazione di memmove è diversa da quella di RtlMoveMemory.
IL PUNTO DI VISTA DEL/LA REVERSER
Se però, lasciando attivi questi BPX, lanciamo il programma di Walt Disney compilato con
un compilatore Borland o Microsoft, la console del SoftICE non comparirà mai.
Un disassemblato del file oggetto mostrerà per quanto riguarda il programma compilato con
C++Builder (questo è il 3, ma sugli altri è analogo):
VIRDEF01:00000020 push ebp VIRDEF01:00000021 mov ebp, esp VIRDEF01:00000023 ; #line 27 VIRDEF01:00000023 mov _pluto, offset aWaltDisney ; "Walt Disney" VIRDEF01:0000002D ; #line 28 VIRDEF01:0000002D mov _pippo, offset asc_7_C ; " " VIRDEF01:00000037 ; #line 29 VIRDEF01:00000037 push 0Ch VIRDEF01:00000039 push _pluto VIRDEF01:0000003F push _pippo VIRDEF01:00000045 call _memcpy VIRDEF01:0000004A add esp, 0Ch VIRDEF01:0000004D ; #line 31 VIRDEF01:0000004D mov eax, 1 VIRDEF01:00000052 ; #line 32 VIRDEF01:00000052 pop ebp VIRDEF01:00000053 retn 0Ch
Così il compilatore Borland non utilizza memcpy, bensì una funzione dal nome _memcpy;
implementata da Borland all'interno di proprie librerie di runtime, ed esportata da esse o
compilate all'interno dell'eseguibile a seconda delle opzioni di compilazione. In Builder
3, la libreria che le esporta è cw3240mt.
Se quindi un'applicazione è stata compilata tramite un compilatore Borland, anche se
all'interno di esso è stata chiamata CopyMemory, nessuna chiamata a memcpy
verrà mai eseguita. Ci si potrebbe chiedere cosa succede se, anziché una chiamata a RtlCopyMemory,
venga invocata direttamente la funzione memcpy; bè, la situazione non cambia,
Borland userà comunque la propria implementazione.
Il Visual C++ si comporta nello stesso modo, e _memcpy è contenuta il MSVCRTD.LIB
.text:00000034 push 0Ch .text:00000036 mov eax, ds:?pluto@@3PADA ; char * pluto .text:0000003B push eax .text:0000003C mov ecx, ds:?pippo@@3PADA ; char * pippo .text:00000042 push ecx .text:00000043 call _memcpy
Se si chiama direttamente memcpy dicendo al compilatore di utilizzare le
librerie dinamiche di Runtime, la memcpy chiamata si troverà in MSVCRTD.DLL.
Anche Delphi ha un comportamento simile. In Delphi 5
pippo, pluto:PChar; ----------------------------------------------------- pippo := ' '; pluto := 'Walt Disney'; CopyMemory (pippo, pluto, 12);
farà si che il compilatore usi la funzione System::Move all'interno di
VCL50.DLL; ciò evidentemente se la funzione non venga dichiarata esplicitamente come external.
Risulta adesso chiaro il significato delle prime due affermazioni precedenti
Per il programmatore le funzioni hmemcpy e memcpy sono del tutto
equivalenti; egli può adesso usare memcpy (o, indifferentemente, RtlCopyMemory,
o CopyMemory) quando avrebbe usato, in ambiente a 16 bit, hmemcpy. I
dettagli dell'implementazione verranno scelti dal compilatore, e non riguardano per nulla
il programmatore. La "parziale verità" dell'affermazione consiste nel fatto che
un tale comportamento è assolutamente indipendente dal sistema operativo; tutta la
piattaforma Win32 si comporterà nello stesso modo, e quindi se è vero che memcpy
è supportata da Windows NT al posto di hmemcpy, è anche vero che è supportata
nello stesso modo da Windows 9x o da Win32s.
Dal punto di vista del reverser le cose vanno invece un po' diversamente in quanto se
l'attività di reversing riguarda un programma compilato con il supporto di runtime o con
l'esplicito riferimento alla memcopy esportata da NTOSKRNL.EXE o da CRTDLL.DLL, sarà
facile risalire a ciò che il programma sta facendo; ma se il programma è stato compilato
con l'opzione di esclusione delle librerie di runtime nessun riferimento al nome della
funzione sarà presente. Bisognerà reversare la funzione (o essere "abituati" a
vederne l'implementazione) per riconoscerla, in quanto essa sarà interamente sviluppata
all'interno del programma.
IL PUNTO DI VISTA DEL/LA CRACKER
Completamente diversa è la situazione che riguarda il cracking.
L'interesse della funzione hmemcpy rivestito in quest'ambito è di tutt'altra
natura, ed ha sempre risentito poco della situazione confusionaria precedentemente
descritta; essa risulta pressocchè irrilevante, cosicchè le relative informazioni sono,
in fin dei conti, qui poco diffuse.
Il motivo di ciò risiede nel fatto che la maggior parte delle persone che si occupano di
cracking usa Windows 9x.
Con questo intendo riferirmi alla struttura "ibrida" dei sistemi operativi di
questa famiglia, e particolarmente al thunking di molte delle funzioni di Windows 9x.
Cioè, Windows 9x "fa finta" di essere un sistema operativo a 32 bit, ma molte
funzioni sono implementate come overlay di codice a 16 bit. In pratica, la funzione a 32
bit fa riferimento, per svolgere alcune operazioni, al sottostante codice a 16bit; proprio
questo è il motivo per il quale Matt Pietrek chiama Windows 95 " Win 32s done
properly".
Molte funzioni all'interno di USER32DLL passano uno o più parametri, attraverso lo stack,
alla funzione QT_THUNK esportata da KERNEL32.DLL; il codice iniziale di ogni funzione si
limita a porre in CL un indice che servirà da puntatore in una tabella in cui verrà
letto l'indirizzo della corrispondente funzione a 16 bit in USER.EXE (passato a QT_THUNK
in EDX), a cui QT_THUNK trasferisce il controllo. Da qui, il codice eseguito è a 16 bit,
e le chiamate alle funzioni del kernel faranno riferimento a KRNL386.EXE; attraverso una
serie di call, sarà comunque spesso hmemcopy ad occuparsi di ogni spostamento di
aree di memoria, anche di quelle relative al contenuto degli editbox (e questo è appunto
l'interesse che la funzione riveste nell' ambito del cracking).
Altre funzioni passano l'indirizzo corrispondente alla funzione FT_Thunk tramite una
variabile, ma anche in questo contesto il controllo viene passato al codice a 16 bit, e
l'andamento delle cose non differisce dal precedente.
Quindi il/la cracker può benissimo inserire un breakpoint su questa funzione, che sarà
poi quella che effettivamente farà il lavoro. Non ha bisogno di sapere dove sono CopyMemory
o MoveMemory; anzi, non ha neanche bisogno di sapere che esistono.
In particolare poi , l'implementazione di diverse funzioni (un esempio per tutti:
WaitMessage) si avvale di codice contenuto all'interno della famosa BOZOSLIVEHERE,
nel contesto della quale viene sempre chiamata hmemcpy (esistono due chiamate ad hmemcpy
in essa). In pratica, non è mai il programma a chiamare hmemecpy (abbiamo visto
che molto raramente verrebbe eseguita una chiamata diretta ad una funzione di questo tipo,
ed essa in Win32 sarebbe comunque memcpy), ma è sempre il sistema operativo. E
questo vale almeno fino a Windows 98 (non ho mai dato un'occhiata a Windows ME).
I sistemi NT invece sono dei veri sistemi a 32 bit; inoltre la loro architettura è
diversa. Forse potrebbe essere ravvisata una qualche analogia architetturale tra le
chiamate che alcune funzioni di USER32.DLL (o anche di GDI.DLL o KERNEL32.DLL) eseguono
alle API "native" di NT, e l'uso del sottosistema a 16 bit in Windows 9x.
Se infatti lo schema attraverso il quale viene eseguita la chiamata in windows 9x risulta
di questo tipo:
mov cl, indice push ebp mov ebp, esp push ecx sub esp, 3Ch push parametri :... call passa indirizzo a QT_THUNK xor ecx, ecx mov cl,[ebp - 04] mov edx,[base tabella indirizzi+4*ecx] mov eax, QT_THUNK jmp eax :... leave retn n
il che equivale in pratica a:
mov edx, indirizzo push parametro call QT_THUNK ret
quello relativo a Win NT è di questo tipo
mov eax, numero funzione lea edx, puntatore ai parametri int 2E ret
L'exception handler di INT 2E è KiSystemService in NTOSKRNL.EXE, il
quale si occuperà di ritrovare l'indirizzo di codice a cui saltare, una volta effettuata
la transizione in kernel mode, in una tabella apposita.
Ma l'apparente similitudine termina qui. L'implementazione delle funzioni è assolutamente
diversa.
In ogni caso, la copiatura delle stringhe nei buffer degli editbox e le operazioni
similari non passano mai per la funzione memcpy. Per rendersene conto non è
necessario l'uso di alcun disassemblatore; basta far girare uno stesso programma, che
preveda l'introduzione di caratteri in EditBox, in Windows 9x, con SoftICE ed un BPX su hmemcpy,
e su WinNT, con SoftICE ed un BPX su memcpy. Nel primo caso la console di SoftICE
comparirà più volte durante l'inserimento di ogni carattere, nel secondo non comparirà
mai.
E vi è di più. Infatti, sebbene la versione di KRNL386.EXE fornita con i sistemi NT sia
assolutamente analoga a quella fornita con i 9x, ed esporti regolarmente hmemcpy,
il sottosistema WOW si comporta comunque in maniera diversa dai Win9x.
Anche avendo consentito a SoftICE di importare i simboli di KRNL386.EXE, un bpx su hmemecpy,
sebbene venga regolarmente accettato da SoftICE, non servirà a nulla. Come abbiamo visto,
in molte situazioni hmemcpy viene invocata da BOZOSLIVEHERE; ma verificando
il contenuto delle funzioni esportate dalla versione di USER.EXE (la DLL che esporta
BOZOSLIVEHERE) fornita con i sistemi NT, BOZOSLIVEHERE non esiste affatto. E
neanche la ricerca di sequenze tipiche del codice della funzione (anzi, della chiamata che
la funzione esegue) ha successo.
Non vi è soluzione, allora? Probabilmente no; non pare che sui sistemi NT si possa fare
affidamento su una funzione "universale", tramite la quale tutti gli spostamenti
e le copie di aree di memoria vengano intercettati.
Però, per quanto riguarda l'inserimento di caratteri in controlli di tipo EditBox, forse
qualcosa si può fare.
La gestione di questo tipo di controlli è affidata di solito alla funzione EditWndProc
esportata da USER32.DLL. Mentre in Win 9x la copia dei caratteri inseriti avviene, dopo le
opportune diramazioni, sempre tramite la solita hmemcpy, in WinNT è gestita dalla
funzione stessa. Un breakpoint sull'istruzione repz movsd
della funzione consente allora di intercettare l'evento. Da lì, è possibile risalire al
destino dei caratteri inseriti, un po' come avviene per hmemcpy.
In WinNT 4.0 l'istruzione si trova al VA 77E73FCA, almeno nella versione di USER32.DLL
fornita con il Service Pack 6.
Nella RC2 ITA di Windows 2000 è in 77E1A6FA; nella versione finale ENG e nel ServicePack
1 ITA è in 77E3271C; ritengo probabile quindi che si trovi allo stesso VA anche nella
versione finale italiana.
Se però non dovesse trovarsi lì, cercatela. A volte, anche girovagare e curiosare, senza
meta e senza scopo, può essere gratificante e produttivo.
Se adesso state leggendo questo, vuol dire che siete giunti alla fine: bravi! O siete
stupidi, o avete un gran coraggio!
Ma comunque stiano le cose, è ora che la finiate di baloccarvi con le stupidaggini
scritte dalle alghe, e torniate alle cose serie.
|
Re Mi Sol La La La Sol
(che sono appunto le note finali di NUBI DI IERI SUL NOSTRO DOMANI ODIERNO di Elio e le
Storie Tese)
Disclaimer |