0 0 0

无句柄获取进程路径(无硬编码/代码10行/兼容WIN64)

admin
987 0

本文共计5933个字,预计阅读时长22.2分钟。

首先明确一个事实,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。

在NT5的年代,PsReferenceProcessFilePointer是一个没有导出的函数,如果要从EPROCESS取得FilePointer,大概要4个硬编码(简单说就是自行实现PsReferenceProcessFilePointer)。但从VISTA-6000开始,PsReferenceProcessFilePointer被导出了,所以从此之后获得进程路径就不需要任何硬编码了。代码如下:

PUNICODE_STRING PsGetProcessFullName(PEPROCESS pTargetProcess)
{
PFILE_OBJECT pFileObject=NULL;
POBJECT_NAME_INFORMATION pObjectNameInfo=NULL;
if(!NT_SUCCESS(PsReferenceProcessFilePointer(pTargetProcess,&pFileObject)))
return NULL;
if(!NT_SUCCESS(IoQueryFileDosDeviceName(pFileObject,&pObjectNameInfo)))
return NULL;
return &(pObjectNameInfo->Name);//尚未释放内存 以及 ObDereferenceObject
}

此外,IoQueryFileDosDeviceName是从XP开始才有的API。如果要支持WIN2000,除了要自己实现PsReferenceProcessFilePointer之外,还需要自己实现IoQueryFileDosDeviceName:1.使用ObQueryNameString查询FileObject的NT路径;2.把NT路径自行转为DOS路径。

除此之外,还有另外一个只需要一个硬编码即可获取的路径方法: Process->SeAuditProcessCreationInfo.ImageFileName->Name。 因为需要硬编码,因此只推荐在NT5系统上使用这个方法。可惜这个地方的数据似乎还不一定存在。见WRK:

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;

}

除了EPROCESS里记录了两份路径数据,在PEB里还记录了几份(如果是WOW64进程则还分PEB32和PEB64),他们分别是: PEB->ProcessParameters->ImagePathName PEB->ProcessParameters->CommandLine PEB->ProcessParameters->WindowTitle PEB->Ldr->InLoadOrderLinks->FullDllName PEB->Ldr->InMemoryOrderLinks->FullDllName 获取这些数据需要读取进程内存,而且在RING3可以修改掉,所以一般不推荐从PEB里取数据。

最后于 9月前 被admin编辑 ,原因:

最新回复 ( 0 )
全部楼主