C# winform 魔兽MH全图制作教程(2):创建项目与关键类

C# winform 魔兽MH全图制作教程(2):创建项目与关键类

 

一、开发开始:

  1. 准备好VS,新建一个winform项目,窗体命名为WarMapHack 如图:

  2. 开始写代码,上一章讲到把魔兽游戏窗体和进程等信息看做一个对象,用来保存所有信息,且属性为封装静态.
    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; } }
    } }
  3. 有了魔兽实体,剩下就是获得信息,根据上一章的结构图可以看到我们主要写内存的API函数,这个函数位于Kernel32.dll 库文件中,这个文件位于:C:\WINDOWS\system32

  4. 那么我们如何在C#中调用这个API:WriteProcessMemory

 

二、WriteProcessMemory最主要参数来源:

    1. 引入命名空间:
      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);
    2. 想要成功必需知道参数的意义,WriteProcessMemory返回为true的话代表方法写入内存成功。

    3. 第一个参数: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);
    4. 参数介绍: 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);
      只有一个参数,就是OpenProcess方法返回的值。
    5. 有了以上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);
      CreateToolhelp32Snapshot进程快照。这三个函数的所用也很简单,只是写起来比较麻烦。
      第一个参数: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;
              }
      退出方法前CreateToolhelp32Snapshot返回的句柄同样需要关闭。
      好,截止到目前我们的Api.cs所需要的写内存函数声明完毕,这一节也讲解了WriteProcessMemory函数最主要的前两个参数,关于第三个参数也就是byte[]类型,它代表内存基址的偏移数值,关于全图等功能的偏移会在以后章节中全部贴出,包括显示单位,清除迷雾,分辨幻想.等.....
      结构图更新如下:


      下一章将讲解:WriteProcessMemory 的调用和其他函数的实现方式。
      该章节源代码下载:

      W.je(zlawliet)作品,转载注明出处: http://www.cnblogs.com/tango/articles/2409329.html

原文地址:https://www.cnblogs.com/tango/p/2979827.html