Windows内存修改

你可能用过金山游侠,GameMaster8,Cheat Engine,那你有没有想过自己实现一个简易的内存修改工具?本文将介绍在Windows平台下实现一个简易内存修改工具的方法,只要你有一些windows内存管理机制的基本知识以及一定的c++编程能力,你就可以利用本文介绍的Windows内存管理API自己实现一个简易的内存修改工具。
本文将先介绍Windows的相关API,然后介绍下利用其实现内存修改工具的思路,最后介绍一个自己在windows api基础上封装的API(以下称内存管理及修改API或内存修改API),包括各API函数的说明以及如何通过对这套API的简单调用实现内存修改。
关于内存管理API以及简易的演示程序,代码较长,感兴趣的朋友可去https://github.com/jimth001/Windows-Memory-Modification.git 查看。

相关的windows API

1) GetSystemInfo

用来填充SYSTEM_INFO结构体信息,函数原型如下:


voidWINAPI GetSystemInfo(_Out_ LPSYSTEM_INFO lpSystemInfo)

lpSystemInfo:指向一个供函数返回信息的SYSTEM_INFO结构体。SYSTEM_INFO定义如下:

typedefstruct _SYSTEM_INFO {union {DWORD dwOemId;// Obsoletefield...do not usestruct {WORD wProcessorArchitecture;//处理器架构WORD wReserved;} DUMMYSTRUCTNAME;} DUMMYUNIONNAME;DWORD dwPageSize;//分页大小LPVOID lpMinimumApplicationAddress;//最小寻址空间LPVOID lpMaximumApplicationAddress;//最大寻址空间DWORD_PTR dwActiveProcessorMask;//处理器掩码DWORD dwNumberOfProcessors;//处理器数目DWORD dwProcessorType;//处理器类型DWORD dwAllocationGranularity;//虚拟内存空间的粒度WORD wProcessorLevel;//处理器等级WORD wProcessorRevision;//处理器版本} SYSTEM_INFO, *LPSYSTEM_INFO;

2) GetPerformanceInfo

用来填充PERFORMANCE_INFORMATION结构体信息,用法类似于GetSystemInfo,比GetSystemInfo多一个参数,第二个参数是结构体的大小

PERFORMANCE_INFORMATION结构体定义如下:

typedef struct _PERFORMANCE_INFORMATION {DWORD  cb;//该结构体大小,调用GetPerformanceInfo前要先将其初始化SIZE_T CommitTotal;// 系统当前提交的页面总数SIZE_T CommitLimit;// 系统可提交的页面总数SIZE_T CommitPeak;// 系统历史提交页面峰值SIZE_T PhysicalTotal;// 按页分配的总的物理内存SIZE_T PhysicalAvailable;// 系统当前提交的页面总数SIZE_T SystemCache;SIZE_T KernelTotal;SIZE_T KernelPaged;SIZE_T KernelNonpaged;SIZE_T PageSize;// The size of a page, in bytes.DWORD  HandleCount;// The current number of open //handles.DWORD ProcessCount;//DWORD ThreadCount;//}PERFORMANCE_INFORMATION, *PPERFORMANCE_INFORMATION;

3) CreateToolhelp32Snapshot

CreateToolhelp32Snapshot可以通过获取进程信息为指定的进程、进程使用的堆[HEAP]、模块[MODULE]、线程建立一个快照。

函数原型如下:

HANDLE WINAPI CreateToolhelp32Snapshot(DWORD dwFlags, //用来指定“快照”中需要返回的对象,可以是TH32CS_SNAPPROCESS(在快照中包含系统中所有的进程)等

DWORD th32ProcessID //一个进程ID号,用来指定要获取哪一个进程的快照,当获取系统进程列表或获取 当前进程快照时可以设为0

);

调用成功,返回快照的句柄,否则返回INVALID_HANDLE_VALUE

4) Process32First

process32First是一个进程获取函数,当我们利用函数CreateToolhelp32Snapshot()获得当前运行进程的快照后,我们可以利用process32First函数来获得第一个进程的句柄。

