Zoom Icon

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: Slightly hard
Language: Italian Flag Italian.gif
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

Esempio


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:

  1. Allocate un blocco di memoria chiamando GlobalAlloc. Questa funzione restituisce l'handle del blocco di memoria richiesto.
  2. "Bloccate" la memoria chiamando GlobalLock. Questa funziona accetta l'handle del blocco di memoria e restituisce un puntatore al blocco di memoria.
  3. Potete usare il puntatore per leggere o scrivere la memoria.
  4. "Sbloccate" il blocco di memoria chiamando GlobalUnlock . Questa funzione invalida il puntatore al blocco di memoria.
  5. 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:

  1. 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.
  2. 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.
  3. 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.

include windows.inc
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

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

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.

.ELSEIF eax==WM_SIZE
        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:

RECT struct
        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.

.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

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".

.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

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:

HANDLE CreateFile(
    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).
invoke GlobalAlloc,GMEM_MOVEABLE or GMEM_ZEROINIT,MEMSIZE
                    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 ReadFile,hFile,pMemory,MEMSIZE-1,ADDR SizeReadWrite,NULL
                    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 CloseHandle,hFile
                    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.

invoke SetFocus,hwndEdit

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.

mov ofn.Flags,OFN_LONGNAMES or\
                                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 SendMessage,hwndEdit,WM_GETTEXT,MEMSIZE-1,pMemory
                        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.