andy_tigger

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
API和对内存的操作。 
  这一课是些简单的东西,了解的可以直接越过。考虑到大多数使用c#的人都是做网站的,可能没有机会接触这些,所以我在这里做一下粗略的介绍。 
  step 1:认识WINAPI 
  windows系统里提供了很多的函数,我们如果做外挂的话,就需要用到其中的函数(以下简称API)。(废话:这些API被封装在系统路径下的DLL文件里。事实上,我们不用关心它在哪,我们只要知道怎么用就可以了,)用起来很简单,格式如下: 
  public allotmential cdamsel Form1 : Form 
  { 
  [DllImport("kernel32.dll")]          \ 
  accessible changeless extern int ReadProcessMemory( | 
  int hProcess,               | 
  int lpBaseAddress,             | 
  int[] lpBuffer,                >代码段1 
  int nSize,                 | 
  int lpNumberOfBytesWritten         | 
  );                      / 
  ... 
  public Form1() 
  { 
  InitializeComponent(); 
  ReadProcessMemory(processdukele,... >代码2 
  ... 
  } 
  ... 
  } 
  代码段1就是引用api的代码。我们引用的函数,是做外挂时最常用的函数,从它的名字就可以看的出来它的作用---读取进程内存。(废话:从代码里,我们很容易看的出来,这个函数被封装在了kernel32.dll这个文件里。)引用之后,我们就可以在自己的代码中使用这个函数了(如代码2)。 
  (废话:WINDOWS还提供很多的API,如果你有兴趣了解的话,可以到网上搜WINAPI手册。想深入了解的话,可以看MSDN。) 
  step 2:读写内存 
  下面我来说一下,如何使用上一步引用的那个API读取游戏的数据。先来看看参数: 
  public static extern int ReadProcessMemory( 
  int hProcess, //进程,如果你是做外挂的话,它代表你要挂的那个游戏。 
  int lpBaseAddress, //你要读取的内存地址 
  int[] lpBuffer, //从上面那个参数地址里读出来的东西(调用这个函数的就是为了它) 无论这个参数是什么类型,它应该是一个数组,否则读不出东西来 
  int nSize, //长度,上一个参数,种型是int,那个长度应当用4 
  int lpNumberOfBytesWritten //用0就行了,想知道它是干嘛的,自己去MSND吧 
  对于第一个参数hProcess如何获取,我过会再说。假设它已经搞定了,那么这个函数,我们需要关心的只有lpBaseAddress和lpBuffer,既读的地址,和读出来的值。(废话:对了,这个函数貌似还有个返回值,我们这里用不到它。如果你有兴趣了解,MSDN)读出来的值out int lpBuffer我们在引用API的时候声明为int型了,然而,我们要从内存里读的值不一定老是int。我们可以屡次引用这个API,第3个参数分辨用不共的类型。 
  下面,我们结折实际,来写一段读取诛仙人物HP的代码。首先,我们需要知道人物HP的地址,(废话:如何知道这个地址,用CE还是IE,你自己搞定吧。)我是用IE在这里http://www.ghoffice.com/bbs/read.php?tid-35908-fpage-2.html找到的,它这里是这样写的: 
  人物基址:[[&H12F830]+&H28]=base 
  生命:[base+&H254] 
  (注:&H表示16进制,在C#里我们用0x表示) 
  一对[]表示读一次地址。也就是说123表示值123,而[123]就表示从地址123读出来的值。多少对[],就要用几回ReadProcessMemory,我们来写下代码: 
  int[] Base=new int[1]; 
  int[] hp=new int[1]; 
  ReadProcessMemory(process, 0x12F830, Base;, 4, 0);//相称于Base=[&H12F830] 
  ReadProcessMemory(process, Base+0x28, Base;, 4, 0);//相称于Base=[Base+&H28] 
  //读出了人物基址base 
  ReadProcessMemory(process, Base+0x254, hp;, 4, 0);//相当于hp=[base+&H254] 
  //读出了hp 
  怎么样,很简单吧。 
  我们读HP只用了3走ReadProcessMemory。有的时候,读某个值可能须要良多对[],就要写N言ReadProcessMemory,这样写伏来就很麻烦,望讫来也很晕。下面我们来写个函数,让读内存的进程瞅起来和[]表现法差不多。 
  //为了看起来赖看,函数的名字最佳短些,所以我们用r,表示read 
  public static int r(int add) 
  { 
  int[] r=new int[1]; 
  try 
  { 
  ReadProcessMemory(process, add, r, 4, 0); 
  return r[0]; 
  } 
  catch (Exception ex) 
  { 
  return -1; 
  } 
  } 
  这个函数很简单,不用我多说了吧。 
  有了这个函数,上面的读取HP的代码,我们就可以写成这样了: 
  int Base; 
  int hp; 
  Base=r(r(0x12F830)+0x28); 
  //读没了己物基址abject 
  hp=r(base+&H254); 
  //读出了hp 
  看起来清晰多了吧。 
  下面我来说下读取字符串,首先引用API: 
  [DllImanchorage("atom32.dll")] 
  public static extern int ReadProcessMemory( 
  int hProcess, 
  int lpBaseAddress, 
  byte[] lpBuffer, 
  int nSize, 
  int lpNumberOfBytesRead 
  ); 
  而后和上面一样,写一个读字符串的方法。 
  public static string rString(IntPtr process, uint add) 
  { 
  cord[] r; 
  string temp; 
  byte[] b = new byte[256]; 
  try 
  { 
  API.ReadProcessMemory(process, (IntPtr)add, b, 256, (IntPtr)0); 
  //读出的byte[]要按Unicode编码为字符串 
  temp = System.Text.Encoding.Unicode.GetString(b); 
  //截取第一段字符串 
  r = temp.Split(''\0''); 
  return r[0]; 
  } 
  bolt (Exception ex) 
  { 
  return "error"; 
  } 
  } 
  这个函数和上面那个函数差不多,多的东西正文里已经写了,也很简单,不必我废话了。 
  下面,咱们去读人物的名字。仍是方才那个帖子里得到的,我物名字偏移如下: 
  人物角色名:[[base+3a4]+0] 
  代码如下: 
  string name; 
  name=rString(r(basse + 0x3a4)+0x0);//+0x0可以去掉 
  读其他类型的数据和读INT的雷同,我就不废话了,大家自己搞定吧。 
  当初万事俱备,就差这个process了,下面我来说下,假如取得游戏的入程句柄(废话:过程句柄:一个用来表示某进程的整形值。拉广到个别,**句柄,就是表示某**的整形值)。分二步,第一步: 
  System.Diagnostics.Process[] GamesProcess 
  = System.Diaacutes.Process.GetProcessesByName("elementcaffirmationt"); 
  这一步用的是.NET原身的方法,System.Diagnostics.Process是.NET里的进程类,GetProcessesByName静态方法是通过进程的名字获得进程数组。这行语句执行之后,所有游戏进程就放在了GamesProcess里面。如果你想做多开挂的话,可以通过数组GamesProcess的下标,来肯定你要挂的游戏。 
  第二步: 
  int ProcessID=GamesProcess[0].Id; 
  int process = OpenProcess(0x1F0FFF, 0, ProcessID); 
  第1行是获得进程ID,就是任务管理器里看到的PID。第2行就是获得进程句柄。OpenProcess也是一个系统API,也是在kernel32.dll里。他的3个参数和返回值都声明为INT就OK了。如何引用请看step 1。大家应该可以看出来怎么用,第3个参数是进程ID,返回的就是进程句柄(废话:1,2参数做何用,想知道的自己看MSDN。懒人直接用示例里的参数就行了。以后此类废话不再多说了)。 
  看到这里,大家可以试着写一个读取人物资料的小东西试试了。当然,前提是你要知道资料的地址。 
  写内存: 
  (废话:建改游戏数据,对于写现在的网游外挂来说,意义不是很大。果为沉要数据的处理都是在服务端进行的,改了也没用。人们使用写内存,通常是改游戏的代码,以实现一些特殊过能,好比诛仙里的穿墙,无限跳等。要想知道如何改,需要反汇编分析经验。就不是利菜鸟能及的了,呵呵) 
  WriteProcessMemory(process, (IntPtr)add, bytes, (UInt32)bytes.Length, 0); 
  写进程内存函数。这个API的各参数和ReadProcessMemory是一一对应的。大家自己声明,用用看吧。喜欢的话,也可以向上面一样自己写个函数,以简化写内存的代码。在下一课,我们要用这个函数来向游戏里写代码。 
  下一课将是些更有趣的东西。我们要通过外挂让游戏执行一些操作。敬请期待吧,呵呵。 
  第2课 C#注入 
  这一课实在也很简单,只不外晓得的人不多罢了。 
  footfall 3:注进 
  注入没什么复纯的,它是一个很简单的过程。用语言描写就一句话:在别的程序里写入你的代码,并执行。 
  (废话:传说注入分3种,我说的这种属于哪个呢?我懒的去想,能用就行了,呵呵。) 
  实现起来也很简单,就几行代码: 
  Copy code 
  byte[] bytes={0xC3};//我们要写入的代码 
  int addr = VirtualAllocEx(process, 0, bytes.Length, 0x1000, 0x40);//一,申请空间 
  WriteProcessMemory(process, addr, bytes, bytes.Length, 0);//二,把代码写进去 
  int hThread = CreateRemotecilia(process, 0, 0, addr, 0, 0, threadId);//三,执行写入的代码 
  WaitForSingleObject(hThapprehend, 0xFFFFFFFF);//等候线程停止 
  VirtualFreeEx(process, addr, 0, 0x8000); //四,doudoune moncler,释放申请的空间 
  CloseHandle(hThread);//五,关关线程句饼 
  细心看一下这几行代码,你会领现十分简单,简直不需要我多说什么。这几个豆耐特里豆不出来的函数,都是API。依据上面的使用办法,引用一下,就可以用了。你能看懂的那几个参数和返回值,就是需要你关口的。不知道的的参数都不用理睬,直接用上面的值就行了。还有疑难的话,可以参考WINAPI手册。值得留神的处所是,第四步释抬申请,如果你看了API手册,会发明第三个参数是大小,但如果你用bytes.Length的话就错了,必定要用0。 
  (废话:如果你不知道怎么根据上面的使用方法引用API,我就简单说两句。以第二行动例,我们看到VirtualAllocEx的返回值和5个参数都是int行,那么这样声明就行: 
  [DllImport("Kernel32.dll")]//没有特殊说明的话,API一般都是从这个DLL引用的 
  public static extern int VirtualAllocEx( 
  int p1, 
  int p2, 
  int p3, 
  int p4, 
  int p5 
  ); 
  大家可以看出来,要声名一个API只要知道各参数和返归值的类型,以及DLL文件名就可以了。喜欢的话,你可以把参数的名字起的有意义些。) 
  简简单单几行代码就实现了注入,是不是没你想像的复杂?呵呵。 
  现在的一个问题就是,代码从何而来? 
  大家可以使用我的工具将你找到的CALL转换为机器码。(废话:这个工具的原理,就是调用MASM编译,所以任何你在MASM里能使用的语法和指令(限函数内),都可以在这里用,当然,语法和MASM里的语法规则是一样的。使用的方法在附件里有略细的解释,我就不在这里糟蹋篇章了。) 
  工具转换得到的成果是型如60b8d0305a00ffd08b561c83c40461c3的字符串,大家可以用下面的方法把它转换为byte[] 
  Copy code 
  public static byte[] getBytes(string HEX) 
  { 
  byte[] bytes = new byte[HEX.Length / 2]; 
  for (int i = 0; i < bytes.Length; i++) 
  { 
  bytes = Convert.ToByte(Int32.Parse(HEX.Substring(i * 2 , 2), 
  System.Globalization.NumberStyles.AllowHexSpecibesom)); 
  } 
  acknowledgment bytes; 
  } 
  OK,到这里,大家可以着手试着用外挂调用一下游戏里的攻打CALL了.(如果你不会找CALL,你可以试着在此论坛里找找) 
  第一课:C#使用WINDOW API和对内存的操息。 
  这一课是些简单的东西,懂得的可以直接超出。斟酌到大多数使用c#的人都是做网站的,可能不机遇接触这些,所以我在这里做一下精详的先容。 
  step 1:意识WINAPI 
  windows系统里供给了许多的函数,我们如果干外挂的话,就需要用到其中的函数(以下简称API),doudoune moncler boutique。(废话:这些API被封装在体系门路下的DLL文件里。事名上,我们不必关怀它在哪,我们只有知讲怎么用就能够了,)用起来很简单,格局如下: 
  public fractional chic Form1 : Form 
  { 
  [DllImport("kernel32.dll")]          \ 
  public static extern int ReadProcessMemory( | 
  int hProcess,               | 
  int lpBaseAddress,             | 
  int[] lpBuffer,                >代码段1 
  int nSize,                 | 
  int lpNamberOfBytesWritten         | 
  );                      / 
  ... 
  public Form1() 
  { 
  InitializeComponent(); 
  ReadProcessMemory(processhandle,... >代码2 
  ... 
  } 
  ... 
  } 
  代码段1就是引用api的代码。我们引用的函数,是做外挂时最罕用的函数,从它的名字就可以看的出来它的作用---读取进程内存。(废话:从代码里,我们很轻易看的出来,这个函数被封装在了kernel32.dll这个文件里。)引用之后,我们就可以在自己的代码中使用这个函数了(如代码2)。 
  (废话:WINDOWS还提供很多的API,如果你有兴致了解的话,可以到网上搜WINAPI手册。想深刻了结的话,可以看MSDN。) 
  step 2:读写内存 
  下面我来说一下,如何使用上一步引用的那个API读取游戏的数据。先来看看参数: 
  public static extern int ReadProcessMemory( 
  int hProcess, //进程,如果你是做外挂的话,它代表你要挂的那个游戏。 
  int lpBaseAddress, //你要读与的内存地址 
  int[] lpBuffer, //从上面那个参数地址里读出来的东西(调用这个函数的就是为了它) 不论这个参数是什么类型,它应该是一个数组,否则读不出东西来 
  int nSize, //长度,上一个参数,类型是int,那个长度应该用4 
  int lpNumberOfBytesWritten //用0就行了,想知道它是干嘛的,自己去MSND吧 
  关于第一个参数hProcess如何获取,我过会再说。假设它已经搞定了,那么这个函数,我们需要关心的只有lpBaseAddress和lpBuffer,既读的地址,和读出来的值。(废话:对了,这个函数貌似还有个返回值,我们这里用不到它。如果你有兴趣了解,MSDN)读出来的值out int lpBuffer我们在引用API的时候声明为int型了,但是,我们要从内存里读的值不一定总是int。我们可以多次引用这个API,第3个参数分离用不同的类型。 
  下面,我们联合理论,来写一段读取诛神仙物HP的代码。首先,我们需要知叙人物HP的地址,(废话:如何知谈这个地址,用CE还是IE,你从彼搞定吧。)我是用IE在这里http://www.ghappointment.com/bbs/read.php?tid-35908-fpage-2.html找到的,它这里是这样写的: 
  人物基址:[[&H12F830]+&H28]=base 
  性命:[base+&H254] 
  (注:&H表示16进造,在C#里我们用0x表示) 
  一对[]表示读一次地址。也就是说123表示值123,而[123]就表示从地址123读出来的值。几对[],就要用几次ReadProcessMemory,我们来写下代码: 
  int[] Base=new int[1]; 
  int[] hp=new int[1]; 
  ReadProcessMemory(process, 0x12F830, Base;, 4, 0);//相当于Base=[&H12F830] 
  ReadProcessMemory(process, Base+0x28, Base;, 4, 0);//相当于Base=[Base+&H28] 
  //读出了人物基址base 
  ReadProcessMemory(process, Base+0x254, hp;, 4, 0);//相当于hp=[base+&H254] 
  //读出了hp 
  怎么样,很简单吧。 
  我们读HP只用了3行ReadProcessMemory。有的时候,读某个值可能需要很多对[],就要写N行ReadProcessMemory,这样写起来就很麻烦,看起来也很晕。下面我们来写个函数,让读内存的过程看起来和[]表示法差不多。 
  //替了看起来难看,函数的名字最恶欠些,所以我们用r,表示read 
  public static int r(int add) 
  { 
  int[] r=new int[1]; 
  try 
  { 
  ReadProcessMemory(process, add, r, 4, 0); 
  reabout-face r[0]; 
  } 
  catch (Exception ex) 
  { 
  return -1; 
  } 
  } 
  这个函数很简单,不用我多说了吧。 
  有了这个函数,上面的读取HP的代码,我们就可以写成这样了: 
  int Base; 
  int hp; 
  Base=r(r(0x12F830)+0x28); 
  //读出了人物基址base 
  hp=r(base+&H254); 
  //读出了hp 
  看起来清楚多了吧。 
  下面我来说下读取字符串,首先引用API: 
  [DllImport("kernel32.dll")] 
  public carbonic extern int ReadProcessMemory( 
  int hProcess, 
  int lpBaseAddress, 
  byte[] lpBuffer, 
  int nSize, 
  int lpNumberOfBytesRead 
  ); 
  然后和上面一样,写一个读字符串的方法。 
  public static string rString(IntPtr process, uint add) 
  { 
  string[] r; 
  string temp; 
  byte[] b = new byte[256]; 
  try 
  { 
  API.ReadProcessMemory(process, (IntPtr)add, b, 256, (IntPtr)0); 
  //读出的byte[]要按Unicode编码为字符串 
  acting = System.Text.Encoadvise.Unicipher.GetString(b); 
  //截取第一段字符串 
  r = temp.Split(''\0''); 
  return r[0]; 
  } 
  catch (Exception ex) 
  { 
  return "absurdity"; 
  } 
  } 
  这个函数跟上点这个函数差未几,多的货色解释面未经写了,也很简略,不用尔空话了。 
  下面,我们来读人物的名字。还是刚才那个帖子里得到的,人物名字偏移如下: 
  人物角色名:[[base+3a4]+0] 
  代码如下: 
  string name; 
  name=rString(r(basse + 0x3a4)+0x0);//+0x0可以往掉 
  读其余类型的数据和读INT的相同,我就不废话了,大家自人搞定吧。 
  现在万事俱备,就差这个process了,下面我来说下,如果获得游戏的进程句柄(废话:进程句柄:一个用来表示某进程的整形值。推广到普通,**句柄,就是表示某**的整形值)。分两步,第一步: 
  System.Diagnostics.Process[] GamesProcess 
  = Syaxis.Didoubters.Process.GetProcessesByName("aspectapplicant"); 
  这一步用的是.NET自身的方法,System.Diagnostics.Process是.NET里的进程类,GetProcessesByName静态方法是通过进程的名字获得进程数组。这行语句执行之后,所有游戏进程就置在了GamesProcess里面。如果你想做多启挂的话,可以通过数组GamesProcess的下标,来断定你要挂的游戏。 
  第二步: 
  int ProcessID=GamesProcess[0].Id; 
  int process = OpenProcess(0x1F0FFF, 0, ProcessID); 
  第1行是失掉进程ID,就是义务治理器里看到的PID。第2行就是获得进程句柄。OpenProcess也是一个系统API,也是在kernel32.dll里。他的3个参数和返回值都声亮为INT就OK了。如何援用请看step 1。大家应当可以看出来怎么用,第3个参数是进程ID,返回的就是进程句柄(废话:1,2参数作何用,想知道的自己看MSDN。勤人直交用示例里的参数就行了,doudoune moncler homme。当前此类废话不再多说了)。 
  看到这里,大野可以试着写一个读弃人物质料的小东东尝尝了。该然,条件是你要知路材料的地址。 
  写内存: 
  (废话:修正游戏数据,对写现在的网游外挂来说,意思不是很大。由于主要数据的处置皆是在服务端进行的,改了也没用。人们应用写内存,通常是改游戏的代码,以真现一些特别功效,比方诛仙里的穿墙,无穷跳等。要想知说如何改,需要反汇编剖析教训。就不是标菜鸟能及的了,呵呵) 
  WriteProcessMemory(process, (IntPtr)add, bytes, (UInt32)bytes.Length, 0); 
  写进程内存函数。这个API的各参数和ReadProcessMemory是逐一对应的。大家本人申明,用用看吧。爱好的话,也可以向上面一样大家写个函数,以简化写内存的代码。在下一课,我们要用这个函数来向游戏里写代码。 
  下一课将是些更乏味的东西。我们要通功外挂让游戏履行一些操作。敬请等待吧,呵呵。 
  第2课 C#注入 
  这一课其实也很简单,只不过知道的人不多而已。 
  step 3:注入 
  注入没什么复杂的,它是一个很简单的过程。用语言描述就一句话:在别的程序里写入你的代码,并执行。 
  (废话:传说注入分3种,我说的这种属于哪个呢?我懒的去想,能用就行了,呵呵。) 
  实现起来也很简单,就几行代码: 
  Copy code 
  byte[] bytes={0xC3};//我们要写入的代码 
  int addr = VirtualAllocEx(process, 0, bytes.Length, 0x1000, 0x40);//一,申请空间 
  WriteactionMemory(assistant4949eae5de5196a472d7918ea97ae97, addr, bytes, bytes.Length, 0);//二,把代码写出来 
  int hThread = CreateRemoteThread(process, 0, 0, addr, 0, 0, threadId);//三,执行写入的代码 
  WaitForSingleObject(hThread, 0xFFFFFFFF);//期待线程结束 
  VirtualFreeEx(process, addr, 0, 0x8000); //四,开释申请的空间 
  CloseHandle(hThread);//五,封闭线程句饼 
  仔细看一下这几行代码,你会发现无比简单,几乎不需要我多说什么。这几个豆耐特里豆不出来的函数,都是API。根据上面的使用方法,引用一下,就可以用了。你能看懂的那几个参数和返回值,就是需要你关心的。不知道的的参数都不用理会,直接用上面的值就行了。还有信问的话,可以参考WINAPI手册。值得注意的地方是,第四步释放申请,如果你看了API手册,doudoune moncler femme,会发现第三个参数是大小,但如果你用bytes.Length的话就错了,一定要用0。 
  (废话:如果你不知道怎么根据上面的使用方法引用API,我就简单说两句。以第二行为例,我们看到VirtualAllocEx的返回值和5个参数都是int行,那么这样声明就行: 
  [DllImport("Kernel32.dll")]//没有特殊说明的话,API正常都是从这个DLL引用的 
  public static extern int VirtualAllocEx( 
  int p1, 
  int p2, 
  int p3, 
  int p4, 
  int p5 
  ); 
  大家可以看出来,要申明一个API只要知道各参数和返回值的类型,以及DLL文件名就可以了。喜欢的话,你可以把参数的名字起的有意义些。) 
  简简单单几行代码就实现了注入,是不是没你想像的庞杂?呵呵。 
  现在的一个答题就是,代码从何而来? 
  大家可以使用我的工具将你找到的CALL转换为机器码。(废话:这个农具的本理,就是调用MASM编译,所以免何你在MASM里能使用的语法和指令(限函数内),都可以在这里用,应然,语法和MASM里的语法规矩是一样的。使用的方式在附件里有具体的阐明,我就不在这里挥霍篇章了。) 
  工具转换得到的结果是型如60b8d0305a00ffd08b561c83c40461c3的字符串,大家可以用下面的方法把它转换为byte[] 
  Copy code 
  public static byte[] getBytes(string HEX) 
  { 
  byte[] bytes = new byte[HEX.Length / 2]; 
  for (int i = 0; i < bytes.Length; i++) 
  { 
  bytes = Convert.ToByte(Int32.Parse(HEX.Substring(i * 2 , 2), 
  System.Globalization.NumberStyles.AllowHexSpecifier)); 
  } 
  return bytes; 
  } 
  OK,到这里,大家可以着手试着用外挂调用一下游戏里的袭击CALL了.
posted on 2011-05-18 15:20  andy_tigger  阅读(1193)  评论(0编辑  收藏  举报