函数原型如下:


BOOL WINAPI Process32First(HANDLE hSnapshot,//_in(in表示该参数是导入数据的)LPPROCESSENTRY32 lppe//_out(out表示该参数是导出数据的)//是指向PROCESSENTRY32结构的指针);

PROCESSENTRY32 结构定义如下:

typedef struct tagPROCESSENTRY32 {DWORD dwSize; // 结构大小;DWORD cntUsage; // 此进程的引用计数;DWORD th32ProcessID; // 进程ID;DWORD th32DefaultHeapID; // 进程默认堆ID;DWORD th32ModuleID; // 进程模块ID;DWORD cntThreads; // 此进程开启的线程计数;DWORD th32ParentProcessID;// 父进程ID;LONG pcPriClassBase; // 线程优先权;DWORD dwFlags; // 保留;WCHAR szExeFile[MAX_PATH]; // 进程全名;} PROCESSENTRY32;

5) Process32Next

Process32Next是一个进程获取函数,当我们利用函数CreateToolhelp32Snapshot()获得当前运行进程的快照后,我们可以利用Process32Next函数来获得下一个进程的句柄。

函数原型如下:

Process32Next(Handle hsnapShot,LPPROCESSENTRY32 lppe)

若成功获取了下一个进程的信息则返回TRUE,与CreateToolhelp32Snapshot,Process32First搭配使用,用来枚举当前系统快照相关的所有进程。

6) GetProcessMemoryInfo

用于获取指定进程的内存使用情况。

函数原型如下:

GetProcessMemoryInfo(HANDLE Process,PPROCESS_MEMORY_COUNTERS ppsmemCounters,DWORD cb);

Process是获取内存使用情况的进程句柄。

ppsmemCounters是返回内存使用情况的结构。

cb是结构的大小。

PPROCESS_MEMORY_COUNTERS结构定义如下:


typedef struct _PROCESS_MEMORY_COUNTERS{DWORD  cb;DWORD  PageFaultCount;//缺页中断次数.SIZE_T PeakWorkingSetSize;//使用内存峰值(峰值工作集)SIZE_T WorkingSetSize;//当前使用内存(当前工作集)SIZE_T QuotaPeakPagedPoolUsage;// 使用页面缓存池高峰SIZE_T QuotaPagedPoolUsage;//当前使用页面缓存池SIZE_T QuotaPeakNonPagedPoolUsage;// 使用非分页缓存池高//峰SIZE_T QuotaNonPagedPoolUsage;// 使用非分页缓存池SIZE_T PagefileUsage;// 使用分页文件SIZE_T PeakPagefileUsage;// 使用分页文件高峰} PROCESS_MEMORY_COUNTERS,*PPROCESS_MEMORY_COUNTERS;

7) OpenProcess

OpenProcess 函数用来打开一个已存在的进程对象,并返回进程的句柄。

函数原型如下:

HANDLE OpenProcess(DWORD dwDesiredAccess, //期望得到的访问权限(标志)BOOL bInheritHandle, // 是否继承句柄DWORD dwProcessId// 进程标识符); 

如成功,返回值为指定进程的句柄。

如失败,返回值为空,可调用GetLastError获得错误代码。

8) VirtualQueryEx

用来查询地址空间中内存地址的信息。

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

参数说明如下:

hProcess 进程句柄。

lpAddress 查询内存的地址。

lpBuffer 指向MEMORY_BASIC_INFORMATION结构的指针,用于接收内存信息。

dwLength MEMORY_BASIC_INFORMATION结构的大小。

返回值:函数写入lpBuffer的字节数,如果不等于sizeof(MEMORY_BASIC_INFORMATION)表示失败

MEMORY_BASIC_INFORMATION定义如下:


