分享
 
 
 

使用 Visual Basic 通过 32 位 地址访问内存(中英对照)

王朝vb·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

使用 Visual Basic 通过 32 位 地址访问内存

2001年7月6日

马尼拉,菲律宾

作者:Chris Vega [gwapo@models.com]

当我们谈论“真的”指针和内存地址,我们大都会想到Visual Basic的局限性,比如,由于 VB 没有作为变量声明的指针数据类型,它不能直接访问内存。当某些场合需要一个变量的“地址”而不是它的值的时候,这一点混淆就显得特别明显。例如,那个变量位于内存(当前进程、其它进程或者动态链接库的虚拟空间)中的何处。

是的,VB 确实“没有”指针变量,但是你是否曾试过将一个正规的 VB 数据类型转变为一个指针?你是否认为这是不可能的?好吧,还是再想一下,因为在 Visual Basic 中(从发行版本5开始),Microsoft 提供了一系列便利的函数以将你的正规变量转换为指针,它们是:

1] VarPtr - 返回一个变量或者数组元素的地址

StrPtr - 返回字符串的地址

Visual Basic 中除字符串以外的变量,都位于它的内存位置上,你可以通过调用 VarPtr 函数获取这个变量的地址。字符串实际上是作为 BSTR 储存的,这是一个指向“字符数组的指针”的指针,这样你就需要 StrPtr 以得到“字符数组的指针”的地址,而不是用 VarPtr 获得 BSTR 的地址。

范例:

Dim ptrMyPointer As Long

Dim intMyInteger As Integer

Dim strMyString As String * 25

' 这就是一个调用

ptrMyPointer = VarPtr(intMyInteger)

' 将内存中 intMyInteger 这个变量的32位地址赋予 ptrMyPointer

strMyString = "变量的地址:" & Hex(ptrMyPointer)

MsgBox strMyString

' 这是另一个调用

ptrMyPointer = StrPtr(strMyString)

' 给出字符数组首元素的地址,例如,字符串的第一个字母。

2] VarPtrArray - 返回变量数组的地址

VarPtrStringArray - 返回字符传数组的地址

Visual Basic 中数组被包存在 SAFEARRAY 结构中,你需要使用 VarPtrArray 函数以获取数组的地址,但是在使用该函数之前,你必须手工把它从 msvbvm50.dll 中声明到 VB 程序中。

范例:

' 对于 VB 5

' ========

Declare Function VarPtrArray Lib "msvbvm50.dll" Alias "VarPtr" (Var() as Any) As Long

' 对于 VB 6

' ========

Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Var() as Any) As Long

' 调用

Dim lngSafeArrayAddress As Long

Dim lngArrayOfLongs(6) As Long

Dim i As Long

Randomize

For i = 0 to 6

lngArrayOfLongs = Int(Rnd * &HFFFF)

Next

lngSafeArrayAddress = VarPtrArray(lngArrayOfLongs())

' 返回数组 lngArrayOfLongs 的安全地址,s of an Array , you

' 你可以将这些地址用于快速排序或其它用途。

事实上,VarPtrStringArray 更难以用于你的程序中,因为你需要创建一个类型库并手工将该类型库引用到 VB 程序中。要做一个类型库,你需要一个 MIDL 编译器,它是一个用于将 *.odl 文件编译成类型库的命令行工具。

对于 VB5,创建一个文本文件并且保存为 VB5StrPtr.odl,加入以下内容:

----------开始剪切--------------------------------------------------

#define RTCALL _stdcall

[uuid(6E814F00-7439-11D2-98D2-00C04FAD90E7),lcid (0), version(5.0), helpstring("VarPtrStringArray Support for VB5")]

library PtrLib

{

importlib ("stdole2.tlb");

[dllname("msvbvm50.dll")]

module ArrayPtr

{

[entry("VarPtr")]

long RTCALL VarPtrStringArray([in] SAFEARRAY (BSTR) *Ptr);

}

}

----------结束剪切-------------------------------------------------

用以下命令编译:MIDL /t VB5StrPtr.odl

对于 VB6,创建一个文本文件并且保存为 VB6StrPtr.odl,加入以下内容:

-----------开始剪切--------------------------------------------------

#define RTCALL _stdcall

[uuid(C6799410-4431-11d2-A7F1-00A0C91110C3),lcid (0), version(6.0), helpstring("VarPtrStringArray Support for VB6")]

library PtrLib

