內存的操作,瞭解了個大概

c#對內存的操作~~
最近一直不務正,老打算用C#寫個外掛出來。
這方面對C#來說是個弱項,但並不表示無法做到。
下面寫個簡單的例子,和大家交流一下。
以windows中的掃雷爲例,比如說讀取雷的數量。
1.首先導入API(對底層的操作都要用API):
[DllImport("kernel32.dll")]
private static extern IntPtr OpenProcess(uint flag,bool ihh,int processid);
openprocess是用來打開進程的,要對系統中的某一個進程進行讀寫,必須先打開進程。第一個參數爲打開的標記,例如全權打開進程爲0x1F0FFF(16進制數)。第二個沒什麼好說的,第三個參數爲要打開進程的PID.現在第一個參數爲常量,就差第三個參數PID了,PID怎麼得到呢?用.net的方法是:
Process[] p = Process.GetProcessesByName("winmine");
p[0].Id;這樣就能返回相應進程的PID了。
它的返回值爲相應進程的句柄。它是下面API將要使用的。
IntPtr handle = OpenProcess(0x1F0FFF, false, processid);

2.第二步就可以讀取了。
[DllImport("kernel32.dll")]
private static extern bool ReadProcessMemory(IntPtr handle,int address,int[] buffer,int size,int[] nor);
readrpocessmemory可以用來讀取某個進程地址的值。第一個參數就是上面API返回的值;第二個是要讀取進程的地址;第三個參數爲讀取出的內容,要求爲指針,它相當於一個out類型的參數,讀出的內容並不是以函數返回值的方式得到;第四個爲讀取值的字節大小,int爲4,byte爲1,就是這樣;第五個參數也要求爲一個指針,一般用數組就可以了,數組名相當於指針。
int[] result=new int[1];
int[] lpdw=new int[1];//這樣定義就可以了,一個元素的數組,能起到指針的作用。
bool b = ReadProcessMemory(handle, 0x1005194, result, 4, lpdw);
讀出的東西到哪去了,result數組中的內容就是了。由於只有一個元素,result[0]就是你要的東西了。

基本上做外掛,讀出內存是最基本的東西,一個遊戲中人物的生命值,真氣值,等等基礎的信息如果不知道,下一步就更加無法進行了。
除了上面的兩個API,還可能用到以下幾個:
寫內存:
        [DllImport("kernel32.dll")]
        public static extern Int32 WriteProcessMemory(
            IntPtr hProcess,
            IntPtr lpBaseAddress, 
            [In, Out] byte[] buffer, 
            int size, 
            out IntPtr lpNumberOfBytesWritten
            );
創建線程:
[DllImport("kernel32", EntryPoint = "CreateRemoteThread")]
        public static extern int CreateRemoteThread(
            int hProcess,
            int lpThreadAttributes,
            int dwStackSize,
            int lpStartAddress,
            int lpParameter,
            int dwCreationFlags,
            ref int lpThreadId
            );

開闢指定進程的內存空間:
        [DllImport("Kernel32.dll")]
        public static extern System.Int32 VirtualAllocEx(
         System.IntPtr hProcess,
         System.Int32 lpAddress,
         System.Int32 dwSize,
         System.Int16 flAllocationType,
         System.Int16 flProtect
         );
釋放內存空間:
        [DllImport("Kernel32.dll")]
        public static extern System.Int32 VirtualFreeEx(
        int hProcess,
        int lpAddress,
        int dwSize,
        int flAllocationType
        );
關閉句柄:
        [DllImport("kernel32.dll", EntryPoint = "CloseHandle")]
        public static extern int CloseHandle(int hObject);
------------------------------------------------------------------------------------
------------------------------------------------------------------------------------
   下面說下sendmessage的問題,其實不論用API還是.net的方法,對現在的遊戲都難以發揮作用,爲什麼呢?因爲早就被屏掉了。sendmessge與sendkey其實本質上是同一個東西。現在流行的模擬按鍵的庫winio大家可能聽說過,它應用了驅動程序的相關技術,使自己運行在ring0級別上(一般程序運行在ring3級上,一些驅動程序,操作系統核心模塊才運行在ring0級),這樣能繞過遊戲的檢測。不過由於winio的名氣太大,使用太大衆化,所以最近可能也不能用了。

   現在流行的方法是使用CALL,CALL就是調用遊戲本身內部的函數,比如攻擊怪物,調用相應的攻擊函數,而不是發送按鍵。不過找CALL是個需要知識的耐心的過程。流行的工具是OD,本菜鳥還沒研究透,另外CE這個工具也是必不可少的,例如上面的地址0x1005194就是用CE找出來的,CE怎麼使用呢,再說就扯的太遠了,網上有不少的教程,有興趣的可以查一下。


整理一下,啓動VS,創建一個button和一個label,在form1中複製以下代碼,然後啓動程序,啓動掃雷,就可以看到了
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace saolei
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private const uint PROCESS_ALL_ACCESS = 0x1f0fff;
        [DllImport("kernel32.dll")]
        public extern static IntPtr OpenProcess(UInt32 dwdesiredaccess, int binherithandle, Int32 dwprocessid);
        [DllImport("kernel32.dll")]
        public extern static bool ReadProcessMemory(IntPtr hprocess, UInt32 lpbaseaddress, int[] plbuffer, UInt32 nsize, Int32[] lpnbr);
        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            Process[] p = Process.GetProcessesByName("winmine");
            IntPtr handle = OpenProcess(0x1F0FFF, 0, p[0].Id);
            int[] result = new int[1];
            int[] lpdw = new int[1];
            bool b = ReadProcessMemory(handle, 0x1005194, result, 4, lpdw);
            this.label1.Text = result[0].ToString();
        }
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章