解密NtQueryVirtualMemory

   NtQueryVirtualMemory是windows的一个未公开API(导出但未形成文档),他的作用主要是查询指定进程的某个虚拟地址控件所在的内存对象的一些信息。

原型(prototype):

NTSTATUS NTAPI NtQueryVirtualMemory(
          IN HANDLE               	ProcessHandle,     			//目标进程句柄
          IN PVOID                	BaseAddress,     			//目标内存地址
          IN MEMORY_INFORMATION_CLASS 	MemoryInformationClass,  		//查询内存信息的类别
          OUT PVOID               	Buffer,       				//用于存储获取到的内存信息的结构地址
          IN ULONG                	Length,       				//Buffer的最大长度
          OUT PULONG              	ResultLength OPTIONAL); 		//存储该函数处理返回的信息的长度的ULONG的地址 

第一个参数是目标进程的句柄,第二个参数是要查询的内存地址,第五个和第六个参数为Buffer长度,和函数处理结果返回的长度。

第三个参数类型MEMORY_INFORMATION_CLASS是一个枚举类型其定义如下:

//MEMORY_INFORMATION_CLASS定义
typedef enum _MEMORY_INFORMATION_CLASS
{
 MemoryBasicInformation,     		//内存基本信息
 MemoryWorkingSetInformation,   	//工作集信息
 MemoryMappedFilenameInformation   	//内存映射文件名信息
} MEMORY_INFORMATION_CLASS;

第四个参数是根据第三个参数选用不同的结构去接收内存信息的地址。

其对应关系如下:

0x00:使用MemoryBasicInformation时,Buffer应当指向的结构为MEMORY_BASIC_INFORMATION,其定义如下:

