ZwQueryVirtualMemory枚举进程模块

01.用ZwQueryVirtualMemory枚举进程模块 
02.枚举进程模块通常可以使用诸如:CreateToolhelp32Snapshot,Module32First,Module32Next 等"Tool Help Functions"接口来实现, 并且这也是最通用的方法(从Win95就开始支持了), 但是今天我们要介绍的是ntdll.dll导出的未文档化接口ZwQueryVirtualMemory,。相比前面所介绍的方法,该方法可以检测出隐藏的模块(类似IceSword)。 
03.我们先来看下这个接口的原型: 
04. 
05.//-------------------------------------------------------------------------------------------------  
06. 
07.NTSTATUS 
08.NTAPI 
09.ZwQueryVirtualMemory( 
10.            IN HANDLE ProcessHandle, 
11.            IN PVOID BaseAddress, 
12.            IN MEMORY_INFORMATION_CLASS MemoryInformationClass, 
13.            OUT PVOID MemoryInformation, 
14.            IN ULONG MemoryInformationLength, 
15.            OUT PULONG ReturnLength OPTIONAL ); 
16. 
17.typedef enum _MEMORY_INFORMATION_CLASS { 
18.            MemoryBasicInformation, 
19.            MemoryWorkingSetList, 
20.            MemorySectionName, 
21.            MemoryBasicVlmInformation 
22.} MEMORY_INFORMATION_CLASS; 
23. 
24.参数说明: 
25.           ProcessHandle - 目标进程句柄 
26.           BaseAddress    - 要查询的虚拟内存基址 
27.           MemoryInformationClass - 要查询的内存信息类 
28.           MemoryInformation - 用户提供的缓冲区,返回内存相关信息 
29.           MemoryInformationLength - 缓冲区长度(字节为单位) 
30.           ReturnLength - 实际返回的内存信息长度(字节为单位) 
31.返回值: 
32.           NTSTATUS - 返回STATUS_SUCCESS或一个错误状态码 
33. 
34.//-------------------------------------------------------------------------------------------------  
35. 
36.我们要枚举进程模块信息, 需要用到两类内存信息MemoryBasicInformation和MemorySectionName, 
37.前者返回内存的基本信息, 比如: 内存区的基址,大小以及页面的各种属性等等, 而后者则返回内存段的名字, 
38.也就是我们所要找的模块名. 利用前者我们可以过滤出类型为MEM_IMAGE的内存段并得到内存段的基址和属性, 利用后者我们可以得到模块名. 
39.另外,需要注意的是该方法找出来的设备名是诸如\Device\HarddiskVolume1之类的名称,所以我们需要把它转换为我们习惯的DOS设备名,如C:\,D:\等。不能自以为是的认为\Device\HarddiskVolume1对应C盘\Device\HarddiskVolume2对应D盘。该转换是通过调用QueryDosDevice来实现的。 
40.具体看代码: 
41.// CheckDll.cpp : 定义控制台应用程序的入口点。  
42.//  
43.  
44.#include "stdafx.h"  
45.  
46.#include <windows.h>  
47.#include <winternl.h>  
48.  
49.#include <string>  
50.#include <map>  
51.using namespace std; 
52.  
53.#pragma warning(disable:4312)  
54.  
55.typedef enum _MEMORY_INFORMATION_CLASS 
56.{ 
57.   MemoryBasicInformation, 
58.   MemoryWorkingSetList, 
59.   MemorySectionName 
60.}MEMORY_INFORMATION_CLASS; 
61.  
62.typedef 
63.NTSTATUS 
64.(WINAPI *ZWQUERYVIRTUALMEMORY) ( 
65.                         IN HANDLE ProcessHandle, 
66.                         IN PVOID BaseAddress, 
67.                         IN MEMORY_INFORMATION_CLASS MemoryInformationClass, 
68.                         OUT PVOID MemoryInformation, 
69.                         IN ULONG MemoryInformationLength, 
70.                         OUT PULONG ReturnLength OPTIONAL 
71.                         ); 
72.  
73.map<wstring, wstring> g_mapDevice2Path; 
74.  
75.void ConvertVolumePaths( 
76.                   IN PWCHAR DeviceName, 
77.                   IN PWCHAR VolumeName 
78.                   ) 
79.{ 
80.   DWORD  CharCount = MAX_PATH + 1; 
81.   PWCHAR Names     = NULL; 
82.   PWCHAR NameIdx      = NULL; 
83.   BOOL   Success      = FALSE; 
84.  
85.   for (;;) 
86.   { 
87.      //  
88.      //  Allocate a buffer to hold the paths.  
89.      Names = (PWCHAR) new BYTE [CharCount * sizeof(WCHAR)]; 
90.  
91.      if ( !Names ) 
92.      { 
93.         //  
94.         //  If memory can't be allocated, return.  
95.         return; 
96.      } 
97.  
98.      //  
99.      //  Obtain all of the paths  
100.      //  for this volume.  
101.      Success = GetVolumePathNamesForVolumeNameW( 
102.         VolumeName, Names, CharCount, &CharCount 
103.         ); 
104.  
105.      if ( Success ) 
106.      { 
107.         break; 
108.      } 
109.  
110.      if ( GetLastError() != ERROR_MORE_DATA ) 
111.      { 
112.         break; 
113.      } 
114.  
115.      //  
116.      //  Try again with the  
117.      //  new suggested size.  
118.      delete [] Names; 
119.      Names = NULL; 
120.   } 
121.  
122.   if ( Success ) 
123.   { 
124.      //  
125.      //  Display the various paths.  
126.      for ( NameIdx = Names; 
127.         NameIdx[0] != L'\0'; 
128.         NameIdx += wcslen(NameIdx) + 1 ) 
129.      { 
130.         g_mapDevice2Path[DeviceName] = NameIdx; 
131.      } 
132.   } 
133.  
134.   if ( Names != NULL ) 
135.   { 
136.      delete [] Names; 
137.      Names = NULL; 
138.   } 
139.  
140.   return; 
141.} 
142.  
143.BOOL InitDevice2Path() 
144.{ 
145.   BOOL   bRet               = FALSE;  
146.   DWORD  CharCount           = 0; 
147.   WCHAR  DeviceName[MAX_PATH] = L""; 
148.   DWORD  Error              = ERROR_SUCCESS; 
149.   HANDLE FindHandle          = INVALID_HANDLE_VALUE; 
150.   BOOL   Found              = FALSE; 
151.   size_t Index              = 0; 
152.   BOOL   Success                = FALSE; 
153.   WCHAR  VolumeName[MAX_PATH] = L""; 
154.  
155.   //  
156.   //  Enumerate all volumes in the system.  
157.   FindHandle = FindFirstVolumeW(VolumeName, ARRAYSIZE(VolumeName)); 
158.  
159.   if (FindHandle == INVALID_HANDLE_VALUE) 
160.   { 
161.      Error = GetLastError(); 
162.      wprintf(L"FindFirstVolumeW failed with error code %d\n", Error); 
163.      return bRet; 
164.   } 
165.  
166.   for (;;) 
167.   { 
168.      //  
169.      //  Skip the \\?\ prefix and remove the trailing backslash.  
170.      Index = wcslen(VolumeName) - 1; 
171.  
172.      if (VolumeName[0]     != L'\\' || 
173.         VolumeName[1]     != L'\\' || 
174.         VolumeName[2]     != L'?'  || 
175.         VolumeName[3]     != L'\\' || 
176.         VolumeName[Index] != L'\\') 
177.      { 
178.         Error = ERROR_BAD_PATHNAME; 
179.         wprintf(L"FindFirstVolumeW/FindNextVolumeW returned a bad path: %s\n", VolumeName); 
180.         break; 
181.      } 
182.  
183.      //  
184.      //  QueryDosDeviceW doesn't allow a trailing backslash,  
185.      //  so temporarily remove it.  
186.      VolumeName[Index] = L'\0'; 
187.  
188.      CharCount = QueryDosDeviceW(&VolumeName[4], DeviceName, ARRAYSIZE(DeviceName)); 
189.  
190.      VolumeName[Index] = L'\\'; 
191.  
192.      if ( CharCount == 0 ) 
193.      { 
194.         Error = GetLastError(); 
195.         wprintf(L"QueryDosDeviceW failed with error code %d\n", Error); 
196.         break; 
197.      } 
198.  
199.      ConvertVolumePaths(DeviceName, VolumeName); 
200.  
201.      //  
202.      //  Move on to the next volume.  
203.      Success = FindNextVolumeW(FindHandle, VolumeName, ARRAYSIZE(VolumeName)); 
204.  
205.      if ( !Success ) 
206.      { 
207.         Error = GetLastError(); 
208.  
209.         if (Error != ERROR_NO_MORE_FILES) 
210.         { 
211.            wprintf(L"FindNextVolumeW failed with error code %d\n", Error); 
212.            break; 
213.         } 
214.  
215.         //  
216.         //  Finished iterating  
217.         //  through all the volumes.  
218.         Error = ERROR_SUCCESS; 
219.         break; 
220.      } 
221.   } 
222.  
223.   FindVolumeClose(FindHandle); 
224.   FindHandle = INVALID_HANDLE_VALUE; 
225.  
226.   return bRet; 
227.} 
228.  
229.void DeviceName2PathName(OUT WCHAR* szPathName, IN const WCHAR* szDeviceName) 
230.{ 
231.   memset(szPathName, 0, MAX_PATH * 2); 
232.   wstring strDeviceName = szDeviceName; 
233.   size_t pos = strDeviceName.find(L'\\', 9); 
234.   wstring strTemp1 = strDeviceName.substr(0, pos); 
235.   wstring strTemp2 = strDeviceName.substr(pos + 1); 
236.   wstring strDriverLetter  = g_mapDevice2Path[strTemp1]; 
237.   wstring strPathName = strDriverLetter + strTemp2; 
238.  
239.   wcscpy_s(szPathName, MAX_PATH, strPathName.c_str()); 
240.} 
241.  
242./**
243.* 枚举指定进程加载的模块
244.* @param dwProcessId 进程Id
245.* @return void
246.*/ 
247.void EnumProcessModules(IN DWORD dwProcessId) 
248.{ 
249.   DWORD dwStartAddr = 0x00000000; 
250.   BYTE szBuffer[MAX_PATH * 2 + 4] = {0}; 
251.   WCHAR szModuleName[MAX_PATH] = {0}; 
252.   WCHAR szPathName[MAX_PATH] = {0}; 
253.   MEMORY_BASIC_INFORMATION mbi; 
254.   PUNICODE_STRING usSectionName;    
255.   ZWQUERYVIRTUALMEMORY fnZwQueryVirtualMemory; 
256.   HANDLE hProcess =NULL; 
257.  
258.   hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, dwProcessId); 
259.  
260.   if (hProcess == NULL) 
261.   { 
262.      wprintf(L"Open Process %d Error\n", dwProcessId); 
263.      return; 
264.   } 
265.  
266.   dwStartAddr = 0x00000000; 
267.  
268.   fnZwQueryVirtualMemory = (ZWQUERYVIRTUALMEMORY) 
269.      ::GetProcAddress(GetModuleHandleA("ntdll.dll"), 
270.      "ZwQueryVirtualMemory" ); 
271.  
272.   if(fnZwQueryVirtualMemory) 
273.   { 
274.      do 
275.      { 
276.         if (fnZwQueryVirtualMemory( 
277.            hProcess, 
278.            (PVOID)dwStartAddr, 
279.            MemoryBasicInformation, 
280.            &mbi, 
281.            sizeof(mbi), 
282.            0) >= 0 ) 
283.         { 
284.            if(mbi.Type == MEM_IMAGE) 
285.            { 
286.                if (fnZwQueryVirtualMemory( 
287.                   hProcess, 
288.                   (PVOID)dwStartAddr, 
289.                   MemorySectionName, 
290.                   szBuffer, 
291.                   sizeof(szBuffer), 
292.                   0) >= 0 ) 
293.                { 
294.                   usSectionName = (PUNICODE_STRING)szBuffer; 
295.                   if( _wcsnicmp(szModuleName, usSectionName->Buffer, usSectionName->Length / sizeof(WCHAR)) ) 
296.                   { 
297.                      wcsncpy_s(szModuleName, usSectionName->Buffer, usSectionName->Length / sizeof(WCHAR) ); 
298.                      szModuleName[usSectionName->Length / sizeof(WCHAR)] = UNICODE_NULL; 
299.                      DeviceName2PathName(szPathName, szModuleName); 
300.                      wprintf(L"[0x%.8x]\t%s\n", dwStartAddr, szPathName); 
301.                  } 
302.                } 
303.            } 
304.  
305.         } 
306.         // 递增基址,开始下一轮查询!  
307.         dwStartAddr += 0x1000; 
308.      }while( dwStartAddr < 0x80000000 ); 
309.   } 
310.  
311.   CloseHandle(hProcess); 
312.} 
313.  
314./**
315.* 提升当前进程权限函数("SeDebugPrivilege"读、写控制权限)
316.* @param void
317.* @return TRUE-成功;FALSE-失败
318.*/ 
319.BOOL EnableDebugPriv() 
320.{ 
321.   HANDLE hToken; 
322.   TOKEN_PRIVILEGES tkp; 
323.   LUID Luid; 
324.  
325.   if (!OpenProcessToken(GetCurrentProcess(), 
326.      TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) 
327.   { 
328.      return FALSE; 
329.   } 
330.  
331.   if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Luid )) 
332.   { 
333.      CloseHandle(hToken); 
334.      return FALSE; 
335.   } 
336.  
337.   tkp.PrivilegeCount = 1; 
338.   tkp.Privileges[0].Luid = Luid; 
339.   tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 
340.  
341.   if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)) 
342.   { 
343.      CloseHandle(hToken); 
344.      return FALSE; 
345.   } 
346.  
347.   return TRUE; 
348.} 
349.  
350.int _tmain(int argc, _TCHAR* argv[]) 
351.{ 
352.   DWORD dwProcessId = 4; 
353.  
354.   if (argc != 2) 
355.   { 
356.      wprintf(L"usage:CheckDll ProcessId"); 
357.      return 1; 
358.   } 
359.  
360.   dwProcessId = _ttoi(argv[1]); 
361.  
362.   InitDevice2Path(); 
363.  
364.   // 首先提示权限  
365.   if (EnableDebugPriv()) 
366.   { 
367.      EnumProcessModules(dwProcessId); 
368.   } 
369.  
370.   g_mapDevice2Path.clear(); 
371.  
372.   return 0; 
373.} 

posted @ 2012-12-06 17:08  小金马  阅读(3588)  评论(0编辑  收藏  举报