在得到进程EProcess之后,对于进程完整路径的获得一般有两种方法,一种是访问的进程的PEB结构,在PEB结构中保存有进程的完整路径,另一种方法就是采用访问_FILE_OBJECT的方法。
访问PEB的方法便存在线程靠挂的问题,因为运行于Ring0层的线程是无法去访问用户地址空间的,需要将线程暂时靠挂到目标呢进程,进而去访问进程的PEB结构。我一般都采用的访问_FILE_OBJECT的方法,避免了线程的靠挂问题,而且访问peb的方法会存在一个问题:如果想要进行进程拦截,在进程未启动之前就阻止进程的启动,此时进程空间还未开辟,peb结构都不存在,只能去访问exe文件在ring0层的文件对象,通过文件对象来获得进程的完整路径。
进程靠挂函数原型
1 VOID KeStackAttachProcess ( 2 PKPROCESS Process, 3 PRKAPC_STATE ApcState 4 );
访问目标进程的用户地址空间之后返回ring0运行调用函数
VOID KeUnstackDetachProcess(
PRKAPC_STATE ApcState
);
kd> dt _eprocess
+0x1b0 Peb : Ptr32 _PEB //X86 Peb在EProcess中的偏移量
kd> dt _peb
+0x00c Ldr : Ptr32 _PEB_LDR_DATA ;进程加载的模块链表
关于进程加载模块链表是有三条链表
InLoadOrderModuleList //模块的加载顺序
InMemoryOrderModuleList // 模块在内存中的顺序
InInitializationOrderModuleList // 模块的初始化顺序
进程的第一模块就是exe文件,对于exe的路径就是取InLoadOrderModuleList 中的第一模块的FullDllName。
第二种方法,用_FILE_OBJECT 的方法
以下为Windbg的调试过程,在虚拟机中打开calc.exe然后获得calc.exe的完整路径
Win7 X64
1 kd> !process 0 0 2 3 PROCESS fffffa801a619b30 4 SessionId: 1 Cid: 0b10 Peb: 7fffffd6000 ParentCid: 05fc 5 DirBase: 5f017000 ObjectTable: fffff8a00296ac00 HandleCount: 79. 6 Image: calc.exe
kd> dt _eprocess fffffa801a619b30
+0x268 SectionObject : 0xfffff8a0`01ba1f30 Void
kd> dt _section_object 0xfffff8a0`01ba1f30
nt!_SECTION_OBJECT
+0x000 StartingVa : (null)
+0x008 EndingVa : 0xfffff880`03bd4ba8 Void
+0x010 Parent : 0xfffff880`03bd4b90 Void
+0x018 LeftChild : (null)
+0x020 RightChild : 0xfffffa80`19e84300 Void
+0x028 Segment : 0xfffff8a0`01b19850 _SEGMENT_OBJECT
kd> dt _segment 0xfffff8a0`01b19850
nt!_SEGMENT
+0x000 ControlArea : 0xfffffa80`1a687d30 _CONTROL_AREA
+0x008 TotalNumberOfPtes : 0xe3
+0x00c SegmentFlags : _SEGMENT_FLAGS
+0x010 NumberOfCommittedPages : 0
+0x018 SizeOfSegment : 0xe3000
+0x020 ExtendInfo : 0x00000000`ffbf0000 _MMEXTEND_INFO
+0x020 BasedAddress : 0x00000000`ffbf0000 Void
+0x028 SegmentLock : _EX_PUSH_LOCK
+0x030 u1 : <unnamed-tag>
+0x038 u2 : <unnamed-tag>
+0x040 PrototypePte : 0xfffff8a0`01b19898 _MMPTE
+0x048 ThePtes : [1] _MMPTE
kd> dt _CONTROL_AREA 0xfffffa80`1a687d30
nt!_CONTROL_AREA
+0x000 Segment : 0xfffff8a0`01b19850 _SEGMENT
+0x008 DereferenceList : _LIST_ENTRY [ 0x00000000`00000000 – 0xfffffa80`1a8a1018 ]
+0x018 NumberOfSectionReferences : 1
+0x020 NumberOfPfnReferences : 0x97
+0x028 NumberOfMappedViews : 1
+0x030 NumberOfUserReferences : 2
+0x038 u : <unnamed-tag>
+0x03c FlushInProgressCount : 0
+0x040 FilePointer : _EX_FAST_REF //与X86的不同,X86下FilePointer的类型为_FILE_OBJECT,X64加了一层封装
kd> dt _EX_FAST_REF 0xfffffa80`1a687d30+0x040
nt!_EX_FAST_REF
+0x000 Object : 0xfffffa80`1a685073 Void //真正的_FILE_OBJECT 最后一位要进行清0操作, 文件对象的最后一位必然为0
+0x000 RefCnt : 0y0011
+0x000 Value : 0xfffffa80`1a685073
kd> dt _File_object 0xfffffa80`1a685070 //_File_Object 最后一位清0
nt!_FILE_OBJECT
+0x000 Type : 0n5
+0x002 Size : 0n216
+0x008 DeviceObject : 0xfffffa80`19376530 _DEVICE_OBJECT
+0x010 Vpb : 0xfffffa80`19376470 _VPB
+0x018 FsContext : 0xfffff8a0`000f5140 Void
+0x020 FsContext2 : 0xfffff8a0`019715d0 Void
+0x028 SectionObjectPointer : 0xfffffa80`19e5ec88 _SECTION_OBJECT_POINTERS
+0x030 PrivateCacheMap : (null)
+0x038 FinalStatus : 0n0
+0x040 RelatedFileObject : (null)
+0x048 LockOperation : 0 ”
+0x049 DeletePending : 0 ”
+0x04a ReadAccess : 0x1 ”
+0x04b WriteAccess : 0 ”
+0x04c DeleteAccess : 0 ”
+0x04d SharedRead : 0x1 ”
+0x04e SharedWrite : 0 ”
+0x04f SharedDelete : 0x1 ”
+0x050 Flags : 0x44042
+0x058 FileName : _UNICODE_STRING “\Windows\System32\calc.exe” //完整路径
+0x068 CurrentByteOffset : _LARGE_INTEGER 0x0
+0x070 Waiters : 0
+0x074 Busy : 0
+0x078 LastLock : (null)
+0x080 Lock : _KEVENT
+0x098 Event : _KEVENT
+0x0b0 CompletionContext : (null)
+0x0b8 IrpListLock : 0
+0x0c0 IrpList : _LIST_ENTRY [ 0xfffffa80`1a685130 – 0xfffffa80`1a685130 ]
+0x0d0 FileObjectExtension : (null)
WinXP X86
kd> !process 0 0
PROCESS 8971d6f8 SessionId: 0 Cid: 0378 Peb: 7ffdc000 ParentCid: 0664
DirBase: 0f8c02a0 ObjectTable: e23027a8 HandleCount: 44.
Image: calc.exe
kd> dt _eprocess 8971d6f8
+0x138 SectionObject : 0xe22963d0 Void
kd> dt _section_object 0xe22963d0
nt!_SECTION_OBJECT
+0x000 StartingVa : (null)
+0x004 EndingVa : (null)
+0x008 Parent : (null)
+0x00c LeftChild : (null)
+0x010 RightChild : (null)
+0x014 Segment : 0xe18e4820 _SEGMENT_OBJECT
kd> dt _segment 0xe18e4820
nt!_SEGMENT
+0x000 ControlArea : 0x89769478 _CONTROL_AREA
+0x004 TotalNumberOfPtes : 0x1f
+0x008 NonExtendedPtes : 0x1f
+0x00c WritableUserReferences : 0
+0x010 SizeOfSegment : 0x1f000
+0x018 SegmentPteTemplate : _MMPTE
+0x020 NumberOfCommittedPages : 0
+0x024 ExtendInfo : (null)
+0x028 SystemImageBase : (null)
+0x02c BasedAddress : 0x01000000 Void
+0x030 u1 : __unnamed
+0x034 u2 : __unnamed
+0x038 PrototypePte : 0xe18e4860 _MMPTE
+0x040 ThePtes : [1] _MMPTE
kd> dt _CONTROL_AREA 0x89769478
nt!_CONTROL_AREA
+0x000 Segment : 0xe18e4820 _SEGMENT
+0x004 DereferenceList : _LIST_ENTRY [ 0x0 – 0x0 ]
+0x00c NumberOfSectionReferences : 1
+0x010 NumberOfPfnReferences : 0x1d
+0x014 NumberOfMappedViews : 1
+0x018 NumberOfSubsections : 4
+0x01a FlushInProgressCount : 0
+0x01c NumberOfUserReferences : 2
+0x020 u : __unnamed
+0x024 FilePointer : 0x8958a9b0 _FILE_OBJECT
+0x028 WaitingForDeletion : (null)
+0x02c ModifiedWriteCount : 0
+0x02e NumberOfSystemCacheViews : 0
kd> dt _FILE_OBJECT 0x8958a9b0
nt!_FILE_OBJECT
+0x000 Type : 0n5
+0x002 Size : 0n112
+0x004 DeviceObject : 0x899ab900 _DEVICE_OBJECT
+0x008 Vpb : 0x8994c1d8 _VPB //卷参数块,由此得到所属卷设备,即盘符
+0x00c FsContext : 0xe228f710 Void
+0x010 FsContext2 : 0xe1f5ea90 Void
+0x014 SectionObjectPointer : 0x894bbbe4 _SECTION_OBJECT_POINTERS
+0x018 PrivateCacheMap : (null)
+0x01c FinalStatus : 0n0
+0x020 RelatedFileObject : (null)
+0x024 LockOperation : 0 ”
+0x025 DeletePending : 0 ”
+0x026 ReadAccess : 0x1 ”
+0x027 WriteAccess : 0 ”
+0x028 DeleteAccess : 0 ”
+0x029 SharedRead : 0x1 ”
+0x02a SharedWrite : 0 ”
+0x02b SharedDelete : 0x1 ”
+0x02c Flags : 0x44042
+0x030 FileName : _UNICODE_STRING “\WINDOWS\system32\calc.exe” //完整路径
+0x038 CurrentByteOffset : _LARGE_INTEGER 0x0
+0x040 Waiters : 0
+0x044 Busy : 0
+0x048 LastLock : (null)
+0x04c Lock : _KEVENT
+0x05c Event : _KEVENT
+0x06c CompletionContext : (null)
结构体的定义
ypedef struct _CONTROL_AREA64 { PVOID64 Segment; PVOID64 p1; PVOID64 p2; ULONG64 NumberOfSectionReferences; ULONG64 NumberOfPfnReferences; ULONG64 NumberOfMappedViews; ULONG64 NumberOfUserReferences; union { ULONG LongFlags; ULONG Flags; } u; PVOID64 FilePointer; } CONTROL_AREA64, *PCONTROL_AREA64; typedef struct _CONTROL_AREA { PVOID Segment; LIST_ENTRY DereferenceList; ULONG NumberOfSectionReferences; ULONG NumberOfPfnReferences; ULONG NumberOfMappedViews; ULONG NumberOfSystemCacheViews; ULONG NumberOfUserReferences; union { ULONG LongFlags; ULONG Flags; } u; PFILE_OBJECT FilePointer; } CONTROL_AREA, *PCONTROL_AREA; typedef struct _SEGMENT64 { PVOID64 ControlArea; ULONG TotalNumberOfPtes; ULONG NonExtendedPtes; ULONG Spare0; }SEGMENT64,*PSEGMENT64; typedef struct _SEGMENT { struct _CONTROL_AREA *ControlArea; ULONG TotalNumberOfPtes; ULONG NonExtendedPtes; ULONG Spare0; } SEGMENT, *PSEGMENT; typedef struct _SECTION_OBJECT { PVOID StartingVa; PVOID EndingVa; PVOID Parent; PVOID LeftChild; PVOID RightChild; PSEGMENT Segment; } SECTION_OBJECT, *PSECTION_OBJECT; typedef struct _SECTION_OBJECT64 { PVOID64 StartingVa; PVOID64 EndingVa; PVOID64 Parent; PVOID64 LeftChild; PVOID64 RightChild; PVOID64 Segment; } SECTION_OBJECT64, *PSECTION_OBJECT64;
BOOLEAN GetProcessPathBySectionObject(ULONG_PTR ulProcessID,WCHAR* wzProcessPath) { PEPROCESS EProcess = NULL; PSECTION_OBJECT SectionObject = NULL; PSECTION_OBJECT64 SectionObject64 = NULL; PSEGMENT Segment = NULL; PSEGMENT64 Segment64 = NULL; PCONTROL_AREA ControlArea = NULL; PCONTROL_AREA64 ControlArea64 = NULL; PFILE_OBJECT FileObject = NULL; BOOLEAN bGetPath = FALSE; if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)ulProcessID, &EProcess))) { switch(WinVersion) { case WINDOWS_XP: //x86 XP sp3 { SectionObjectOfEProcess = 0x138; if (SectionObjectOfEProcess!=0&&MmIsAddressValid((PVOID)((ULONG_PTR)EProcess + SectionObjectOfEProcess))) { SectionObject = *(PSECTION_OBJECT*)((ULONG_PTR)EProcess + SectionObjectOfEProcess); if (SectionObject && MmIsAddressValid(SectionObject)) { Segment = (PSEGMENT)SectionObject->Segment; if (Segment && MmIsAddressValid(Segment)) { ControlArea = Segment->ControlArea; if (ControlArea && MmIsAddressValid(ControlArea)) { FileObject = ControlArea->FilePointer; if (FileObject&&MmIsAddressValid(FileObject)) { bGetPath = GetPathByFileObject(FileObject, wzProcessPath); if (!bGetPath) { DbgPrint("SectionObject: 0x%08X, FileObject: 0x%08X\n", SectionObject, FileObject); } } } } } } break; } case WINDOWS_7: //Win 7 x64 sp1 { SectionObjectOfEProcess = 0x268; if (SectionObjectOfEProcess!=0&&MmIsAddressValid((PVOID)((ULONG_PTR)EProcess + SectionObjectOfEProcess))) { SectionObject64 = *(PSECTION_OBJECT64*)((ULONG_PTR)EProcess + SectionObjectOfEProcess); if (SectionObject64 && MmIsAddressValid(SectionObject64)) { Segment64 = (PSEGMENT64)(SectionObject64->Segment); if (Segment64 && MmIsAddressValid(Segment64)) { ControlArea64 = (PCONTROL_AREA64)Segment64->ControlArea; if (ControlArea64 && MmIsAddressValid(ControlArea64)) { FileObject = (PFILE_OBJECT)ControlArea64->FilePointer; if (FileObject&&MmIsAddressValid(FileObject)) { FileObject = (PFILE_OBJECT)((ULONG_PTR)FileObject & 0xFFFFFFFFFFFFFFF0); bGetPath = GetPathByFileObject(FileObject, wzProcessPath); if (!bGetPath) { DbgPrint("SectionObject: 0x%08X, FileObject: 0x%08X\n", SectionObject, FileObject); } } } } } } break; } } } if (bGetPath==FALSE) { wcscpy(wzProcessPath,L"Unknow"); } return bGetPath; }
获得文件对象以后,获得完整的路径
//传入文件对象,返回完整路径
1 BOOLEAN GetPathByFileObject(PFILE_OBJECT FileObject, WCHAR* wzPath) 2 { 3 BOOLEAN bGetPath = FALSE; 4 CHAR szIoQueryFileDosDeviceName[] = "IoQueryFileDosDeviceName"; 5 CHAR szIoVolumeDeviceToDosName[] = "IoVolumeDeviceToDosName"; 6 CHAR szRtlVolumeDeviceToDosName[] = "RtlVolumeDeviceToDosName"; 7 8 POBJECT_NAME_INFORMATION ObjectNameInformation = NULL; 9 __try 10 { 11 if (FileObject && MmIsAddressValid(FileObject) && wzPath) 12 { 13 14 if (NT_SUCCESS(IoQueryFileDosDeviceName(FileObject,&ObjectNameInformation))) //注意该函数调用后要释放内存 15 { 16 wcsncpy(wzPath,ObjectNameInformation->Name.Buffer,ObjectNameInformation->Name.Length); 17 18 bGetPath = TRUE; 19 20 ExFreePool(ObjectNameInformation); 21 } 22 23 24 if (!bGetPath) 25 { 26 27 if (IoVolumeDeviceToDosName||RtlVolumeDeviceToDosName) 28 { 29 NTSTATUS Status = STATUS_UNSUCCESSFUL; 30 ULONG_PTR ulRet= 0; 31 PVOID Buffer = ExAllocatePool(PagedPool,0x1000); 32 33 if (Buffer) 34 { 35 // ObQueryNameString : \Device\HarddiskVolume1\Program Files\VMware\VMware Tools\VMwareTray.exe 36 memset(Buffer, 0, 0x1000); 37 Status = ObQueryNameString(FileObject, (POBJECT_NAME_INFORMATION)Buffer, 0x1000, &ulRet); 38 if (NT_SUCCESS(Status)) 39 { 40 POBJECT_NAME_INFORMATION Temp = (POBJECT_NAME_INFORMATION)Buffer; 41 42 WCHAR szHarddiskVolume[100] = L"\\Device\\HarddiskVolume"; 43 44 if (Temp->Name.Buffer!=NULL) 45 { 46 if (Temp->Name.Length / sizeof(WCHAR) > wcslen(szHarddiskVolume) && 47 !_wcsnicmp(Temp->Name.Buffer, szHarddiskVolume, wcslen(szHarddiskVolume))) 48 { 49 // 如果是以 "\\Device\\HarddiskVolume" 这样的形式存在的,那么再查询其卷名。 50 UNICODE_STRING uniDosName; 51 52 if (NT_SUCCESS(IoVolumeDeviceToDosName(FileObject->DeviceObject, &uniDosName))) 53 { 54 if (uniDosName.Buffer!=NULL) 55 { 56 57 wcsncpy(wzPath, uniDosName.Buffer, uniDosName.Length); 58 wcsncat(wzPath, Temp->Name.Buffer + wcslen(szHarddiskVolume) + 1, Temp->Name.Length - (wcslen(szHarddiskVolume) + 1)); 59 bGetPath = TRUE; 60 } 61 62 ExFreePool(uniDosName.Buffer); 63 } 64 65 else if (NT_SUCCESS(RtlVolumeDeviceToDosName(FileObject->DeviceObject, &uniDosName))) 66 { 67 if (uniDosName.Buffer!=NULL) 68 { 69 70 wcsncpy(wzPath, uniDosName.Buffer, uniDosName.Length); 71 wcsncat(wzPath, Temp->Name.Buffer + wcslen(szHarddiskVolume) + 1, Temp->Name.Length - (wcslen(szHarddiskVolume) + 1)); 72 bGetPath = TRUE; 73 } 74 75 ExFreePool(uniDosName.Buffer); 76 } 77 78 } 79 else 80 { 81 // 如果不是以 "\\Device\\HarddiskVolume" 这样的形式开头的,那么直接复制名称。 82 83 wcsncpy(wzPath, Temp->Name.Buffer, Temp->Name.Length); 84 bGetPath = TRUE; 85 } 86 } 87 } 88 89 ExFreePool(Buffer); 90 } 91 } 92 } 93 } 94 } 95 __except(1) 96 { 97 DbgPrint("GetPathByFileObject Catch __Except\r\n"); 98 bGetPath = FALSE; 99 } 100 101 return bGetPath; 102 }