typedef struct _MEMORY_BASIC_INFORMATION {
    PVOID 		BaseAddress;
    PVOID 		AllocationBase;
    DWORD 		AllocationProtect;
    SIZE_T 		RegionSize;
    DWORD 		State;
    DWORD 		Protect;
    DWORD 		Type;
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;

0x01:使用MemoryWorkingSetInformation时,Buffer应当指向的结构为MEMORY_WORKING_SET_INFORMATION,其定义如下:

typedef struct _MEMORY_WORKING_SET_INFORMATION {
	ULONG		SizeOfWorkingSet;
	DWORD		WsEntries[ANYSIZE_ARRAY];
} MEMORY_WORKING_SET_INFORMATION, *PMEMORY_WORKING_SET_INFORMATION;

0x02:当使用MemoryMappedFilenameInformation  时,Buffer应当指向结构为MEMORY_MAPPED_FILE_NAME_INFORMATION,其定义如下:

#define _MAX_OBJECT_NAME 1024/sizeof(WCHAR)
typedef struct _MEMORY_MAPPED_FILE_NAME_INFORMATION {
 UNICODE_STRING Name;
 WCHAR     Buffer[_MAX_OBJECT_NAME];
} MEMORY_MAPPED_FILE_NAME_INFORMATION, *PMEMORY_MAPPED_FILE_NAME_INFORMATION; 

第一种和第二种没有太多需要解释的地方,至于第三种的MEMORY_MAPPED_FILE_NAME_INFORMATION的定义形式需要说明一下,第三种使用方法目前资料很少,我看多资料都是直接直接传了个数组进去,然后很多人就发表评论说为什么要传那个长度呢?为什么不可以传这个长度呢?无人回答……那好我们自己来找找标准用法吧。

翻阅了一下NT2k的源码,看看这个函数的实现:

NTSTATUS
NtQueryVirtualMemory (
    IN HANDLE ProcessHandle,
    IN PVOID BaseAddress,
    IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
    OUT PVOID MemoryInformation,
    IN ULONG MemoryInformationLength,
    OUT PULONG ReturnLength OPTIONAL
     )

/*++

Routine Description:

    This function provides the capability to determine the state,
    protection, and type of a region of pages within the virtual address
    space of the subject process.

    The state of the first page within the region is determined and then
    subsequent entries in the process address map are scanned from the
    base address upward until either the entire range of pages has been
    scanned or until a page with a nonmatching set of attributes is
    encountered. The region attributes, the length of the region of pages
    with matching attributes, and an appropriate status value are
    returned.

    If the entire region of pages does not have a matching set of
    attributes, then the returned length parameter value can be used to
    calculate the address and length of the region of pages that was not
    scanned.

Arguments:


    ProcessHandle - An open handle to a process object.

    BaseAddress - The base address of the region of pages to be
        queried. This value is rounded down to the next host-page-
        address boundary.

    MemoryInformationClass - The memory information class about which
        to retrieve information.

    MemoryInformation - A pointer to a buffer that receives the
        specified information.  The format and content of the buffer
        depend on the specified information class.


        MemoryBasicInformation - Data type is PMEMORY_BASIC_INFORMATION.

            MEMORY_BASIC_INFORMATION Structure


            ULONG RegionSize - The size of the region in bytes
                beginning at the base address in which all pages have
                identical attributes.

            ULONG State - The state of the pages within the region.

                State Values                        State Values

                MEM_COMMIT - The state of the pages within the region
                    is committed.

                MEM_FREE - The state of the pages within the region
                    is free.

                MEM_RESERVE - The state of the pages within the
                    region is reserved.

            ULONG Protect - The protection of the pages within the
                region.


                Protect Values                        Protect Values

                PAGE_NOACCESS - No access to the region of pages is
                    allowed. An attempt to read, write, or execute
                    within the region results in an access violation
                    (i.e., a GP fault).

                PAGE_EXECUTE - Execute access to the region of pages
                    is allowed. An attempt to read or write within
                    the region results in an access violation.

                PAGE_READONLY - Read-only and execute access to the
                    region of pages is allowed. An attempt to write
                    within the region results in an access violation.

                PAGE_READWRITE - Read, write, and execute access to
                    the region of pages is allowed. If write access
                    to the underlying section is allowed, then a
                    single copy of the pages are shared. Otherwise,
                    the pages are shared read-only/copy-on-write.

                PAGE_GUARD - Read, write, and execute access to the
                    region of pages is allowed; however, access to
                    the region causes a "guard region entered"
                    condition to be raised in the subject process.

                PAGE_NOCACHE - Disable the placement of committed
                    pages into the data cache.

            ULONG Type - The type of pages within the region.


                Type Values

                MEM_PRIVATE - The pages within the region are
                    private.

                MEM_MAPPED - The pages within the region are mapped
                    into the view of a section.

                MEM_IMAGE - The pages within the region are mapped
                    into the view of an image section.

    MemoryInformationLength - Specifies the length in bytes  of
        the memory information buffer.

    ReturnLength - An optional pointer which, if specified,
        receives the number of bytes placed in the process
        information buffer.


Return Value:

    Returns the status

    TBS


Environment:

    Kernel mode.

--*/
{
    KPROCESSOR_MODE PreviousMode;
    PEPROCESS TargetProcess;
    NTSTATUS Status;
    PMMVAD Vad;
    BOOLEAN PteIsZero;
    PVOID Va;
    BOOLEAN Found;
    SIZE_T TheRegionSize;
    ULONG NewProtect;
    ULONG NewState;
    PVOID FilePointer;
    ULONG_PTR BaseVpn;
    MEMORY_BASIC_INFORMATION Info;
    LOGICAL Attached;

    Found = FALSE;
    PteIsZero = FALSE;

    //
    // Make sure the user's buffer is large enough for the requested operation.
    //

    //
    // Check argument validity.
    //
    switch (MemoryInformationClass) {
        case MemoryBasicInformation:
                if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION)) {
                    return STATUS_INFO_LENGTH_MISMATCH;
                }
                break;

        case MemoryWorkingSetInformation:
                if (MemoryInformationLength < sizeof(ULONG)) {
                    return STATUS_INFO_LENGTH_MISMATCH;
                }
                break;

        case MemoryMappedFilenameInformation:
                FilePointer = NULL;
                break;
        default:
            return STATUS_INVALID_INFO_CLASS;
    }

    PreviousMode = KeGetPreviousMode();

    if (PreviousMode != KernelMode) {

        //
        // Check arguments.
        //

        try {

            ProbeForWrite(MemoryInformation,
                          MemoryInformationLength,
                          sizeof(ULONG_PTR));

            if (ARGUMENT_PRESENT(ReturnLength)) {
                ProbeForWriteUlong(ReturnLength);
            }

        } except (EXCEPTION_EXECUTE_HANDLER) {

            //
            // If an exception occurs during the probe or capture
            // of the initial values, then handle the exception and
            // return the exception code as the status value.
            //

            return GetExceptionCode();
        }
    }
    if (BaseAddress > MM_HIGHEST_USER_ADDRESS) {
        return STATUS_INVALID_PARAMETER;
    }

    if ((BaseAddress >= MM_HIGHEST_VAD_ADDRESS)
#if defined(MM_SHARED_USER_DATA_VA)
            ||
         (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA)
#endif
             ) {

        //
        // Indicate a reserved area from this point on.
        //

        if ( MemoryInformationClass == MemoryBasicInformation ) {

            try {
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationBase =
                                      (PCHAR) MM_HIGHEST_VAD_ADDRESS + 1;
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationProtect =
                                                                      PAGE_READONLY;
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->BaseAddress =
                                                       PAGE_ALIGN(BaseAddress);
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->RegionSize =
                                    ((PCHAR)MM_HIGHEST_USER_ADDRESS + 1) -
                                                (PCHAR)PAGE_ALIGN(BaseAddress);
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->State = MEM_RESERVE;
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Protect = PAGE_NOACCESS;
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Type = MEM_PRIVATE;

                if (ARGUMENT_PRESENT(ReturnLength)) {
                    *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
                }

#if defined(MM_SHARED_USER_DATA_VA)
                if (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA) {

                    //
                    // This is the page that is double mapped between
                    // user mode and kernel mode.
                    //

                    ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationBase =
                                (PVOID)MM_SHARED_USER_DATA_VA;
                    ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Protect =
                                                                 PAGE_READONLY;
                    ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->RegionSize =
                                                                 PAGE_SIZE;
                    ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->State =
                                                                 MEM_COMMIT;
                }
#endif

            } except (EXCEPTION_EXECUTE_HANDLER) {

                //
                // Just return success.
                //
            }

            return STATUS_SUCCESS;
        } else {
            return STATUS_INVALID_ADDRESS;
        }
    }

    if ( ProcessHandle == NtCurrentProcess() ) {
        TargetProcess = PsGetCurrentProcess();
    } else {
        Status = ObReferenceObjectByHandle ( ProcessHandle,
                                             PROCESS_QUERY_INFORMATION,
                                             PsProcessType,
                                             PreviousMode,
                                             (PVOID *)&TargetProcess,
                                             NULL );

        if (!NT_SUCCESS(Status)) {
            return Status;
        }
    }

    if (MemoryInformationClass == MemoryWorkingSetInformation) {

        MmLockPagableSectionByHandle(ExPageLockHandle);

        Status = MiGetWorkingSetInfo (MemoryInformation,
                                      MemoryInformationLength,
                                      TargetProcess);
        MmUnlockPagableImageSection(ExPageLockHandle);

        if ( ProcessHandle != NtCurrentProcess() ) {
            ObDereferenceObject (TargetProcess);
        }
        try {

            if (ARGUMENT_PRESENT(ReturnLength)) {
                *ReturnLength = ((((PMEMORY_WORKING_SET_INFORMATION)
                                    MemoryInformation)->NumberOfEntries - 1) *
                                        sizeof(ULONG)) +
                                        sizeof(MEMORY_WORKING_SET_INFORMATION);
            }

        } except (EXCEPTION_EXECUTE_HANDLER) {
        }

        return STATUS_SUCCESS;
    }

    //
    // If the specified process is not the current process, attach
    // to the specified process.
    //

    if ( ProcessHandle != NtCurrentProcess() ) {
        KeAttachProcess (&TargetProcess->Pcb);
        Attached = TRUE;
    }
    else {
        Attached = FALSE;
    }

    //
    // Get working set mutex and block APCs.
    //

    LOCK_WS_AND_ADDRESS_SPACE (TargetProcess);

    //
    // Make sure the address space was not deleted, if so, return an error.
    //

    if (TargetProcess->AddressSpaceDeleted != 0) {
        UNLOCK_WS_AND_ADDRESS_SPACE (TargetProcess);
        if (Attached == TRUE) {
            KeDetachProcess();
            ObDereferenceObject (TargetProcess);
        }
        return STATUS_PROCESS_IS_TERMINATING;
    }

    //
    // Locate the VAD that contains the base address or the VAD
    // which follows the base address.
    //

    Vad = TargetProcess->VadRoot;
    BaseVpn = MI_VA_TO_VPN (BaseAddress);

    for (;;) {

        if (Vad == (PMMVAD)NULL) {
            break;
        }

        if ((BaseVpn >= Vad->StartingVpn) &&
            (BaseVpn <= Vad->EndingVpn)) {
            Found = TRUE;
            break;
        }

        if (BaseVpn < Vad->StartingVpn) {
            if (Vad->LeftChild == (PMMVAD)NULL) {
                break;
            }
            Vad = Vad->LeftChild;

        } else {
            if (BaseVpn < Vad->EndingVpn) {
                break;
            }
            if (Vad->RightChild == (PMMVAD)NULL) {
                break;
            }
            Vad = Vad->RightChild;
        }
    }

    if (!Found) {

        //
        // There is no virtual address allocated at the base
        // address.  Return the size of the hole starting at
        // the base address.
        //

        if (Vad == NULL) {
            TheRegionSize = ((PCHAR)MM_HIGHEST_VAD_ADDRESS + 1) -
                                                (PCHAR)PAGE_ALIGN(BaseAddress);
        } else {
            if (Vad->StartingVpn < BaseVpn) {

                //
                // We are looking at the Vad which occupies the range
                // just before the desired range.  Get the next Vad.
                //

                Vad = MiGetNextVad (Vad);
                if (Vad == NULL) {
                    TheRegionSize = ((PCHAR)MM_HIGHEST_VAD_ADDRESS + 1) -
                                                (PCHAR)PAGE_ALIGN(BaseAddress);
                } else {
                    TheRegionSize = (PCHAR)MI_VPN_TO_VA (Vad->StartingVpn) -
                                                (PCHAR)PAGE_ALIGN(BaseAddress);
                }
            } else {
                TheRegionSize = (PCHAR)MI_VPN_TO_VA (Vad->StartingVpn) -
                                                (PCHAR)PAGE_ALIGN(BaseAddress);
            }
        }

        UNLOCK_WS_AND_ADDRESS_SPACE (TargetProcess);

        if (Attached == TRUE) {
            KeDetachProcess();
            ObDereferenceObject (TargetProcess);
        }

        //
        // Establish an exception handler and write the information and
        // returned length.
        //

        if ( MemoryInformationClass == MemoryBasicInformation ) {
            try {

                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationBase =
                                                                            NULL;
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->AllocationProtect =
                                                                            0;
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->BaseAddress =
                                                            PAGE_ALIGN(BaseAddress);
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->RegionSize =
                                                                    TheRegionSize;
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->State = MEM_FREE;
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Protect = PAGE_NOACCESS;
                ((PMEMORY_BASIC_INFORMATION)MemoryInformation)->Type = 0;

                if (ARGUMENT_PRESENT(ReturnLength)) {
                    *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
                }

            } except (EXCEPTION_EXECUTE_HANDLER) {

                //
                // Just return success.
                //
            }

            return STATUS_SUCCESS;
        }
        return STATUS_INVALID_ADDRESS;
    }

    //
    // Found a VAD.
    //

    Va = PAGE_ALIGN(BaseAddress);
    Info.BaseAddress = Va;

    //
    // There is a page mapped at the base address.
    //

    if (Vad->u.VadFlags.PrivateMemory) {
        Info.Type = MEM_PRIVATE;
    } else if (Vad->u.VadFlags.ImageMap == 0) {
        Info.Type = MEM_MAPPED;

        if ( MemoryInformationClass == MemoryMappedFilenameInformation ) {
            if (Vad->ControlArea) {
                FilePointer = Vad->ControlArea->FilePointer;
            }
            if ( !FilePointer ) {
                FilePointer = (PVOID)1;
            } else {
                ObReferenceObject(FilePointer);
            }
        }

    } else {
        Info.Type = MEM_IMAGE;
    }

    Info.State = MiQueryAddressState (Va, Vad, TargetProcess, &Info.Protect);

    Va = (PVOID)((PCHAR)Va + PAGE_SIZE);

    while (MI_VA_TO_VPN (Va) <= Vad->EndingVpn) {

        NewState = MiQueryAddressState (Va,
                                        Vad,
                                        TargetProcess,
                                        &NewProtect);

        if ((NewState != Info.State) || (NewProtect != Info.Protect)) {

            //
            // The state for this address does not match, calculate
            // size and return.
            //

            break;
        }
        Va = (PVOID)((PCHAR)Va + PAGE_SIZE);
    } // end while

    Info.RegionSize = ((PCHAR)Va - (PCHAR)Info.BaseAddress);
    Info.AllocationBase = MI_VPN_TO_VA (Vad->StartingVpn);
    Info.AllocationProtect = MI_CONVERT_FROM_PTE_PROTECTION (
                                             Vad->u.VadFlags.Protection);

    //
    // A range has been found, release the mutexes, deattach from the
    // target process and return the information.
    //