{

importlib ("stdole2.tlb");

[dllname("msvbvm60.dll")]

module ArrayPtr

{

[entry("VarPtr")]

long RTCALL VarPtrStringArray([in] SAFEARRAY (BSTR) *Ptr);

}

}

----------结束剪切-------------------------------------------------

用以下命令编译:MIDL /t VB6StrPtr.odl

现在,你有了类型库,将该类型库引用到 VB 程序中,然后你可以用以下方式获取字符串数组:

Dim MyArrayOfStrings(3) As String

Dim AddressOfArray As Long

MyArrayOfStrings(0)="Chris"

MyArrayOfStrings(1)="Vega"

MyArrayOfStrings(2)="gwapo@models.com"

' 调用

AddressOfArray = VarPtrStringArray ( MyArrayOfStrings() )

' 给出数组首元素的地址,而且是该首元素的第一个字符,

' 例如,这里是字符“C”在内存中的地址

' *** 怎样?你没有 MIDL 编译器?或者你不愿麻烦自己创建类型库并手工引用?

' 这里有一种足够容易的简单方法。

' 因为 StrPtr 函数具有获得字符串地址的能力,而字符串数组的元素全部都是字符串,

' 所以你应该清楚了,你可以对数组的首元素进行这个调用

AddressOfArray = StrPtr ( MyArrayOfStrings(0) )

' 返回更上述方法完全相同的结果

3] ObjPtr - 返回一个对象的地址

面向对象编程由众多对象组成,而这些对象和它的众多属性一起保存在内存中,作为一种结构化的布局。你需要调用 ObjPtr 函数来获取它的位置。

范例:

' 你要知道 Form1 处于内存何处,本方法告诉你它在线程中的地址

Dim objMyObject As New Form1

MsgBox "Form1 位于:" & Hex( ObjPtr( objMyObject ) )

好,到此为止你一定在想:无论如何,怎么才能把这些地址变成实际有用的东西呢?其实如果你这样想答案就很清楚了:地址是一个内存中的位置,而你的变量中保存的就是一个内存中的位置,并且有它本身在内存中的位置。搞糊涂了?我们让它简单一点,你可以简单的认为这个地址是保存数据的位置,数据是可读写的,而你需要通过这个地址来读写它。唔,Visual Basic 能够做这种事情吗?

不能,如果你只是简单的考虑 Visual Basic 的能力的话,但是你的程序可以使用 API函数。我现在谈到的 API 是从 KERNEL32.DLL 输出的运行库,名为 RtlMoveMemory 和 RtlCopyMemory。

它太吸引人了。首先我们已经找到了通过把变量转变为指针来得到内存地址的方法,现在我们又有了读写这些地址所指内容的方法。但是只要将这两个声明中的任一个加入你的程序中,而不是全部,因为它们的功能是一样的。我建议使用第二个,因为它被所有的 Windows 系统支持,而 RtlCopyMemory 则不然。

Private Declare Sub CopyMemory _

Lib "kernel32" Alias _

"RtlCopyMemory" _

(Destination As Any, _

Source As Any, _

ByVal length As Long)

' RtlCopyMemory 将一块内存的内容复制到另一块中。

' 或者

Private Declare Sub CopyMemory _

Lib "kernel32" Alias _

"RtlMoveMemory" _

(Destination As Any, _

Source As Any, _

ByVal length As Long)

' RtlMoveMemory 可以向前或向后移动内存,匹配的或不匹配的,

' 以 4字节的块为单位,后面为所有保留的字节。

参数:

Destination

指向要移动的目标。

Source

指向要复制的内存。

Length

指定要复制的字节数。

为了使它更容易使用,你可以把下面内容复制粘贴到 modMemory.bas 中:

------------开始剪切------------------------------------------------------------------

Attribute VB_Name = "modMemory"

' =============================================================================

' 复制内存 API

' =============================================================================

Private Declare Sub CopyMemory _

Lib "kernel32" Alias _

"RtlMoveMemory" _

(Destination As Any, _

Source As Any, _

ByVal length As Long)

' =============================================================================

' 数据长度

' =============================================================================

Public Enum e_BinaryData

DefineByte = 1 ' 8 位数据

DefineWord = 2 ' 16 位数据

DefineDoubleWord = 4 ' 32 位数据

DefineQuadWord = 8 ' 64 位数据

End Enum

' =============================================================================

' 允许直接读 MemPointer 指向的内存

' 用和 Asm 一样的字节数定义 (DB, DW, DD, DX)