typedef struct _MEMORY_BASIC_INFORMATION {PVOID  BaseAddress;// 与lpAddress参数的值相同,但是四舍五入为页面的边界值。PVOID  AllocationBase;// 指明用VirtualAlloc函数分配内存区域的基地址。lpAddress在该区域之内。DWORD  AllocationProtect;//区域被初次保留时赋予的保护属性SIZE_T RegionSize;// 用于指明内存块从基地址即BaseAddress开始的所有页面的大小(以字节为计量单位)这些页面与含有用LpAddress参数设定的地址的页面拥有相同的保护属性、状态和类型。DWORD State;//页面的状态,有三种可能值://MEM_COMMIT、MEM_FREE和MEM_RESERVE,//committed:已在物理介质上存在//free:空闲的,不可访问,可分配的。//reserved:预留的DWORD  Protect;//页面的属性,其可能的取值与//AllocationProtect相同DWORD  Type;//该内存块的类型,有三种可能值://MEM_IMAGE、MEM_MAPPED和MEM_PRIVATE} MEMORY_BASIC_INFORMATION,*PMEMORY_BASIC_INFORMATION;

9) ReadProcessMemory

读取指定进程私有地址空间中指定地址的数据。

函数原型如下:


BOOL ReadProcessMemory(HANDLE hProcess,// [in]远程进程句柄。被读取者,若为//INVALID_HANDLE_VALUE,则目标进程为自身进程PVOID pvAddressRemote,// [in]远程进程中的内存地址。从何处读取PVOIDpvBufferLocal, // [out]本地进程中内存地址. 函数将读取的//内容写入此处DWORD dwSize, // [in]要传送的字节数。要写入多少PDWORDpdwNumBytesRead//[out]实际传送的字节数. 函数返回时报告//实际写入多少);

10) WriteProcessMemory

将数据写到指定进程的指定地址。

函数原型和参数说明基本同ReadProcessMemory,只是数据流向不同。

11) VirtualAllocEx

VirtualAllocEx函数的作用是在指定进程的虚拟空间保留或提交内存区域,除非指定MEM_RESET参数,否则将该内存区域置0。

函数原型如下:


LPVOID VirtualAllocEx(HANDLE hProcess,// 申请内存所在进程的句柄LPVOIDlpAddress,// 保留页面的内存地址;一般用NULL自//动分配 。SIZE_TdwSize,// 欲分配的内存大小,字节单位;注意实际//分配的内存大小是页内存大小的整数倍DWORD flAllocationType,//DWORD flProtect//);

执行成功就返回分配内存的首地址,否则返回NULL。

12) VirtualFreeEx

在其他进程中释放申请的虚拟内存空间。


BOOL WINAPI VirtualFreeEx(HANDLEhProcess,// 目标进程的句柄。该句柄必须拥有//PROCESS_VM_OPERATION 权限。LPVOIDlpAddress,//指向要释放的虚拟内存空间首地址的指//针。如果 dwFreeType 为 MEM_RELEASE, 则该参数必须为//VirtualAllocEx的返回值SIZE_T dwSize,// 虚拟内存空间的字节数。//如果 dwFreeType 为MEM_RELEASE,则 dwSize 必须为0 . //按 VirtualAllocEx审请时的大小全部释放。//如果dwFreeType 为MEM_DECOMMIT, 则释放从lpAddress //开始的一个或多个字节 ,即 lpAddress +dwSizeDWORDdwFreeType);

返回值:

成功:为非0值(零)。

失败:为0(零)。可调用GetLastError获取失败的消息。

13) GetModuleFileNameEx

获取指定进程已加载模块的文件的完整路径


DWORD WINAPI GetModuleFileNameEx(_In_   HANDLE  hProcess,//指定进程的句柄_In_opt_ HMODULE  hModule,// 要处理的模块_Out_   LPTSTR  lpFilename,// 指向接收到模块的完全限定路//径null结尾的缓冲区。如果文件名的大小比n大小参数的值越大,//文件名被截断_In_   DWORD   nSize//nSize是这个缓冲区的大小,用于防止溢出);

如果函数成功,返回值指定复制到缓冲区中的字符串的长度。如果函数失败,返回值是零。若想获得更多的错误信息,调用GetLastError。

14) VirtualProtectEx