#if !(defined(_MIALT4K_))

    UNLOCK_WS_AND_ADDRESS_SPACE (TargetProcess);

#else

    UNLOCK_WS_UNSAFE (TargetProcess);

    if (TargetProcess->Wow64Process != NULL) {
        
        Info.BaseAddress = PAGE_4K_ALIGN(BaseAddress);

        MiQueryRegionFor4kPage(Info.BaseAddress,
                               MI_VPN_TO_VA_ENDING(Vad->EndingVpn),
                               &Info.RegionSize,
                               &Info.State,
                               &Info.Protect,
                               TargetProcess);
    }

    UNLOCK_ADDRESS_SPACE (TargetProcess);

#endif

    if (Attached == TRUE) {
        KeDetachProcess();
        ObDereferenceObject (TargetProcess);
    }

#if DBG
    if (MmDebug & MM_DBG_SHOW_NT_CALLS) {
        if ( !MmWatchProcess ) {
            DbgPrint("queryvm base %lx allocbase %lx protect %lx size %lx\n",
                Info.BaseAddress, Info.AllocationBase, Info.AllocationProtect,
                Info.RegionSize);
            DbgPrint("    state %lx  protect %lx  type %lx\n",
                Info.State, Info.Protect, Info.Type);
        }
    }
#endif //DBG

    if ( MemoryInformationClass == MemoryBasicInformation ) {
        try {

            *(PMEMORY_BASIC_INFORMATION)MemoryInformation = Info;

            if (ARGUMENT_PRESENT(ReturnLength)) {
                *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
            }

        } except (EXCEPTION_EXECUTE_HANDLER) {
        }
        return STATUS_SUCCESS;
    }

    //
    // Try to return the name of the file that is mapped.
    //

    if ( !FilePointer ) {
        return STATUS_INVALID_ADDRESS;
    } else if ( FilePointer == (PVOID)1 ) {
        return STATUS_FILE_INVALID;
    }

    //
    // We have a referenced pointer to the file. Call ObQueryNameString
    // and get the file name
    //
    //获取文件名///
    Status = ObQueryNameString(
                FilePointer,
                MemoryInformation,
                MemoryInformationLength,
                ReturnLength
                );
    //获取文件名/                                  ObDereferenceObject(FilePointer);
    return Status;
}

 只需要看一下函数最后的处理方式,获取文件名的时候使用了ObQueryNameString这个函数,而且是直接把Buffer和buffer的长度作为参数使用,若要继续探究下去,我们还要去看一下ObQueryNameString这个函数的实现方法,可以看源码,但是我们有更快捷的途径,查看MSDN吧,MSDN形成的文档毕竟比源码直观。

 

