转自:http://www.cppblog.com/aurain/archive/2010/07/05/119361.html
- 用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;
- }