您的位置:首页 > 产品设计 > UI/UE

用ZwQueryVirtualMemory枚举进程模块

2016-12-23 22:09 513 查看
转自:http://www.cppblog.com/aurain/archive/2010/07/05/119361.html

[cpp] view
plain copy

用ZwQueryVirtualMemory枚举进程模块

枚举进程模块通常可以使用诸如:CreateToolhelp32Snapshot,Module32First,Module32Next 等"Tool Help Functions"接口来实现, 并且这也是最通用的方法(从Win95就开始支持了), 但是今天我们要介绍的是ntdll.dll导出的未文档化接口ZwQueryVirtualMemory,。相比前面所介绍的方法,该方法可以检测出隐藏的模块(类似IceSword)。

我们先来看下这个接口的原型:

//-------------------------------------------------------------------------------------------------

NTSTATUS

NTAPI

ZwQueryVirtualMemory(

IN HANDLE ProcessHandle,

IN PVOID BaseAddress,

IN MEMORY_INFORMATION_CLASS MemoryInformationClass,

OUT PVOID MemoryInformation,

IN ULONG MemoryInformationLength,

OUT PULONG ReturnLength OPTIONAL );

typedef enum _MEMORY_INFORMATION_CLASS {

MemoryBasicInformation,

MemoryWorkingSetList,

MemorySectionName,

MemoryBasicVlmInformation

} MEMORY_INFORMATION_CLASS;

参数说明:

ProcessHandle - 目标进程句柄

BaseAddress - 要查询的虚拟内存基址

MemoryInformationClass - 要查询的内存信息类

MemoryInformation - 用户提供的缓冲区,返回内存相关信息

MemoryInformationLength - 缓冲区长度(字节为单位)

ReturnLength - 实际返回的内存信息长度(字节为单位)

返回值:

NTSTATUS - 返回STATUS_SUCCESS或一个错误状态码

//-------------------------------------------------------------------------------------------------

我们要枚举进程模块信息, 需要用到两类内存信息MemoryBasicInformation和MemorySectionName,

前者返回内存的基本信息, 比如: 内存区的基址,大小以及页面的各种属性等等, 而后者则返回内存段的名字,

也就是我们所要找的模块名. 利用前者我们可以过滤出类型为MEM_IMAGE的内存段并得到内存段的基址和属性, 利用后者我们可以得到模块名.

另外,需要注意的是该方法找出来的设备名是诸如\Device\HarddiskVolume1之类的名称,所以我们需要把它转换为我们习惯的DOS设备名,如C:\,D:\等。不能自以为是的认为\Device\HarddiskVolume1对应C盘\Device\HarddiskVolume2对应D盘。该转换是通过调用QueryDosDevice来实现的。

具体看代码:

// CheckDll.cpp : 定义控制台应用程序的入口点。

//

#include "stdafx.h"

#include <windows.h>

#include <winternl.h>

#include <string>

#include <map>

using namespace std;

#pragma warning(disable:4312)

typedef enum _MEMORY_INFORMATION_CLASS

{

MemoryBasicInformation,

MemoryWorkingSetList,

MemorySectionName

}MEMORY_INFORMATION_CLASS;

typedef

NTSTATUS

(WINAPI *ZWQUERYVIRTUALMEMORY) (

IN HANDLE ProcessHandle,

IN PVOID BaseAddress,

IN MEMORY_INFORMATION_CLASS MemoryInformationClass,

OUT PVOID MemoryInformation,

IN ULONG MemoryInformationLength,

OUT PULONG ReturnLength OPTIONAL

);

map<wstring, wstring> g_mapDevice2Path;

void ConvertVolumePaths(

IN PWCHAR DeviceName,

IN PWCHAR VolumeName

)

{

DWORD CharCount = MAX_PATH + 1;

PWCHAR Names = NULL;

PWCHAR NameIdx = NULL;

BOOL Success = FALSE;

for (;;)

{

//

// Allocate a buffer to hold the paths.

Names = (PWCHAR) new BYTE [CharCount * sizeof(WCHAR)];

if ( !Names )

{

//

// If memory can't be allocated, return.

return;

}

//

// Obtain all of the paths

// for this volume.

Success = GetVolumePathNamesForVolumeNameW(

VolumeName, Names, CharCount, &CharCount

);

if ( Success )

{

break;

}

if ( GetLastError() != ERROR_MORE_DATA )

{

break;

}

//

// Try again with the

// new suggested size.

delete [] Names;

Names = NULL;

}

if ( Success )

{

//

// Display the various paths.

for ( NameIdx = Names;

NameIdx[0] != L'\0';

NameIdx += wcslen(NameIdx) + 1 )

{

g_mapDevice2Path[DeviceName] = NameIdx;

}

}

if ( Names != NULL )

{

delete [] Names;

Names = NULL;

}

return;

}

BOOL InitDevice2Path()

