0x01
使用kernel32.dll中的ReadProcessMemory和WriteProcessMemory内存函数可以完成对程序内存的读写。
我在写读内存整数型、写内存整数型和读内存小数型三个方法的时候测试可以正确读写进程地址的内存,但是我在写内存小数型的时候遇到了问题,记录一下。
0x02
ReadProcessMemory函数原型如下:
BOOL ReadProcessMemory(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead
);
hProcess 远程进程句柄,指被读取者进程句柄
lpBaseAddress 远程进程中内存地址,指具体从何处读取
lpBuffer 本地进程中内存地址,指函数将读取的内容写入此处
nSize 要传送的字节数,指要写入多少
lpNumberOfBytesRead 实际传送的字节数,指函数返回时报告实际写入多少
WriteProcessMemory函数原型如下:
BOOL WriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesWritten
);
0x03
测试类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace C_Sharp读写内存
{
public abstract class test
{
[DllImport("kernel32.dll", EntryPoint = "ReadProcessMemory")]
public static extern int _MemoryReadByteSet(int hProcess, int lpBaseAddress, byte[] lpBuffer, int nSize, int lpNumberOfBytesRead);
[DllImport("kernel32.dll", EntryPoint = "ReadProcessMemory")]
public static extern int _MemoryReadInt32(int hProcess, int lpBaseAddress, ref int lpBuffer, int nSize, int lpNumberOfBytesRead);
[DllImport("kernel32.dll", EntryPoint = "WriteProcessMemory")]
public static extern int _MemoryWriteByteSet(int hProcess, int lpBaseAddress, byte[] lpBuffer, int nSize, int lpNumberOfBytesWritten);
[DllImport("kernel32.dll", EntryPoint = "WriteProcessMemory")]
public static extern int _MemoryWriteInt32(int hProcess, int lpBaseAddress, ref int lpBuffer, int nSize, int lpNumberOfBytesWritten);
[DllImport("kernel32.dll", EntryPoint = "GetCurrentProcess")]
public static extern int GetCurrentProcess();
[DllImport("kernel32.dll", EntryPoint = "OpenProcess")]
public static extern int OpenProcess(int dwDesiredAccess, int bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll", EntryPoint = "CloseHandle")]
public static extern int CloseHandle(int hObject);
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
public static extern int _CopyMemory_ByteSet_Float(ref float item, ref byte source, int length);
const int PROCESS_POWER_MAX = 2035711;
///
/// 读内存整数型
///
/// 进程ID
/// 0x地址
/// 0失败
public static int ReadMemoryInt32(int pID, int bAddress)
{
int num = 0;
int handle = GetProcessHandle(pID);
int num3 = test._MemoryReadInt32(handle, bAddress, ref num, 4, 0);
test.CloseHandle(handle);
if (num3==0)
{
return 0;
}
else
{
return num;
}
}
///
/// 写内存整数型
///
/// 进程ID
/// 0x地址
/// 写入值
/// false失败 true成功
public static bool WriteMemoryInt32(int pID, int bAddress,int value)
{
int handle = GetProcessHandle(pID);
int num2 = test._MemoryWriteInt32(handle, bAddress, ref value, 4, 0);
test.CloseHandle(handle);
return num2 != 0;
}
///
/// 读内存小数型
///
/// 进程ID
/// 0x地址
/// 0失败
public static float ReadMemoryFloat(int pID, int bAddress)
{
//byte[] array = test.GetVoidByteSet(4);
byte[] array = new byte[4];//不取空字节集也可以正确转换成单精度小数型
int handle = GetProcessHandle(pID);
int temp = test._MemoryReadByteSet(handle, bAddress, array, 4, 0);
if (temp == 0)
{
return 0f;
}
else
{
return test.GetFloatFromByteSet(array, 0);
}
}
///
/// 写内存小数型
///
/// 进程ID
/// 0x地址
/// 写入数据
/// false失败
public static bool WriteMemoryFloat(int pID, int bAddress, float value)
{
//byte[] byteSet = test.GetByteSet(value);
byte[] byteSet = BitConverter.GetBytes(value);//https://msdn.microsoft.com/en-us/library/yhwsaf3w
//byte[] byteSet = Encoding.GetEncoding("gb2312").GetBytes(value.ToString());
return test.WriteMemoryByteSet(pID, bAddress, byteSet, 0);
}
///
/// 写内存字节集
///
/// 进程ID
/// 0x地址
/// 字节数据
/// 写入长度 0代表字节数据的长度
/// false失败
private static bool WriteMemoryByteSet(int pID, int bAddress, byte[] value, int length=0)
{
int handle = test.GetProcessHandle(pID);
int nSize = (length == 0) ? value.Length : length;
int tmp = test._MemoryWriteByteSet(handle, bAddress, value, nSize, 0);//byte[]属于引用类型 引用类型不用ref也是以传址方式进行运算
//test.CloseHandle(pID);
return tmp != 0;
}
///
/// 取空白字节集
///
///
///
public static byte[] GetVoidByteSet(int num)
{
if (num<=0)
{
num = 1;
}
string text = "";
for (int i = 0; i < num; i++)
{
text += "0";
}
return Encoding.UTF8.GetBytes(text);
}
///
/// 取进程句柄
///
/// 进程ID
/// 进程句柄
public static int GetProcessHandle(int pID)
{
if (pID == -1)
{
return test.GetCurrentProcess();
}
else
{
return test.OpenProcess(PROCESS_POWER_MAX, 0, pID);
}
}
///
/// 字节集转小数型
///
/// 字节集
/// 索引
///
public static float GetFloatFromByteSet(byte[] sourceValue, int index)
{
float result = 0f;
test._CopyMemory_ByteSet_Float(ref result, ref sourceValue[index], 4);
return result;
}
///
/// 获取字节集
///
/// 需要转换到字节集的数据
///
public static byte[] GetByteSet(float data)
{
return Encoding.UTF8.GetBytes(data.ToString());
}
}
}
0x04
使用写内存字节集来写入小数时注意,要将float类型的参数用BitConvert.GetBytes(value)转换成字节数组,而不能使用Encoding.UTF8.GetBytes(value.ToString())将字符串中所有字符编码成一个字节序列。
这样会导致我们写入进程指定地址的值不是小数型,而是文本型。用Cheat Engine可以很直观的观察出来。
0x05
BitConvert类传送门:https://msdn.microsoft.com/zh-cn/library/3kftcaf9.aspx