BOOL WINAPI VirtualProtectEx(_In_  HANDLE hProcess,_In_  LPVOID lpAddress,_In_  SIZE_T dwSize,_In_  DWORD  flNewProtect,_Out_PDWORD lpflOldProtect);

内存修改实现的基本原理:

在权限足够的情况下,打开想要修改的进程,我们可以获得其私有地址空间所有内存块的信息,然后进行筛选作为搜索的候选块。首次搜索时,使用ReadProcessMemory读取这些候选块,根据选择的内存对齐原则进行遍历,把读取到的数据转化为搜索类型,比对是否一致,若一致,将其地址信息记录在相应的数据结构中(search_info)。当想要搜索的数据变动后,再次搜索,这时只按照search_info记录的地址进行读取,这样就能逐渐搜索出想要找的变量。若确定了想要找的变量,将想要写入的值转换为字符串(本项目中使用unsigned char*),使用WriteProcessMemory写入,这样就完成了修改。

内存管理及修改API(made by myself)说明:

1) 获取机器分页大小

 DWORD  WINAPI getSystemPageSize();

2) 提升权限

 BOOL WINAPIEnablePrivileges();

3) 获取进程列表

/* OUTvector<PROCESS_MEMORY_COUNTERS>&OUTvector<PROCESSENTRY32>&return string描述进程信息*/string  WINAPIgetProcessList(vector<PROCESS_MEMORY_COUNTERS>& vct_pmc,vector<PROCESSENTRY32>& vct_pe32);

4) 首次搜索

/*vector<MEMORY_BASIC_INFORMATION>memInfo,筛选出的待搜索块search_info&v_s_rslt,保存搜索结果的数据结构HANDLE handle,进程句柄DWORD s_type,搜索类型,支持FLOAT_T INT_TDOUBLE_T STRING_TDWORD pagesize,系统页面大小char* charexpval,要搜索的STRING_T值int *intexpval,要搜索的INT_T值float*floatexpval,要搜索的FLOAT_T值double*doubleexpval要搜索的DOUBLE_T值*/BOOL WINAPIsearchAll(vector<MEMORY_BASIC_INFORMATION> memInfo, search_info&v_s_rslt, HANDLE handle, DWORD s_type, DWORD pagesize, char* charexpval,int *intexpval, float *floatexpval, double *doubleexpval);

5) 附加到进程(打开进程)

/*DWORD IN pid,要打开的进程DWORD OUT hProcess成功后返回的进程句柄*/BOOL WINAPIattachToProcess(DWORD Pid, HANDLE & hProcess);

6) 按属性筛选要搜索的内存块

/*
HANDLE INhProcess 进程句柄vector OUTmemInfo 保存内存块信息的数据vector<MEMORY_BASIC_INFORMATION>DWORD INstateFilter 按state类型过滤内存块DWORD IN typeFilter 按type类型过滤内存块DWORD IN protectFilter 按protect类型过滤内存块*/BOOL WINAPI getProcessMemBlockEX(HANDLE hProcess, vector<MEMORY_BASIC_INFORMATION>& memInfo,DWORD stateFilter, DWORD typeFilter, DWORD protectFilter, DWORD* memSize);

7) 申请一块虚存区

/*int size,申请的虚存大小HANDLE handle进程句柄*/LPVOID WINAPI AllocVirtual(intsize, HANDLE handle);

8) 释放虚存区

/*LPVOID Buffer,释放的虚地址指针HANDLE handle进程句柄*/BOOL WINAPIFreeVirtual(LPVOID Buffer, HANDLE handle);

9) 再次搜索

/*HANDLE hProcess,//进程句柄search_info &v_s_rslt,//保存上次搜索信息的数据结构LPVOIDexpected_val//新的搜索值,对字符串传入char*而不是string **/BOOL WINAPI nextScan(HANDLE hProcess,search_info & v_s_rslt, LPVOID expected_val);

