C# winform 魔兽MH全图制作教程(2):创建项目与关键类
一、开发开始:
- 准备好VS,新建一个winform项目,窗体命名为WarMapHack 如图:
- 开始写代码,上一章讲到把魔兽游戏窗体和进程等信息看做一个对象,用来保存所有信息,且属性为封装静态.
War.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text;
namespace WarMacpHack { /// <summary>/// 魔兽游戏信息 /// </summary> class War { //初始化.. public static void WarInit() { War.GameName = "Warcraft III"; War.ProcessName = "War3"; War.DllName = "game.dll"; War.State = "未运行"; War.BaseAddre = IntPtr.Zero; War.HWnd = IntPtr.Zero; War.PId = 0; War.Version = ""; }
//游戏名称 private static string gameName;
//进程名称 private static string processName;
//进程id private static int pId;
//版本信息 private static string version;
//游戏主要进程模块名称 private static string dllName;
//窗体句柄 private static IntPtr hWnd;
//游戏内存基址 private static IntPtr baseAddre;
//游戏状态提示信息 private static string state;
public static string GameName { get { return War.gameName; } set { War.gameName = value; } }
public static string ProcessName { get { return War.processName; } set { War.processName = value; } }
public static int PId { get { return War.pId; } set { War.pId = value; } }
public static string Version { get { return War.version; } set { War.version = value; } }
public static string DllName { get { return War.dllName; } set { War.dllName = value; } }
public static IntPtr HWnd { get { return War.hWnd; } set { War.hWnd = value; } }
public static IntPtr BaseAddre { get { return War.baseAddre; } set { War.baseAddre = value; } }
public static string State { get { return War.state; } set { War.state = value; } }
} } - 有了魔兽实体,剩下就是获得信息,根据上一章的结构图可以看到我们主要写内存的API函数,这个函数位于Kernel32.dll 库文件中,这个文件位于:C:\WINDOWS\system32
- 那么我们如何在C#中调用这个API:WriteProcessMemory?
二、WriteProcessMemory最主要参数来源:
-
- 引入命名空间:
using System.Runtime.InteropServices;
[DllImport("kernel32.dll", EntryPoint = "WriteProcessMemory")]
public static extern bool WriteProcessMemory(IntPtr hProcess, int lpBaseAddress, Byte[] lpBuffer, int nSize, out int lpNumberOfBytesWritten);
- 想要成功必需知道参数的意义,WriteProcessMemory返回为true的话代表方法写入内存成功。
- 第一个参数:IntPtr 类型的 hProcess , 我们需要打开一个进程句柄,那么这个句柄的值从何而来?会百度或MSDN的就会知道:由 OpenProcess 返回的进程句柄。而OpenProcess这个函数也为系统中的API,所以我们一下子用到两个API。因此Api.cs中声明如下:
/// <summary>
/// 写内存byte[]
/// </summary>
/// <param name="hProcess"></param>
/// <param name="lpBaseAddress"></param>
/// <param name="lpBuffer"></param>
/// <param name="nSize"></param>
/// <param name="lpNumberOfBytesWritten"></param>
/// <returns></returns>
[DllImport("kernel32.dll", EntryPoint = "WriteProcessMemory")]
public static extern bool WriteProcessMemory(IntPtr hProcess, int lpBaseAddress, Byte[] lpBuffer, int nSize, out int lpNumberOfBytesWritten);
/// <summary>
/// 进程提权
/// </summary>
/// <param name="dwDesiredAccess"></param>
/// <param name="bInheritHandle"></param>
/// <param name="dwProcessId"></param>
/// <returns></returns>
[DllImport("kernel32.dll", EntryPoint = "OpenProcess")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); - 参数介绍: dwDesiredAccess //渴望得到的访问权限(标志) 最大权限为:0x1F0FFF ; 为了方便可以声明变量为: //int PROCESS_ALL_ACCESS = 0x1F0FFF;
bInheritHandle //是否继承句柄,传值为:false
dwProcessId: // 进程标示符(也就是我们在War.cs中定义的变量pId,关于获得会在以后的章节中讲到)
如果成功的话两个函数就可以被正常的调用,并且返回正确有效的值,其实这些API还不能满足我们的需要,比如我们还需要在后进行关闭句柄:CloseHandle 因为它不会被C#垃圾回收,所以还需要声明:/// <summary>
/// 关闭一个内核对象
/// </summary>
/// <param name="hObject"></param>
[DllImport("kernel32.dll", EntryPoint = "CloseHandle")]
public static extern void CloseHandle(IntPtr hObject); - 有了以上API,基本上可以完成WriteProcessMemory的第一参数,下面看第二参数,也就是lpBaseAddress内存基本地址,因为它需要从魔兽的进程中获得Game.dll的信息,Game.dll包含游戏的版本信息,内存基址...这些信息对于我们完成全图功能很重要。
/// <summary>
/// 进程快照
/// </summary>
/// <param name="flags"></param>
/// <param name="processid"></param>
/// <returns></returns>
[DllImport("Kernel32.dll", EntryPoint = "CreateToolhelp32Snapshot")]
public static extern IntPtr CreateToolhelp32Snapshot(uint flags, uint processid);
/// <summary>
/// 得到进程信息
/// </summary>
/// <param name="Handle"></param>
/// <param name="Me"></param>
/// <returns></returns>
[DllImport("Kernel32.dll", EntryPoint = "Module32First")]
public static extern int Module32First(IntPtr Handle, ref MODULEENTRY32 Me);
[DllImport("Kernel32.dll", EntryPoint = "Module32Next")]
public static extern int Module32Next(IntPtr Handle, ref MODULEENTRY32 Me);
第一个参数:flags 表示要获取快照中的哪类信息,有以下值:
我们只用到: TH32CS_SNAPMODULE - 在快照中包含在processid中指定的进程的所有的模块,processid也就是第二个参数。 值: TH32CS_SNAPMODULE = 8;
第二个参数:processid (同样是进程id,也就是War.cs中的字段pId)
Module32First 和 Module32Next:
两个函数的第一个参数都为:CreateToolhelp32Snapshot返回的数据类型IntPtr的值
Module32First 和 Module32Next 两个函数像一对兄弟组合取出我们想要的Module模块信息,当然它们不只能获得这些,所以两个函数的第二个参数。数据结构体:MODULEENTRY32 用来存储两个兄弟拿到的所有信息。而且是ref 引用传递,ref的参数会在方法中更改其值以后反应到作为参数值的变量上,就是说会将获得信息直接设置到结构中。 将此结构同样定义到Api.cs:/// <summary>
/// 枚举进程信息
/// </summary>
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct MODULEENTRY32
{
public uint dwSize;
public uint th32ModuleID;
public uint th32ProcessID;
public uint GlblcntUsage;
public uint ProccntUsage;
IntPtr modBaseAddr;
public uint modBaseSize;
public IntPtr hModule;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string szModule;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szExePath;
}
好,截止到目前我们的Api.cs所需要的写内存函数声明完毕,这一节也讲解了WriteProcessMemory函数最主要的前两个参数,关于第三个参数也就是byte[]类型,它代表内存基址的偏移数值,关于全图等功能的偏移会在以后章节中全部贴出,包括显示单位,清除迷雾,分辨幻想.等.....
结构图更新如下:
下一章将讲解:WriteProcessMemory 的调用和其他函数的实现方式。
该章节源代码下载:W.je(zlawliet)作品,转载注明出处: http://www.cnblogs.com/tango/articles/2409329.html
- 引入命名空间: