用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;

}

你可能感兴趣的:(用ZwQueryVirtualMemory枚举进程模块)