windows 內核下獲取進程路徑
思路:
1):在EPROCESS結構中獲取。
此時要用到一個導出函數:PsGetProcessImageFileName,申明如下:
NTSYSAPI
UCHAR *
PsGetProcessImageFileName(
__in PEPROCESS Process
);
此函數獲取的是一個簡單的進程名,並不是絕對路徑。
2):ZwQueryInformationProcess。
要想獲取進程的絕對路徑,可用一個未公開的函數:ZwQueryInformationProcess。MSDN上說win8以后此函數不支持了,但筆者在win7,win8 的x86,x64都試過了,都可以正常使用。筆者一般使用的時候用MmGetSystenRoutineAddress來找此函數的地址,找到后查找ProcessImageFileName(27號功能)信息,就可以得到UNICODE_STRING 類型的絕對進程路徑,但此絕對路徑並不是我我們在應用層看到的類似"C:\windows\abc.exe"這樣的路徑,而是這樣的表示方法"\Device\harddiskvolume1\windows\abc.exe".
3):從FILE_OBJECT中獲取
如果得到FILE_OBJECT的話可以從FILE_OBJECT中獲取。
typedef struct _FILE_OBJECT { CSHORT Type; CSHORT Size; PDEVICE_OBJECT DeviceObject; PVPB Vpb; PVOID FsContext; PVOID FsContext2; PSECTION_OBJECT_POINTERS SectionObjectPointer; PVOID PrivateCacheMap; NTSTATUS FinalStatus; struct _FILE_OBJECT *RelatedFileObject; BOOLEAN LockOperation; BOOLEAN DeletePending; BOOLEAN ReadAccess; BOOLEAN WriteAccess; BOOLEAN DeleteAccess; BOOLEAN SharedRead; BOOLEAN SharedWrite; BOOLEAN SharedDelete; ULONG Flags; UNICODE_STRING FileName; LARGE_INTEGER CurrentByteOffset; __volatile ULONG Waiters; __volatile ULONG Busy; PVOID LastLock; KEVENT Lock; KEVENT Event; __volatile PIO_COMPLETION_CONTEXT CompletionContext; KSPIN_LOCK IrpListLock; LIST_ENTRY IrpList; __volatile PVOID FileObjectExtension; } FILE_OBJECT, *PFILE_OBJECT;
FILE_OBJECT中兩個重要的成員:FileName中含有除驅動器外的路徑,比如是這樣的"\WINDOWS\system32\notepad.exe"
此時再用另外一個函數IoVolumeDeviceToDosName,就可以將傳入的DeviceObject轉化為驅動器路徑。
NTSTATUS IoVolumeDeviceToDosName( _In_ PVOID VolumeDeviceObject, _Out_ PUNICODE_STRING DosName );
將DosName和FileName拼接起來就可以得到絕對路徑"C:|windows\system32\notepad.exe".
最后用完后記得要把DosName的Buffer空間釋放掉。
MSDN如是說:
IoVolumeDeviceToDosName allocates the string buffer pointed to by the Buffer member of the UNICODE_STRING structure that the DosName parameter points to. After this buffer is no longer required, a caller of this routine should call the ExFreePool routine to free the buffer.
Starting with Windows Vista, you must ensure that APCs are not disabled before calling this routine. The KeAreAllApcsDisabled routine can be used to verify that APCs are not disabled
下面說幾個常見的類型轉換函數:
ObReferenceObjectByHandle
此函數可以將句柄轉化成內核對應的結構,例如:
進程句柄(HANDLE)------>進程活動鏈指針(PEPROCESS)
文件句柄(HANDLE)------>文件對象指針(PFILE_OBJECT)
懶的說了,還是看圖吧,一圖勝千言:
PsGetProcessId
根據EPROCESS指針獲取進程ID
最后貼一段練習的代碼片段:
VOID GetProcPath( IN PRECORD_LIST pRecord, IN PHANDLE pHandle ) { NTSTATUS status = STATUS_SUCCESS; PUNICODE_STRING pUniImage = NULL; ULONG ulImageLen; UNICODE_STRING uniFunName = RTL_CONSTANT_STRING(L"ZwQueryInformationProcess"); if (NULL == ZwQueryInformationProcess) { __try { ZwQueryInformationProcess = (PFUN_ZwQueryInformationProcess)MmGetSystemRoutineAddress(&uniFunName); } __except (EXCEPTION_EXECUTE_HANDLER) { KdPrint(("exception occured.\n")); } if (NULL == ZwQueryInformationProcess) { KdPrint(("MmGetSystemRoutineAddress failed .\n")); return; } } status = ZwQueryInformationProcess(*pHandle, ProcessImageFileName, pUniImage, 0, &ulImageLen); if (STATUS_INFO_LENGTH_MISMATCH != status) { KdPrint(("ZwQueryInformationProcess error code:(%x).\n", status)); return; } pUniImage = ExAllocatePoolWithTag(NonPagedPool, ulImageLen, MEMTAG); if (NULL == pUniImage) { KdPrint((" no enough resources .\n")); return; } status = ZwQueryInformationProcess(*pHandle, ProcessImageFileName, pUniImage, ulImageLen, &ulImageLen); if (!NT_SUCCESS(status)) { KdPrint(("ZwQueryInformationProcess error code:(%x).\n", status)); ExFreePool(pUniImage); return; } KdPrint(("ParentProcPath:(%wZ).\n", pUniImage)); ExFreePool(pUniImage); } NTSTATUS GetProcessIdByHandle( IN const PHANDLE pHandle, IN PULONG pPid) { NTSTATUS status = STATUS_SUCCESS; PEPROCESS pEprocess = NULL; status = ObReferenceObjectByHandle(*pHandle, 0, *PsProcessType, KernelMode, &pEprocess, NULL); if (!NT_SUCCESS(status)) { KdPrint((" error code:(%08x) \n", status)); return status; } *pPid = (ULONG)PsGetProcessId(pEprocess); ObDereferenceObject(pEprocess); return status; }