枚舉本地遠端NT系統進程 |
|
conundrum
尊榮會員 發表:893 回覆:1272 積分:643 註冊:2004-01-06 發送簡訊給我 |
枚舉本地遠端NT系統進程 http://www.sron.net/art/article.php?articleid=227 Windows2000中有個工具taskmgr.exe就可以比較詳細的查看當前系統進程資訊,但是那是 Windows GUI程式,有時候是不是覺得命令行下的東西更方便呢?其實已經有不少命令行下的 枚舉系統進程的工具了,M$的Resource Kit中好象也有,但去瞭解他們是怎麼實現的,自己 動手做出來,是不是更有意思呢:) 進程通常被定義為一個正在運行的程式的實例,它由兩部分組成: <1>作業系統用來管理進程的內核對象。內核對象也是系統用來存放關於進程的統計資訊的 地方。 <2>位址空間。它包含所有可執行模組或DLL模組的代碼和資料。它還包含動態記憶體分配 的空間,如線程的堆疊和堆分配空間。 枚舉系統進程的實現方法大概有四種,其中有一種可以用來枚舉遠端NT系統的進程,前提 是有遠端系統的管理員許可權。 <<第一部分:調用PSAPI函數枚舉系統進程>> M$的Windows NT開發小組開發了自己Process Status函數,包含在PSAPI.DLL檔 中,這些函數只能在高於NT4.0以後的版本中使用。PSAPI一共有14個函數[實際PSAPI.DLL 輸出函數有19個,但其中有5個函數有兩個版本,分別是ANSI和Unicode版本],通過調用 這些函數,我們可以很方便的取得系統進程的所有資訊,例如進程名、進程ID、父進程ID、進 程優先順序、映射到進程空間的模組列表等等。為了方便起見,以下的例副程式只獲取進程的名 字和ID。 簡單的程式如下: /************************************************************************* Module:ps.c 說明:調用PSAPI函數枚舉系統進程名和ID,Only for NT/2000 ****/ #include #include #include "psapi.h" #pragma comment(lib,"psapi.lib") void PrintProcessNameAndID( DWORD processID ) { char szProcessName[MAX_PATH] = "unknown"; //取得進程的控制碼 HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID ); //取得進程名稱 if ( hProcess ) { HMODULE hMod; DWORD cbNeeded; if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded) ) GetModuleBaseName( hProcess, hMod, szProcessName, sizeof(szProcessName) ); } //回顯進程名稱和ID printf( "\n%-20s%-20d", szProcessName, processID ); CloseHandle( hProcess ); } void main( ) { DWORD aProcesses[1024], cbNeeded, cProcesses; unsigned int i; //枚舉系統進程ID列表 if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) ) return; // Calculate how many process identifiers were returned. //計算進程數量 cProcesses = cbNeeded / sizeof(DWORD); // 輸出每個進程的名稱和ID for ( i = 0; i < cProcesses; i ) PrintProcessNameAndID( aProcesses[i] ); return; } <<第二部分:調用ToolHelp API枚舉本地系統進程>> 在第一部分提到的PSAPI函數只能枚舉NT系統的進程,在Windows9x環境下我們可以通 過調用ToolHelp API函數來達到枚舉系統進程的目的。M$的Windows NT開發小組因為不 喜歡ToolHelp函數,所以沒有將這些函數添加給Windows NT,所以他們開發了自己的 Process Status函數,就是第一部分提到的PSAPI了。但是後來M$已經將ToolHelp函 數添加給了Windows 2000。ToolHelp共有12個函數,通過調用這些函數可以方面的取得 本地系統進程的詳細資訊,以下這個簡單的例子只調用了三個函數,獲取我們所需要系統進程名 字和進程ID。程式如下: /********************************************************************** Module:ps.c 說明:調用ToolHelp函數枚舉本地系統進程名和ID,Only for 9x/2000 **********************************************************************/ #include #include #include int main() { HANDLE hProcessSnap = NULL; PROCESSENTRY32 pe32 = {0}; hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hProcessSnap == (HANDLE)-1) { printf("\nCreateToolhelp32Snapshot() failed:%d",GetLastError()); return 1; } pe32.dwSize = sizeof(PROCESSENTRY32); printf("\nProcessName ProcessID"); if (Process32First(hProcessSnap, &pe32)) { do { printf("\n%-20s%d",pe32.szExeFile,pe32.th32ProcessID); }while (Process32Next(hProcessSnap, &pe32)); } else { printf("\nProcess32Firstt() failed:%d",GetLastError()); } CloseHandle (hProcessSnap); return 0; } <<第三部分:調用NTDLL.DLL中未公開API枚舉本地系統進程>> 第一部分和第二部分說的是調用MS公開的API來枚舉系統進程,在NTDLL.DLL中其實 有一個未公開API,也可以用來枚舉系統進程。此方法是從別處看來的,我可沒這本事自己發 現哦,出處記不清楚了,好像是pwdump2 中的源代碼中的一部分吧。 OK!那個未公開API就是NtQuerySystemInformation,使用方法如下: ///////////////////////////////////////////////////////////////////// /////////// #include #include #include typedef unsigned long NTSTATUS; typedef unsigned short USHORT; typedef unsigned long ULONG; typedef unsigned long DWORD; typedef long LONG; typedef __int64 LONGLONG; typedef struct { USHORT Length; USHORT MaxLen; USHORT *Buffer; } UNICODE_STRING; struct process_info { ULONG NextEntryDelta; ULONG ThreadCount; ULONG Reserved1[6]; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ProcessName; ULONG BasePriority; ULONG ProcessId; }; typedef NTSTATUS (__stdcall *NtQuerySystemInformation1)( IN ULONG SysInfoClass, IN OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG RetLen ); int main() { HINSTANCE hNtDll; NtQuerySystemInformation1 NtQuerySystemInformation; NTSTATUS rc; ULONG ulNeed = 0; void *buf = NULL; size_t len = 0; struct process_info *p ; int done; hNtDll = LoadLibrary ("NTDLL"); if (!hNtDll) return 0; NtQuerySystemInformation = (NtQuerySystemInformation1)GetProcAddress (hNtDll, "NtQuerySystemInformation"); if (!NtQuerySystemInformation) return 0; do { len = 0x1000; buf = realloc (buf, len); if (!buf) return 0; rc = NtQuerySystemInformation (5, buf, len, &ulNeed); } while (rc == 0xc0000004); // STATUS_INFO_LEN_MISMATCH if (rc <0) { free (buf); return 0; } printf("\nProcessName ProcessID"); p = (struct process_info *)buf; done = 0; while (!done) { if ((p->ProcessName.Buffer != 0)) { printf("\n%-20S%d",p->ProcessName.Buffer,p->ProcessId); } done = p->NextEntryDelta == 0; p = (struct process_info *)(((char *)p) p->NextEntryDelta); } free (buf); FreeLibrary (hNtDll); return 0; } <<第四部分:從PDH中取得本地/遠端系統進程資訊>> 前面說的三種方法都只能枚舉本地的系統進程,如何枚舉遠端系統的進程呢?目前我只知道 從PDH中取得進程資訊。 OK!我先簡單的說說PDH是什麼東西,hoho~難的偶也不會。PDH是英文Performance Data Helper的縮寫,Windows NT一直在更新這個稱為Performance Data的資料庫, 這個資料庫包含了大量的資訊,例如CPU使用率,記憶體使用率,系統進程資訊等等一大堆有 用的資訊,可以通過註冊表函數來訪問。注意哦,Windows 9x中並沒有配置這個資料庫。但 是,這個資料庫中的資訊佈局很複雜,很多人並不願意使用它,包括我。而且剛開始的時候,它 也沒有自己特定的函數,只能通過現有的註冊表函數來操作。後來,為了使該資料庫的使用變得 容易,MS開發了一組Performance Data Helper函數,包含在PDH.DLL文件中。 Windows 2000默認是允許遠端註冊表操作的,所以我們就可以通過連接遠端系統的註冊表, 從它的PDH中取得我們所需要的系統進程資訊了,當然這需要遠端系統的Admin許可權。 OK!我們下面所舉的例子是直接利用註冊表函數來從本地/遠端系統的PDH資料庫中取得我們所 需要的資料的,我們並沒有利用PDH API。 程式碼如下: /************************************************************************** Module:ps.c Author:mikeblas@nwlink.com Modify:ey4s Http://www.ey4s.org Date:2001/6/23 ******/ #include #include #include #define INITIAL_SIZE 51200 #define EXTEND_SIZE 12800 #define REGKEY_PERF "software\\microsoft\\windows nt\\currentversion\\perflib" #define REGSUBKEY_COUNTERS "Counters" #define PROCESS_COUNTER "process" #define PROCESSID_COUNTER "id process" #define UNKNOWN_TASK "unknown" #define MaxProcessNum 52//最大進程數量 #pragma comment(lib,"mpr.lib") typedef struct ProcessInfo { char ProcessName[128]; DWORD dwProcessID; }pi; void banner(); int ConnIPC(char *,char *,char *); DWORD GetProcessInfo(pi *,char *,char *,char *); int main(int argc,char **argv) { int i,iRet; pi TaskList[MaxProcessNum]; banner(); if(argc==1) { iRet=GetProcessInfo(TaskList,NULL,NULL,NULL); printf("\nProcess Info for [LOCAL]:"); } else if(argc==4) { iRet=GetProcessInfo(TaskList,argv[1],argv[2],argv[3]); printf("\nProcess Info for [%s]:",argv[1]); } else { printf("\nUsage:%s ",argv[0]); return 1; } if(iRet>0) for(i=0,printf("\nProcessName ProcessID"); i printf("\n%-20s %d",TaskList[i].ProcessName,TaskList[i].dwProcessID),i ); return 0; } DWORD GetProcessInfo(pi *ProList,char *ip,char *user,char *pass) { DWORD rc,dwType,dwSize,i,dwProcessIdTitle,dwProcessIdCounter,dwRet=-1; HKEY hKeyNames; LPSTR buf = NULL,p,p2; CHAR szSubKey[1024],szProcessName[MAX_PATH]; PPERF_DATA_BLOCK pPerf; PPERF_OBJECT_TYPE pObj; PPERF_INSTANCE_DEFINITION pInst; PPERF_COUNTER_BLOCK pCounter; PPERF_COUNTER_DEFINITION pCounterDef; HKEY ghPerfKey =NULL, // get perf data from this key ghMachineKey = NULL; // get title index from this key BOOL bRemote=FALSE; // Look for the list of counters. Always use the neutral // English version, regardless of the local language. We // are looking for some particular keys, and we are always // going to do our looking in English. We are not going // to show the user the counter names, so there is no need // to go find the corresponding name in the local language. __try { if((ip)&&(user)&&(pass)) { if(ConnIPC(ip,user,pass)!=0) { printf("\nConnect to %s failed.",ip); __leave; } else bRemote=TRUE; } //連接本地or遠端註冊表 if(RegConnectRegistry(ip,HKEY_PERFORMANCE_DATA, &ghPerfKey)!=ERROR_SUCCESS) { printf("\nRegConnectRegistry() 1 failed:%d",GetLastError()); __leave; } ` if(RegConnectRegistry(ip,HKEY_LOCAL_MACHINE,&ghMachineKey)!=ERROR_SUCCESS) { printf("\nRegConnectRegistry() 2 failed:%d",GetLastError()); __leave; } sprintf( szSubKey, "%s\\x", REGKEY_PERF,MAKELANGID( LANG_ENGLISH, SUBLANG_NEUTRAL)); if(RegOpenKeyEx(ghMachineKey,szSubKey,0,KEY_READ,&hKeyNames)!=ERROR_SUCCESS) __leave; // 從counter names取得需要的緩衝區大小 if(RegQueryValueEx(hKeyNames,REGSUBKEY_COUNTERS,NULL,&dwType,NULL,&dwSize)!= ERROR_SUCCESS) __leave; //分配記憶體 buf = (LPSTR) malloc( dwSize ); if (buf == NULL) __leave; memset( buf, 0, dwSize ); // read the counter names from the registry if(RegQueryValueEx(ghPerfKey,REGSUBKEY_COUNTERS,NULL,&dwType,(LPBYTE) buf,&dwSize)!= ERROR_SUCCESS) __leave; // now loop thru the counter names looking for the following counters: // 1. "Process" process name // 2. "ID Process" process id // the buffer contains multiple null terminated strings and then // finally null terminated at the end. the strings are in pairs of // counter number and counter name. p = buf; while (*p) { if (p>buf) for( p2=p-2; isdigit(*p2); p2--) ; if (stricmp(p, PROCESS_COUNTER) == 0) { // look backwards for the counter number for( p2=p-2; isdigit(*p2); p2--) ; strcpy( szSubKey, p2 1 ); } else if (stricmp(p, PROCESSID_COUNTER) == 0) { // look backwards for the counter number for( p2=p-2; isdigit(*p2); p2--) ; dwProcessIdTitle = atol( p2 1 ); } // next string p = (strlen(p) 1); } // free the counter names buffer free( buf ); // allocate the initial buffer for the performance data dwSize = INITIAL_SIZE; buf = (LPSTR) malloc( dwSize ); while (TRUE) { if (buf == NULL) __leave; memset( buf, 0, dwSize ); rc=RegQueryValueEx(ghPerfKey,szSubKey,NULL,&dwType,(LPBYTE) buf,&dwSize); pPerf = (PPERF_DATA_BLOCK) buf; // check for success and valid perf data block signature if ((rc == ERROR_SUCCESS) && (dwSize > 0) && (pPerf)->Signature[0] == (WCHAR)'P' && (pPerf)->Signature[1] == (WCHAR)'E' && (pPerf)->Signature[2] == (WCHAR)'R' && (pPerf)->Signature[3] == (WCHAR)'F' ) break; // if buffer is not big enough, reallocate and try again if (rc == ERROR_MORE_DATA) { dwSize = EXTEND_SIZE; buf = (LPSTR) realloc( buf, dwSize ); } else __leave; } // set the perf_object_type pointer pObj = (PPERF_OBJECT_TYPE) ((DWORD)pPerf pPerf->HeaderLength); //loop thru the performance counter definition records looking //for the process id counter and then save its offset pCounterDef = (PPERF_COUNTER_DEFINITION) ((DWORD)pObj pObj->HeaderLength); for (i=0; i<(DWORD)pObj->NumCounters; i ) { if (pCounterDef->CounterNameTitleIndex == dwProcessIdTitle) { dwProcessIdCounter = pCounterDef->CounterOffset; break; } pCounterDef ; } pInst = (PPERF_INSTANCE_DEFINITION) ((DWORD)pObj pObj->DefinitionLength); // loop thru the performance instance data extracting each process name // and process id for (i=0; i < (DWORD)pObj->NumInstances-1 && i { // pointer to the process name p = (LPSTR) ((DWORD)pInst pInst->NameOffset); // convert it to ascii rc = WideCharToMultiByte( CP_ACP,0,(LPCWSTR)p,-1,szProcessName,sizeof(szProcessName),NULL,NULL); // if we cant convert the string then use a default value if (!rc) strcpy( ProList[i].ProcessName, UNKNOWN_TASK ); else strncpy(ProList[i].ProcessName, szProcessName,sizeof(ProList[i].ProcessName)-1); // get the process id pCounter = (PPERF_COUNTER_BLOCK) ((DWORD)pInst pInst->ByteLength); ProList[i].dwProcessID = *((LPDWORD) ((DWORD)pCounter dwProcessIdCounter)); // next process pInst = (PPERF_INSTANCE_DEFINITION) ((DWORD)pCounter pCounter->ByteLength); } dwRet=i; }//end of try __finally { if (buf) free( buf ); RegCloseKey( hKeyNames ); RegCloseKey( HKEY_PERFORMANCE_DATA ); if(bRemote) { char tmp[52],tmp2[96]; strncpy(tmp,ip,sizeof(tmp)-1); wsprintf(tmp2,"\\\\%s\\ipc$",tmp); WNetCancelConnection2(tmp2,CONNECT_UPDATE_PROFILE,TRUE); } } return dwRet; } ///////////////////////////////////////////////////////////////////// /////////// int ConnIPC(char *RemoteName,char *User,char *Pass) { NETRESOURCE nr; char RN[50]="\\\\"; strncat(RN,RemoteName,sizeof(RN)-11); strcat(RN,"\\ipc$"); nr.dwType=RESOURCETYPE_ANY; nr.lpLocalName=NULL; nr.lpRemoteName=RN; nr.lpProvider=NULL; if(WNetAddConnection2(&nr,Pass,User,FALSE)==NO_ERROR) return 0; else return 1; } //////////////////////////////////////////////////////////////////// //////////// void banner() { printf("\nPsList ==>Local and Remote process list" "\nPower by ey4s" "\nhttp://www.ey4s.org" "\n2001/6/22\n"); } ///////////////////////////////////////////////////////////////////// /////////// 程式在Windows2000、VC 6.0環境下編譯,運行良好。注意哦,遠端機器要允許IPC 連接和遠端操作註冊表才可以哦,並且需要Admin許可權.發表人 - conundrum 於 2004/07/24 17:37:27 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |