背景:
最近有个功能点是获取进程名和进程完整路径,一般在内核层获取进程名使用“ PsGetProcessImageFileName”,跟进了一下这个函数,原理是直接读取相应进程EPROCESS结构体的ImageFileName成员,但是这个成员定义是UCHAR[15],所以获取的进程名最多只能 (15+‘\0’)个字符,超过 16个字节就给截断了,然后仔细研究了一下这一块。
NTKERNELAPI
UCHAR *
PsGetProcessImageFileName(
__in PEPROCESS Process
);
IDA查看该函数
所以这个函数获取进程名是通过 PEPROCESS+0x5A8
1: kd> dt _EPROCESS
ntdll!_EPROCESS
+0x000 Pcb : _KPROCESS
+0x438 ProcessLock : _EX_PUSH_LOCK
+0x440 UniqueProcessId : Ptr64 Void
...
+0x518 SectionObject : Ptr64 Void
...
+0x5a0 ImageFilePointer : Ptr64 _FILE_OBJECT
+0x5a8 ImageFileName : [15] UChar
+0x5b7 PriorityClass : UChar
+0x5b8 SecurityPort : Ptr64 Void
+0x5c0 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
通过windbg查看EPROCESS,可见0x5A8的偏移处正是ImageFileName
一、EPROCESS中与路径相关的数据结构
随便找个进程举例,这里使用notepad++.exe进程
1.通过_SE_AUDIT_PROCESS_CREATION_INFO成员
1: kd> !process 0 0 notepad++.exe
PROCESS ffffe7062c059080
SessionId: 1 Cid: 1458 Peb: 00bed000 ParentCid: 157c
DirBase: aaff0002 ObjectTable: ffffd602daf294c0 HandleCount: 287.
Image: notepad++.exe
1: kd> dt _EPROCESS ffffe7062c059080 -y SeAuditProcessCreationInfo
ntdll!_EPROCESS
+0x5c0 SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
找到EPROCESS的SeAuditProcessCreationInfo成员
1: kd> dt _SE_AUDIT_PROCESS_CREATION_INFO ffffe7062c059080+0x5c0
ntdll!_SE_AUDIT_PROCESS_CREATION_INFO
+0x000 ImageFileName : 0xffffe706`2b876d40 _OBJECT_NAME_INFORMATION
1: kd> dt 0xffffe706`2b876d40 _OBJECT_NAME_INFORMATION
ntdll!_OBJECT_NAME_INFORMATION
+0x000 Name : _UNICODE_STRING "\Device\HarddiskVolume3\soft\Notepad++\notepad++.exe"
(说明:\Device\HarddiskVolume3 是一个设备对象符号链接,表示硬盘的一个卷。\soft\Notepad++\notepad++.exe 是该卷上的路径,即 \Device\HarddiskVolume3\soft\Notepad++\notepad++.exe 表示 Notepad++ 可执行文件的完整路径,其中 Notepad++ 安装在硬盘的第三个卷的 \soft\ 目录下)
所以通过 Process->SeAuditProcessCreationInfo.ImageFileName->Name 可以获取到进程完整路径。
2.通过SectionObject
1: kd> dt _EPROCESS ffffe7062c059080 -y SectionObject
ntdll!_EPROCESS
+0x518 SectionObject : 0xffffd602`da1073f0 Void
1: kd> !object 0xffffd602`da1073f0
Object: ffffd602da1073f0 Type: (ffffe70624e96900) Section
ObjectHeader: ffffd602da1073c0 (new version)
HandleCount: 0 PointerCount: 1
Directory Object: 00000000 Name: \soft\Notepad++\notepad++.exe
以前的系统是EPROCESS的SectionObject成员是 _SECTION_OBJECT结构的指针, 而该结构的0x14处存放着_SEGMENT_OBJECT结构指针,_SEGMENT_OBJECT结构偏移0x0处是_CONTROL_AREA结构的指针,该结构偏移0x24处是_FILE_OBJECT结构的指针,利用该结构的_DEVICE_OBJECT,FileName两个成员和RtlVolumeDeviceToDosName函数可以获取进程文件的DOS完整路径。
现在Win10的我还没搞懂…
总结来说: Process->SectionObject->Segment->ControlArea->FilePointer(_EPROCESS->_SECTION_OBJECT->_SEGMENT_OBJECT->_CONTROL_AREA->_FILE_OBJECT)
3.通过 PEB的 Ldr
1: kd> dt _EPROCESS ffffe7062c059080 -y Peb
ntdll!_EPROCESS
+0x550 Peb : 0x00000000`00bed000 _PEB
1: kd> dt 0x00000000`00bed000 _PEB
ntdll!_PEB
+0x000 InheritedAddressSpace : 0 ''
+0x001 ReadImageFileExecOptions : 0 ''
+0x002 BeingDebugged : 0 ''
+0x003 BitField : 0x4 ''
+0x003 ImageUsesLargePages : 0y0
+0x003 IsProtectedProcess : 0y0
+0x003 IsImageDynamicallyRelocated : 0y1
+0x003 SkipPatchingUser32Forwarders : 0y0
+0x003 IsPackagedProcess : 0y0
+0x003 IsAppContainer : 0y0
+0x003 IsProtectedProcessLight : 0y0
+0x003 IsLongPathAwareProcess : 0y0
+0x004 Padding0 : [4] ""
+0x008 Mutant : 0xffffffff`ffffffff Void
+0x010 ImageBaseAddress : 0x00000000`00dc0000 Void
+0x018 Ldr : 0x00007ffe`1d6fc4c0 _PEB_LDR_DATA
+0x020 ProcessParameters : 0x00000000`01381d70 _RTL_USER_PROCESS_PARAMETERS
+0x028 SubSystemData : (null)
+0x030 ProcessHeap : 0x00000000`01380000 Void
...
首先找到PEB结构,然后找到Ldr,该成员是_PEB_LDR_DATA结构的指针,然后在_PEB_LDR_DATA的0x10处找到 _LDR_DATA_TABLE_ENTRY数据结构.但是需要注意的是,上面的成员获取必须在对应进程的上下文.
1: kd> dt 0x00007ffe`1d6fc4c0 _PEB_LDR_DATA
ntdll!_PEB_LDR_DATA
+0x000 Length : 0x58
+0x004 Initialized : 0x1 ''
+0x008 SsHandle : (null)
+0x010 InLoadOrderModuleList : _LIST_ENTRY [ 0x00000000`01382790 - 0x00000000`01382e50 ]
+0x020 InMemoryOrderModuleList : _LIST_ENTRY [ 0x00000000`013827a0 - 0x00000000`01382e60 ]
+0x030 InInitializationOrderModuleList : _LIST_ENTRY [ 0x00000000`01382620 - 0x00000000`01382e70 ]
+0x040 EntryInProgress : (null)
+0x048 ShutdownInProgress : 0 ''
+0x050 ShutdownThreadId : (null)
1: kd> dt _LDR_DATA_TABLE_ENTRY 0x00000000`01382790
ntdll!_LDR_DATA_TABLE_ENTRY
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x00000000`01382600 - 0x00007ffe`1d6fc4d0 ]
+0x010 InMemoryOrderLinks : _LIST_ENTRY [ 0x00000000`01382610 - 0x00007ffe`1d6fc4e0 ]
+0x020 InInitializationOrderLinks : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ]
+0x030 DllBase : 0x00000000`00dc0000 Void
+0x038 EntryPoint : 0x00000000`00ff0949 Void
+0x040 SizeOfImage : 0x4d7000
+0x048 FullDllName : _UNICODE_STRING "C:\soft\Notepad++\notepad++.exe"
+0x058 BaseDllName : _UNICODE_STRING "notepad++.exe"
+0x068 FlagGroup : [4] "???"
+0x068 Flags : 0x22c4
...
总结来说就是: PEB->Ldr->InLoadOrderLinks->FullDllName或者 PEB->Ldr->InMemoryOrderLinks->FullDllName
4.通过 PEB的 ProcessParameters
1: kd> dt _EPROCESS ffffe7062c059080 -y Peb
ntdll!_EPROCESS
+0x550 Peb : 0x00000000`00bed000 _PEB
1: kd> dt 0x00000000`00bed000 _PEB
ntdll!_PEB
+0x000 InheritedAddressSpace : 0 ''
+0x001 ReadImageFileExecOptions : 0 ''
+0x002 BeingDebugged : 0 ''
+0x003 BitField : 0x4 ''
+0x003 ImageUsesLargePages : 0y0
+0x003 IsProtectedProcess : 0y0
+0x003 IsImageDynamicallyRelocated : 0y1
+0x003 SkipPatchingUser32Forwarders : 0y0
+0x003 IsPackagedProcess : 0y0
+0x003 IsAppContainer : 0y0
+0x003 IsProtectedProcessLight : 0y0
+0x003 IsLongPathAwareProcess : 0y0
+0x004 Padding0 : [4] ""
+0x008 Mutant : 0xffffffff`ffffffff Void
+0x010 ImageBaseAddress : 0x00000000`00dc0000 Void
+0x018 Ldr : 0x00007ffe`1d6fc4c0 _PEB_LDR_DATA
+0x020 ProcessParameters : 0x00000000`01381d70 _RTL_USER_PROCESS_PARAMETERS
+0x028 SubSystemData : (null)
+0x030 ProcessHeap : 0x00000000`01380000 Void
...
1: kd> dt 0x00000000`01381d70 _RTL_USER_PROCESS_PARAMETERS
ntdll!_RTL_USER_PROCESS_PARAMETERS
+0x000 MaximumLength : 0x784
+0x004 Length : 0x784
+0x008 Flags : 0x6001
+0x00c DebugFlags : 0
+0x010 ConsoleHandle : (null)
+0x018 ConsoleFlags : 0
+0x020 StandardInput : (null)
+0x028 StandardOutput : (null)
+0x030 StandardError : (null)
+0x038 CurrentDirectory : _CURDIR
+0x050 DllPath : _UNICODE_STRING ""
+0x060 ImagePathName : _UNICODE_STRING "C:\soft\Notepad++\notepad++.exe"
+0x070 CommandLine : _UNICODE_STRING ""C:\soft\Notepad++\notepad++.exe" "C:\Users\pc\Desktop\DESKTOP-0I0A8RO.LOG""
+0x080 Environment : 0x00000000`01380fe0 Void
...
同样是_PEB结构,偏移0x020处是_RTL_USER_PROCESS_PARAMETERS结构.同样需要在目标进程的上下文空间.
总结来说就是: PEB->ProcessParameters->ImagePathName 或PEB->ProcessParameters->CommandLine 或PEB->ProcessParameters->WindowTitle
获取这些数据需要读取进程内存,而且在RING3可以修改掉,所以一般不推荐从PEB里取数据
二、微软提供的获取进程路径相关函数分析
1. GetProcessImageFileName函数:
DWORD GetProcessImageFileName(
[in] HANDLE hProcess,,//目标进程句柄
[out] LPSTR lpImageFileName,//提供的空间,用于保存获取到的进程路径
[in] DWORD nSize//空间大小
);//返回实际路径长度
DLL | Kernel32.dll on Windows 7 and Windows Server 2008 R2; Psapi.dll (if PSAPI_VERSION=1) on Windows 7 and Windows Server 2008 R2; Psapi.dll on Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP |
---|
官方文档:https://learn.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getprocessimagefilenamea
使用IDA查看Kernel32.dll或者Psapi.dll
无论是Kernel32.dll或者Psapi.dll导出的这个函数是个转发函数,它由api-ms-win-core-psapi-l1-1-0.dll导出,有经验的朋友可能知道很多api-ms*系dll在磁盘上根本找不到,找到它拖进ida,发现它只是个字符串
实际上微软试图将api体系结构和具体的实现分开,但往往一个dll中包含了大量不同体系的实现(如kernelbase),这样微软提出了一种名为(virtual dlls)的方案,通过虚拟dll建立一张映射表来转发到实现dll,这样就能把api体系与实现分开。
具体细节可参考:https://blog.quarkslab.com/runtime-dll-name-resolution-apisetschema-part-i.html
写个demo并下断点:
void GetImagePath()
{
DWORD dwResult = ERROR_SUCCESS;
wchar_t imagePath[MAX_PATH] = { 0 };
__debugbreak();
dwResult = GetProcessImageFileName(GetCurrentProcess(), imagePath,MAX_PATH);
if (dwResult != 0) {
LOGE("imagePath:%ws", imagePath);
}
__debugbreak();
}
发现这个函数的实现在win32kfull.sys中,在win32kfull.sys中查看GetProcessImageFileName伪代码
__int64 __fastcall GetProcessImageFilename(void **a1, wchar_t **a2)
{
void *v2; // rcx
__int64 v4; // rbx
__int64 ProcessImageFileName; // rax
wchar_t *v6; // rax
wchar_t *v7; // rax
HANDLE Handle; // [rsp+60h] [rbp+18h] BYREF
v2 = *a1;
Handle = 0i64;
v4 = 0i64;
if ( ObOpenObjectByPointer(v2, 0x200u, 0i64, 0x400u, (POBJECT_TYPE)PsProcessType, 0, &Handle) >= 0 )
{
ProcessImageFileName = GetProcessImageFileName(Handle);
v4 = ProcessImageFileName;
if ( ProcessImageFileName )
{
v6 = wcsrchr(*(const wchar_t **)(ProcessImageFileName + 8), 0x5Cu);
if ( v6 )
v7 = v6 + 1;
else
v7 = *(wchar_t **)(v4 + 8);
*a2 = v7;
}
ZwClose(Handle);
}
return v4;
}
ObOpenObjectByPointer用于根据给定的内核对象指针获取该对象的句柄。这个函数通常用于从内核模式代码中获取对象的句柄,获取玩该进程对象的句柄后调用了GetProcessImageFileName(Handle),跟进去看,伪代码如下:
void *__fastcall GetProcessImageFileName(HANDLE ProcessHandle)
{
void *v1; // rbx
void *v3; // rax
ULONG ProcessInformationLength; // [rsp+48h] [rbp+10h] BYREF
v1 = 0i64;
ProcessInformationLength = 0;
if ( ZwQueryInformationProcess(ProcessHandle, ProcessImageFileNameWin32, 0i64, 0, &ProcessInformationLength) == 0xC0000004
&& ProcessInformationLength >= 0x10 )
{
v3 = (void *)AllocFreeTmpBuffer(ProcessInformationLength + 2);
v1 = v3;
if ( v3 )
{
memset(v3, 0, ProcessInformationLength + 2i64);
if ( ZwQueryInformationProcess(ProcessHandle, ProcessImageFileNameWin32, v1, ProcessInformationLength, 0i64) < 0 )
{
FreeTmpBuffer(v1);
return 0i64;
}
}
}
return v1;
}
绕了一圈发现是调用了ZwQueryInformationProcess,其中的ProcessInformationClass=0x2B(ProcessImageFileNameWin32)
在内核中,Zw* 函数最终会被映射到 Nt* 函数上,直接查看NtQueryInformationProcess的伪代码:
case 0x2B:
result = ObReferenceObjectByHandleWithTag(
Handle,
0x1000u,
(POBJECT_TYPE)PsProcessType,
v12,
0x79517350u,
&Object,
0i64);
DeviceMapInformation = result;
if ( result < 0 )
return result;
v18 = (struct _EX_RUNDOWN_REF *)Object;
v19 = (struct _FILE_OBJECT *)*((_QWORD *)Object + 0xB4);
v197 = v19;
if ( v19 )
{
if ( ExAcquireRundownProtection_0((PEX_RUNDOWN_REF)Object + 0x8B) )
{
ObfReferenceObject(v19);
ExReleaseRundownProtection_0(v18 + 0x8B);
}
else
{
DeviceMapInformation = 0xC000010A;
}
}
else
{
DeviceMapInformation = PsReferenceProcessFilePointer(Object, &v197);
v19 = (struct _FILE_OBJECT *)v197;
}
ObfDereferenceObjectWithTag(v18, 0x79517350u);
if ( DeviceMapInformation < 0 )
return DeviceMapInformation;
v20 = IoQueryFileDosDeviceName(v19, &ObjectNameInformation);
HalPutDmaAdapter((PADAPTER_OBJECT)v19);
if ( v20 >= 0 )
{
v21 = ObjectNameInformation;
v22 = ObjectNameInformation->Name.MaximumLength + 0x10;
if ( v22 <= (unsigned int)v5 )
{
*(_WORD *)v6 = ObjectNameInformation->Name.Length;
*(_WORD *)(v6 + 2) = v21->Name.MaximumLength;
if ( v21->Name.MaximumLength )
{
v9 = v6 + 0x10;
memmove((void *)(v6 + 0x10), v21->Name.Buffer, v21->Name.MaximumLength);
}
*(_QWORD *)(v6 + 8) = v9;
}
else
{
v20 = 0xC0000004;
WorkingSetInformation = 0xC0000004;
}
if ( a5 )
*a5 = v22;
ExFreePoolWithTag(v21, 0);
}
return v20;
分析上面代码:
1.使用ObReferenceObjectByHandleWithTag 通过句柄引用进程对象并将指针存储到 Object,然后获取文件对象, 通过检查 v19 是否非空,判断是否存在文件对象。
2.如果存在,通过 ExAcquireRundownProtection_0 获取 rundown 保护,并对文件对象进行引用计数操作。如果获取 rundown 保护失败,则返回错误码 0xC000010A;
3.如果不存在文件对象,调用 PsReferenceProcessFilePointer 获取进程的文件指针,并返回文件对象指针 v19。
4.调用 IoQueryFileDosDeviceName 查询文件对象的 DOS 设备名称( 这个函数里边主要调用ObQueryNameString函数,该函数主要是遍历对象目录树,组建文件路径)
5.如果查询 DOS 设备名称成功,将相关信息拷贝到输出缓冲区中,并通过 ExFreePoolWithTag 释放分配的内存。
总结:
1.GetProcessImageFileName需要传入一个进程句柄,然后调用NtQueryInformationProcess函数的0x2b号功能;
2.首先获取进程内核对象,然后通过硬编码的方式直接获取文件对象并判断文件对象是否存在,不存在则通过PsReferenceProcessFilePointer重新获取文件对象;
3.获取到文件对象后调用 IoQueryFileDosDeviceName 查询文件对象的 DOS 设备名称,并将结果存储在 ObjectNameInformation 中。
换句话说, EPROCESS内部的一个成员(这里应该说是“EPROCESS的成员的成员的成员的成员”更为准确)名为FileObject,它记录了进程的路径。通过IoQueryFileDosDeviceName查询FileObject的信息即可获得进程路径。所以,获取进程路径,可以简化为获取进程的FileObject。
接下来看一下REACTOS是怎么获取FileObject的:
NTSTATUS NTAPI PsReferenceProcessFilePointer (
IN PEPROCESS Process,
OUT PFILE_OBJECT * FileObject) {
PSECTION Section;
PAGED_CODE();
/* Lock the process */ExAcquireRundownProtection(&Process->RundownProtect);
/* Get the section */Section = Process->SectionObject;if (Section){
/* Get the file object and reference it */
*FileObject = MmGetFileObjectForSection((PVOID)Section);
ObReferenceObject(*FileObject);}
/* Release the protection */ExReleaseRundownProtection(&Process->RundownProtect);
/* Return status */return Section ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
}
PFILE_OBJECT NTAPI MmGetFileObjectForSection ( IN PVOID Section ) { PSECTION_OBJECT Section; ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); ASSERT(SectionObject != NULL);
/* Check if it's an ARM3, or ReactOS section */if (MiIsRosSectionObject(SectionObject) == FALSE){
/* Return the file pointer stored in the control area */
Section = SectionObject;
return Section->Segment->ControlArea->FilePointer;}
/* Return the file object */return ((PROS_SECTION_OBJECT)SectionObject)->FileObject;
}
简单来说, FileObject是这么取得的:Process->SectionObject->Segment->ControlArea->FilePointer。
2.QueryFullProcessImageName函数
BOOL QueryFullProcessImageNameA(
[in] HANDLE hProcess,
[in] DWORD dwFlags,
[out] LPSTR lpExeName,
[in, out] PDWORD lpdwSize
);
注意 [in] dwFlags
Value | Meaning |
---|---|
0 | The name should use the Win32 path format. |
PROCESS_NAME_NATIVE 0x00000001 | The name should use the native system path format. |
官方文档:https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-queryfullprocessimagenamea
使用IDA查看伪代码:
BOOL __stdcall QueryFullProcessImageNameW(HANDLE hProcess, DWORD dwFlags, LPWSTR lpExeName, PDWORD lpdwSize)
{
BOOL v4; // ebx
DWORD v5; // esi
bool v7; // cl
ULONG v10; // r12d
PVOID ProcessHeap; // rbp
const void **Heap; // rax
const void **v13; // rdi
NTSTATUS InformationProcess; // eax
__int64 v15; // rcx
__int64 v17; // rcx
__int64 v18; // rcx
v4 = 0;
v5 = dwFlags & 1;
v7 = 0;
if ( (dwFlags & 0xFFFFFFFE) == 0 )
v7 = !(dwFlags & 1 & (unsigned __int8)((dwFlags & 1) - 1));
if ( !v7 )
{
v18 = 0xC00000F0i64;
goto LABEL_19;
}
if ( *lpdwSize > 0x7FFFFFF7 )
{
v18 = 0xC000000Di64;
LABEL_19:
BaseSetLastNTError(v18);
return v4;
}
v10 = 2 * *lpdwSize + 0x10;
ProcessHeap = NtCurrentPeb()->ProcessHeap;
Heap = (const void **)RtlAllocateHeap(ProcessHeap, KernelBaseGlobalData, v10);
v13 = Heap;
if ( !Heap )
{
v18 = 0xC0000017i64;
goto LABEL_19;
}
InformationProcess = NtQueryInformationProcess(hProcess, (PROCESSINFOCLASS)(0x10 * (v5 ^ 1) + 0x1B), Heap, v10, 0i64);
if ( InformationProcess == 0xC0000004 )
InformationProcess = 0xC0000023;
if ( InformationProcess < 0 )
{
v17 = (unsigned int)InformationProcess;
LABEL_14:
BaseSetLastNTError(v17);
goto LABEL_11;
}
memcpy_0(lpExeName, v13[1], *(unsigned __int16 *)v13);
v15 = *(unsigned __int16 *)v13 >> 1;
if ( (unsigned int)v15 >= *lpdwSize )
{
v17 = 0xC0000023i64;
goto LABEL_14;
}
lpExeName[v15] = 0;
v4 = 1;
*lpdwSize = v15;
LABEL_11:
RtlFreeHeap(ProcessHeap, 0, v13);
return v4;
}
还是调用的 NtQueryInformationProcess,根据不同的dwFlags调用 NtQueryInformationProcess的不同的功能
dwFlags有0和1两个,根据0x10 * (dwFlags^ 1) + 0x1B分别为0x2B和0X1B;
分别对应了ring0和ring3不同的形式,那么根据逻辑传入的查询参数分别为27或43,我们写一个简单程序验证一下。
#include <windows.h>
#include <stdio.h>
#include <winternl.h>
#pragma comment(lib,"ntdll.lib")
int main()
{
UNICODE_STRING usRing0 = {0};
UNICODE_STRING usRing3 = { 0 };
NtQueryInformationProcess(GetCurrentProcess(),(PROCESSINFOCLASS)27,&usRing0, 0x1000, NULL);
NtQueryInformationProcess(GetCurrentProcess(), (PROCESSINFOCLASS)43, &usRing3, 0x1000, NULL);
getchar();
}
0x2B对应的PROCESSINFOCLASS为ProcessImageFileNameWin32和 GetProcessImageFileName函数底层实现一样;
0x1B对应的PROCESSINFOCLASS为ProcessImageFileName,就是查看PsQueryFullProcessImageName的伪代码发现是直接通过硬编码的方式读取EPROCESS的SeAuditProcessCreationInfo.ImageFileName->Name
case 0x1B:
result = ObReferenceObjectByHandleWithTag(
Handle,
0x1000u,
(POBJECT_TYPE)PsProcessType,
v12,
0x79517350u,
&Object,
0i64);
if ( result < 0 )
return result;
if ( (unsigned int)v5 >= 0x10 )
{
v16 = v5 - 0x10;
}
else
{
v6 = (unsigned __int64)&v250;
v16 = 0;
}
if ( (unsigned int)v5 >= 0x10 )
v9 = v230 + 0x10;
v187 = v16;
DeviceMapInformation = PsQueryFullProcessImageName(Object, v6, v9, &v187);
ObfDereferenceObjectWithTag(Object, 0x79517350u);
if ( DeviceMapInformation >= 0 && !v9 )
DeviceMapInformation = 0xC0000004;
if ( a5 && ((int)(DeviceMapInformation + 0x80000000) < 0 || DeviceMapInformation == 0xC0000004) )
*a5 = v187 + 0x10;
return DeviceMapInformation;
__int64 __fastcall PsQueryFullProcessImageName(__int64 a1, _OWORD *a2, void *a3, unsigned int *a4)
{
const void **v6; // rdx
unsigned int v7; // ecx
unsigned __int64 v8; // rcx
unsigned int v9; // ebx
void *v10; // rax
__int128 v12; // [rsp+28h] [rbp-20h]
v6 = *(const void ***)(a1 + 0x5C0);
v7 = *a4;
*a4 = *((unsigned __int16 *)v6 + 1);
if ( *((unsigned __int16 *)v6 + 1) > v7 )
{
return 0xC0000004;
}
else
{
v12 = *(_OWORD *)v6;
v8 = (unsigned __int64)*v6 >> 0x10;
v9 = 0;
v10 = 0i64;
if ( WORD1(*(_OWORD *)v6) )
v10 = a3;
*((_QWORD *)&v12 + 1) = v10;
*a2 = v12;
if ( (_WORD)v8 )
memmove(a3, v6[1], *((unsigned __int16 *)v6 + 1));
}
return v9;
}
3.SeLocateProcessImageName函数
除此之外,还有一个可获取的路径方法: Process->SeAuditProcessCreationInfo.ImageFileName->Name
NTSTATUS
SeLocateProcessImageName(in PEPROCESS Process,deref_out PUNICODE_STRING *pImageFileName
)
/*++
Routine Description
This routine returns the ImageFileName information from the process, if available. This is a "lazy evaluation" wrapper
around SeInitializeProcessAuditName. If the image file name information has already been computed, then this call simply
allocates and returns a UNICODE_STRING with this information. Otherwise, the function determines the name, stores the name in the
EPROCESS structure, and then allocates and returns a UNICODE_STRING. Caller must free the memory returned in pImageFileName.
Arguments
Process - process for which to acquire the name
pImageFileName - output parameter to return name to caller
Return Value
NTSTATUS.
--*/
{ NTSTATUS Status = STATUS_SUCCESS; PVOID FilePointer = NULL; PVOID PreviousValue = NULL; POBJECT_NAME_INFORMATION pProcessImageName = NULL; PUNICODE_STRING pTempUS = NULL; ULONG NameLength = 0;
PAGED_CODE();
*pImageFileName = NULL;
if (NULL == Process->SeAuditProcessCreationInfo.ImageFileName) {
//
// The name has not been predetermined. We must determine the process name. First, reference the
// PFILE_OBJECT and lookup the name. Then again check the process image name pointer against NULL.
// Finally, set the name.
//
Status = PsReferenceProcessFilePointer( Process, &FilePointer );
if (NT_SUCCESS(Status)) {
//
// Get the process name information.
//
Status = SeInitializeProcessAuditName(
FilePointer,
TRUE, // skip audit policy
&pProcessImageName // to be allocated in nonpaged pool
);
if (NT_SUCCESS(Status)) {
//
// Only use the pProcessImageName if the field in the process is currently NULL.
//
PreviousValue = InterlockedCompareExchangePointer(
(PVOID ? &Process->SeAuditProcessCreationInfo.ImageFileName,
(PVOID) pProcessImageName,
(PVOID) NULL
);
if (NULL != PreviousValue) {
ExFreePool(pProcessImageName); // free what we caused to be allocated.
}
}
ObDereferenceObject( FilePointer );
}}
if (NT_SUCCESS(Status)) {
//
// Allocate space for a buffer to contain the name for returning to the caller.
//
NameLength = sizeof(UNICODE_STRING) + Process->SeAuditProcessCreationInfo.ImageFileName->Name.MaximumLength;
pTempUS = ExAllocatePoolWithTag( NonPagedPool, NameLength, 'aPeS' );
if (NULL != pTempUS) {
RtlCopyMemory(
pTempUS,
&Process->SeAuditProcessCreationInfo.ImageFileName->Name,
NameLength
);
pTempUS->Buffer = (PWSTR)(((PUCHAR) pTempUS) + sizeof(UNICODE_STRING));
*pImageFileName = pTempUS;
} else {
Status = STATUS_NO_MEMORY;
}}
return Status;
}
( 用 SeLocateProcessImageName(eproc,&UsImageName)的时候,取到别的程序全路径是正常的,而system的值是Null)
参考:
1.NtQueryInformationProcess逆向:https://idiotc4t.com/redteam-research/untitled
2. 彻底改掉进程名 :https://bbs.kanxue.com/thread-96427.htm
3.驱动中获取进程完整路径名:https://boxcounter.com/technique/2009-07-23-%E9%A9%B1%E5%8A%A8%E4%B8%AD%E8%8E%B7%E5%8F%96%E8%BF%9B%E7%A8%8B%E5%AE%8C%E6%95%B4%E8%B7%AF%E5%BE%84%E5%90%8D/
4.在内核驱动中,获得到当前进程的全路径:https://codeantenna.com/a/hpQLrSNn87