写在开头:看了一些视频教程,感觉OD为什么别人学个破解那么容易,我就那么难了呢,可能是没有那么多时间吧。
解释:个人见解:所谓内存补丁,即:通过修改运行程序的内容,来达到某种目的的操作。修改使用OpenProcess打开,WriteProcessMemory写入,CloseHandle关闭。部分需要读取数据判断使用:ReadProcessMemory
0x1 看教程
关于有的学习教程,确实要看看视频才能了解别人的操作,或者很简单一个东西,如果没有别人的指导那么自己操作确实不太容易。
0x2 学习到部分概念
肯定不可能一味的模仿,做一样的东西,所以需要学以致用就很关键了。于是乎,用vs2013 c++写了几行代码,用于自己测试,用C#写内存补丁
0x3 网上检索
没有人生而知之,所以网上查询也是很关键的一步,查询哪些内容呢?就是查询C#如何写内存补丁,代码大同小异不过不一定能用。
0x4 代码实践
网上找到的代码也是要在实践中得出能否使用的。所以这一步也是必不可免的。
于是乎有了下面的代码。需要使用OD找到代码的位置即和在内存中和代码的相差位置。
MCF程序 C++ OK方法中
void CMFCTestDlg::OnBnClickedOk() { CString str; GetDlgItemText(IDC_EDIT1, str); if (str == "test123456789"){ ::MessageBox(NULL, L"OK", L"提示", 0); } else{ ::MessageBox(NULL, L"Fail", L"提示", 0); } }
C#程序中调用,首先贴一个帮助类,来源网上。当然对其中添加和修改了部分方法。
public abstract class ApiHelper { [DllImportAttribute("kernel32.dll", EntryPoint = "ReadProcessMemory")] public static extern bool ReadProcessMemory ( IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, int nSize, IntPtr lpNumberOfBytesRead ); [DllImportAttribute("kernel32.dll", EntryPoint = "OpenProcess")] public static extern IntPtr OpenProcess ( int dwDesiredAccess, bool bInheritHandle, int dwProcessId ); [DllImport("kernel32.dll")] private static extern void CloseHandle ( IntPtr hObject ); //写内存 [DllImportAttribute("kernel32.dll", EntryPoint = "WriteProcessMemory")] public static extern bool WriteProcessMemory ( IntPtr hProcess, IntPtr lpBaseAddress, int[] lpBuffer, int nSize, IntPtr lpNumberOfBytesWritten ); //获取窗体的进程标识ID public static int GetPid(string windowTitle) { int rs = 0; Process[] arrayProcess = Process.GetProcesses(); foreach (Process p in arrayProcess) { if (p.MainWindowTitle.IndexOf(windowTitle) != -1) { rs = p.Id; break; } } return rs; } //根据进程名获取PID public static int GetPidByProcessName(string processName, ref IntPtr baseAddress) { Process[] arrayProcess = Process.GetProcessesByName(processName); foreach (Process p in arrayProcess) { baseAddress = p.MainModule.BaseAddress; return p.Id; } return 0; } //根据进程名获取PID public static int GetPidByProcessName(string processName) { Process[] arrayProcess = Process.GetProcessesByName(processName); foreach (Process p in arrayProcess) { return p.Id; } return 0; } //根据窗体标题查找窗口句柄(支持模糊匹配) public static IntPtr FindWindow(string title) { Process[] ps = Process.GetProcesses(); foreach (Process p in ps) { if (p.MainWindowTitle.IndexOf(title) != -1) { return p.MainWindowHandle; } } return IntPtr.Zero; } //读取内存中的值 public static int ReadMemoryValue(int baseAddress, string processName) { try { byte[] buffer = new byte[2]; IntPtr byteAddress = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0); //获取缓冲区地址 IntPtr hProcess = OpenProcess(0x1F0FFF, false, GetPidByProcessName(processName)); ReadProcessMemory(hProcess, (IntPtr)baseAddress, byteAddress, buffer.Length, IntPtr.Zero); //将制定内存中的值读入缓冲区 CloseHandle(hProcess); return Marshal.ReadInt32(byteAddress); } catch { return 0; } } //将值写入指定内存地址中 public static bool WriteMemoryValue(int baseAddress, string processName, int[] value) { IntPtr hProcess = OpenProcess(0x1F0FFF, false, GetPidByProcessName(processName)); //0x1F0FFF 最高权限 bool result = WriteProcessMemory(hProcess, (IntPtr)baseAddress, value, value.Length, IntPtr.Zero); CloseHandle(hProcess); return result; } }
最后是在具体按钮中的调用了。
private string processName = "MFCTest"; // private void button1_Click(object sender, EventArgs e) { IntPtr startAddress = IntPtr.Zero; int pid = ApiHelper.GetPidByProcessName(processName, ref startAddress); if (pid == 0) { MessageBox.Show("哥们启用之前该运行吧!"); return; } int baseAddress = startAddress.ToInt32() + 0x1000; int value = ReadMemoryValue(baseAddress); // 读取基址(该地址不会改变) int address = baseAddress + 0x14F3; // 获取2级地址 value = ReadMemoryValue(address); bool result = WriteMemory(address, new int[] { 144 }); address = address + 0x1; result = WriteMemory(address, new int[] { 144 }); MessageBox.Show(result ? "成功" : "失败"); } //读取制定内存中的值 public int ReadMemoryValue(int baseAdd) { return ApiHelper.ReadMemoryValue(baseAdd, processName); } //将值写入指定内存中 public bool WriteMemory(int baseAdd, int[] value) { return ApiHelper.WriteMemoryValue(baseAdd, processName, value); }
0x5 总结
只有不断学习才能知道新的知识,同时在学习中进步。很多不懂的概念其实很简单。当然前提是你明白以后。
其中注意一点。
测试代码下载:WriteProcessMemory的buffer填入一个数组也是可以的。需要计算长度。然后nSize就是,前面的数组作为几个字节进行使用。
// 重构了些许代码 //将值写入指定内存地址中 public static bool WriteMemoryValue(int baseAddress, string processName, int[] value, int len) { IntPtr hProcess = OpenProcess(0x1F0FFF, false, GetPidByProcessName(processName)); //0x1F0FFF 最高权限 bool result = WriteProcessMemory(hProcess, (IntPtr)baseAddress, value, len, IntPtr.Zero); CloseHandle(hProcess); return result; } // 144=0x90 表示 nop bool result = ApiHelper.WriteMemoryValue(address, processName, new int[] { 144 + 144 * 256 }, 2);