10) 将数据写入指定线性地址

/*
HANDLEhProcess,//要写入的进程LPVOID address,//要写入的地址void * write_val,//要写入的变量指针,只能是string,int,double,float四种DWORD valType,//写入的变量类型,可选值见头文件宏定义int stringAppendZeroNUM,//字符串后追加的字符数量char endSet//字符串后追加字符,内存中string和char结尾不同*/BOOL WINAPI writeData(HANDLE hProcess, LPVOID address,void * write_val, DWORD valType, int stringAppendZeroNUM,charendSet);

11) 设置mylasterror的函数

 voidmySetLastError(DWORD e);

12) 内部的error code函数,通过此获取错误类型,头文件有错误类型定义

 DWORDmyGetLastError();

13) 必要的初始化函数,必须先初始化,目前只有检查对齐类型操作

 voidinit_for_mem_manage();

14) 检查机器内存对齐类型

 voidcheck_mem_align_scheme();

15) 获取内存使用的系统信息

string WINAPI getSystemInfo_toString();

16) 获取内存性能使用相关信息

string WINAPI getPerformanceInfo_toString();

17) 获取进程私有地址空间信息

string WINAPI processInfoToString(PROCESSENTRY32 pe, PROCESS_MEMORY_COUNTERS*pmc);

内存修改API相关数据结构的定义:

