Tutorial 13: I Memory Mapped Files
From UIC
Tutorial 13: I Memory Mapped Files
Contents |
| Tutorial 13: I Memory Mapped Files | |
|---|---|
| Author: | Iczelion |
| Email: | Traduttore: -NeuRaL_NoiSE |
| Website: | Mirror |
| Date: | 30/12/2008 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| Comments: | Formattazione Wiki: Antelox |
Introduzione
Vi mostrero' cosa sono i memory mapped files e come usarli per i vostri scopi. Usare un memory mapped file e' abbastanza semplice, come vedrete in questo tutorial.
Tools
Preliminari
Se esaminate l'esempio del tutorial precedente con molta attenzione, vi accorgerete che ha una limitazione abbastanza grave: che si fa se il file che volete leggere e' piu' grande del blocco di memoria allocato? O che facciamo se la stringa che vogliamo cercare e' tagliata a meta' alla fine del blocco di memoria? La risposta tradizionale alla prima domanda e' che si devono leggere i dati dal file ripetutamente, fino a che non si raggiunge la fine del file. La risposta alla seconda domanda e' che dovreste prepararvi alla particolare situazione della fine del blocco di memoria. Quest viene chiamato "problema di bonduary value". Presenta mal di testa per i programmatori e causa innumerevoli bugs. Sarebbe bello se potessimo allocare un blocco di memoria molto grande, abbastanza da contenere tutto il file, ma il nostro programma sarebbe riempito come un uovo dalle risorse. La salvezza ci arriva con il file mapping. Usando il file mapping, potete pensare all'intero file come gia' caricato in memoria, e potete pertanto usare un puntatore di memoria per leggere o scrivere dati sul file. E' davvero cosi' semplice. Non c'e' piu' bisogno di usare le funzioni API riguardanti la memoria e, separatamente, le funzioni API riguardanti l' I/O con i files, perche' sono un tutt'uno, con il file mapping. Ai memory mapped files possono accedere tutti i processi del sistema, non solo il processo che li crea. Il file mapping e' inoltre usato come mezzo per condividere dati tra i processi. Usando il file mapping in questo modo, non c'e' nessun file fisicamente coinvolto. E' piu' come un blocco di memoria riservato che tutti i processi possono *vedere*. Ma condividere i dati tra i processi e' un argomento delicato, e non va trattato superficialmente. Dovete implementare la process e la thread synchronization o altrimenti le vostre applicazioni crasheranno in breve tempo. Non affronteremo l'argomento del file mapping come un mezzo per creare una regione di memoria condivisa in questo tutorial. Ci concentreremo invece sull'uso del file mapping come modo per "mappare" un file in memoria. Infatti, il PE loader usa il file mapping per caricare gli eseguibili in memoria. E' molto conveniente perche' solo le porzioni necessarie possono essere lette selettivamente dal file sul disco. Sotto Win32, dovreste usare il file mapping quanto piu' possibile. Ci sono pero' anche alcune limitazioni nel file mapping. Una volta creato un memory mapped file, la sua grandezza non puo' essere variata durante quella sessione. Percio' il file mapping e' eccezionale per i files di sola lettura o per operazioni con i files che non modificano la grandezza del file. Cio' non significa che non potete usare il file mapping se volete aumentare la grandezza del file. Potete stimare la nuova grandezza e creare il memory mapped file basandovi su di essa, e il file aumentera' a quella grandezza. E' semplicemente un piccolo inconveniente, ecco tutto. Ok, abbastanza spiegazioni. Tuffiamoci nell'implementazione del file mapping. Per usare il file mapping, questi tre passi devono essere seguiti:
- chiamate CreateFile, esclusivamente per aprire il file che volete mappare. Dovete specificare il parametro dwShareMode come 0 per evitare che gli altri processi scrivano sul file durante la sessione.
- chiamate CreateFileMapping con il file handle restituito dal CreateFile come uno dei parametri. Questa funzione crea un file mapping object dal file aperto da CreateFile.
- chiamate MapViewOfFile per mappare una regione del file selezionata (o l'intero file) in memoria. Questa funzione restituisce un puntatore al primo byte della regione del mapped file.
- Usate il puntatore per leggere o scrivere sul file.
- chiamate UnmapViewOfFile per "demappare" il file.
- chiamate CloseHandle con l'handle del mapped file come parametro per chiudere il mapped file.
- chiamate CloseHandle di nuovo, ma stavolta con il file handle restituito da CreateFile per chiudere fisicamente il file.
Essay
Il seguente programma vi fa aprire un file con un dialog box "apri file". Esso apre il file usando il file mapping. Se riesce ad aprirlo con successo, il titolo della finestra viene cambiato con il nome del file aperto. Potete salvare il file con un altro nome selezionando il menuitem File/Save. Il programma copiera' l'intero contenuto del file aperto nel nuovo file. Notate che non dovete chiamare GlobalAlloc per allocare un blocco di memoria in questo programma.
includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib
includelib comdlg32.lib
.const
IDM_OPEN equ 1
IDM_SAVE equ 2
IDM_EXIT equ 3
MAXSIZE equ 260
.data
ClassName db "Win32ASMFileMappingClass",0
AppName db "Win32 ASM File Mapping Example",0
MenuName db "FirstMenu",0
ofn OPENFILENAME <>
FilterString db "All Files",0,"*.*",0
db "Text Files",0,"*.txt",0,0
buffer db MAXSIZE dup(0)
hMapFile HANDLE 0 ; Handle del memory mapped file, deve essere
;inizializzato con 0 perche' lo vogliamo usare anche
;come flag nella sezione WM_DESTROY.
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hFileRead HANDLE ? ; Handle del file sorgente
hFileWrite HANDLE ? ; Handle del file di output
hMenu HANDLE ?
pMemory DWORD ? ; puntatore ai dati del file sorgente
SizeWritten DWORD ? ; numero di bytes effettivamente scritti da WriteFile
FileSize DWORD ? ; low DWORD della file size (dimensione file) del file sorgente
FileSizeHighWord DWORD ? ; high DWORD della file size del file sorgente
.code
start:
invoke GetModuleHandle, NULL
mov hInstance,eax
invoke GetCommandLine
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:SDWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInstance
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,OFFSET MenuName
mov wc.lpszClassName,OFFSET ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,0
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc
invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,\
ADDR AppName, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,300,200,NULL,NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,SW_SHOWNORMAL
invoke UpdateWindow, hwnd
.WHILE TRUE
invoke GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
invoke TranslateMessage, ADDR msg
invoke DispatchMessage, ADDR msg
.ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc uses ebx hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
mov eax,uMsg
.IF eax==WM_CREATE
invoke GetMenu,hWnd ; Ottieni l'handle del menu
mov hMenu,eax
mov ofn.lStructSize,SIZEOF ofn
push hWnd
pop ofn.hwndOwner
push hInstance
pop ofn.hInstance
mov ofn.lpstrFilter, OFFSET FilterString
mov ofn.lpstrFile, OFFSET buffer
mov ofn.nMaxFile,MAXSIZE
.ELSEIF eax==WM_DESTROY
.if hMapFile!=0
call CloseMapFile
.endif
invoke PostQuitMessage,NULL
.ELSEIF eax==WM_COMMAND
mov eax,wParam
.if lParam==0
.if ax==IDM_OPEN
mov ofn.Flags, OFN_FILEMUSTEXIST or \
OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
invoke GetOpenFileName, ADDR ofn
.if eax==TRUE
invoke CreateFile,ADDR buffer,\
GENERIC_READ ,\
0,\
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\
NULL
mov hFileRead,eax
invoke CreateFileMapping,hFileRead,NULL,PAGE_READONLY,0,0,NULL
mov hMapFile,eax
push ebx
mov eax,OFFSET buffer
movzx bx,ofn.nFileOffset
add eax,ebx
pop ebx
invoke SetWindowText,hWnd,eax
invoke EnableMenuItem,hMenu,IDM_OPEN,MF_GRAYED
invoke EnableMenuItem,hMenu,IDM_SAVE,MF_ENABLED
.endif
.elseif eax==IDM_SAVE
mov ofn.Flags,OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
invoke GetSaveFileName, ADDR ofn
.if eax==TRUE
invoke CreateFile,ADDR buffer,\
GENERIC_READ or GENERIC_WRITE ,\
FILE_SHARE_READ or FILE_SHARE_WRITE,\
NULL,CREATE_NEW,FILE_ATTRIBUTE_ARCHIVE,\
NULL
mov hFileWrite,eax
invoke MapViewOfFile,hMapFile,FILE_MAP_READ,0,0,0
mov pMemory,eax
invoke GetFileSize,hFileRead,ADDR FileSizeHighWord
invoke WriteFile,hFileWrite,pMemory,eax,ADDR SizeWritten,NULL
invoke UnmapViewOfFile,pMemory
call CloseMapFile
invoke CloseHandle,hFileWrite
invoke SetWindowText,hWnd,ADDR AppName
invoke EnableMenuItem,hMenu,IDM_OPEN,MF_ENABLED
invoke EnableMenuItem,hMenu,IDM_SAVE,MF_GRAYED
.endif
.else
invoke DestroyWindow, hWnd
.endif
.endif
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
CloseMapFile PROC
invoke CloseHandle,hMapFile
mov hMapFile,0
invoke CloseHandle,hFileRead
ret
CloseMapFile endp
end start
Analisi
GENERIC_READ ,\
0,\
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\
NULL
Quando l'utente seleziona un file nel dialog box "apri file", noi chiamiamo CreateFile per aprirlo. Notate che specifichiamo GENERIC_READ per aprire questo file con accesso di sola lettura e dwShareMode e' zero perche' non vogliamo che altri processi modifichino il file durante la nostra operazione.
Quindi chiamiamo CreateFileMapping per creare un memory mapped file dal file aperto. CreateFileMapping ha la seguente sintassi:
HANDLE hFile, ;// handle del file da mappare
LPSECURITY_ATTRIBUTES lpFileMappingAttributes, ;// attributi di sicurezza opzionali
DWORD flProtect, ;// protezione per il mapping object
DWORD dwMaximumSizeHigh, ;// high-order 32 bits della grandezza dell'object
DWORD dwMaximumSizeLow, ;// low-order 32 bits della grandezza dell'object
LPCTSTR lpName ;// nome del file-mapping object
);
Dovreste prima sapere che CreateFileMapping non deve mappare per forza l'intero file in memoria. Potete usare la funzione anche per mappare solo una parte del file fisico in memoria. Voi specificate la grandezza del memory mapped file nei parametri dwMaximumSizeHigh e dwMaximumSizeLow. Se specificate una grandezza che e' piu' grande della grandezza effettiva del file, il file fisico verra' espanso alla nuova grandezza. Se volete che il memory mapped file sia di dimensioni identiche al file fisico, mettete zero in entrambi i parametri. Potete usare NULL nel parametro lpFileMappingAttributes per dire a Windows di creare un memory mapped file con gli attributi di sicurezza predefiniti. flProtect definisce la protezione desiderata per il memory mapped file. Nel nostro esempio, noi usiamo PAGE_READONLY per permettere solo operazioni di lettura sul memory mapped file. Notate che questo attributo non deve contraddire l'attributo usato in CreateFile o altrimenti CreateFileMapping fallira'. lpName punta al nome del memory mapped file. Se volete condividere questo file con altri processi, dovete dargli un nome. Ma, nel nostro esempio, il nostro processo e' l'unico che usa questo file quindi ignoreremo questo parametro.
mov eax,OFFSET buffer
movzx bx,ofn.nFileOffset
add eax,ebx
pop ebx
invoke SetWindowText,hWnd,eax
Se CreateFileMapping ha successo, cambiamo il titolo della finestra con il nome del file aperto. Il nome del file con l'intera path viene conservato in buffer; noi vogliamo mostrare solo il nome del file nella barra e quindi dobbiamo aggiungere il valore del membro nFileOffset della struttura OPENFILENAME all'indirizzo di buffer.
invoke EnableMenuItem,hMenu,IDM_SAVE,MF_ENABLED
Come precauzione, non vogliamo che l'utente apra piu' files alla volta, percio' disattiviamo (o meglio ingrigiamo) il menu item "Open" e abilitiamo il menu item "Save". EnableMenuItem e' usata per cambiare gli attributi di un menu item. Dopo di cio', aspettiamo che l'utente selezioni il menu item File/Save per chiudere il nostro programma. Se l'utente sceglie di chiudere il programma, dobbiamo chiudere il memory mapped file e il file fisico con il seguente codice:
.if hMapFile!=0
call CloseMapFile
.endif
invoke PostQuitMessage,NULL
In questo spezzone, quando la window procedure riceve il messaggio WM_DESTROY, verifica prima se il valore di hMapFile e' zero o no. Se non e' zero, chiama la funzione CloseMapFile che contiene il codice seguente:
invoke CloseHandle,hMapFile
mov hMapFile,0
invoke CloseHandle,hFileRead
ret
CloseMapFile endp
CloseMapFile chiude il memory mapped file e il file fisico cosi' che non ci sara' nessuna perdita di risorse quando il nostro programma esce a Windows. Se l'utente sceglie di salvare i dati in un altro file, il programma gli presenta un dialog box "Salva con nome". Dopo che l'utente ha scritto il nome del nuovo file, il file viene creato con la funzione CreateFile.
mov pMemory,eax
Immediatamente dopo che il file di output e' stato creato, chiamiamo MapViewOfFile per mappare in memoria la porzione desiderata del memory mapped file. Questa funzione ha la seguente sintassi:
HANDLE hFileMappingObject, ;// file-mapping object da mappare nell'address space
DWORD dwDesiredAccess, ;// modo di accesso
DWORD dwFileOffsetHigh, ;// high-order 32 bits del file offset
DWORD dwFileOffsetLow, ;// low-order 32 bits del file offset
DWORD dwNumberOfBytesToMap ;//numero di bytes da mappare
);
dwDesiredAccess specifica che operazione vogliamo eseguire sul file. Nel nostro esempio, vogliamo solo leggere i dati, percio' usiamo FILE_MAP_READ. dwFileOffsetHigh e dwFileOffsetLow specificano il file offset iniziale della porzione di file che volete mappare in memoria. Nel nostro caso, vogliamo leggere tutto il file percio' cominciamo a mappare dall'offset 0 in avanti. dwNumberOfBytesToMap specifica il numero di bytes da mappare in memoria. Se volete mappare tutto il file (specificato da CreateFileMapping), passate 0 a MapViewOfFile. Dopo aver chiamato MapViewOfFile, la porzione desiderata e' caricata in memoria. Vi sara' dato il puntatore al blocco di memoria che contiene i dati dal file.
Trova la lunghezza del file, che e' restituita in eax. Se il file e' piu' grande di 4 GB, la high DWORD della lunghezza del file e' conservata in FileSizeHighWord.
Scrive i dati che sono mappati in memoria nel file di output.
Quando abbiamo finito con il file sorgente, lo "demappiamo" dalla memoria...
invoke CloseHandle,hFileWrite
...e chiudiamo tutti i files.
Ripristina il titolo originale della finestra.
invoke EnableMenuItem,hMenu,IDM_SAVE,MF_GRAYED
Abilita il menu item "Open" e disabilita "Save As".
Disclaimer
I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.
Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevole e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.