首页
论坛
课程
招聘
[原创]进程完整路径获取方式详解
2011-2-10 20:23 37370

[原创]进程完整路径获取方式详解

2011-2-10 20:23
37370
标 题: 【原创】进程完整路径获取方式详解
作 者: zyhfut
时 间: 2011-2-10,20:28:57
链 接: http://bbs.pediy.com/showthread.php?t=129136

获取进程对应可执行文件的完整路径,在编程中经常遇到。下面做了下详细分析。欢迎指正!
获取进程完整路径方法小结
By Angelkiss  2011年2月9日星期三
最近在做一个xx项目,少不了要拦截进程创建.拦截进程创建的方法在这里就不多说了,免得跑题(其实也就是各种Hook的运用).拦截到进程创建后,需要获得进程对应的可执行文件完整路径,因为只有进一步分析可执行文件,才能获得更多信息来判断该进程的好坏.于是有了本文的产生.
很多人对于获取进程的完整路径可能不屑一顾.以前写过的代码中确实也遇到过,利用google几分钟内,你会得到几个Api,例如:GetModuleFileNameEx,GetProcessImageFileName等.但对于Ring3层提供的Api,Windows内核层做了什么,又有多少人很明确的知道?杀毒软件与病毒木马一直玩着猫和老鼠的游戏,病毒木马稍作手脚,Ring3获取进程完整路径时就会失败.只有我们知道这些Api的本质,才能以不变应万变.下面将从Windows内核层分析这些Api的本质,并提供几种获取进程完整路径及防止别人获取进程完整路径的方法.
一.        进程内核数据结构中与路径信息相关位置
        _EPROCESS->_SE_AUDIT_PROCESS_CREATION_INFO->_OBJECT_NAME_INFORMATION  



从上面的截图可以看到,在进程内核对象EPROCESS偏移0x1f4处存放着_SE_AUDIT_PROCESS_CREATION_INFO结构的指针,该指针处又存放着_OBJECT_NAME_INFORMATION结构的指针,而该结构中存放着该进程的NT式文件路径.
        _EPROCESS->_SECTION_OBJECT->_SEGMENT_OBJECT->_CONTROL_AREA->_FILE_OBJECT:




从上面的截图可以看到进程内核对象偏移0x138处存放着指向_SECTION_OBJECT结构的指针,而该结构的0x14处存放着_SEGMENT_OBJECT结构指针,_SEGMENT_OBJECT结构偏移0x0处是_CONTROL_AREA结构的指针,该结构偏移0x24处是_FILE_OBJECT结构的指针,利用该结构的_DEVICE_OBJECT,FileName两个成员和RtlVolumeDeviceToDosName函数可以获取进程文件的DOS完整路径
        _PEB->_PEB_LDR_DATA->_LDR_DATA_TABLE_ENTRY:



可以看到_PEB偏移0xc处是_PEB_LDR_DATA数据结构,该结构偏移0xc处是_LDR_DATA_TABLE_ENTRY数据结构.但是需要注意的是,上面的成员获取必须在对应进程的上下文.
        _PEB->_RTL_USER_PROCESS_PARAMETERS:



同样是_PEB结构,偏移0x10处是_RTL_USER_PROCESS_PARAMETERS结构.同样需要在目标进程的上下文空间.
上面就是进程内核对象中存放路径的相关位置,当然也许还有其他地方,麻烦知道的大牛告诉我,谢谢!
二.        微软提供的获取进程路径相关函数分析
1.        GetProcessImageFileName函数:
DWORD GetProcessImageFileName(
  HANDLE hProcess,//目标进程句柄
  LPTSTR lpImageFileName,//提供的空间,用于保存获取到的进程路径
  DWORD nSize//空间大小
);//返回实际路径长度
上面的函数获取的进程可执行文件NT式完整路径.从前面的分析中我们可以看到, _EPROCESS->_SE_AUDIT_PROCESS_CREATION_INFO->_OBJECT_NAME_INFORMATION该处存放的就是NT式完整路径.我们来验证下:
0:000> uf PSAPI!GetProcessImageFileNameA
PSAPI!GetProcessImageFileNameA:
76bc3dbd 8bff            mov     edi,edi
76bc3dbf 55              push    ebp
76bc3dc0 8bec            mov     ebp,esp
76bc3dc2 53              push    ebx
76bc3dc3 8b5d10          mov     ebx,dword ptr [ebp+10h]
76bc3dc6 56              push    esi
76bc3dc7 57              push    edi
76bc3dc8 8d5c1b08        lea     ebx,[ebx+ebx+8]
76bc3dcc 53              push    ebx
76bc3dcd 33ff            xor     edi,edi
76bc3dcf 57              push    edi
76bc3dd0 ff155c10bc76    call    dword ptr [PSAPI!_imp__LocalAlloc (76bc105c)]
76bc3dd6 8bf0            mov     esi,eax
76bc3dd8 3bf7            cmp     esi,edi
76bc3dda 7504            jne     PSAPI!GetProcessImageFileNameA+0x23 (76bc3de0)

