首页 > 系统相关 >用ZwQueryVirtualMemory枚举进程模块

用ZwQueryVirtualMemory枚举进程模块

时间:2023-09-19 10:04:10浏览次数:44  
标签:return VolumeName 枚举 Names 模块 Error WCHAR NULL 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或一个错误状态码

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

我们要枚举进程模块信息, 需要用到两类内存信息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;
}





标签:return,VolumeName,枚举,Names,模块,Error,WCHAR,NULL,ZwQueryVirtualMemory
From: https://blog.51cto.com/u_15487030/7521751

相关文章

  • java基础-Junit 注解 枚举-day12
    目录1.Junit2.注解annotation3.枚举1.Junit白盒测试黑盒测试自行baidu了解java单元测试packagecom.msb01;importorg.junit.After;importorg.junit.Assert;importorg.junit.Before;importorg.junit.Test;/***@Auther:jack.chen*@Date:2023/9/18-0......
  • 巧用枚举解决业务场景的 Switch 语句—Java 实践
    巧用枚举解决业务场景的Switch语句——Java实践Switch语句是一种常见的流程控制语句,用于根据不同的条件执行不同的代码块。然而,当业务场景变得复杂时,使用大量的Switch语句可能导致代码冗长、难以维护和扩展。本文将介绍如何巧妙地使用枚举类型来优化和简化Switch语句的使......
  • 前端模块化
     1.为什么需要模块化随着前端应用的日益复杂,我们的项目代码已经逐渐膨胀到了不得不花大量时间去管理的程度了。而模块化就是一种最主流的代码组织方式,它通过把复杂的代码按照功能的不同划分为不同的模块单独维护,从而提高开发效率、降低维护成本。模块化可以使你能够更容易地重......
  • Node.js URL 模块:解析和操作 URL
    任何基于Web的应用程序不可或缺的方面之一是其有效使用URL的能力。无论是解析传入的URL还是构建URL来发出请求,清楚地了解Node.js中的URL模块对于现代Web开发人员来说至关重要。在这篇博文中,我们将探索Node.js的URL模块,从基本的URL解析开始,逐步推进到更复杂的......
  • JavaScript 模块之间的差异:CJS、AMD、UMD 和 ESM
    JavaScript的世界是一个不断发展和发展的技术领域。多年来,开发人员尝试使用不同的模块系统来提供更有组织性和协作性的工作环境。在这篇博文中,我们将研究四种流行的JavaScript模块系统之间的差异:CommonJS(CJS)、异步模块定义(AMD)、通用模块定义(UMD)和ECMAScript模块(ES......
  • RK3568开发板外接超声波传感器测距模块-迅为电子
    超声波传感器测距模块1模块说明HC-SR04传感器模块如下图所示:   只需要在 Trig 管脚输入一个 10US 以上的高电平,系统便可发出 8 个 40KHZ 的超声波脉冲,然后检测回波信号。当检测到回波信号后,通过 Echo 管脚输出。根据 Echo管脚输出高电平的持续时间可以计算距离值,......
  • RK3568开发板外接超声波传感器测距模块
      超声波传感器测距模块1模块说明HC-SR04传感器模块如下图所示:   只需要在Trig管脚输入一个10US以上的高电平,系统便可发出8个40KHZ的超声波脉冲,然后检测回波信号。当检测到回波信号后,通过Echo管脚输出。根据Echo管脚输出高电平的持续时间可以计算距离......
  • DC电源模块保护内外部电路至关重要
    BOSHIDADC电源模块保护内外部电路至关重要DC电源模块是现代电路设计中经常使用的设备,它能够将交流电转化成为直流电并提供给电路中的各种电子设备使用。在现代电子设备中,大部分都需要稳定可靠的直流电源才能正常工作。然而,如果DC电源模块出现故障,将会对设备和使用者造成不可估量......
  • 编译python扩展模块:-ltensorflow_framework
    参考:https://blog.csdn.net/u012947309/article/details/116736684  =======================================       =======================================......
  • 关于vue2的模块级总结
    前阵子在赶一个项目的进度,一直没时间做总结,今日闲来无事,消化一下。背景vue2的项目,面向受众为g端内容1.项目原因,单路由下包含详情&列表两页面。根据v-if跳转,笔者这里用的是动态组件的方式2.同样由于项目原因,使用的模块级vuex,因而在使用时,也有了许多盲点:(如图:)使用createNa......