ObQueryNameString

The ObQueryNameString routine supplies the name, if any, of a given object to which the caller has a pointer.

NTSTATUS
  ObQueryNameString(
    IN PVOID  
Object,
    OUT POBJECT_NAME_INFORMATION  
ObjectNameInfo,
    IN ULONG  
Length,
    OUT PULONG  
ReturnLength
    ); 

Parameters
Object
Pointer to the object for which the name is requested. This parameter is required and cannot be NULL.
ObjectNameInfo
Pointer to a caller-allocated buffer that receives the object name information, formatted as follows.

typedef struct _OBJECT_NAME_INFORMATION {
  UNICODE_STRING Name;
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;

Length
Size, in bytes, of the buffer pointed to by ObjectNameInfo. This parameter is optional and can be zero. If it is zero, ReturnLength receives the size, in bytes of the buffer needed to hold the object name information. A reasonable size for the buffer to accommodate most object names is 1024 bytes.
ReturnLength
Pointer to a caller-allocated variable that receives the size, in bytes, of the returned object name information, including a NULL-terminator and all path separators in the name. If ObQueryNameString returns STATUS_INFO_LENGTH_MISMATCH, it sets this parameter to the required buffer length.

Return Value

ObQueryNameString returns STATUS_SUCCESS or an NTSTATUS value such as the following:

STATUS_INFO_LENGTH_MISMATCH
The buffer pointed to by ObjectNameInfo is too small to hold the requested object name information. ReturnLength points to the required buffer size. In this case, no object name information is returned. This is an error code.

Comments

If the given object is named and the type-specific query-name method succeeds, the returned string is the full path to the given object. In this case,ObQueryNameString setsName.Buffer to the address immediately afterObjectNameInfo.

If the given object is unnamed, or if the creator of the object type does not supply a query-name method,ObQueryNameString setsName.Buffer to NULL and setsName.Length andName.MaximumLength to zero.

The storage for ObjectNameInfo can be allocated from paged or nonpaged pool.

Callers of ObQueryNameString must be running at IRQL < DISPATCH_LEVEL.

 

我们要找的MEMORY_MAPPED_FILE_NAME_INFORMATION的定义原因就是上述用画线加大字体所描述的:

如果给定的对象是命名的,并且指定类型的查询名称的方法成功,则返回的字符串为指定对象的全路径名称。在这种情况下,ObQueryNameString将会设置Name的buffer字段指向紧接着ObjectNameInfo结构后面的地址。

简单来说就是这个函数如果执行成功,并且查询的对象确实有名称,那么他就把名称存放放在传入的参数ObjectNameInfo + sizeof(UNICODE_STRING)的地方,所以这个ObjectNameInfo的定义就应该是:

#define _MAX_OBJECT_NAME 1024/sizeof(WCHAR)
typedef struct _MEMORY_MAPPED_FILE_NAME_INFORMATION {
 UNICODE_STRING Name;
 WCHAR     Buffer[_MAX_OBJECT_NAME];
} MEMORY_MAPPED_FILE_NAME_INFORMATION, *PMEMORY_MAPPED_FILE_NAME_INFORMATION;

至于Buffer的大小为什么是1024Bytes 前面参数介绍也有说。

 

目前流行的会涉及到这个函数的用法是用于枚举进程所有模块,通常用Ring3的现成API CreateToolhelp32Snapshot 这种方法来枚举,只从进程的PEB的三条模块LINK中枚举,但是有很多系统加载的,或者特意在PEB的模块链表中抹去了模块就无法被枚举出来,这时候NtQueryVirtualMemory就登场了,NtQueryVirtualMemory用于枚举进程模块的时候有点类似“暴力内存搜索”,即指定一个片内存区域,然后对于这段内存区域内所有的可以用于对齐模块的地址使用NtQueryVirtualMemory获取内存信息,就可以没有任何遗漏的检测出所有模块。

 

下面给出一个用来检测某一地址所处的模块全路径名的方法的代码片段:

        MEMORY_BASIC_INFORMATION MemBasciInfo;
	ULONG ulRetLen = 0;

	NTSTATUS status = NtQueryVirtualMemory(
		GetCurrentProcess(), 
		pTargetLinearAddress, 
		MemoryBasicInformation, 
		&MemBasciInfo, 
		sizeof(MEMORY_BASIC_INFORMATION), 
		&ulRetLen);

	if (!NT_SUCCESS(status))
	{
		return FALSE;
	}


	if (MEM_IMAGE != MemBasciInfo.Type)
	{
		return FALSE;
	}

	MEMORY_MAPPED_FILE_NAME_INFORMATION MappedFileName;
	ZeroMemory(&MappedFileName, sizeof(MappedFileName));
	MappedFileName.Name.Buffer = MappedFileName.Buffer;
	MappedFileName.Name.Length = 0;
	MappedFileName.Name.MaximumLength = sizeof(MappedFileName.Buffer);

	status = NtQueryVirtualMemory(
		GetCurrentProcess(), 
		MemBasciInfo.AllocationBase, 
		MemoryMappedFilenameInformation, 
		&MappedFileName, 
		sizeof(MappedFileName), 
		&ulRetLen);

	if (!NT_SUCCESS(status))
	{
		return FALSE;
	}

	if (0 == MappedFileName.Name.Length || NULL == MappedFileName.Name.Buffer )
	{
		return FALSE;
	}

 

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值