PSAPI!GetProcessImageFileNameA+0x1f:
76bc3ddc 33db            xor     ebx,ebx
76bc3dde eb53            jmp     PSAPI!GetProcessImageFileNameA+0x76 (76bc3e33)

PSAPI!GetProcessImageFileNameA+0x23:
76bc3de0 57              push    edi
76bc3de1 53              push    ebx
76bc3de2 56              push    esi
76bc3de3 6a1b            push    1Bh
76bc3de5 ff7508          push    dword ptr [ebp+8]
76bc3de8 ff15d810bc76    call    dword ptr [PSAPI!_imp__NtQueryInformationProcess (76bc10d8)]
76bc3dee 3d040000c0      cmp     eax,0C0000004h
76bc3df3 7503            jne     PSAPI!GetProcessImageFileNameA+0x3b (76bc3df8)

PSAPI!GetProcessImageFileNameA+0x38:
76bc3df5 83c01f          add     eax,1Fh

PSAPI!GetProcessImageFileNameA+0x3b:
76bc3df8 3bc7            cmp     eax,edi
76bc3dfa 7d12            jge     PSAPI!GetProcessImageFileNameA+0x51 (76bc3e0e)

PSAPI!GetProcessImageFileNameA+0x3f:
76bc3dfc 50              push    eax
76bc3dfd ff15e410bc76    call    dword ptr [PSAPI!_imp__RtlNtStatusToDosError (76bc10e4)]
76bc3e03 50              push    eax
76bc3e04 ff156010bc76    call    dword ptr [PSAPI!_imp__SetLastError (76bc1060)]
76bc3e0a 33db            xor     ebx,ebx
76bc3e0c eb1e            jmp     PSAPI!GetProcessImageFileNameA+0x6f (76bc3e2c)

PSAPI!GetProcessImageFileNameA+0x51:
76bc3e0e 0fb706          movzx   eax,word ptr [esi]
76bc3e11 57              push    edi
76bc3e12 57              push    edi
76bc3e13 ff7510          push    dword ptr [ebp+10h]
76bc3e16 ff750c          push    dword ptr [ebp+0Ch]
76bc3e19 50              push    eax
76bc3e1a ff7604          push    dword ptr [esi+4]
76bc3e1d 57              push    edi
76bc3e1e 57              push    edi
76bc3e1f ff156810bc76    call    dword ptr [PSAPI!_imp__WideCharToMultiByte (76bc1068)]
76bc3e25 8bd8            mov     ebx,eax
76bc3e27 3bdf            cmp     ebx,edi
76bc3e29 7401            je      PSAPI!GetProcessImageFileNameA+0x6f (76bc3e2c)

PSAPI!GetProcessImageFileNameA+0x6e:
76bc3e2b 4b              dec     ebx

PSAPI!GetProcessImageFileNameA+0x6f:
76bc3e2c 56              push    esi
76bc3e2d ff155810bc76    call    dword ptr [PSAPI!_imp__LocalFree (76bc1058)]