{

BOOL bRet = FALSE;

DWORD CharCount = 0;

WCHAR DeviceName[MAX_PATH] = L"";

DWORD Error = ERROR_SUCCESS;

HANDLE FindHandle = INVALID_HANDLE_VALUE;

BOOL Found = FALSE;

size_t Index = 0;

BOOL Success = FALSE;

WCHAR VolumeName[MAX_PATH] = L"";

//

// Enumerate all volumes in the system.

FindHandle = FindFirstVolumeW(VolumeName, ARRAYSIZE(VolumeName));

if (FindHandle == INVALID_HANDLE_VALUE)

{

Error = GetLastError();

wprintf(L"FindFirstVolumeW failed with error code %d\n", Error);

return bRet;

}

for (;;)

{

//

// Skip the \\?\ prefix and remove the trailing backslash.

Index = wcslen(VolumeName) - 1;

if (VolumeName[0] != L'\\' ||

VolumeName[1] != L'\\' ||

VolumeName[2] != L'?' ||

VolumeName[3] != L'\\' ||

VolumeName[Index] != L'\\')

{

Error = ERROR_BAD_PATHNAME;

wprintf(L"FindFirstVolumeW/FindNextVolumeW returned a bad path: %s\n", VolumeName);

break;

}

//

// QueryDosDeviceW doesn't allow a trailing backslash,

// so temporarily remove it.

VolumeName[Index] = L'\0';

CharCount = QueryDosDeviceW(&VolumeName[4], DeviceName, ARRAYSIZE(DeviceName));

VolumeName[Index] = L'\\';

if ( CharCount == 0 )

{

Error = GetLastError();

wprintf(L"QueryDosDeviceW failed with error code %d\n", Error);

break;

}

ConvertVolumePaths(DeviceName, VolumeName);

//

// Move on to the next volume.

Success = FindNextVolumeW(FindHandle, VolumeName, ARRAYSIZE(VolumeName));

if ( !Success )

{

Error = GetLastError();

if (Error != ERROR_NO_MORE_FILES)

{

wprintf(L"FindNextVolumeW failed with error code %d\n", Error);

break;

}

//

// Finished iterating

// through all the volumes.

Error = ERROR_SUCCESS;

break;

}

}

FindVolumeClose(FindHandle);

FindHandle = INVALID_HANDLE_VALUE;

return bRet;

}

void DeviceName2PathName(OUT WCHAR* szPathName, IN const WCHAR* szDeviceName)

{

memset(szPathName, 0, MAX_PATH * 2);

wstring strDeviceName = szDeviceName;

size_t pos = strDeviceName.find(L'\\', 9);

wstring strTemp1 = strDeviceName.substr(0, pos);

wstring strTemp2 = strDeviceName.substr(pos + 1);

wstring strDriverLetter = g_mapDevice2Path[strTemp1];

wstring strPathName = strDriverLetter + strTemp2;

wcscpy_s(szPathName, MAX_PATH, strPathName.c_str());

}

/**

* 枚举指定进程加载的模块

* @param dwProcessId 进程Id

* @return void

*/

void EnumProcessModules(IN DWORD dwProcessId)

{

DWORD dwStartAddr = 0x00000000;

BYTE szBuffer[MAX_PATH * 2 + 4] = {0};

WCHAR szModuleName[MAX_PATH] = {0};

WCHAR szPathName[MAX_PATH] = {0};

MEMORY_BASIC_INFORMATION mbi;

PUNICODE_STRING usSectionName;

ZWQUERYVIRTUALMEMORY fnZwQueryVirtualMemory;

HANDLE hProcess =NULL;

hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwProcessId);

if (hProcess == NULL)

{

wprintf(L"Open Process %d Error\n", dwProcessId);

return;

}

dwStartAddr = 0x00000000;

fnZwQueryVirtualMemory = (ZWQUERYVIRTUALMEMORY)

::GetProcAddress(GetModuleHandleA("ntdll.dll"),

"ZwQueryVirtualMemory" );

if(fnZwQueryVirtualMemory)

{

do

{

if (fnZwQueryVirtualMemory(

hProcess,

(PVOID)dwStartAddr,

MemoryBasicInformation,

&mbi,

sizeof(mbi),

0) >= 0 )

{

if(mbi.Type == MEM_IMAGE)

{

if (fnZwQueryVirtualMemory(

hProcess,

(PVOID)dwStartAddr,

MemorySectionName,

szBuffer,

sizeof(szBuffer),

0) >= 0 )

{

usSectionName = (PUNICODE_STRING)szBuffer;

if( _wcsnicmp(szModuleName, usSectionName->Buffer, usSectionName->Length / sizeof(WCHAR)) )

{

wcsncpy_s(szModuleName, usSectionName->Buffer, usSectionName->Length / sizeof(WCHAR) );

szModuleName[usSectionName->Length / sizeof(WCHAR)] = UNICODE_NULL;

DeviceName2PathName(szPathName, szModuleName);

wprintf(L"[0x%.8x]\t%s\n", dwStartAddr, szPathName);

}

}

}

}

// 递增基址,开始下一轮查询!

dwStartAddr += 0x1000;

}while( dwStartAddr < 0x80000000 );

}

CloseHandle(hProcess);

}

/**

* 提升当前进程权限函数("SeDebugPrivilege"读、写控制权限)

* @param void

* @return TRUE-成功;FALSE-失败

*/

BOOL EnableDebugPriv()

{

HANDLE hToken;

TOKEN_PRIVILEGES tkp;

LUID Luid;

if (!OpenProcessToken(GetCurrentProcess(),

TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))

{

return FALSE;

}

if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Luid ))

{

CloseHandle(hToken);

return FALSE;

}

tkp.PrivilegeCount = 1;

tkp.Privileges[0].Luid = Luid;

tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))

{

CloseHandle(hToken);

return FALSE;

}

return TRUE;

}

int _tmain(int argc, _TCHAR* argv[])

{

DWORD dwProcessId = 4;

if (argc != 2)

{

wprintf(L"usage:CheckDll ProcessId");

return 1;

}

dwProcessId = _ttoi(argv[1]);

InitDevice2Path();

// 首先提示权限

if (EnableDebugPriv())

{

EnumProcessModules(dwProcessId);

}

g_mapDevice2Path.clear();

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++