设为首页 加入收藏

TOP

7.1 实现进程内存块枚举(一)
2023-09-23 15:43:45 】 浏览:282
Tags:7.1 程内存

Windows操作系统中,每个进程的虚拟地址空间都被划分为若干内存块,每个内存块都具有一些属性,如内存大小、保护模式、类型等。这些属性可以通过VirtualQueryEx函数查询得到。

该函数可用于查询进程虚拟地址空间中的内存信息的函数。它的作用类似于Windows操作系统中的Task Manager中的进程选项卡,可以显示出一个进程的内存使用情况、模块列表等信息。使用VirtualQueryEx函数,可以枚举一个进程的所有内存块。该函数需要传入要查询的进程的句柄、基地址和一个MEMORY_BASIC_INFORMATION结构体指针。它会返回当前内存块的基地址、大小、状态(free/commit/reserve)、保护模式等信息。

在实现对内存块的枚举之前,我们先通过ReadProcessMemory函数实现一个内存远程内存读取功能,如下代码所示,首先,通过OpenProcess函数打开进程句柄,获得当前进程的操作权限。然后,调用EnumMemory函数,传入进程句柄以及起始地址和终止地址参数,依次读取每一页内存,通过循环打印其内存数据。

#include <iostream>
#include <windows.h>

// 枚举内存实现
void EnumMemory(HANDLE Process, DWORD BeginAddr, DWORD EndAddr)
{
  // 每次读入长度
  const DWORD pageSize = 1024;

  BYTE page[pageSize];
  DWORD tmpAddr = BeginAddr;
  while (tmpAddr <= EndAddr)
  {
    ReadProcessMemory(Process, (LPCVOID)tmpAddr, &page, pageSize, 0);
    for (int x = 0; x < pageSize; x++)
    {
      if (x % 15 == 0)
      {
        printf("| 0x%08X \n", tmpAddr + x);
      }
      printf("0x%02X ", page[x]);
    }
    tmpAddr += pageSize;
  }
}

int main(int argc, char* argv[])
{
  HANDLE process;

  // 打开当前进程
  process = OpenProcess(PROCESS_ALL_ACCESS, false, GetCurrentProcessId());

  // 枚举内存 从0x401000 - 0x7FFFFFFF
  EnumMemory(process, 0x00401000, 0x7FFFFFFF);

  system("pause");
  return 0;
}

上述代码简单明了,易于理解,但并没有实现过滤特定内存属性的功能。如果需要对特定类型的内存进行分析,需要结合VirtualQueryEx函数实现内存属性的查询和过滤。

接着我们进入本章的重点,实现枚举进程内存块,要实现该功能首先读者必须要了解一个结构体_SYSTEM_INFO该结构体是系统信息结构,可用于存储系统硬件和系统配置信息,而我们所需要的内存块数据同样可以使用该结构进行存储。

根据具体需求,可以通过调用GetSystemInfo函数来获得_SYSTEM_INFO结构体的信息。GetSystemInfo函数可以返回系统的硬件信息,包括有多少个处理器,每个处理器有多少个核心,系统页大小等信息,该结构体的定义如下所示;

    typedef struct _SYSTEM_INFO {
      union {
      DWORD dwOemId;                          // 兼容性保留
      struct {
        WORD wProcessorArchitecture;          // 操作系统处理器体系结构
        WORD wReserved;                       // 保留
      } DUMMYSTRUCTNAME;
      } DUMMYUNIONNAME;
      DWORD     dwPageSize;                   // 页面大小和页面保护和承诺的粒度
      LPVOID    lpMinimumApplicationAddress;  // 指向应用程序和dll可访问的最低内存地址的指针
      LPVOID    lpMaximumApplicationAddress;  // 指向应用程序和dll可访问的最高内存地址的指针
      DWORD_PTR dwActiveProcessorMask;        // 处理器掩码
      DWORD     dwNumberOfProcessors;         // 当前组中逻辑处理器的数量
      DWORD     dwProcessorType;              // 处理器类型,兼容性保留
      DWORD     dwAllocationGranularity;      // 虚拟内存的起始地址的粒度
      WORD      wProcessorLevel;              // 处理器级别
      WORD      wProcessorRevision;           // 处理器修订
    } SYSTEM_INFO, *LPSYSTEM_INFO;

接着就是要查询内存块的状态了,我们可通过VirtualQueryEx函数实现查询进程虚拟地址空间中的内存信息,其原型定义如下:

SIZE_T VirtualQueryEx(
  HANDLE                    hProcess,
  LPCVOID                   lpAddress,
  PMEMORY_BASIC_INFORMATION lpBuffer,
  SIZE_T                    dwLength
);

参数说明:

  • hProcess:进程句柄。需要查询的进程的句柄
  • lpAddress:基地址。需要查询的内存块的基地址
  • lpBuffer:内存信息缓冲区。 PMEMORY_BASIC_INFORMATION 结构指针,用于存储查询结果。它包含了取得的内存块信息,如基地址、保护属性、状态、大小等
  • dwLength:缓冲区大小。缓冲区的大小,以字节为单位。如果缓冲区太小,则函数将返回指定的内存块信息长度存放到此处,不会写入完整的信息

该函数返回实际填充到缓冲区中的字节数。如果函数失败,则返回0。当我们需要了解特定进程的内存使用情况时,可以使用VirtualQueryEx()函数枚举进程内存中的所有内存块,并按需查询其中的属性值。

#include <iostream>
#include <windows.h>
#include <Psapi.h>

#pragma comment(lib,"psapi.lib")

// 枚举特定进程内存块信息
VOID ScanProcessMemory(HANDLE hProc)
{
  SIZE_T stSize = 0;
  PBYTE pAddress = (PBYTE)0;
  SYSTEM_INFO sysinfo;
  MEMORY_BASIC_INFORMATION mbi = { 0 };

  //获取页的大小
  ZeroMemory(&sysinfo, sizeof(SYSTEM_INFO));
  GetSystemInfo(&sysinfo);

  // 得到的镜像基地址
  pAddress = (PBYTE)sysinfo.lpMinimumApplicationAddress;

  printf("------------------------------------------------
首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇C++系列十:日常学习-进程间通讯 下一篇C++笔记(细碎小知识点)1

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目