Zoom Icon

Tutorial 14: I Processi

From UIC

Tutorial 14: I Processi

Contents


Tutorial 14: I Processi
Author: Iczelion
Email: Traduttore: -NeuRaL_NoiSE
Website: Mirror
Date: 07/01/2009 (dd/mm/yyyy)
Level: Slightly hard
Language: Italian Flag Italian.gif
Comments: Formattazione Wiki: Antelox


Introduzione

Impareremo cos'è un processo, come crearlo e come terminarlo.


Tools

Esempio


Preliminari

Cos'e' un processo? Cito questa definizione dalla Win32 API reference:

"Un processo e' un'applicazione in esecuzione che consiste di un address space virtuale privato, codice, dati, e altre risorse del sistema operativo, come files, pipes, e synchronization objects che sono visibili per il processo."

Come potete capire dalla definizione di cui sopra, un processo "possiede" diversi oggetti: l'address space, i/il modulo/i in esecuzione, e qualsiasi cosa che i moduli in esecuzione aprano o creino. Come minimo, un processo deve essere costituito da un modulo in esecuzione, un address space privato e un thread. Ogni processo deve avere almeno un thread. Cos'e' un thread? Un thread e' in realta' una "coda di esecuzione". Quando Windows crea per la prima volta un processo, crea solo UN thread per processo. Questo thread solitamente comincia ad essere eseguito dalla prima istruzione nel modulo. Se il processo successivamente ha bisogno di piu' threads, esso puo' esplicitamente crearli. Quando Windows riceve un comando per creare un processo, crea il memory address space privato per quel processo e quindi mappa il file eseguibile in tale spazio. Dopo di cio', crea il thread primario per il processo. Sotto Win32, potete anche creare processi per i vostri programmi chiamando la funzione CreateProcess. CreateProcess ha la seguente sintassi:

BOOL CreateProcess(
    LPCTSTR  lpApplicationName,         // puntatore al nome del modulo eseguibile
    LPTSTR  lpCommandLine,                 // puntatore alla stringa della riga di comando
    LPSECURITY_ATTRIBUTES  lpProcessAttributes, // puntatore agli attributi di sicurezza del processo
    LPSECURITY_ATTRIBUTES  lpThreadAttributes, // puntatore agli attributi di sicurezza del thread
    BOOL  bInheritHandles,                     // flag di "eredita' degli handle" (handle inherit flag)
    DWORD  dwCreationFlags,                // flags di creazione
    LPVOID  lpEnvironment,                 // puntatore al nuovo blocco di environment (ambiente, NdT)
    LPCTSTR  lpCurrentDirectory,        // puntatore al nome della directory corrente
    LPSTARTUPINFO  lpStartupInfo,    // puntatore a STARTUPINFO
    LPPROCESS_INFORMATION  lpProcessInformation  // puntatore a PROCESS_INFORMATION
   );

Non vi preoccupate per il numero dei parametri. Ne possiamo ignorare la maggior parte.

lpApplicationName --> Il nome del file eseguibile (con o senza la path) che volete eseguire. Se questo parametro e' NULL, dovete fornire il nome del file eseguibile nel parametro lpCommandLine.

lpCommandLine --> Gli argomenti della riga di comando del programma che volete eseguire. Notate che se lpApplicationName e' NULL, questo parametro deve contenere anche il nome del file eseguibile. Come questo : "notepad.exe readme.txt"

lpProcessAttributes e lpthreadAttributes --> Specificano gli attributi di sicurezza per i processi e per il thread primario. Se sono NULLs, verranno usati gli attributi di sicurezza predefiniti.

bInheritHandles --> Una flag che specifica se volete che il nuovo processo erediti tutti gli handle aperti dal vostro processo.

dwCreationFlags --> Diverse flags che determinano il comportamento del processo che volete creare; ad esempio, volete che il processo venga creato ma immediatamente sospeso, in modo tale da permettervi di esaminarlo o modificarlo prima che parta? Potete anche specificare la classe di priorita' del/i thread(s) nel nuovo processo. Questa classe di priorita' e' usata per determinare la priorita' di comparsa dei threads all'interno del processo. Normalmente usiamo le flags CREATE_NEW_CONSOLE e NORMAL_PRIORITY_CLASS.