' =============================================================================

Function ReadMem(ByVal MemPointer As Long, _

SizeInBytes As e_BinaryData)

Select Case SizeInBytes

Case DefineByte

Dim DB As Byte

CopyMemory DB, ByVal MemPointer, 1

ReadMem = DB

Case DefineWord

Dim DW As Integer

CopyMemory DW, ByVal MemPointer, 2

ReadMem = DW

Case DefineDoubleWord

Dim DD As Long

CopyMemory DD, ByVal MemPointer, 4

ReadMem = DD

Case DefineQuadWord

Dim DX As Double

CopyMemory DX, ByVal MemPointer, 8

ReadMem = DX

End Select

End Function

' =============================================================================

' 允许直接写 MemPointer 指向的内存

' 用和 Asm 一样的字节数定义 (DB, DW, DD, DX)

' =============================================================================

Sub WriteMem(ByVal MemPointer As Long, _

SizeInBytes As e_BinaryData, _

ByVal DataToWrite)

CopyMemory ByVal MemPointer, VarPtr(DataToWrite), SizeInBytes

End Sub

------------结束剪切---------------------------------------------------------------

用例:

通过内存为变量赋值:

Dim ptrVariable As Long

Dim xCounter As Long

ptrVariable = VarPtr(ptrVariable)

WriteMem ptrVariable, DefineWord, &HFFFF

' 与 ptrVariable = &HFFFF 等价

读内存的内容,使用:

ptrVariable = ReadMem(ptrVariable, DefineWord)

现在我们能够获得指针并访问它们了。但是如果你一步步跟着以上步骤看下来,你可能奇怪一条原本的 Visual Basic 赋值操作比这里介绍的直接内存赋值操作快得多。然而本文旨在指出可以使用 Visual Basic 访问内存,而这一点的主要意义不仅在于读取和分析变量,接下来,你可以通过获得内存地址简单地处理运行的 DLL。同时利用 modMemory.bas 和 PE (Portable Executable) 文件格式的知识,你可以分析 DLL 主体,看看它们是如何处理的。最好的是,可以获取它所有输出函数的列表;差点忘记,可以把它们 spy 出来或者干脆获取函数体的副本进行反汇编,比低级语言访问更多的内容,这也是 C 语言被称为工业标准的原因;现在你可以书写跟 C 表现相同的 Visual Basic 程序,祝你好运!

- Chris Vega [gwapo@models.com]

Accessing Memory by 32-bit Addresing in Windows using Visual Basic

July 6, 2001

Manila, Philippines

By: Chris Vega [gwapo@models.com]

When we talk about *real* Pointer and Memory Addressing, most of us thinks of Visual Basic limitations, ie, VB cannot access memory because VB has no pointer datatype for a variable declarations. This confusion grow even larger when a scenarios needed one *address* of a variable instead of its value, ie, from where in memory was that variable located into a virtual space of currently running process or a process or dynamic library.

Yes, there is actually *no* pointer variable for VB, but have you ever tried to turn a regular VB Datatype into a Pointer? do you think its not possible? well, think again, cause in Visual Basic (starting from release version 5), a serries of handy funtions is presented by Microsoft to turn this regular variables of yours into a pointer, these are:

1] VarPtr - Returns the Address of a Variable or Array Element

StrPtr - Returns the Address of String

Variables in Visual Basic, except Strings are located into its

Memory Location, you can get the Address of this variable by

calling VarPtr Function. Strings however are stored as BSTR's,

a pointer to a "pointer on array of characters", where you need

StrPtr to have the address of "pointer to the array of characters"

instead an address to BSTR if you used VarPtr in String.

ex.

Dim ptrMyPointer As Long

Dim intMyInteger As Integer

Dim strMyString As String * 25

' A call

ptrMyPointer = VarPtr(intMyInteger)

' gives ptrMyPointer a 32-bit Address of the Variable

' intMyInteger in Memory

strMyString = "Address of Variable : " & Hex(ptrMyPointer)

MsgBox strMyString

' Next, a call

ptrMyPointer = StrPtr(strMyString)

' gives the Address of the First Element of the Array of

' Character, ie, First letter of the String.

2] VarPtrArray - Returns the Address of an Array of Variables

VarPtrStringArray - Returns the Address of an Array of Strings

Arrays in Visual Basic are store in SAFEARRAYs, and you need to

use the function VarPtrArray to get the address of this array, but

before you can use the function, you need to manually declare the

