Curiosando nel Sistema Operativo, ovvero:
Reversing da strapazzo e funzione hmemcpy


23/10/2000

by Alga

 

 

UIC's Home Page

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.


Curiosando nel Sistema Operativo, ovvero:
Reversing da strapazzo e funzione hmemcpy


Written by Alga

Introduzione


L'essere umano ha un innato bisogno di classificare; gli essere umani poco evoluti, se non classificano, almeno categorizzano.

Personalmente credo che classificare dia un senso di sicurezza: ogni cosa ha una sua precisa collocazione, e ciò dà la sensazione di avere la situazione sotto controllo. Quando un oggetto, una persona, un evento, un concetto non sono invece inquadrabili nella consuetudine, ingenerano diffidenza e disagio; sembra manchino i parametri per giudicarli, cosicchè le valutazioni sono spesso pessimistiche, molto più negative di quanto magari l'oggetto della valutazione non meriti in realtà.


Perché vi sto raccontando tutto questo? Qualora non si fosse già capito, sto cercando disperatamente una motivazione che faccia apparire migliore ai miei occhi ciò che sto scrivendo, che non mi appare granchè né come forma, né come contenuti tecnici; allora voglio illudermi che ciò avvenga non tanto perché per ciò che scrivo sia in realtà una schifezza immonda, quanto perché non si capisce neanche cosa sia.
E' un tutorial? No di certo! E' un articolo? E da quando in qua le alghe si permettono persino di scrivere articoli? No, non è un articolo. E' solo l'espressione del comportamento indecoroso di un'alga ficcanaso? Quando mai, le alghe non hanno naso…è solo una puntualizzazione, nata da una risposta che avevo preparato per un post comparso sul forum della UIC, che riguardava essenzialmente la famigerata (per i/le crackers, almeno) funzione hmemcpy.


L'argomento non è, di per sé, molto interessante, ma è tuttavia molto sentito, a giudicare dalla frequenza con la quale compaiono i quesiti su questo argomento; le risposte tuttavia non sembrano mai esaurienti, o convincenti, o accurate: in una parola, soddisfacenti. Ma le alghe, si sa, sono pignole, così avevo deciso di rispondere al post (firmato col nick "Soucer") cercando di essere più esauriente possibile.
Avevo quindi preparato una risposta che andava ben oltre le necessità di Soucer, con tutti i tag del caso (per corsivi, grassetti, etc.); indi, dopo aver constatato quanto fosse lunga, preferii non ingolfare il forum con un post lunghissimo che probabilmente, rifacendomi ad esperienze maturate con post in altri forum, sarebbe servito solo a rendermi antipatica.

Restai comunque a disposizione di Soucer, così come di chiunque altro avesse desiderato la risposta, per quanto riguardava l'eventuale invio della risposta completa; Soucer rispose che, almeno parzialmente (anche se sono sicura che resterà deluso leggendola) la cosa era di suo interesse, così decisi di inviarla. Sempre per evitare risposte chilometriche sul forum, magari con difficoltà di lettura, decisi di inviarla in questa forma (riscrivendo tutto da capo, ovviamente L).

Ha a che vedere quanto è scritto qui con il cracking? Poco, ma ha a che vedere. Ha a che vedere con un'Università del Cracking? Bé, forse un po' di più; in un'Università dovrebbe venire fornita una preparazione leggermente più ampia, almeno come cognizioni di base, di quella strettamente specialistica (questo ve lo posso assicurare, in quanto con le Università io ho un certo "feeling" - se non altro perché, essendo un'Alga, vengo spesso studiata negli istituti di Biologia Marina J).

Sicuramente ha però a che vedere con il reversing; curiosare nei sistemi (operativi e non J) altrui è "reversing puro"; farlo in questo modo è fare reversing da strapazzo. Posso reputarmi soddisfatta: esercito puro reversing da strapazzo J.

Si, ma è almeno interessante? Questo non lo so; dal punto di vista delle informazioni "strettamente utili" qui contenute, no, non lo è. Non contiene nulla di più di quanto non avessi già detto a Soucer, e di quanto non sia probabilmente già conosciuto da molti.

Bene, allora vi starete chiedendo: ma se non si sa cos'è, non insegna niente, è di cattiva qualità tecnica ed è anche noioso, perché mai lo stiamo leggendo?!?

Infatti, non ne avete motivo; è molto più costruttivo che chiudiate qui il vostro browser ed iniziate subito a leggere un libro di Matt Pietrek.

Ma, da un punto di vista più generale, se siete curiosi come un'alga (che è il vegetale più curioso che sia mai stato inventato) forse potreste voler sapere cosa ho da dire, e trovarlo interessante per questo; il primo parere toccherà come sempre a Quequero, e se lui deciderà di pubblicarlo giudicherete voi.


Tools usati

Nessuno, se volete solo leggere