lpEnvironment --> Un puntatore all'environment block che contiene diverse stringhe di environment per il nuovo processo. Se questo parametro e' NULL, il nuovo processo eredita l'environment block dal parent process (processo precedente che gli ha dato inizio, NdT).

lpCurrentDirectory --> Un puntatore alla stringa che specfica il drive e la directory correnti per il child process (il processo nuovo, quello appena creato, NdT). NULL se volete che il child process erediti dal parent process.

lpStartupInfo --> Punta ad una struttura STARTUPINFO che specifica come dovra' apparire la finestra principale del nuovo processo. La struttura STARTUPINFO contiene molti membri che specificano l'apparenza della finestra principale del child process. Se non volete niente di speciale, potete riempire la struttura STARTUPINFO con i valori dal parent process chiamando la funzione GetStartupInfo.

lpProcessInformation --> Punta ad una struttura PROCESS_INFORMATION che riceve informazioni di identificazione circa il nuovo processo. La struttura PROCESS_INFORMATION ha i seguenti membri:

PROCESS_INFORMATION STRUCT
        hProcess          HANDLE ?             ; handle del child process
        hThread            HANDLE ?             ; handle del thread primario del child process
        dwProcessId     DWORD ?             ; ID del child process
        dwThreadId      DWORD ?             ; ID del thread primario del child process
    PROCESS_INFORMATION ENDS

L'handle del processo e la ID del processo sono due cose diverse. Una process ID e' un identificatore unico per il processo nel sistema. Un process handle e' un valore restituito da Windows per essere utilizzato con altre funzioni API legate ai processi. Un process handle non puo' essere usato per identificare un processo poiche' non e' unico.

Dopo la chiamata a CreateProcess, un nuovo processo viene creato e la chiamata a CreateProcess ritorna immediatamente. Potete controllare se il nuovo processo e' ancora attivo chiamando la funzione GetExitCodeProcess che ha la seguente sintassi:

BOOL GetExitCodeProcess(
    HANDLE  hProcess,         // handle del processo
    LPDWORD  lpExitCode  // indirizzo che riceve lo stato di chiusura
   );

Se questa chiamata ha successo, lpExitCode contiene il termination status (stato di chiusura, NdT) del processo in questione. Se il valore in lpExitCode e' uguale a STILL_ACTIVE, allora il processo e' ancora in esecuzione.

Potete terminare a piacimento un processo chiamando la funzione TerminateProcess che ha la seguente sintassi:

BOOL TerminateProcess(
    HANDLE  hProcess,     // handle del processo
    UINT  uExitCode           // codice di uscita per il processo
   );

Potete specificare il codice di uscita desiderato per il processo, qualsiasi valore vogliate. TerminateProcess non e' un modo pulito di terminare un processo poiche' nessuna dll relativa al processo sara' informata che questo e' stato terminato.


Essay

L'esempio che segue creera' un nuovo processo quando l'utente seleziona il menu item "create process". Il programma cercera' di eseguire "msgbox.exe". Se l'utente vuole terminare il nuovo processo, puo' selezionare il menu item "terminate process". Il programma controllera' prima se il nuovo processo e' gia' stato distrutto, e se no, chiamera' la funzione TerminateProcess per distruggerlo.

include windows.inc
includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib

.const
IDM_CREATE_PROCESS equ 1
IDM_TERMINATE equ 2
IDM_EXIT equ 3

.data
ClassName db "Win32ASMProcessClass",0
AppName  db "Win32 ASM Process Example",0
MenuName db "FirstMenu",0
processInfo PROCESS_INFORMATION <>
programname db "msgbox.exe",0

.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hMenu HANDLE ?
ExitCode DWORD ?              ; contiene il codice di uscita del processo, dalla chiamata            
                              ; GetExitCodeProcess.