function from msvbvm50.dll to your VB Application.

ex.

' for VB 5

' ========

Declare Function VarPtrArray _

Lib "msvbvm50.dll" Alias "VarPtr" _

(Var() as Any) As Long

' for VB 6

' ========

Declare Function VarPtrArray _

Lib "msvbvm60.dll" Alias "VarPtr" _

(Var() as Any) As Long

' The Call

Dim lngSafeArrayAddress As Long

Dim lngArrayOfLongs(6) As Long

Dim i As Long

Randomize

For i = 0 to 6

lngArrayOfLongs = Int(Rnd * &HFFFF)

Next

lngSafeArrayAddress = VarPtrArray(lngArrayOfLongs())

' Returns the Safe Address of an Array lngArrayOfLongs, you

' can simply use 'em for *fast* sorting or many more!

VarPtrStringArray however are more difficult to incorporate into

you application since you need to create a TypeLibrary and manually

refference the Library into VB Application.

To make a Type Library, you need a MIDL compiler, a CommandLine tool

that compiles *.odl file into a Type Library,

For VB5 Create a Text File and Save it to VB5StrPtr.odl with content:

-------------Cut here--------------------------------------------------

#define RTCALL _stdcall

[

uuid(6E814F00-7439-11D2-98D2-00C04FAD90E7),

lcid (0), version(5.0), helpstring("VarPtrStringArray Support for VB5")

]

library PtrLib

{

importlib ("stdole2.tlb");

[dllname("msvbvm50.dll")]

module ArrayPtr

{

[entry("VarPtr")]

long RTCALL VarPtrStringArray([in] SAFEARRAY (BSTR) *Ptr);

}

}

----------End Cut here-------------------------------------------------

And compile it with:

MIDL /t VB5StrPtr.odl

For VB6 Create a Text File and Save it to VB6StrPtr.odl with content:

-------------Cut here--------------------------------------------------

#define RTCALL _stdcall

[

uuid(C6799410-4431-11d2-A7F1-00A0C91110C3),

lcid (0), version(6.0), helpstring("VarPtrStringArray Support for VB6")

]

library PtrLib

{

importlib ("stdole2.tlb");

[dllname("msvbvm60.dll")]

module ArrayPtr

{

[entry("VarPtr")]

long RTCALL VarPtrStringArray([in] SAFEARRAY (BSTR) *Ptr);

}

}

----------End Cut here-------------------------------------------------

And compile it with:

MIDL /t VB6StrPtr.odl

Now, you have the Type Library, and Referrenced the Library to your VB

Application, you can get the Array of Strings in this way:

Dim MyArrayOfStrings(3) As String

Dim AddressOfArray As Long

MyArrayOfStrings(0)="Chris"

MyArrayOfStrings(1)="Vega"

MyArrayOfStrings(2)="gwapo@models.com"

' A call

AddressOfArray = VarPtrStringArray ( MyArrayOfStrings() )

' gives you the Address of the first element in the Array and First

' character of this element, ie, Address where "C" is located in

' Memory

' *** How about it, you dont have MIDL compiler? or dont want to go

' into a process of creating Type Library and Referencing it manually,

' a simple approach of using StrPtr will be handly enough for you, since

' this function has the capability of getting the Address of a String, and

' each element in an Array of Strings is non other than String, so you

' get the picture clear, you have to point your call to the first element

' of the Array of String and call

AddressOfArray = StrPtr ( MyArrayOfStrings(0) )

' returns the same result as the above call

3] ObjPtr - Returns the Address of an Object

Object Oriented Programming consist of Objects, and these objects also

stored into Memory together with all of its properties, as a structured

layout, and to obtain its location you need to call ObjPtr Funtion

ex.

' You want to know where is your Form1 resides in Memory, this

' Method gives you the Address, in Thread

Dim objMyObject As New Form1

MsgBox "Form1 located at : " & Hex( ObjPtr( objMyObject ) )

Ok, from this point, you are thinking on, How in the world should this Address becomes

useful in anyways? well the answer is very clear if you think this way, an Address is

a Location in Memory, and your Variables is a Location in Memory with its own Location

in Memory, confused? well, to make it simple, you can simply think that this Address is

a Location where Datas are stored, and Datas are READABLE and WRITABLE, but you need the

Address to have it Written or Read the Data on it, Hmmm, Is Visual Basic Capable of

doing these things?

Well, not, if you think plain as in Visual Basic Capability, but APIs are functions that

