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