.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
    invoke GetMenu,hwnd
    mov  hMenu,eax
    .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 hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
    LOCAL startInfo:STARTUPINFO
    mov   eax,uMsg
    .IF eax==WM_DESTROY
        invoke PostQuitMessage,NULL
    .ELSEIF eax==WM_INITMENUPOPUP
        invoke GetExitCodeProcess,processInfo.hProcess,ADDR ExitCode
        .if eax==TRUE
            .if ExitCode==STILL_ACTIVE
                invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_GRAYED
                invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_ENABLED
            .else
                invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_ENABLED
                invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_GRAYED
            .endif
        .else
            invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_ENABLED
            invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_GRAYED
        .endif
    .ELSEIF eax==WM_COMMAND
        mov eax,wParam
        .if lParam==0
            .if ax==IDM_CREATE_PROCESS
                invoke GetStartupInfo,ADDR startInfo
                invoke CreateProcess,ADDR programname,NULL,NULL,NULL,FALSE,\
                                        NORMAL_PRIORITY_CLASS,\
                                        NULL,NULL,ADDR startInfo,ADDR processInfo
            .elseif ax==IDM_TERMINATE
                invoke GetExitCodeProcess,processInfo.hProcess,ADDR ExitCode
                .if ExitCode==STILL_ACTIVE
                    invoke TerminateProcess,processInfo.hProcess,0
                .endif
            .else
                invoke DestroyWindow,hWnd
            .endif
        .endif
    .ELSE
        invoke DefWindowProc,hWnd,uMsg,wParam,lParam
        ret
    .ENDIF
    xor    eax,eax
    ret
WndProc endp
end start


Analisi

Il programma crea la finestra principale e rileva il menu handle per uso futuro. Quindi aspetta che l'utente selezioni un comando dal menu. Quando l'utente seleziona il menu item "Process" dal menu principale, noi elaboriamo il messaggio WM_INITMENUPOPUP per modificare i menu items dentro il menu di popup prima che venga mostrato.

.ELSEIF eax==WM_INITMENUPOPUP
        invoke GetExitCodeProcess,processInfo.hProcess,ADDR ExitCode
        .if eax==TRUE
            .if ExitCode==STILL_ACTIVE
                invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_GRAYED
                invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_ENABLED
            .else
                invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_ENABLED
                invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_GRAYED
            .endif
        .else
            invoke EnableMenuItem,hMenu,IDM_CREATE_PROCESS,MF_ENABLED
            invoke EnableMenuItem,hMenu,IDM_TERMINATE,MF_GRAYED
        .endif

Perche' vogliamo elaborare questo messaggio ? Perche' vogliamo preparare i menu items del menu di popup prima che l'utente li possa vedere. Nel nostro esempio, se il nuovo processo non e' ancora cominciato, vogliamo abilitare il menu item "start process" e disabilitare "terminate process". Facciamo il contrario se il nuovo processo e' gia' attivo. Prima controlliamo se il nuovo processo e' ancora in esecuzione chiamando la funzione GetExitCodeProcess con l'handle del processo che e' stato riempito dalla funzione CreateProcess. Se GetExitCodeProcess restituisce FALSE, significa che il processo non e' partito ancora, e quindi disabilitiamo il menu item "terminate process". Se GetExitCodeProcess restituisce TRUE, sappiamo che un nuovo processo e' stato avviato, ma dobbiamo sapere se e' ancora in esecuzione. Percio' compariamo il valore in ExitCode con il valore STILL_ACTIVE, e se sono uguali, il processo e' ancora in esecuzione: dobbiamo disabilitare pertanto il menu item "start process" poiche' non vogliamo far cominciare piu' processi contemporaneamente.

.if ax==IDM_CREATE_PROCESS
                invoke GetStartupInfo,ADDR startInfo
                invoke CreateProcess,ADDR programname,NULL,NULL,NULL,FALSE,\
                                        NORMAL_PRIORITY_CLASS,\
                                        NULL,NULL,ADDR startInfo,ADDR processInfo

Quando l'utente seleziona il menu item "start process", chiamiamo la funzione GetStartupInfo per riempire la struttura startupinfo che passeremo alla funzione CreateProcess. Dopo di cio' chiamiamo la funzione CreateProcess per avviare il nuovo processo.

.elseif ax==IDM_TERMINATE
                invoke GetExitCodeProcess,processInfo.hProcess,ADDR ExitCode
                .if ExitCode==STILL_ACTIVE
                    invoke TerminateProcess,processInfo.hProcess,0
            .endif

Quando l'utente seleziona il menu item "terminate process", controlliamo se il nuovo processo e' ancora attivo chiamando la funzione GetExitCodeProcess. Se e' ancora attivo, chiamiamo la funzione TerminateProcess per "ucciderlo".


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.