are ready for use by you application, the APIs im blabing about is a RunTime Libararies

called RtlMoveMemory and RtlCopyMemory, exported by KERNEL32.DLL.

Aint it charming? First we have found a way to achieve a Memory Address by converting a

Variable into a Pointer, Now we have ways to Read and Write to anf from these addresses,

but how you may ask? By adding either one of this Declarations to your Application, but not

both, since they funtion the same, i suggest use the second one since it supported by all

Windows System, while RtlCopyMemory is not.

Private Declare Sub CopyMemory _

Lib "kernel32" Alias _

"RtlCopyMemory" _

(Destination As Any, _

Source As Any, _

ByVal length As Long)

' RtlCopyMemory copies the contents of one buffer to another.

' OR

Private Declare Sub CopyMemory _

Lib "kernel32" Alias _

"RtlMoveMemory" _

(Destination As Any, _

Source As Any, _

ByVal length As Long)

' RtlMoveMemory moves memory either forward or backward, aligned or unaligned,

' in 4-byte blocks, followed by any remaining bytes.

Parameters:

Destination

Points to the destination of the move.

Source

Points to the memory to be copied.

Length

Specifies the number of bytes to be copied.

To make it more easy to Use, Included the File modMemory.bas for Copy and Paste

in this Article:

------------cut here------------------------------------------------------------------

Attribute VB_Name = "modMemory"

' =============================================================================

' Copy Memory API

' =============================================================================

Private Declare Sub CopyMemory _

Lib "kernel32" Alias _

"RtlMoveMemory" _

(Destination As Any, _

Source As Any, _

ByVal length As Long)

' =============================================================================

' Data Sizes

' =============================================================================

Public Enum e_BinaryData

DefineByte = 1 ' 8 Bits Data

DefineWord = 2 ' 16 Bits Data

DefineDoubleWord = 4 ' 32 Bits Data

DefineQuadWord = 8 ' 64 Bits Data

End Enum

' =============================================================================

' Allows Direct Reading from Memory Pointed by MemPointer

' with definition of bytes used as in Asm (DB, DW, DD, DX)

' =============================================================================

Function ReadMem(ByVal MemPointer As Long, _

SizeInBytes As e_BinaryData)

Select Case SizeInBytes

Case DefineByte

Dim DB As Byte

CopyMemory DB, ByVal MemPointer, 1

ReadMem = DB

Case DefineWord

Dim DW As Integer

CopyMemory DW, ByVal MemPointer, 2

ReadMem = DW

Case DefineDoubleWord

Dim DD As Long

CopyMemory DD, ByVal MemPointer, 4

ReadMem = DD

Case DefineQuadWord

Dim DX As Double

CopyMemory DX, ByVal MemPointer, 8

ReadMem = DX

End Select

End Function

' =============================================================================

' Allows Direct Writing to Memory Pointed by MemPointer

' with definition of bytes used as in Asm (DB, DW, DD, DX)

' =============================================================================

Sub WriteMem(ByVal MemPointer As Long, _

SizeInBytes As e_BinaryData, _

ByVal DataToWrite)

CopyMemory ByVal MemPointer, VarPtr(DataToWrite), SizeInBytes

End Sub

------------end cut here---------------------------------------------------------------

Usage:

To assign to a variable using memory:

Dim ptrVariable As Long

Dim xCounter As Long

ptrVariable = VarPtr(ptrVariable)

WriteMem ptrVariable, DefineWord, &HFFFF

' Same as ptrVariable = &HFFFF

To read from a Memory, use:

ptrVariable = ReadMem(ptrVariable, DefineWord)

Wow, we got a Pointer and we can access them now, but if you time instructions, you

may be amazed that a raw Visual Basic Assignment Operations is much faster that

of the Direct Memory Assignment Operation, but what i am pointing out here is that

Memory can now be Accessed Using Visual Basic, and the global Idea of this is to

Read and Analyse not only the Variable, from this Downward, you can simply run through

running DLL by acquiring their memory addresses, equiped with modMemory.bas with

knowledge of Portable Executable format, Bingo, you can play with the DLL Body, See

how they process and best of all, Get a List of all of its Exported Functions; and

before i forgot, why not spy em out or even get a copy of their function body to

Disassembly purpose, all of which and more are accessible on Low Level Languages,

thats why C was declared as Industry Standard; Now you can write Visual Basic

Application that can performs like C, goodluck

- Chris Vega [gwapo@models.com]

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有