• 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

  • 相关阅读:
    Android 逐帧动画isRunning 一直返回true的问题
    android动画学习
    android信号强度
    在代码中创建Drawable资源
    通过ComponentName获取相应的Widget
    EditText和输入法相关的问题
    Android 沉浸式状态栏
    安防设置功能出现的错误
    自定义seekbar
    android 新控件 AppBarLayout 使用
  • 原文地址:https://www.cnblogs.com/tango/p/2979827.html
Copyright © 2020-2023  润新知