- un debugger (tipicamente SoftICE)
- un disassemblatore (tipicamente, IDA)
- compilatori vari (tipicamente, Borland e Microsoft)
- documentazione (tipicamente, help file ed header file dal Platform SDK di Microsoft)
- Quick View (aka Anteprima) e Dumpbin (se siete particolarmente cattivi, potreste anche fare a meno di IDA)
se volete sperimentare da soli

URL o FTP del programma

Non applicabile (o in alternativa, se volete, tutte le versioni dei sistemi operativi Microsoft dal 1993 ai giorni nostri)

Notizie sul programma 

Non conosco notizie. Conosco solo nocaie e nosempronie.

Essay

Come già detto in introduzione, hmemcpy è una funzione molto "amata" dai/dalle crackers che lavorano su sistemi operativi Windows della serie 9x,. Inserendo un breakpoint su di essa infatti è possibile risalire spesso alle procedure che nei programmi target si occupano del trattamento delle stringhe di caratteri (lettura dall'EditBox, copia in altre locazioni per elaborazione successive, etc.). Questa è una tecnica che funziona praticamente sempre (anche se poi stabilire come esattamente il programma tratta la stringa di dati è un'altra cosa J).

Quando però qualcuno passa ad un sistema Windows NT, scopre (con sommo dispiacere) che il metodo non funziona.
Questo è facilmente verificabile scorrendo i vari forum "cracking related"; almeno un paio di richieste al mese riguardano questo argomento.

La ripetitività del quesito nasce probabilmente dal fatto che, come ho già accennato, la risposta non risulta soddisfacente, e ciò non contribuisce certo a diffonderla; la causa risiede probabilmente nella "parzialità" di vedute che chi si occupa di cracking ha riguardo all'argomento. La questione ha infatti risvolti diversi a seconda dell'ambito in cui la si considera: programmazione, reversing, cracking; e la risposta è fornita di solito da persone che si occupano esclusivamente di cracking.

La risposta che ho più frequentemente letto su forum in lingua inglese suona all'incirca così "Windows NT doesn't support hmemcpy. Use memcpy instead". La prima affermazione è semplicemente priva di senso. La seconda è una parziale verità. Il mio obiettivo qui è spiegarne il perché.

PREMESSA
Con "libreria standard ANSI" gli stessi Kernighan e Ritchie si riferiscono agli header delle dichiarazioni di funzione e delle definizioni dei tipi che, oltre a seguire lo standard, anche se non fanno parte propriamente del linguaggio C, si trovano sicuramente in qualunque distribuzione di qualunque ambiente di ogni compilatore C. Questo in pratica vuol dire che ogni compilatore C ANSI standard supporterà le funzioni.
Nei sistemi operativi Windows, molte funzioni di libreria standard ANSI del linguaggio C (in cui il sistema operativo è prevalentemente scritto), hanno un equivalente tra le API. Esse sono prevalentemente esportate dalla libreria di Runtime C, ma non solo (vedi ad esempio wsprintf, esportata da USER32.DLL, o lstrscpy, esportata da KERNEL32.DLL).

L'header < string.h > (libreria standard) contiene diverse definizioni di funzione, di cui alcune sono state concepite essenzialmente per trattare vettori di caratteri. In generale, le funzioni che effettuano copie o spostamento di caratteri forniscono risultati indefiniti se le aree di spostamento o di copia si sovrappongono (in altri termini, se la grandezza delle stringhe o la dimensione dei vettori è superiore a quella delle locazioni di memoria da cui sono divise); ciò vale per tutte le funzioni con un'unica eccezione.

L'eccezione è appunto una funzione per lo spostamento di vettori di caratteri, dichiarata come void *memmove(s, ct, n); esiste un equivalente di essa che, analogamente a tutte le altre, funziona solo se le aree da copiare non si sovrappongono, ed è dichiarata come void *memcopy(s, ct, n). La necessità di avere due funzioni è chiara in un contesto in cui la memoria disponibile è poca; memmove deve mantenere un intermedio della copia per evitare di sovrascrivere ciò che ancora deve copiare, e quindi consuma più memoria ed è più lenta. In esse s rappresenta la locazione di memoria iniziale dell'area nella quale verranno copiati i caratteri, ct è la locazione iniziale dell'area contenente i caratteri da copiare ed n è il numero di caratteri.

La funzione hmemcpy fa la sua comparsa in KRNL386.EXE di Windows 3.1 (era assente in Windows 3.0):

 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.

Note finali

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

Ancora una volta vi ricordo che è il software va crackato e non rubato, che è vietato dalla legge eseguire delle copie a scopo di backup dei CD pirata, che è illegale disassemblare, decompilare o effettuare il reverse engineering dei prodotti Microsoft servendosi dei debugger distribuiti gratuitamente con il Platform SDK...urp, mi sono confusa!

Grazie sempre a Quequero che sopporta le mie scemenze.

Buon reversing a tutti.