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