• Regolamento Macrocategoria DEV
    Prima di aprire un topic nella Macrocategoria DEV, è bene leggerne il suo regolamento. Sei un'azienda o un hosting/provider? Qui sono anche contenute informazioni per collaborare con Sciax2 ed ottenere l'accredito nella nostra community!

Sulle DLL ... (2)

nothing

Utente Esperto
Autore del topic
4 Gennaio 2010
1.249
58
Miglior risposta
0
Se quello che avete letto in

http://www.sciax2.it/forum/c-c/c-sulle-dll-453824.html#post2873233

vi è chiaro, allora possiamo fare un passo avanti.

Supponiamo di conoscere la firma della funzione ma di non avere una Import Library (una .lib) da indicare al linker per potere individuare l'entry point della stessa all'interno della DLL. Oppure (ed è il motivo più importante) di voler lavorare dinamicamente, chiamando le funzioni che più ci interessano secondo particolari esigenze.

Dico che è il più importante perché ha risvolti pesanti nel campo di virus e malware particolarmente pericolosi (rootkit).

Infatti, per utilizzi standard, la "Import Library" è ottenibile a partire dalla DLL (tramite i comandi DUMPBIN e LIB che avrete a disposizione se avete installato Visual Studio), secondo procedure semplici e documentate.

Diverso il discorso se manca la firma della funzione (ricordo, nome, valore restituito, numero e tipo di parametri) perché in quel caso non c'è modo di ottenere tali informazioni dalla DLL e si deve ricorrere al reverse engineering.

Tornando all'utilizzo dinamico delle funzioni delle DLL, Windows mette a disposizione (da sempre) alcune API specifiche, ovvero

Perfavore, Entra oppure Registrati per vedere i Link!

Perfavore, Entra oppure Registrati per vedere i Link!

Perfavore, Entra oppure Registrati per vedere i Link!


La prima e la seconda funzione sono utilizzate per ottenere un riferimento (sostanzialmente l'indirizzo virtuale a partire dal quale la DLL che ci interessa è caricata in memoria) con la differenza che la prima (LoadLibrary) legge dal disco e carica in memoria la DLL se questa non è già caricata (e altre cose minori) mentre la seconda (GetModuleHandle) si attende che la DLL sia già stata caricata in memoria e mappata nello spazio di indirizzamento del processo e ne ottiene l'indirizzo.

Se la DLL è di tipo custom (una vostra DLL) o non è tra quelle più utilizzate, allora si rende necessario usare la LoadLibrary.
Ma se la DLL è una tra quelle più comuni (e usate praticamente da tutti i processi), come ntdll.dll o kernel32.dll, allora basta la GetModuleHandle.

A questo punto, il programma dell'esempio precedente diventa

Codice:
Perfavore, Entra oppure Registrati per vedere i codici!

con le linee aggiunte/modificate in rosso che vediamo in dettaglio

1) con la typedef creiamo un nuovo tipo di dato (puntatore a funzione che restituisce un valore NTSTATUS e che non accetta parametri) che descrive la "firma" della funzione che intendiamo richiamare

2) dichiariamo una variabile di nome DynamicGetVersion (o quello che meglio ci pare) che sarà un puntatore a funzione del tipo appena dichiarato. In pratica, essendo un puntatore, conterrà l'indirizzo della nostra funzione (appena ce lo scriveremo ...)

3) otteniamo l'indirizzo in memoria della dll kernel32.dll nella variabile hDll tramite la GetModuleHandle

4) inseriamo l'indirizzo della GetVersion nel puntatore a funzione, a partire da quello della kernel32.dll, tramite la GetProcessAddress. A questo punto la variabile DynamicGetVersion contiene l'indirizzo della funzione GetVersion

5) utilizziamo il puntatore per richiamare la funzione esattamente come se l'indirizzo l'avessimo ottenuto staticamente.

Per capire meglio, modifichiamo il codice per visualizzare qualche dato e fare qualche prova, ovvero aggiungiamo le linee in rosso dopo quelle indicate

Codice:
Perfavore, Entra oppure Registrati per vedere i codici!

e otterremo a video delle linee simili (gli indirizzi possono variare da sistema a sistema)

kernel32 base: 0x7C800000
GetVersion address: 0x7C81127A


che indicano dove è posizionata la DLL in memoria e dove, al suo interno, inizia la API GetVersion (ricordate sempre che sono indirizzi virtuali).

Per capire ancora bene come l'indirizzo sia quello effettivamente utilizzato dal programma, commentiamo la linea

// DynamicGetVersion = (PF_DynamicGetVersion) GetProcAddress( hDll, "GetVersion" );

in modo che il valore in DynamicGetVersion sia 0. Avremo

kernel32 base: 0x7C800000
GetVersion address: 0x00000000

e ovviamente, essendo diventato un puntatore NULL, l'utilizzo di DynamicGetVersion nella chiamata causerà un errore di tipo

Unhandled exception at 0x00000000 in <vostro_eseguibile>: 0xC0000005: Access violation reading location 0x00000000.

e il programma si bloccherà (non sapendo dove sia la funzione da eseguire) confermando che è proprio la GetProcAddress che fornisce l'indirizzo della funzione al programma e non la Import Library.

Spero che quello che ho scritto vi sia chiaro (o almeno non troppo oscuro ... ). Anche questa volta, se avete dubbi esprimeteli e commentate ... la prossima volta, discuteremo di un esempio più concreto di chiamata dinamica di funzioni da DLL ...
 
Riferimento: Sulle DLL ... (2)

Codice:
Perfavore, Entra oppure Registrati per vedere i codici!

*PF_DynamicGetVersion conterrà l'indirizzo della dll. E fin qui mi è chiaro ma __stdcall ed il VOID non capisco cosa descrivino.



Codice:
Perfavore, Entra oppure Registrati per vedere i codici!

L'utilizzo della funziona invece è derivata dalla GetVersion() oppure da:

Codice:
Perfavore, Entra oppure Registrati per vedere i codici!
 
Riferimento: Sulle DLL ... (2)

Codice:
Perfavore, Entra oppure Registrati per vedere i codici!

*PF_DynamicGetVersion conterrà l'indirizzo della dll.

PF_DynamicGetVersion fa parte della descrizione del tipo, non è la variabile che conterrà il valore. La variabile è la DynamicGetVersion.

Per capire meglio, è come se con una riga simile

int x;

tu dicessi che int conterrà il valore (mentre è x che conterrà il valore e int è il tipo).


E' un modificatore specifico dei compilatori Microsoft che indica la calling convention. Ovvero indica che la funzione chiamata pulisce lo stack dai parametri passati dal chiamante. Tutte le API di Windows lavorano in questo modo. Si inseriscono i dati nello stack, in ordine inverso, e si chiama la funzione. Questa provvede a ripulire lo stack.


E' il modo per dichiarare che la funzione non utilizza alcun parametro.
 
Riferimento: Sulle DLL ... (2)

Il prefisso L posto prima della costante stringa è usato dai compilatori Microsoft per indicare che la stringa è UNICODE

L'ho usata perché ho creato un progetto Unicode ma non è necessario e se non utilizzi Unicode per le stringhe, quella L la puoi (anzi la devi) eliminare.

Leggi

Perfavore, Entra oppure Registrati per vedere i Link!
 
Riferimento: Sulle DLL ... (2)

Ma allora qualcuno che posta cose interessanti su questo forum c'è :emoji_smiley:.