日本好好热aⅴ|国产99视频精品免费观看|日本成人aV在线|久热香蕉国产在线

  • <cite id="ikgdy"><table id="ikgdy"></table></cite>
    1. 西西軟件園多重安全檢測(cè)下載網(wǎng)站、值得信賴的軟件下載站!
      軟件
      軟件
      文章
      搜索

      首頁(yè)編程開(kāi)發(fā)C#.NET → .Net 實(shí)現(xiàn)《植物大戰(zhàn)僵尸》游戲修改器

      .Net 實(shí)現(xiàn)《植物大戰(zhàn)僵尸》游戲修改器

      前往專題相關(guān)軟件相關(guān)文章發(fā)表評(píng)論 來(lái)源:本站整理時(shí)間:2010/12/23 10:49:03字體大。A-A+

      作者:佚名點(diǎn)擊:939次評(píng)論:0次標(biāo)簽: 植物大戰(zhàn)僵尸 修改器

      • 類型:修改器(游戲工具)大。296KB語(yǔ)言:中文 評(píng)分:6.9
      • 標(biāo)簽:
      立即下載
       前不久玩植物大戰(zhàn)僵尸,不停地玩啊玩,也通關(guān)了,準(zhǔn)備開(kāi)始享受一下IMBA的感覺(jué)!巴嫱嫘∮螒颉蹦J街杏嘘P(guān)“誰(shuí)笑到最后”,一來(lái)就有5000的陽(yáng)光,隨你布置,布置完后開(kāi)始攻擊,過(guò)關(guān)挺容易。但是畢竟5000的陽(yáng)光可布置的植物有限,總覺(jué)得不過(guò)癮,于是找來(lái)《金山游俠》改陽(yáng)光數(shù)量。好好享受了幾次imba的感覺(jué)。


        不用說(shuō),我當(dāng)然不甘心用別人的工具,我要自己來(lái)。我選擇.NET Framework 3.5作為該程序的實(shí)現(xiàn)平臺(tái)。

      整個(gè)過(guò)程總結(jié)如下:

      一.獲取具有窗體的進(jìn)程集合

      二.在所選進(jìn)程的私有地址空間內(nèi)查找數(shù)據(jù)

      三.跟蹤所選進(jìn)程的數(shù)據(jù)修改情況,獲得所要修改的數(shù)據(jù)的唯一地址

      四.修改該地址中的數(shù)據(jù)內(nèi)容


      這就好辦了,思路有了,我們就根據(jù)思路來(lái)搜集和整理相關(guān)知識(shí):

      進(jìn)程

      進(jìn)程只是個(gè)被動(dòng)容器,其中包含了很多資源。

      System.Diagnostics命名空間中的 Process類表示進(jìn)程。Process類中的 GetProcesses() 方法可以獲取系統(tǒng)中所有進(jìn)程。判斷MainWindowHandle 是否為空可以確定進(jìn)程是否包含主窗體。


      我當(dāng)前的宿主OS是XP,在32位的 Windwos NT/2000/XP 中,進(jìn)程地址空間有4GB,但卻只能訪問(wèn)其地址空間底部的2GB,另外2GB留給內(nèi)核模式相關(guān)的一些東西用。在這部分可訪問(wèn)的2GB空間中,最低和最高的64KB不能訪問(wèn),于是可訪問(wèn)的地址范圍是:0x00010000 到 0x7ffeffff。


      我們還需要讀和寫(xiě)進(jìn)程的內(nèi)存,System.Diagnostics.Process 提供的方法不能做到。還好,Windows的kernel32庫(kù)中的進(jìn)程相關(guān)API可以幫到:ReadProcessMemory和 WriteProcessMemory,可以將他們表達(dá)為如下C#語(yǔ)句:

      [DllImport("kernel32.dll", SetLastError = true)]

      static extern bool ReadProcessMemory(

      IntPtr hProcess,

      IntPtr lpBaseAddress,

      [Out] byte[] lpBuffer,

      int dwSize,

      out int lpNumberOfBytesRead

      );

      [DllImport("kernel32.dll", SetLastError = true)]

      static extern bool WriteProcessMemory(

      IntPtr hProcess,

      IntPtr lpBaseAddress,

      byte[] lpBuffer,

      uint nSize,

      out int lpNumberOfBytesWritten);



      名字很直觀:讀/寫(xiě)進(jìn)程內(nèi)存。

      兩個(gè)方法簽名基本相同,我這里簡(jiǎn)單解釋一下:

      hProcess :進(jìn)程句柄

      lpBaseAddress:基地址,也就是起始地址(起始位置)

      lpBuffer:從基地址起讀取或要寫(xiě)入的內(nèi)存值

      nSize:讀取或?qū)懭氲臄?shù)量,單位是字節(jié)

      lpNumberOfBytesRead、 lpNumberOfBytesWritten:用作傳出,表示實(shí)際讀取或?qū)懭氲臄?shù)量



      好了!開(kāi)始實(shí)戰(zhàn)吧!



      創(chuàng)建一個(gè)C#的Windows forms項(xiàng)目

      在窗體上我這樣布局:




      為類添加如下幾個(gè)成員:

      List _windowedProcesses = new List();//存放有窗體的進(jìn)程集合

      private List _addrList = new List();//存放作為結(jié)果的地址列表

      bool isFirstSearch = true;//是否是第一次搜索

      Process _selectedProcess;//所選進(jìn)程



      還要獲取有窗體的進(jìn)程并列出來(lái),讓使用者選擇需要的進(jìn)程

      private void RefreshProcessList()

      {

      listBox1.Items.Clear();

      _windowedProcesses.Clear();

      textBox2.Enabled = false;//在沒(méi)得到唯一的地址前不能寫(xiě)入

      foreach (var p in System.Diagnostics.Process.GetProcesses())

      {

      if (p.MainWindowHandle != IntPtr.Zero)//進(jìn)程有窗口

      {

      if (!string.IsNullOrEmpty(p.MainWindowTitle))//窗體名不為空。因?yàn)橛行⿻r(shí)候會(huì)有一些進(jìn)程如iexplorer.exe ,它有窗口,但窗口沒(méi)名稱且沒(méi)顯示。所以應(yīng)該排除一下

      {

      listBox1.Items.Add(p.MainWindowTitle);

      _windowedProcesses.Add(p);

      }

      }

      }

      }

      于是可以在我們的窗體裝載和單擊刷新按鈕時(shí)調(diào)用該方法

      private void Form1_Load(object sender, EventArgs e)

      {

      RefreshProcessList();

      }

      private void btnRefreshPList_Click(object sender, EventArgs e)

      {

      RefreshProcessList();

      }



      為什么要區(qū)別是否是第一次搜索?因?yàn)榈谝淮嗡阉魇窃谡麄(gè)進(jìn)程可訪問(wèn)內(nèi)存范圍內(nèi)查找,而之后的查找是基于第一次找到的地址。這樣做不是唯一的,但是最好的方法。



      下面是搜索按鈕單擊的事件處理代碼:

      private void button1_Click(object sender, EventArgs e)

      {

      if (_selectedProcess == null) return;

      if (isFirstSearch)

      {

      uint baseAddr = 0x00010000;

      uint endAddr = 0x7ffeffff;

      for (uint i = baseAddr; i < endAddr; i += (4 * 1024))

      {

      var addrs = CreateAddrList(new IntPtr(i), int.Parse(textBox1.Text));

      if (addrs !=null )

      _addrList.AddRange( addrs);

      }

      isFirstSearch = false;

      }

      else

      {

      RefreshAddrList(int.Parse(textBox1.Text));

      }



      label2.Text = "找到結(jié)果”+ _addrList.Count.ToString() + "個(gè)";



      if (_addrList.Count == 1)

      textBox2.Enabled = true;

      }

      很明顯CreateAddrList是第一次查找掉用的方法,RefreshAddrList是之后查找調(diào)用的方法。在第一次查找中,我們以4KB作一次跳躍。為什么查找的地址范圍如此本文開(kāi)始已作說(shuō)明,這里就不再贅述。



      好了,現(xiàn)在來(lái)看看CreateAddrList方法:

      private List CreateAddrList(IntPtr baseAddr, int value)

      {

      int bytesRead;

      byte[] buffer = new byte[4096];

      bool ok;

      List result = new List();



      ok = ReadProcessMemory(_selectedProcess.Handle, baseAddr, buffer, 4096, out bytesRead);



      if (!ok)

      return null ;



      int currentVal;

      for (int i = 0; i < 4096 - 3; i++)

      {

      currentVal = BitConverter.ToInt32(buffer, i);

      if (currentVal == value)

      {

      IntPtr addr = new IntPtr(baseAddr.ToInt32() + i);

      result.Add(addr);

      i += 3;

      }

      }

      return result;

      }

      該方法用以創(chuàng)建地址列表。它接受2個(gè)參數(shù),分別是基地址和要查找的值。

      我們用ReadProcessMemory 一次讀取4KB的值,并把它存放在buffer中。由于buffer 是byte[] ,所以需要用BitConverter.ToInt32()把buffer中的一部分值轉(zhuǎn)成Int32以和要查找的值進(jìn)行比對(duì)。

      如果值匹配,則把對(duì)應(yīng)地址添加到該方法的 result中以供方法返回。



      接下來(lái)是RefreshAddrList方法:

      . private void RefreshAddrList(int value)

      {

      var la = _addrList.ToList();

      _addrList.Clear();



      byte[] buffer = new byte[4];

      int bytesRead;



      foreach (var i in la)

      {

      ReadProcessMemory(_selectedProcess.Handle, i, buffer, 4, out bytesRead);

      if (BitConverter.ToInt32(buffer, 0) == value)

      _addrList.Add(i);

      }

      }

      因?yàn)橐鶕?jù)第一次查找的地址結(jié)果進(jìn)行查找并要更新主地址列表,所以要用addrList.ToList()得到一份主地址列表的拷貝。接下來(lái)再在作為第一次搜索結(jié)果的地址表中查找新的值。如果等于之前的值的地址中的數(shù)據(jù)現(xiàn)在還等于新的值,那么就添加到地址列表。

      回看查找按鈕的事件處理代碼可以發(fā)現(xiàn):反復(fù)多次,直到地址列表中只有一個(gè)地址時(shí),就可以確定這就是我們要的地址,此時(shí),我們就可以修改它了。

      private void button2_Click(object sender, EventArgs e)

      {

      int value;

      if (!int.TryParse(textBox2.Text, out value))

      {

      MessageBox.Show("輸入值太大!小心溢出!請(qǐng)重新輸入!");

      return;

      }

      var buffer=BitConverter.GetBytes(value);

      int bytesWritten;

      WriteProcessMemory(_selectedProcess.Handle, _addrList[0], buffer, 4,out bytesWritten);

      }



      哈哈!這樣就完成了。按下F5我又IMBA了一回




      代碼下載:ProgramMemoryEditor

      該程序搜索值的數(shù)據(jù)類型是Int32 ,若我們要修改的程序的某個(gè)數(shù)據(jù)是以其他數(shù)據(jù)類型存儲(chǔ)的,則需要小修改下我們的修改器。

      筆者水平有限,若有疑問(wèn)或更好的建議,務(wù)必不吝賜教。
        植物大戰(zhàn)僵尸
        (41)植物大戰(zhàn)僵尸
        植物大戰(zhàn)僵尸是一款風(fēng)靡全球趣味益智游戲,玩家在游戲中栽種各種類型的植物來(lái)抵御住僵尸們一波波的進(jìn)攻,保衛(wèi)花園。玩法與其他塔防類游戲類似,玩家要放置一系列擁有攻擊或防御功能的植物,來(lái)阻止不斷到來(lái)的僵尸進(jìn)入小屋吃掉住戶的腦子。而游戲場(chǎng)景則是房前草坪,屋后草坪,游泳池,屋頂之一。其中草坪和游泳池還有日夜和是否有霧之分。每個(gè)場(chǎng)景被分為五條或六條軌道,通常僵尸隨機(jī)出現(xiàn)在某一條軌道,并沿直線前進(jìn)。而普通植物也只...更多>>

        相關(guān)評(píng)論

        閱讀本文后您有什么感想? 已有人給出評(píng)價(jià)!

        • 8 喜歡喜歡
        • 3 頂
        • 1 難過(guò)難過(guò)
        • 5 囧
        • 3 圍觀圍觀
        • 2 無(wú)聊無(wú)聊

        熱門評(píng)論

        最新評(píng)論

        發(fā)表評(píng)論 查看所有評(píng)論(0)

        昵稱:
        表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
        字?jǐn)?shù): 0/500 (您的評(píng)論需要經(jīng)過(guò)審核才能顯示)