Tutorial 12: Gestione della Memoria e I/O con i Files
From UIC
Tutorial 12: Gestione della Memoria e I/O con i Files
Contents |
| Tutorial 12: Gestione della Memoria e I/O con i Files | |
|---|---|
| Author: | Iczelion |
| Email: | Traduttore: -NeuRaL_NoiSE |
| Website: | Mirror |
| Date: | 30/12/2008 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| Comments: | Formattazione Wiki: Antelox |
Introduzione
In questo tutorial impareremo le basi della gestione della memoria e delle operazioni di I/O con i files. In piu', utilizzeremo dei dialog boxes comuni come periferiche di input-output.
Tools
Preliminari
La gestione della memoria sotto Win32 dal punto di vista delle applicazioni e' abbastanza semplice e diretta. Ogni processo possiede un memory address space privato di 4 GB. Il modello di memoria utilizzato e' chiamato flat memory model. In questo modello, tutti i segment registers (o selettori) puntano allo stesso indirizzo iniziale e l'offset e' a 32-bits cosicche' un'applicazione puo' accedere alla memoria in qualsiasi punto del proprio address space senza aver bisogno di cambiare il valore dei selettori. Cio' semplifica la gestione della memoria in modo eccezionale. Non ci sono piu' "near" o "far" pointers. Sotto Win16, ci sono due principali categorie di funzioni API: Global e Local. Le funzioni API di tipo Global hanno a che fare con memoria allocata in altri segmenti, pertanto sono funzioni di memoria di tipo "far". Le funzioni API di tipo Local, invece, hanno a che fare con con la memoria locale del processo, e quindi sono funzioni di tipo "near". Sotto Win32, questi due tipi sono identici. Che chiamate GlobalAlloc o LocalAlloc, otterrete sempre lo stesso risultato. I passi per l'allocazione e l'utilizzo della memoria sono i seguenti:
- Allocate un blocco di memoria chiamando GlobalAlloc. Questa funzione restituisce l'handle del blocco di memoria richiesto.
- "Bloccate" la memoria chiamando GlobalLock. Questa funziona accetta l'handle del blocco di memoria e restituisce un puntatore al blocco di memoria.
- Potete usare il puntatore per leggere o scrivere la memoria.
- "Sbloccate" il blocco di memoria chiamando GlobalUnlock . Questa funzione invalida il puntatore al blocco di memoria.
- Liberate il blocco di memoria chiamando GlobalFree. Questa funzione accetta l'handle del blocco di memoria.
Potete anche sostituire "Local" a "Global", ad esempio LocalAlloc, LocalLock, ecc.
L' I/O con i files sotto Win32 e' molto simile a quello sotto DOS. I passaggi necessari sono uguali. Dovete solo sostituire gli interrupts con chiamate API e il gioco e' fatto. I passi necessari sono i segueti:
- Aprite o create un file chiamando la funzione CreateFile. Questa funzione e' molto versatile: oltre ai files, puo' aprire porte di comunicazione, pipes, drives o console. In caso di successo, essa restituisce l'handle del file o della periferica. Potete quindi usare questo handle per eseguire operazioni sul file o sulla periferica. Muovete il file pointer alla locazione desiderata chiamando SetFilePointer.
- Eseguite l'operazione di lettura o scrittura chiamando ReadFile o WriteFile. Queste funzioni trasferiscono dati da un blocco di memoria a un file o viceversa. Percio' dovete allocare un blocco di memoria largo abbastanza da contenere i dati.
- Chiudete il file chiamando FreeHandle. Questa funzione accetta l'handle del file.
Essay
Il programma che segue mostra un dialog box "apri file". Quindi, lascia scegliere all'utente un file di testo da aprire e mostra il contenuto di quel file in un edit control della sua client area. L'utente puo' modificare il testo nell'edit control a suo piacimento, e puo' scegliere di salvare il contenuto in un file.
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
MEMSIZE equ 65535
EditID equ 1 ; ID dell'edit control
.data
ClassName db "Win32ASMEditClass",0
AppName db "Win32 ASM Edit",0
EditClass db "edit",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)
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hwndEdit HWND ? ; Handle dell'edit control
hFile HANDLE ? ; handle del File
hMemory HANDLE ? ; handle del blocco di memoria allocato
pMemory DWORD ? ; puntatore al blocco di memoria allocato
SizeReadWrite DWORD ? ; numero di bytes effettivamente letti o scritti
.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
LOCAL rec:RECT
mov eax,uMsg
.IF eax==WM_CREATE
invoke CreateWindowEx,NULL,ADDR EditClass,NULL,\
WS_VISIBLE or WS_CHILD or ES_LEFT or ES_MULTILINE or\
ES_AUTOHSCROLL or ES_AUTOVSCROLL,0,\
0,0,0,hWnd,EditID,\
hInstance,NULL
mov hwndEdit,eax
invoke SetFocus,hwndEdit
;===============================================================================
; Inizializza i membri della struttura OPENFILENAME
;===============================================================================
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_SIZE
invoke GetClientRect,hWnd,ADDR rec
invoke MoveWindow,hwndEdit,0,0,rec.right,rec.bottom,TRUE
.ELSEIF eax==WM_DESTROY
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 or GENERIC_WRITE ,\
FILE_SHARE_READ or FILE_SHARE_WRITE,\
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\
NULL
mov hFile,eax
invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEMSIZE
mov hMemory,eax
invoke GlobalLock,hMemory
mov pMemory,eax
invoke ReadFile,hFile,pMemory,MEMSIZE-1,ADDR SizeReadWrite,NULL
invoke SendMessage,hwndEdit,WM_SETTEXT,NULL,pMemory
invoke CloseHandle,hFile
invoke GlobalUnlock,pMemory
invoke GlobalFree,hMemory
.endif
invoke SetFocus,hwndEdit
.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 hFile,eax
invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEMSIZE
mov hMemory,eax
invoke GlobalLock,hMemory
mov pMemory,eax
invoke SendMessage,hwndEdit,WM_GETTEXT,MEMSIZE-1,pMemory
invoke lstrlen,pMemory
invoke WriteFile,hFile,pMemory,eax,ADDR SizeReadWrite,NULL
invoke CloseHandle,hFile
invoke GlobalUnlock,pMemory
invoke GlobalFree,hMemory
.endif
invoke SetFocus,hwndEdit
.else
invoke DestroyWindow, hWnd
.endif
.endif
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Analisi
WS_VISIBLE or WS_CHILD or ES_LEFT or ES_MULTILINE or\
ES_AUTOHSCROLL or ES_AUTOVSCROLL,0,\
0,0,0,hWnd,EditID,\
hInstance,NULL
mov hwndEdit,eax
Nella sezione WM_CREATE, creiamo un edit control. Notate che i parametri che specificano x, y, larghezza, altezza del controllo sono tutti zero perche' ridimensioneremo il controllo successivamente, per coprire l'intera client area della parent window. Notate che in questo caso, non dobbiamo chiamare ShowWindow per far comparire l'edit control sullo schermo, perche' includiamo lo stile WS_VISIBLE. Potete usare questo trucco anche nella parent window.
; Inizializza i membri della struttura OPENFILENAME
;==========================================================
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
Dopo aver creato l'edit control, inzializziamo i membri di ofn. Poiche' vogliamo riutilizzare ofn anche nel dialog box "save as", riempiamo solo i membri *comuni* che vengono usati sia da GetOpenFileName che da GetSaveFileName. La sezione WM_CREATE e' un ottimo posto per inizializzare dati che lo necessitano una sola volta.
invoke GetClientRect,hWnd,ADDR rec
invoke MoveWindow,hwndEdit,0,0,rec.right,rec.bottom,TRUE
Riceviamo i messaggi WM_SIZE quando la dimensione della client area della nostra finestra principale cambia. Ne riceviamo uno anche quando la finestra viene creata per la prima volta. Per poter essere in grado di ricevere questo messaggio, gli stili della window class devono includere CS_VREDRAW e CS_HREDRAW. Utilizziamo questa opportunita' per ridimensionare il nostro edit control alla stessa grandezza della client area della parent window. Innanzitutto dobbiamo conoscere la larghezza e l'altezza correnti della client area della parent window. Otteniamo questa informazione chiamando la funzione GetClientRect, che necessita di un puntatore ad una struttura RECT, che e' definita come segue:
left LONG ?
top LONG ?
right LONG ?
bottom LONG ?
RECT ends
Dopo la chiamata a GetClientRect, la struttura RECT e' riempita con le dimensioni correnti della client area. Quindi noi utilizziamo le informazioni per ridimensionare l'edit control chiamando la funzione MoveWindow che, oltre a cambiare la posizione di una finestra, ne puo' anche modificare le dimensioni.
mov ofn.Flags, OFN_FILEMUSTEXIST or \
OFN_PATHMUSTEXIST or OFN_LONGNAMES or\
OFN_EXPLORER or OFN_HIDEREADONLY
invoke GetOpenFileName, ADDR ofn
Quando l'utente seleziona il menu item File/Open, riempiamo il membro Flags della struttura ofn, e chiamiamo la funzione GetOpenFileName per mostrare il dialog box "apri file".
invoke CreateFile,ADDR buffer,\
GENERIC_READ or GENERIC_WRITE ,\
FILE_SHARE_READ or FILE_SHARE_WRITE,\
NULL,OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,\
NULL
mov hFile,eax
Dopo che l'utente ha scelto il file da aprire, chiamiamo CreateFile per aprirlo. Specifichiamo che la funzione dovrebbe tentare di aprire il file per la lettura e per la scrittura. Una volta aperto il file, la funzione restituisce l'handle del file aperto, che noi conserviamo in una variabile globale per uso futuro. Questa funzione ha la seguente sintassi:
LPCTSTR lpFileName, // indirizzo del nome del file
DWORD dwDesiredAccess, // modo di accesso (lettura-scrittura)
DWORD dwShareMode, // share mode (modo di condivisione, NdT)
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // indirizzo del descrittore di sicurezza
DWORD dwCreationDistribution, // come creare
DWORD dwFlagsAndAttributes, // attributi del file
HANDLE hTemplateFile // handle del file con gli attributi da copiare
);
dwDesiredAccess specifica quali operazioni volete eseguire sul file.
- 0 Apre il file per rilevarne gli attributi.
- GENERIC_READ Apre il file per la lettura.
- GENERIC_WRITE Apre il file per la scrittura.
dwShareMode specifica quali operazioni volete permettere agli altri processi di compiere sul file che sta per essere aperto.
- 0 Non dividere il file con altri processi.
- FILE_SHARE_READ Permette agli altri processi di leggere dati dal file che sta per essere aperto.
- FILE_SHARE_WRITE Permette agli altri processi di scrivere dati sul file che sta per essere aperto.
lpSecurityAttributes non ha signficato sotto Windows 95. dwCreationDistribution specifica l'azione che CreateFile eseguira' quando il file specificato in lpFileName esiste o quando non esiste.
- CREATE_NEW Crea un nuovo file. La funzione fallisce se il file specificato esiste gia'.
- CREATE_ALWAYS Crea un nuovo file. La funzione sovrascrive il file se e' gia' esistente.
- OPEN_EXISTING Apre il file. La funzione fallisce se il file specificato non esiste.
- OPEN_ALWAYS Apre il file, se esiste. Se il file non esiste la funzione lo crea come se dwCreationDistribution fosse stato CREATE_NEW.
- TRUNCATE_EXISTING Apre il file. Una volta aperto, il file e' troncato di modo che la sua dimensione e' di zero bytes. Il processo di chiamata deve aprire il file con accesso almeno di GENERIC_WRITE. La funzione fallisce se il file non esiste.
dwFlagsAndAttributes specifica gli attributi del file.
- FILE_ATTRIBUTE_ARCHIVE Il file e' un file d'archivio. Le applicazioni usano questo attributo per marcare i files per il backup o la rimozione.
- FILE_ATTRIBUTE_COMPRESSED Il file o la directory e' compresso. Per un file, cio' significa che tutti i dati nel file sono compressi. Per una directory, cio' significa che la compressione e' predefinita per i nuovi files creati e le subdirectories.
- FILE_ATTRIBUTE_NORMAL Il file non ha attributi settati. Questo attributo e' valido solo se utilizzato da solo.
- FILE_ATTRIBUTE_HIDDEN Il file e' nascosto. Non va incluso in una normale lista dei files nella directory.
- FILE_ATTRIBUTE_READONLY Il file e' di sola lettura. Le applicazioni possono leggere il file ma non scrivervi o cancellarlo.
- FILE_ATTRIBUTE_SYSTEM Il file e' una parte del sistema operativo (o e' usato esclusivamente da esso).
mov hMemory,eax
invoke GlobalLock,hMemory
mov pMemory,eax
Quando il file viene aperto, allochiamo un blocco di memoria per usarlo con le funzioni ReadFile e WriteFile. Specifichiamo la flag GMEM_MOVEABLE per far si che Windows sposti il blocco per rendere l'intera memoria piu' solida. La flag GMEM_ZEROINIT dice a GlobalAlloc di riempire il blocco di memoria appena allocato con degli zeri. Quando GlobalAlloc ritorna con successo, eax contiene l'handle del blocco di memoria allocato. Passiamo questo handle alla funzione GlobalLock che restituisce un puntatore al blocco di memoria.
invoke SendMessage,hwndEdit,WM_SETTEXT,NULL,pMemory
Quando il blocco di memoria e' pronto all'uso, chiamiamo la funzione ReadFile per leggere dati dal file. Quando un file viene aperto o creato per la prima volta, il file pointer e' all'offset 0. Percio', in questo caso, cominciamo a leggere dal primo byte del file in avanti. Il primo parametro di ReadFile e' l'handle del file da leggere, il secondo e' il puntatore al blocco di memoria che deve contenere i dati, successivamente c'e' il numero di bytes da leggere dal file, e il quarto parametro e' l'indirizzo della variabile di dimensione DWORD che verra' riempita con il numero di bytes effettivamente letti dal file. Dopo aver riempito il blocco di memoria con i dati, mettiamo gli stessi nell'edit control mandando il messaggio WM_SETTEXT all'edit control con lParam contenente il puntatore al blocco di memoria. Dopo questa chiamata, l'edit control mostra i dati nella sua client area.
invoke GlobalUnlock,pMemory
invoke GlobalFree,hMemory
.endif
Arrivati a questo punto, non abbiamo piu' bisogno di lasciare il file aperto, poiche' il nostro scopo e' di scrivere i dati modificati dall'edit control in un altro file, non nell'originale. Pertanto chiudiamo il file chiamando CloseHandle con l'handle del file come parametro. Successivamente sblocchiamo il blocco di memoria e lo liberiamo. In realta', non dovete liberare la memoria per forza a questo punto, potreste riutilizzare il blocco durante la successiva operazione di salvataggio. Ma, a scopo dimostrativo, ho deciso di liberare la memoria qui.
Quando il dialog box "apri file" viene mostrato sullo schermo, l'input focus passa a questi. Percio', quando il dialog box viene chiuso, dobbiamo spostare l'input focus di nuovo nell'edit control. Questo termina l'operazione di lettura sul file. A questo punto, l'utente puo' modificare il contenuto dell'edit control. E quando vuole salvare i dati su di un altro file, deve selezionare il menuitem File/Save, che mostra un dialog box "Salva con nome". La creazione del dialog box "Salva con nome" non e' molto diversa da quella per il dialog "Apri file". Infatti, esse differiscono solo nel nome delle funzioni, GetOpenFileName e GetSaveFileName. Potete riutilizzare la maggior parte dei membri della struttura ofn eccetto il membro Flags.
OFN_EXPLORER or OFN_HIDEREADONLY
Nel nostro caso, vogliamo creare un nuovo file, percio' OFN_FILEMUSTEXIST e OFN_PATHMUSTEXIST devono essere evitati altrimenti il dialog box non ci permettera' di creare un file che non esiste gia'. Il parametro dwCreationDistribution della funzione CreateFile deve essere cambiato in CREATE_NEW poiche' vogliamo creare un nuovo file. Il codice rimanente e' identico a quello della sezione "apri file" tranne che per le istruzioni seguenti:
invoke lstrlen,pMemory
invoke WriteFile,hFile,pMemory,eax,ADDR SizeReadWrite,NULL
Mandiamo il messaggio WM_GETTEXT all'edit control per copiare i dati da esso al blocco di memoria che forniamo. Quando i dati sono nel blocco di memoria, determiniamo la lunghezza della stringa copiata in memoria e scriviamo i dati nel nuovo file.
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.