typedef struct search_result_i{LPCVOIDbaseAddress;//基址DWORDoffset;//偏移量inti_val;//内存中的值}search_i_list;typedef struct search_result_d{LPCVOIDbaseAddress;DWORDoffset;doubled_val;}search_d_list;typedef struct search_result_f{LPCVOIDbaseAddress;DWORDoffset;floatf_val;}search_f_list;typedef struct search_result_s{LPCVOIDbaseAddress;DWORDoffset;strings_val;}search_s_list;typedef struct search_info{DWORDs_type;//搜索类型vector<struct search_result_i> i_list;//int搜索结果vector<struct search_result_d> d_list;//double搜索结果vector<struct search_result_f> f_list;//float搜索结果vector<struct search_result_s> s_list;//字符串搜索结果inti_exp;//搜索值doubled_exp; //搜索值floatf_exp; //搜索值strings_exp; //搜索值}search_info;//保存搜索信息的数据结构

内存修改API调用的基本流程:

EnablePrivileges提权

init_for_mem_manage必要的初始化工作,目前只是判断大小端机器

getSystemPageSize获取系统页大小,后面做参数

getProcessList获取进程列表

用户输入pid

attachToProcess附加到指定进程

getProcessMemBlockEX获取指定state,type,protect属性的内存块,

后面搜索只会在这些块里搜

用户选择类型和搜索值

searchAll首次搜索,结果保存在struct search_info

再次输入

nextScan再次搜索

当确认搜索到想要的数据时,根据其地址使用writeData进行修改。

windows平台下的进程内存修改相关推荐

  1. Windows平台下的内存泄漏检测

    Windows平台下的内存泄漏检测 一.使用_CrtDumpMemoryLeaks定位内存泄露 添加对应的头文件 转储内存泄漏信息 程序任意点退出 指定调试信息输出 二.定位具体内存泄露位置 内存快照 ...

  2. 深入探究Windows平台客户端安全问题-进程地址空间入侵和白加黑高阶利用

    标 题: 深入探究Windows平台客户端安全问题-进程地址空间入侵和白加黑高阶利用 时 间: 2014-09-08,00:03:51 前言 为了避免被读者骂"标题党",笔者在文章 ...

  3. Windows平台下安装Hadoop

    http://www.cnblogs.com/kinglau/archive/2013/08/20/3270160.html 1.安装JDK1.6或更高版本 官网下载JDK,安装时注意,最好不要安装到 ...

  4. Windows 平台下面的IOCP技术 Linux下面Epoll 还有FreeBSD下面Kqueue的应用了。跨平台库行业里面最出名的莫过于ACE、ASIO(Boos公司)两大支持库支持IOCP

    http://wenku.baidu.com/view/4117460502020740be1e9b3c.html 游戏服务器集群 自从2003年开发VOIP Radius Server以及修改Gnu ...

  5. Windows平台下使用Active Directory对Subversion进行权限控制(非完美解决方案)

    Windows平台下使用Active Directory对Subversion进行权限控制(非完美解决方案) Windows平台下使用Active Directory对Subversion进行权限控制 ...

  6. Windows平台下搭建Git服务器

    Git没有客户端服务器端的概念,但是要共享Git仓库,就需要用到SSH协议(FTP , HTTPS , SFTP等协议也能实现Git共享,此文档不讨论),但是SSH有客户端服务器端,所以在window ...

  7. Windows平台下搭建Git服务器的图文教程

     Windows平台下搭建Git服务器的图文教程 来源:互联网 作者:佚名 时间:08-27 11:42:10 [大 中 小] Git没有客户端服务器端的概念,但是要共享Git仓库,就需要用到SS ...

  8. linux qt 获取u盘名称,QT windows平台下获取U盘 QComboBox显示U盘盘符

    在windows平台下获取U盘信息,可以调用windows API函数比较方便.本来想用qt 来写的,网上关于这方面的代码比较多,但按照提示的步骤来写的就是无法编译,我也不知道为什么.如果有知道的朋友 ...

  9. linux查看进程的内存使用情况,[转]linux下查看进程内存使用情况

    动态查看一个进程的内存使用 1.top命令 top -d 1 -p pid [,pid ...] //设置为delay 1s,默认是delay 3s 如果想根据内存使用量进行排序,可以shift + ...

最新文章

  1. 我去!一周内,在闲鱼上被疯狂转了2.6万次的架构课程,现在免费!(限时领)...
  2. EMNLP'21 | 基于相似样本检索的在线更新机器翻译系统
  3. android activity之间传值
  4. 10备忘录没有表格_iPhone的【备忘录】竟然有这么多功能
  5. 【JVM性能调优】使用jstack找出最耗CPU的java线程
  6. MYSQL 从头开始-1
  7. Mysql中int(M)的含义
  8. 引用文献管理软件Mendeley
  9. codeforce438D The Child and Sequence
  10. 蓝桥杯 ALGO-22算法训练 数的划分
  11. BZOJ.2816.[ZJOI2012]网络(LCT)
  12. python使用内置函数方法和桶排序方法实现随机数去重、排序输出
  13. P2986 [USACO10MAR]伟大的奶牛聚集(思维,dp)
  14. Atitit 艾提拉博士带来“深度?广度?高度 人员的职业发展之路 ”的主题分享。 目录 1.1. 技术团队气氛的区别 开发架构模式 2 1.2. 技术人员的职业发展有哪些路线? 3 1.3. 主
  15. perl 语言(数组)
  16. 2021新款外卖返利系统源码,美团/饿了么返利三级分销源码外卖返利源码下载
  17. RabbitMQ:Plugin configuration unchanged;解决RabbitMQ启动问题,Win10用户中文问题解决
  18. 让我十二点催她睡觉,我用 Python 轻松解决
  19. 基于opencv的倾斜文本行的校正
  20. Testin云测平台操作步骤

热门文章

  1. phoenix学习四节 Secondary Indexing 二级索引
  2. java transient 关键字_java中的transient关键字详解
  3. Altium Designer(AD) PCB Keep-out-layer和Mechanical 1的区别及使用方法
  4. 样本画册设计基本小知识
  5. html5 section
  6. jsplumb设置锚点_说明 · rysinal/jsPlumb Wiki · GitHub
  7. 专访美满科技CEO:用技术和创新让客户服务更简单
  8. 从键盘输入两个数求他们的最大公约数
  9. 金山词霸2005无法用鼠标取词
  10. Softmax的通俗讲解