您的位置:首页 > 编程语言 > VB

VB制作外挂示例

2006-11-05 22:19 260 查看
[align=left] 使用 Visual Basic 制作修改器 [/align]
[align=left] 作者:Bean(beans@gamehacking.com) [/align]
[align=left] HeYu,Flyinsky 翻译 [/align]
[align=left][/align]

[align=left] "对不起,我想这是不可能的,因为VB是一个如此简单的编程语音。"如果有人这么告诉你,别去理他。我可以肯定告诉你,对于制作修改器这种简单的程序,VB完全可以胜任。[/align]
[align=left] 然而,有个问题必须首先考虑:使用VB编写的修改器需要VB的运行库才能运行。如果考虑到有些使用者(实际上可能是大部分使用者)没有运行库,那么在最后制作的ZIP压缩文件中就必须包含这些庞大的文件。在下面的教程里我将制作一个修改器,如果为它再制作一个安装程序,那么整个修改器的体积将超过1MB。其中包括一个很好的安装和反安装程序,但大部分还是VB40032.DLL这个文件。[/align]
[align=left] 除了以上这点,使用VB制作修改器是非常简单的。一旦制作了多次后,你会发现能很快地制作出一个修改器。而且使用VB制作的修改器能够毫无困难地解决游戏运行时的动态内存分配问题,因此即使是最新的游戏,也可以使用VB制作修改器。在本教程中将不涉及动态内存分配,因为虽然简单,但仍然属于一个高级的选项。[/align]
[align=left] 一些背景知识[/align]
[align=left] 不象C语音,VB不会自动包括普通的API函数的声明,因此我们必须把他们加入我们的项目文件。在几乎所有的修改器中会使用到6个主要的函数,讨论如下:[/align]
[align=left] 1. FindWindow(ClassName, WindowTitle) - FindWindow 返回符合指定的类名( ClassName [/align]
[align=left] )和窗口名( WindowTitle )的窗口句柄。对我们来说,可以让 ClassName 为空( Null ),只给出游戏的 [/align]
[align=left] WindowTitle。函数应该这样声明: Declare Function FindWindow Lib "user32" Alias [/align]
[align=left] "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) [/align]
[align=left] As Long[/align]
[align=left] 2. GetWindowThreadProcessId(WindowHandle, ProcessId) - 在这里我们把 FindWindow [/align]
[align=left] 函数中得到的句柄作为参数,来获得进程标识符(ProcessId )。声明如下: Declare Function [/align]
[align=left] GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId [/align]
[align=left] As Long) As Long[/align]
[align=left] 3. OpenProcess(DesiredAccess, Inherit, ProcessId) - [/align]
[align=left] 这个函数将返回一个我们目标进程的句柄,可以用来对目标进行读写操作。 DesiredAccess [/align]
[align=left] 参数的值决定了句柄对进程的存取权利,对我们来说,要使用 PROCESS_ALL_ACCESS (完全存取权限)。Inherit 应该总是 [/align]
[align=left] False。 ProcessId 是从 GetWindowThreadProcessId 函数中取得的。 Declare Function [/align]
[align=left] OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal [/align]
[align=left] bInheritHandle As Long, ByVal dwProcessId As Long) As Long[/align]
[align=left] 4. CloseHandle(ProcessHandle) - 每一个打开的句柄必须呼叫这个函数来关闭。 Declare Function [/align]
[align=left] CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long[/align]
[align=left] 5. WriteProcessMemory(ProcessHandle, Address, value, Sizeofvalue, [/align]
[align=left] BytesWritten) - 把指定的值 value 写入由 Address 指定的目标地址。 Declare Function [/align]
[align=left] WriteProcessMemory Lib "kernel32" (ByVal hProcess As Long, ByVal [/align]
[align=left] lpBaseAddress As Any, ByVal lpBuffer As Any, ByVal nSize As Long, [/align]
[align=left] lpNumberOfBytesWritten As Long) As Long[/align]
[align=left] 6. ReadProcessMemory(ProcessHandle, Address, value, Sizeofvalue, [/align]
[align=left] BytesWritten) - 把 Address 指定的目标地址的值存入 value 位置的变量中。 Declare Function [/align]
[align=left] WriteProcessMemory Lib "kernel32" (ByVal hProcess As Long, ByVal [/align]
[align=left] lpBaseAddress As Any, ByVal lpBuffer As Any, ByVal nSize As Long, [/align]
[align=left] lpNumberOfBytesWritten As Long) As Long[/align]
[align=left] 这些函数一环扣一环,缺一不可。更详细的内容可以参考VB的帮助文件。[/align]
[align=left] 一个简单的修改器范例[/align]
[align=left] 如何使上面介绍的这些函数一起工作,制作出我们需要的修改器呢?下面是一个为Windows的计算器程序制作修改器的例子。这个修改器将读出计算器窗口中显示的数值,并在点击一个按钮后在计算器窗口中显示我们的名字。 [/align]
[align=left] 首先我们需要找到计算器显示窗口中显示值的地址。本教程不是关于如何进行内存搜索,因而我将只作简单的说明:[/align]
[align=left] · 在计算器窗口中输入123456[/align]
[align=left] · 使用你喜欢的任何一种内存地址搜索程序寻找字串123456[/align]
[align=left] · 使用另一个值重复上面的过程直到只返回1个地址[/align]
[align=left] 那是制作我们的修改器需要的唯一一个地址。在我的计算器程序里这个地址是40B181 hex, 4239745 [/align]
[align=left] dec。用你找到的地址替代在下面的代码里使用的这个地址。[/align]
[align=left] 现在让我们开始设计修改器的界面:[/align]
[align=left] · 在VB中新建一个项目,加入一个文本框( Textbox )、一个按钮和一个计时器( timer [/align]
[align=left] )。文本框用来显示从计算器窗口取得的字串,按钮用来把我们的名字传到计算器窗口[/align]
[align=left] · 把表单( form )的标题( Caption )属性设为 Calculator Trainer[/align]
[align=left] · 把文本框改名为 txtDisplay 并清除 Text 属性[/align]
[align=left] · 把计时器改名为 ReadTimer 并把间隔( interval )设为500[/align]
[align=left] · 把按钮的标题改为 Display Name,按钮的名字改为 btnPasteName[/align]
[align=left] 在这个修改器中我们将使用所有6个函数,ReadProcessMemory、WriteProcessMemory、OpenProcess、GetWindowThreadProcessId、FindWindow [/align]
[align=left] 和 CloseHandle。在项目中插入一个新的模块,增加下列代码。(下面的一些行自动换行了,在你的模块中每一句必须在一行里,或使用延长符_)[/align]
[align=left] Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal [/align]
[align=left] lpClassName As String, ByVal lpWindowName As String) As Long[/align]
[align=left] Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As [/align]
[align=left] Long, lpdwProcessId As Long) As Long[/align]
[align=left] Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As [/align]
[align=left] Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long[/align]
[align=left] Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess As [/align]
[align=left] Long, ByVal lpBaseAddress As Any, ByVal lpBuffer As Any, ByVal nSize As [/align]
[align=left] Long, lpNumberOfBytesWritten As Long) As Long[/align]
[align=left] Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, [/align]
[align=left] ByVal lpBaseAddress As Any, ByVal lpBuffer As Any, ByVal nSize As Long, [/align]
[align=left] lpNumberOfBytesWritten As Long) As Long[/align]
[align=left] Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As [/align]
[align=left]Long[/align]
[align=left] 下面我们要开始写在计时器窗口中显示我们名字的代码了。首先我们使用 FindWindow [/align]
[align=left] 函数取得目标窗口的句柄。把这个返回值保存在一个变量中,并检查它的值是否出错来确保计时器程序正在运行。(FindWindow函数出错时返回0)[/align]
[align=left] Dim hwnd As Long[/align]
[align=left] hwnd = FindWindow(vbNullString, "Calculator")[/align]
[align=left] If (hwnd = 0) Then[/align]
[align=left] MsgBox "Window not found!"[/align]
[align=left] Exit Sub[/align]
[align=left] End If[/align]
[align=left] 注意在这里我们传递了一个 Null 值给 FindWindow 函数,而不是 ClassName。因此任何名为 Calculator [/align]
[align=left] 的窗口都符合条件。如果知道计算器程序窗口的 ClassName,你可以传给它,但这不是必须的。[/align]
[align=left] 现在使用得到的窗口句柄来取得进程标识符( ProcessId )。注意 pid 是作为参数传递给函数的,而不是被赋以函数返回值。[/align]
[align=left] Dim pid As Long[/align]
[align=left] GetWindowThreadProcessId hwnd, pid[/align]
[align=left] 再利用变量pid得到计算器程序的进程句柄。再次检查函数的返回值,如果是非法数据则退出程序。[/align]
[align=left] Dim pHandle As Long[/align]
[align=left] pHandle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)[/align]
[align=left] If (pHandle = 0) Then[/align]
[align=left] MsgBox "Couldn't get a process handle!"[/align]
[align=left] Exit Sub[/align]
[align=left] End If[/align]
[align=left] 在我们的修改器中 WriteProcessMemory 函数是最重要的部分,而且非常容易出错。不妨让我们再仔细讨论一下它的参数。[/align]
[align=left] WriteProcessMemory (ByVal hProcess As Long, ByVal lpBaseAddress As Any, [/align]
[align=left] ByVal lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As)[/align]
[align=left] hProcess 是目标进程的句柄,从上面的 OpenProcess 函数中取得的。[/align]
[align=left] lpBaseAddress 是在计算器程序的虚拟内存中将要被修改的地址,也就是使用内存搜索程序找到的那个地址。(在我的程序里是&H40B181)[/align]
[align=left] lpBuffer 是将要写如上述地址的数据,可以是一个数值、数组、字符串或其他任何数据类型。[/align]
[align=left] nSize 是希望写入 lpBaseAddress 的字节数。这个位置应该与你的数据类型相符。如果写入的是一个长整数( long [/align]
[align=left] ),这里应该是4。如果写入的是一个字符串,那么这里应该是字符串的长度。 [/align]
[align=left] lpNumberOfBytesWritten 是函数执行返回后,写入目标地址的实际字节数。它能被用来确认函数实际的执行情况。[/align]
[align=left] 把我们的数据放到函数中,得到 WriteProcessMemory pHandle, &H40B181, "Beans", 5, 0&。我把0传递到 [/align]
[align=left] lpNumberOfBytesWritten 位置是因为不需要检查两次实际写入的字节数。[/align]
[align=left] 最后通过传递进程句柄给 CloseHandle() 函数来关闭由 OpenProcess 打开的句柄。[/align]
[align=left] CloseHandle hProcess[/align]
[align=left] 现在将所有的代码输入我们的编辑器中。双击按钮,显示它的代码编辑窗口。代码应该加到名为 btnPasteName 的 Click [/align]
[align=left] 事件中。(不必输入注释)[/align]
[align=left] Private Sub btnPasteName_Click()[/align]
[align=left] ' 声明一些需要的变量[/align]
[align=left] Dim hwnd As Long ' 储存 FindWindow 函数返回的句柄[/align]
[align=left] Dim pid As Long ' 储存进程标识符( Process Id )[/align]
[align=left] Dim pHandle As Long ' 储存进程句柄[/align]
[align=left] ' 首先取得目标窗口的句柄[/align]
[align=left] hwnd = FindWindow(vbNullString, "Calculator")[/align]
[align=left] If (hwnd = 0) Then[/align]
[align=left] MsgBox "Window not found!"[/align]
[align=left] Exit Sub[/align]
[align=left] End If[/align]
[align=left] ' 取得进程标识符[/align]
[align=left] GetWindowThreadProcessId hwnd, pid[/align]
[align=left] ' 使用进程标识符取得进程句柄[/align]
[align=left] pHandle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)[/align]
[align=left] If (pHandle = 0) Then[/align]
[align=left] MsgBox "Couldn't get a process handle!"[/align]
[align=left] Exit Sub[/align]
[align=left] End If[/align]
[align=left] ' 在内存地址中写入名字[/align]
[align=left] WriteProcessMemory pHandle, &H40B181, "Beans", 5, 0&[/align]
[align=left] ' 关闭进程句柄[/align]
[align=left] CloseHandle hProcess[/align]
[align=left] End Sub[/align]
[align=left] 完毕。现在单击按钮将使计算器窗口文本变为我们键如的名字。(可能需要最小化计算器程序,再还原,以便程序更新显示)[/align]
[align=left] [/align]
[align=left] 下面将给我们的修改器增加一个新功能。我们将检测计算器程序的窗口显示数据,并在修改器中显示。双击计时器,显示它的代码编辑窗口,然后输入以下代码: [/align]
[align=left] Private Sub ReadTimer_Timer()[/align]
[align=left] ' 声明变量[/align]
[align=left] Dim hwnd As Long ' 储存 FindWindow 函数返回的句柄[/align]
[align=left] Dim pid As Long ' 储存进程标识符[/align]
[align=left] Dim pHandle As Long ' 储存进程句柄[/align]
[align=left] Dim str As String * 20 ' 存储显示文本[/align]
[align=left] ' 取得目标窗口的句柄[/align]
[align=left] hwnd = FindWindow(vbNullString, "Calculator")[/align]
[align=left] If (hwnd = 0) Then Exit Sub[/align]
[align=left] ' 取得进程标识符[/align]
[align=left] GetWindowThreadProcessId hwnd, pid[/align]
[align=left] ' 取得进程句柄[/align]
[align=left] pHandle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)[/align]
[align=left] If (pHandle = 0) Then Exit Sub[/align]
[align=left] ' 读取内存数据[/align]
[align=left] ReadProcessMemory pHandle, &H40B181, str, 20, 0&[/align]
[align=left] ' 在文本框显示[/align]
[align=left] txtDisplay = str[/align]
[align=left] ' 关闭进程句柄[/align]
[align=left] CloseHandle hProcess[/align]
[align=left] End Sub[/align]
[align=left] 在这里出现的新东西是 ReadProcessMemory 函数。从 &H40B181 地址中读出的数据被存入变量 str 中,然后显示在名为 [/align]
[align=left] txtDisplay 的文本框中。[/align]
[align=left] 本教程中所讲的是非常简单的东西,主要是想起抛砖引玉的目的。最重要的是不断学习,不断实践,了解其他的API并在修改器中使用。练习越多,就会觉得越容易。[/align]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: