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

admin · · Windows程序设计
0

本文共计6735个字,预计阅读时长26.9分钟。

首先明确一个事实,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里取数据。
最后于 2023-2-26 被admin编辑 ,原因:

最新回复 ( 0 )
全部楼主
  • 暂无评论