PSAPI!GetProcessImageFileNameA+0x76:
76bc3e33 5f              pop     edi
76bc3e34 5e              pop     esi
76bc3e35 8bc3            mov     eax,ebx
76bc3e37 5b              pop     ebx
76bc3e38 5d              pop     ebp
76bc3e39 c20c00          ret     0Ch
从上面的代码可以看到GetProcessImageFileName是调用NtQueryInformationProcess的0x1b号功能。我们再看下wrk中NtQueryInformationProcess函数中的0x1b号功能代码:
NTSTATUS
NtQueryInformationProcess(
    __in HANDLE ProcessHandle,
    __in PROCESSINFOCLASS ProcessInformationClass,
    __out_bcount(ProcessInformationLength) PVOID ProcessInformation,
    __in ULONG ProcessInformationLength,
    __out_opt PULONG ReturnLength
    )
{
。。。。(省略)
        switch ( ProcessInformationClass ) {
    case ProcessImageFileName:(0x1b号功能)
        {
            ULONG LengthNeeded = 0;
                        //先根据进程句柄获取进程内核对象
            st = ObReferenceObjectByHandle (ProcessHandle,
                                            PROCESS_QUERY_INFORMATION,
                                            PsProcessType,
                                            PreviousMode,
                                            &Process,
                                            NULL);
            if (!NT_SUCCESS (st)) {
                return st;
            }
            //
            // SeLocateProcessImageName will allocate space for a UNICODE_STRING and point pTempNameInfo
            // at that string.  This memory will be freed later in the routine.
            //从进程内核对象中获取路径,然后复制到输出缓冲
            st = SeLocateProcessImageName (Process, &pTempNameInfo);
            if (!NT_SUCCESS(st)) {
                ObDereferenceObject(Process);
                return st;
            }
            LengthNeeded = sizeof(UNICODE_STRING) + pTempNameInfo->MaximumLength;
            //
            // Either of these may cause an access violation. The
            // exception handler will return access violation as
            // status code. No further cleanup needs to be done.
            //
            try {
                if (ARGUMENT_PRESENT(ReturnLength) ) {
                    *ReturnLength = LengthNeeded;
                }
                if (ProcessInformationLength >= LengthNeeded) {
                    RtlCopyMemory(
                        ProcessInformation,
                        pTempNameInfo,
                        sizeof(UNICODE_STRING) + pTempNameInfo->MaximumLength
                        );
                    ((PUNICODE_STRING) ProcessInformation)->Buffer = (PWSTR)((PUCHAR) ProcessInformation + sizeof(UNICODE_STRING));
                } else {
                    st = STATUS_INFO_LENGTH_MISMATCH;
                }
            } except(EXCEPTION_EXECUTE_HANDLER) {
                st = GetExceptionCode ();
            }
            ObDereferenceObject(Process);
            ExFreePool( pTempNameInfo );
            return st;
        }
        。。。。。。。。。。。(省略)
NtQueryInformationProcess函数先根据进程句柄获取进程内核对象,然后调用SeLocateProcessImageName函数,该函数是重点,其代码如下:
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;
}
从上面的代码可以看出,函数首先检查_RPROCESS结构中的_SE_AUDIT_PROCESS_CREATION_INFO处是否为空,如果非空直接复制该处保存的进程路径信息;反之则需要根据进程对象的文件对象重新计算进程路径信息,先调用PsReferenceProcessFilePointer获取进程对象的文件对象,该函数代码如下:
NTSTATUS
PsReferenceProcessFilePointer (
    IN PEPROCESS Process,
    OUT PVOID *OutFileObject
    )

/*++
Routine Description:
    This routine returns a referenced pointer to the FilePointer of Process.  
    This is a rundown protected wrapper around MmGetFileObjectForSection.
Arguments:
    Process - Supplies the process to query.
    OutFileObject - Returns the file object backing the requested section if
                    success is returned.
Return Value:
    NTSTATUS.
Environment:
    Kernel mode, PASSIVE_LEVEL.
--*/
{
    PFILE_OBJECT FileObject;
    PAGED_CODE();
   
    if (!ExAcquireRundownProtection (&Process->RundownProtect)) {
        return STATUS_UNSUCCESSFUL;
    }

    if (Process->SectionObject == NULL) {
        ExReleaseRundownProtection (&Process->RundownProtect);
        return STATUS_UNSUCCESSFUL;
    }
//从进程内核对象的SectionObject处获取文件对象
    FileObject = MmGetFileObjectForSection ((PVOID)Process->SectionObject);
    *OutFileObject = FileObject;
    ObReferenceObject (FileObject);
    ExReleaseRundownProtection (&Process->RundownProtect);
    return STATUS_SUCCESS;
}
这个函数主要从_EPROCESS的_SECTION_OBJECT处获取文件对象,注意要防止进程退出。我们再深入看下MmGetFileObjectForSection这个函数:
PFILE_OBJECT
MmGetFileObjectForSection (
    IN PVOID Section
    )
/*++

Routine Description:

    This routine returns a pointer to the file object backing a section object.

Arguments:

    Section - Supplies the section to query.
Return Value:
    A pointer to the file object backing the argument section.
Environment:
    Kernel mode, PASSIVE_LEVEL.
    The caller must ensure that the section is valid for the
    duration of the call.
--*/
{
    PFILE_OBJECT FileObject;
    ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
    ASSERT (Section != NULL);
    FileObject = ((PSECTION)Section)->Segment->ControlArea->FilePointer;
    return FileObject;
}
看到上面的代码很熟悉吧,上面我们已经分析过了。现在已经获取到进程文件对象了,我们来看下如何获取进程路径信息,
NTSTATUS
SeInitializeProcessAuditName (
    __in __typefix(PFILE_OBJECT) PVOID FileObject,
    __in BOOLEAN bIgnoreAuditPolicy,
    __deref_out POBJECT_NAME_INFORMATION *pAuditName
    )
/*++
Routine Description:
    This routine initializes the executable name for auditing purposes.  It allocates memory for the
    image file name.  This memory is pointed to by pAuditName.  
Arguments:
    FileObject - Supplies a pointer to a file object for the image being
                 executed.
    bIgnoreAuditPolicy - boolean that indicates that the call should proceed without
        regard to the system's auditing policy.         
    pAuditName - Supplies a pointer to a pointer for the object name information.
Return value:
    NTSTATUS.
Environment:
    KeAttached to the target process so not all system services are available.

--*/
{
    NTSTATUS Status;
    OBJECT_NAME_INFORMATION TempNameInfo;
    ULONG ObjectNameInformationLength;
    POBJECT_NAME_INFORMATION pInternalAuditName;
    PFILE_OBJECT FilePointer;
    PAGED_CODE();
    ASSERT (pAuditName != NULL);
    *pAuditName = NULL;
    //
    // Check if the caller would like to get the process name, even if auditing does not
    // require it.
    //
    if (FALSE == bIgnoreAuditPolicy) {
        //
        // At the time of process creation, this routine should only proceed when Object Access or
        // Detailed Tracking auditing is enabled.  In all other cases, the process name is acquired
        // when it is requested.
        //

        if (!SepAdtAuditThisEventWithContext( AuditCategoryObjectAccess, TRUE, FALSE, NULL ) &&
            !SepAdtAuditThisEventWithContext( AuditCategoryDetailedTracking, TRUE, FALSE, NULL )) {
            return STATUS_SUCCESS;
        }
    }
    FilePointer = (PFILE_OBJECT) FileObject;
    //
    // Compute full path for imagefile.
    // This first call to ObQueryNameString is guaranteed to fail.
    // The ObjectNameInformationLength contains only a
    // UNICODE_STRING, so if this call succeeded it would indicate
    // an imagefile name of length 0.  That is bad, so all return
    // values except STATUS_BUFFER_OVERFLOW (from NTFS) and
    // STATUS_BUFFER_TOO_SMALL (from DFS).  This call gives
    // me the buffer size that I need to store the image name.
    //
    pInternalAuditName = &TempNameInfo;
    ObjectNameInformationLength = sizeof(OBJECT_NAME_INFORMATION);
//
    Status = ObQueryNameString (FilePointer,
                                pInternalAuditName,
                                ObjectNameInformationLength,
                                &ObjectNameInformationLength);

    if ((Status == STATUS_BUFFER_OVERFLOW) ||
        (Status == STATUS_BUFFER_TOO_SMALL)) {
        //
        // Sanity check ObQueryNameString.  Different filesystems
        // may be buggy, so make sure that the return length makes
        // sense (that it has room for a non-NULL Buffer in the
        // UNICODE_STRING).
        //
   
        if (ObjectNameInformationLength > sizeof(OBJECT_NAME_INFORMATION)) {
            pInternalAuditName = ExAllocatePoolWithTag (NonPagedPool,
                                 ObjectNameInformationLength, 'aPeS');
            if (pInternalAuditName != NULL) {
                Status = ObQueryNameString (FilePointer,
                                            pInternalAuditName,
                                            ObjectNameInformationLength,
                                            &ObjectNameInformationLength);
                if (!NT_SUCCESS(Status)) {
#if DBG
                    DbgPrint("\n** ObqueryNameString failed with 0x%x.\n", Status);
#endif //DBG

                    //
                    // If the second call to ObQueryNameString did not succeed, then
                    // something is very wrong.  Set the image name to NULL string.
                    //                                          
                    // Free the memory that the first call to ObQueryNameString requested,
                    // and allocate enough space to store an empty UNICODE_STRING.
                    //
                    ExFreePool (pInternalAuditName);
                    ObjectNameInformationLength = sizeof(OBJECT_NAME_INFORMATION);
                    pInternalAuditName = ExAllocatePoolWithTag (NonPagedPool,
                                                                ObjectNameInformationLength,
                                                                'aPeS');
               
                    if (pInternalAuditName != NULL) {
                        RtlZeroMemory(pInternalAuditName, ObjectNameInformationLength);
                    
                        //
                        // Status = STATUS_SUCCESS to allow the process creation to continue.
                        //

                        Status = STATUS_SUCCESS;
                    } else {
                        Status = STATUS_NO_MEMORY;
                    }
                }
            } else {
                Status = STATUS_NO_MEMORY;
            }
        } else {
        
            //
            // If this happens, then ObQueryNameString is broken for the FS on which
            // it was called.
            //
#if DBG
            DbgPrint("\n** ObqueryNameString failed with 0x%x.\n", Status);
#endif //DBG
            ObjectNameInformationLength = sizeof(OBJECT_NAME_INFORMATION);
            pInternalAuditName = ExAllocatePoolWithTag (NonPagedPool,
          ObjectNameInformationLength, 'aPeS');
            if (pInternalAuditName != NULL) {
                RtlZeroMemory(pInternalAuditName, ObjectNameInformationLength);
                //
                // Status = STATUS_SUCCESS to allow the process creation to continue.
                //
                Status = STATUS_SUCCESS;
            } else {
                Status = STATUS_NO_MEMORY;
            }
        }
    } else {
        //
        // If ObQueryNameString returns some other error code, we cannot
        // be certain of which action to take, or whether it has properly
        // set the ReturnLength.  For example, ObQueryNameString has slightly
        // different semantics under DFS than NTFS.  Additionally, 3rd
        // party file systems may also behave unpredictably.  For these reasons,
        // in the case of an unexpected error code from ObQueryNameString
        // we set AuditName to zero length unicode string and allow process
        // creation to continue.
        //
   
#if DBG
        DbgPrint("\n** ObqueryNameString failed with 0x%x.\n", Status);
#endif //DBG

        ObjectNameInformationLength = sizeof(OBJECT_NAME_INFORMATION);
        pInternalAuditName = ExAllocatePoolWithTag(NonPagedPool, ObjectNameInformationLength, 'aPeS');

        if (pInternalAuditName != NULL) {
            RtlZeroMemory(pInternalAuditName, ObjectNameInformationLength);
            //
            // Status = STATUS_SUCCESS to allow the process creation to continue.
            //
            Status = STATUS_SUCCESS;
        } else {
            Status = STATUS_NO_MEMORY;
        }
    }
    *pAuditName = pInternalAuditName;
    return Status;
}
从上面的代码可以看到,起始就是调用一个ObQueryNameString函数,如果失败则将进程路径信息设置成空,返回。关于ObQueryNameString的源码我就不详细分析了,参见博文http://blog.csdn.net/misterliwei/archive/2009/08/20/4467301.aspx,主要是根据内核对象中的_OBJECT_HEADER和_OBJECT_HEADER_NAME_INFO结构获取。到此GetProcessImageFileName函数已经完全暴露在我们面前。
我们再简单回顾下:GetProcessImageFileName需要传入一个进程句柄,然后调用NtQueryInformationProcess函数的0x1b号功能,首先获取进程内核对象,然后调用SeLocateProcessImageName,该函数首先查看_EPROCESS结构0x1f4处的_SE_AUDIT_PROCESS_CREATION_INFO是否为空,如果非空直接从该处获取;反之需要自己获取,并填充到该处。首先会从_EPROCESS中获取_FILE_OBJECT对象,获取方法为:_EPROCESS->_SECTION_OBJECT->_SEGMENT_OBJECT->_CONTROL_AREA->_FILE_OBJECT,获取到文件对象后,利用SeInitializeProcessAuditName函数获取文件对象的路径,这个函数里边主要调用ObQueryNameString函数,该函数是未文档化的函数,简单申明后可以直接使用。该函数主要是遍历对象目录树,组建文件路径。
通过以上的分析,我有了以下简单的想法:
防御:
(1)        该函数调用的前提条件是:进程的句柄。而应用层获取进程句柄的函数是OpenProcess,那么我们只要在这个函数上做下过滤,不让别人将自己映射到它的句柄表即可。
(2)        我们可以看到该函数其实调用的是NtQueryInformationProcess的0x1b号功能,那么我们只要在NtQueryInformationProcesss上做过滤同样可以。更深层数的hook,例如SeLocateProcessImageName、ObQueryNameString等同理。
(3)        从上面分析的过程可以看到,其实信息源是在_FILE_OBJECT对象的目录树中,_EPROCESS中的_SE_AUDIT_PROCESS_CREATION_INFO也只不过是个副本。因此我们可以尝试自己遍历_FILE_OBJECT对象目录树,然后将其到根目录的每个结点名删除,当然_SE_ADUIT_PROCESS_CREATION_INFO处的必须先删除。这可以说从根本上解决了GetProcessImageFileName函数的调用。只是猜想,未实践。不知道这样的擦出,会不会对进程本身产生影响!知道的麻烦告知!谢谢。
保护:
(1)        针对OpenProcess的过滤,我们可以在内核中通过ObOpenObjetByPointer、KeStackAttachProcess将目标对象映射到我们指定的进程空间,然后将句柄传到应用层,就可以直接操作了。当然既然已经到内核层,也就没必要到应用层了。
(2)        对于NtQueryInformationProcess、SeLocateProcessImageName、ObQueryNameString等的Hook,我们既然知道其原理,就可以自己实现了。
(3)        对于最后这种信息的擦除,只能看看别的地方是否曾经保存过进程路径信息了。如_PEB中。
2.        GetModuleFileNameEx函数
我们还是从源码看起,没有什么比源码更具说服力的了。

可以看到代码首先调用PSAPI!FindModule函数,然后调用ReadProcessMemory。我们一个个看。首先是PSAPI!FindModule函数:

可以看到这次调用NtQueryInformationProcess的0号功能函数,获取的数据结构为:
typedef struct _PROCESS_BASIC_INFORMATION {
    PVOID Reserved1;
    PPEB PebBaseAddress;//偏移0x4处
    PVOID Reserved2[2];
    ULONG_PTR UniqueProcessId;
    PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;
可以看到偏移0x4处,也就是上面的ebp-0x1c处保存着Peb的指针,从_PEB结构可以看到偏移0xc处恰好为_LDR结构,

后面的就不看了。可以断定,该函数是从_PEB的_PEB_LDR_DATA中获取进程路径信息。同样我们怎么防护呢?原理与上面的基本相同。这个函数也需要进程句柄,所以可以过滤OpenProcess,同样可以利用更深层次的Hook,以及擦除信息。
3.        ReadProcessMemory+_RTL_USER_PROCESS_PARAMETERS
因为_RTL_USER_PROCESS_PARAMETERS中存放着进程的完整路径,结构如下所示:

这种方法的防御保护方法就不再重复了,同上。
4.        _FILE_OBJECT结构
该种方法是在内核实现的。看下_FILE_OBJECT的结构,

可以看到_FILE_OBJECT对象中的FileName中保存进程路径信息,再根据DeviceObject获取驱动器名,组合下就可以获取进程完整路径。
三.        后记
由前面的分析,我们可以看到:在内核态如果我们想要获取进程的Dos完整路径,我们可以先利用ZwQueryInformationProcess的0x1b号功能,获取到进程的NT路径,然后再利用ZwOpenFile打开文件,再利用ObReferenceObjectByHandle获取到文件对象,然后利用RtlVolumeDeviceToDosName获得驱动器名,再组合下即可。

[2023春季班]《安卓高级研修班(网课)》月薪两万班招生中~

上传的附件:
收藏
点赞1
打赏
分享
最新回复 (33)
雪    币: 233
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
cpfgf 活跃值 2011-2-10 20:40
2
0
学习
雪    币: 318
活跃值: 活跃值 (11)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
CYBER涛 活跃值 1 2011-2-10 21:13
3
0
呵呵,没有遇到过,谢谢您的分享!
雪    币: 357
活跃值: 活跃值 (109)
能力值: ( LV13,RANK:220 )
在线值:
发帖
回帖
粉丝
zyhfut 活跃值 5 2011-2-11 10:10
4
0
都不是很给力嘛
雪    币: 219
活跃值: 活跃值 (41)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wufenjack 活跃值 2011-2-11 10:32
5
0
哈哈,过年了,大家都忙着喝酒呢,谢谢分享!
雪    币: 104
活跃值: 活跃值 (391)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
njxxdx 活跃值 2011-2-11 10:47
6
0
留名!!!!!
雪    币: 301
活跃值: 活跃值 (23)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
kkmylove 活跃值 2 2011-2-11 10:48
7
0
谢谢分享
雪    币: 212
活跃值: 活跃值 (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
juncheng 活跃值 2011-2-11 12:47
8
0
路过学习了
雪    币: 2080
活跃值: 活跃值 (274)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
exile 活跃值 1 2011-2-11 12:52
9
0
不错。。学习
雪    币: 204
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
PentiumM 活跃值 2011-2-11 16:06
10
0
看看~~~,123
雪    币: 142
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xiejienet 活跃值 2011-2-11 16:10
11
0
谢谢楼主的分享
雪    币: 203
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ww66 活跃值 2011-2-11 20:46
12
0
看一下是何代码
雪    币: 121
活跃值: 活跃值 (22)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
salwtp 活跃值 2011-2-11 21:39
13
0
学习一下 嘿嘿 给力给力~
雪    币: 1978
活跃值: 活跃值 (2923)
能力值: ( LV12,RANK:520 )
在线值:
发帖
回帖
粉丝
熊猫正正 活跃值 9 2011-2-11 22:58
14
0
谢谢分享,分析思路很重要~~
雪    币: 104
活跃值: 活跃值 (391)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
njxxdx 活跃值 2011-2-12 18:31
15
0
留名!!!!
雪    币: 203
活跃值: 活跃值 (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
labadavid 活跃值 2011-2-12 20:08
16
0
谢谢楼主的分享
雪    币: 201
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zpclxc 活跃值 2011-2-12 20:41
17
0
学习了,感谢分享~~~
雪    币: 33
活跃值: 活跃值 (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
PEBOSS 活跃值 2011-2-12 21:11
18
0
下载后留名!!!!!
雪    币: 527
活跃值: 活跃值 (86)
能力值: ( LV12,RANK:360 )
在线值:
发帖
回帖
粉丝
Mx¢Xgt 活跃值 7 2011-2-13 18:27
19
0
最好贴这里一份,收藏收藏
雪    币: 303
活跃值: 活跃值 (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
痞子辉 活跃值 1 2011-2-14 12:43
20
0
不错。。。。。很好很强大。。
雪    币: 2071
活跃值: 活跃值 (14)
能力值: (RANK:170 )
在线值:
发帖
回帖
粉丝
Aker 活跃值 4 2011-2-14 15:01
21
0
小问题深入理解,谢谢
雪    币: 322
活跃值: 活跃值 (113)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
高军 活跃值 2011-2-14 16:45
22
0
谢谢分享,备份
雪    币: 47
活跃值: 活跃值 (229)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
winibear 活跃值 2011-2-14 16:57
23
0
感谢分享!!!
雪    币: 284
活跃值: 活跃值 (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
jerrynpc 活跃值 2011-2-15 08:40
24
0
写书吧,写帖子浪费了
雪    币: 196
活跃值: 活跃值 (42)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
十年寒窗 活跃值 2011-2-15 10:33
25
0
谢谢分享。。
游客
登录 | 注册 方可回帖
返回