Leggere i parametri di un qualsiasi processo in esecuzione

martedì 23 settembre 2008 - 18.48

piero87 Profilo | Junior Member

Devo leggere i parametri di un qualsiasi processo già in esecuzione....come faccio? mi hanno consigliato di usare le API, ma non ne ho trovate di utili...mi hanno consigliato la ZwQueryInformationProcess e la ZwReadVirtualMemory ma non riesco a usarle....
Ho trovato il modo di avere la riga di comando del processo corrente (in pratica del processo che esegue il codice, con private static extern System.IntPtr GetCommandLine();), ma vorrei poter scegliere il processo da controllare; cosa devo fare?
Programmo in c# con visual studio 2003 su windows e uso il framework 1.1
Piero

aiedail92 Profilo | Expert

Ciao

Uno dei metodi da utilizzare per poter leggere i parametri della riga di comando per un processo è la "Code Injection". Il principio si basa sulla creazione, da parte del tuo processo, di un thread da far eseguire in remoto sul processo da cui ottenere le informazioni. Il thread chiamerà quindi la funzione per ottenere la riga di comando, e passerà questa informazione indietro al processo creatore. Questo è un lavoro abbastanza complesso, soprattutto se deve essere eseguito da codice gestito (C#, VB.Net) a causa delle complicazioni derivanti dalla gestione e dalla conversione fra memoria gestita e non gestita. Come ho già consigliato quindi, la strada più semplice da eseguire è di creare una libreria in C\C++ ed appoggiarsi ad essa per fare il "lavoro sporco"

Questo è il codice della dll da scrivere in C++, ho allegato un archivio con dentro due dll già compilate che puoi usare:

#include <windows.h> #include <tchar.h> bool EnableDebugPriv(); #define MAXINJECTSIZE 4096 typedef TCHAR *(__stdcall *PGCL)(void); struct Data { //L'indirizzo di GetCommandLine PGCL pGetCmdLine; //Il buffer che contiene il risultato di GetCommandLine TCHAR cmdLine[2000]; //Indica se è avvenuto un errore nel thread bool hasError; }; DWORD __stdcall InjectedFunction(Data *outData); extern "C" __declspec(dllexport) bool GetProcessCommandLine(int processID, TCHAR *outputString) { //Abilita il processo corrente al debugging, se non riesce ritorna false if(!EnableDebugPriv()) { return false; } //Apre il processo su cui creare il thread remoto HANDLE process = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, processID); //Se l'apertura del processo non è andata a buon termine, ritorna false if(process == NULL) { return false; } void *p = NULL; HANDLE hThread = 0; bool result = false; DWORD rc; HMODULE hk32 = 0; Data *remoteData = NULL; Data localData; //Alloca della memoria sul processo p = VirtualAllocEx(process, 0, MAXINJECTSIZE, MEM_COMMIT, PAGE_EXECUTE_READWRITE); //Se l'allocazione della memoria è fallita, procede col cleanup if(p == NULL) { goto cleanUp; } //Alloca la memoria per i dati del processo remoto remoteData = (Data*)VirtualAllocEx(process, 0, sizeof(Data), MEM_COMMIT, PAGE_READWRITE); //Se l'allocazione della memoria è fallita, procede col cleanup if(remoteData == NULL) { goto cleanUp; } //Copia l'indirizzo della funzione da eseguire nel processo bersaglio if(!WriteProcessMemory(process, p, &InjectedFunction, MAXINJECTSIZE, NULL)) { //Se la copia è fallita, procede col cleaup goto cleanUp; } //Carica la dll kernel32 hk32 = LoadLibrary(TEXT("kernel32.dll")); //In caso di fallimento procede col cleanup if(hk32 == NULL) { goto cleanUp; } #ifndef _UNICODE #define PROCNAME "GetCommandLineA" #else #define PROCNAME "GetCommandLineW" #endif //Ottiene l'indirizzo della funzione GetCommandLine localData.pGetCmdLine = (PGCL)GetProcAddress(hk32, PROCNAME); #undef PROCNAME //Se la richiesta è fallita procede al cleanup if(localData.pGetCmdLine == NULL) { goto cleanUp; } //Libera la libreria FreeLibrary(hk32); hk32 = NULL; //Copia l'indirizzo di GetCommandLine sul processo remoto if(!WriteProcessMemory(process, remoteData, &localData, sizeof(Data), NULL)) { //Se l'operazione fallisce procede al cleanup goto cleanUp; } //Crea il thread remoto, impostando come indirizzo della funzione //la funzione creata sul processo bersaglio hThread = CreateRemoteThread(process, NULL, 0, (PTHREAD_START_ROUTINE)p, remoteData, 0, &rc); //Se la creazione del thread fallisce, procede col cleanup if(hThread == NULL) { goto cleanUp; } //Attende il completamento del thread per cinque secondi rc = WaitForSingleObject(hThread, 5000); //Verifica il risultato dell'attesa switch(rc) { //Se l'operazione non è stata eseguita entro i 5 secondi, procede al cleanup case WAIT_TIMEOUT: //Idem se l'operazione è fallita case WAIT_FAILED: goto cleanUp; //Se l'operazione ha avuto successo, recupera i risultati case WAIT_OBJECT_0: //prova a leggere i dati, se fallisce va al cleanup if(!ReadProcessMemory(process, remoteData, &localData, sizeof(Data), NULL)) { goto cleanUp; } //Se sul thread remoto è avvenuto un errore, va al cleanup if(localData.hasError) { goto cleanUp; } //Se invece tutto è andato come doveva, indica la riuscita dell'operazione result = true; break; //Se l'operazione non è prevista, va al cleanup default: goto cleanUp; } cleanUp: //libera le risorse: chiude l'handle del thread CloseHandle(hThread); //Libera la memoria allocata if(p) VirtualFreeEx(process, p, 0, MEM_RELEASE); if(remoteData) VirtualFreeEx(process, remoteData, 0, MEM_RELEASE); //Libera l'handle della dll if(hk32) FreeLibrary(hk32); //Chiude l'handle del processo CloseHandle(process); //Se l'operazione ha avuto esito positivo copia la stringa nel risultato if(result) { //Copia la stringa if(outputString) { _tcscpy(outputString, localData.cmdLine); } //Ritorna true return true; } //Altrimenti ritorna false else { return false; } } bool EnableDebugPriv() { //ottiene il token di accesso del processo che contiene le informazioni di sicurezza HANDLE hToken; LUID sedebugnameValue; //I privilegi di sicurezza del token di sicurezza TOKEN_PRIVILEGES tkp; //Prova ad ottenere il token di accesso associato al processo corrente if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { //In caso di fallimento ritorna false return false; } //Prova ad ottenere il LUID per identificare il privilegio di debug if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue)) { //In caso di fallimento chiude il token e ritorna false CloseHandle( hToken ); return false; } //Imposta le informazioni per i nuovi privilegi, abilitando il privilegio di debug tkp.PrivilegeCount = 1; tkp.Privileges[0].Luid = sedebugnameValue; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; //Tenta di impostare i nuovi privilegi if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof tkp, NULL, NULL)) { //In caso di fallimento chiude il token e ritorna false CloseHandle(hToken); return false; } //Chiude l'handle del token e ritorna true CloseHandle(hToken); return true; } //La funzione sul thread remoto che si occupa di ottenere gli argomenti della riga di comando DWORD __stdcall InjectedFunction(Data *outData) { //Gli argomenti della riga di comando del processo remoto const TCHAR *src; TCHAR *tgt, *end; //Ottiene la riga di comando src = outData->pGetCmdLine(); // tgt = &outData->cmdLine[0]; //Ottiene l'ultimo elemento della stringa end = &outData->cmdLine[(sizeof(outData->cmdLine) / sizeof(TCHAR)) - 1]; // if (src == 0 || tgt == 0 || end == 0) { outData->hasError = true; return 0; } //Copia la riga di comando for (; *src != TEXT('\0') && tgt < end; src++, tgt++) *tgt = *src; *tgt = TEXT('\0'); outData->hasError = false; return 0; }

Da C# per usare la dll devi fare in questo modo:

//Questa è la funzione esportata dalla libreria [DllImport("CmdGetter.dll")] static extern bool GetProcessCommandLine(int processID, IntPtr outputString); //Questa funzione utilizza la funzione esportata dalla dll private string[] GetProcessesCommandLine(Process[] processes) { //Crea l'array dei risultati string[] res = new string[processes.Length]; //Per ogni processo in input cerca gli argomenti for (int i = 0; i < processes.Length; i++) { //Alloca della memoria non gestita dove ottenere il risultato IntPtr _out = Marshal.AllocHGlobal(4000); //Chiama la funzione della dll if (GetProcessCommandLine(processes[i].Id, _out)) { //Se la chiamata va a buon fine converte la memoria //non gestita in una stringa gestita, e la copia nel risultato res[i] = Marshal.PtrToStringAuto(_out); } //Libera la memoria non gestita Marshal.FreeHGlobal(_out); } //Ritorna l'array dei risultati return res; }

Luca

piero87 Profilo | Junior Member

caspita, grazie per l'impegno!!!
domani lo provo!!!!

aiedail92 Profilo | Expert

Ci stavo lavorando già da un po'

Le dll sono comprese nell'allegato, è uno zip che trovi subito sotto all'avatar nel mio post precedente. Una delle due è più piccola ma fa uso della dll di runtime del c++ che potresti non avere, se quella non funziona devi usare l'altra.

Buon divertimento

Luca

piero87 Profilo | Junior Member

quando in c# scrivo:

[DllImport("CmdGetter.dll")]


la cmdgetter dove deve stare? nella system32 di windows?

Piero

aiedail92 Profilo | Expert

Puoi decidere di metterla ovunque, e allora inserire il percorso completo, oppure puoi metterla nella cartella di windows o, ancora meglio, nella cartella dell'eseguibile e lasciare il percorso relativo.

Luca

piero87 Profilo | Junior Member

grazie....spero davvero che questo possa risolvere i miei problemi!! Domani ti faccio sapere, sperando che vada tutto bene!!!
Piero

aiedail92 Profilo | Expert

Ciao

Scrivo per avvisarti che ho aggiornato le librerie per risolvere un piccolo bug (l'handle del processo non veniva chiuso causando sprechi di memoria e degli Handle di sistema). Ho anche aggiornato il codice.

Ti consiglio quindi di scaricare la nuova versione che trovi allegata in questo post.

Luca

piero87 Profilo | Junior Member

grazie! l'ho letto appena in tempo, stavo per mettermi a lavorare! comunque è come prima? perchè qui ci sono più file rispetto all'altro zip...devo usare ugualmente la cmdgetter.dll (o al massimo la cmdgetter_.dll)?
Piero

aiedail92 Profilo | Expert

Sì, la dll da utilizzare è sempre CmdGetter.dll, i file aggiuntivi servono solo ai programmatori C\C++ per poter usare la libreria anche dal loro codice.

Luca

piero87 Profilo | Junior Member

grazie mille!! sembra funzionare....ora faccio qualche altro test, in caso di bisogno mi faccio sentire (speriamo di no!! )

intanto ti accetto la risposta, non posso fare altro, gentile e disponibile come sei stato!!

grazie ancora!

Piero

aiedail92 Profilo | Expert

Figurati, è un piacere

Luca

piero87 Profilo | Junior Member

ciao!!
Mi è sorto un nuovo problema, cioè dovrei sapere per ogni processo anche il suo id.
Volevo chiederti se potevi fare una piccola modifica alla tua dll: nel vettore di stringhe che restituisce la funzione, puoi fare in modo che il primo elemento di ogni stringa sia il processID, separato poi con uno spazio da quello che restituisce già ora?
Intendo questo, in pratica:

processID programma parametri, per ogni riga del vettore.

E' possible?
Grazie
Piero

EDITATO
chiedo scusa ma ho appena risolto da solo il problema; è bastato modificare il codice della DLLimport.

beguroto Profilo | Junior Member

Ciao a tutti,
ho il seguente quesito da porvi.
Ho un sistema di controllo scritto in C# (software 1) che elabora delle variabili che acquisisco dall'esterno tramite seriale.
Vorrei crearmi un secondo programma in C# (software 2) che una volta eseguito va a graficare alcune delel variabili che sto computando nel programma 1 costantemente in esecuzione.
Come posso fare per accedere da un secondo software alle variabili computate dal mio software principale che sta girando? Ho inoltre la necessità di accedere alla variabili del software 1 dal software2 e forzargli alcuni valori, in pratica scivere il valore delel variabili del software 1 dal softwae2!!!
La mia idea era quella di condividere le variabili del software1 su un area di memoria condivisa ed accedervi poi dal software2.
Avete qualche dritta da suggerirmi per iniziare il lavoro? Se avete qualche sorgente da distribuire affinchè possa studiarci su e capire in debug cosa accade realmente (chiamate effettuate, ecc...)..

Vi ringrazio in anticipo per ogni suggerimento che mi verrà dato.


Begs

beguroto Profilo | Junior Member

Ciao a tutti,
ho il seguente quesito da porvi.
Ho un sistema di controllo scritto in C# (software 1) che elabora delle variabili che acquisisco dall'esterno tramite seriale.
Vorrei crearmi un secondo programma in C# (software 2) che una volta eseguito va a graficare alcune delel variabili che sto computando nel programma 1 costantemente in esecuzione.
Come posso fare per accedere da un secondo software alle variabili computate dal mio software principale che sta girando? Ho inoltre la necessità di accedere alla variabili del software 1 dal software2 e forzargli alcuni valori, in pratica scivere il valore delel variabili del software 1 dal softwae2!!!
La mia idea era quella di condividere le variabili del software1 su un area di memoria condivisa ed accedervi poi dal software2.
Avete qualche dritta da suggerirmi per iniziare il lavoro? Se avete qualche sorgente da distribuire affinchè possa studiarci su e capire in debug cosa accade realmente (chiamate effettuate, ecc...)..

Vi ringrazio in anticipo per ogni suggerimento che mi verrà dato.


Begs

piero87 Profilo | Junior Member

mmm....che tipo di dati devi memorizzare? se non sono dati elaborati, puoi usare i registri
Piero

aiedail92 Profilo | Expert

Se i dati devono essere continuamente elaborati (a quanto ho capito anche con una certa frequenza), io invece l'uso dei registri lo sconsiglierei.

Sto scrivendo un codice d'esempio, se riesci a pazientare ancora un po' che lo testo te lo posto

Luca

beguroto Profilo | Junior Member

Ti ringrazio immensamente per la disponibilità.
Appena avrò modo provo a realizzare il progetto di cui sopra ne ho parlato e ti aggiornerò se tutto funziona bene.
Non avrò modo di testarlo poichè al momento mi trovo sul mar Caspio per lavoro e non ho molta flessibilità.

Approfitto però per chiederti ulteriori informazioni...
Dalla seconda applicazione posso quindi srivere sulle variabili del primo processo? Non soltanto posso leggerle giusto?
Hai un testo da consigliarmi per l' uso delle API di windows?

Grazie mille e buon fine settimana.


Begs

piero87 Profilo | Junior Member

mi permetto di darti un paio di link che mi hanno aiutato a usare le API, soprattutto il primo:
http://www.pinvoke.net/
http://www.aleax.it/TutWin32/
http://www.openitalia.net/CMpro-v-p-45.html
Piero

aiedail92 Profilo | Expert

Ciao

Ti ho risposto su quest'altro thread per quanto riguarda il codice:

http://www.dotnethell.it/forum/messages.aspx?ThreadID=25512

Sui siti che ti ha dato Piero trovi del materiale sulle API, io non saprei cosa consigliarti dato che ho sempre fatto tutto da autodidatta; generalmente comunque consulto MSDN Library online:

http://msdn.microsoft.com/en-us/library/aa383749(VS.85).aspx

Luca

beguroto Profilo | Junior Member

Vi ringrazio nuovamente per le informazioni che mi avete fornito.
Grazie per il codice che provvederò ad implementare appena possibile.
Non esiterò a chiedervi altre informazioni se dovessi averne bisogno.

Grazie
Partecipa anche tu! Registrati!
Hai bisogno di aiuto ?
Perchè non ti registri subito?

Dopo esserti registrato potrai chiedere
aiuto sul nostro Forum oppure aiutare gli altri

Consulta le Stanze disponibili.

Registrati ora !
Copyright © dotNetHell.it 2002-2023
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5