用ZwQueryVirtualMemory枚举进程模块

用ZwQueryVirtualMemory枚举进程模块

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或一个错误状态码

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

我们要枚举进程模块信息, 需要用到两类内存信息MemoryBasicInformationMemorySectionName,
前者返回内存的基本信息, 比如: 内存区的基址,大小以及页面的各种属性等等, 而后者则返回内存段的名字,
也就是我们所要找的模块名. 利用前者我们可以过滤出类型为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枚举进程模块)