读书人

vb在调用API的一个Bug可能导致内存泄露

发布时间: 2012-01-10 21:26:51 作者: rapoo

vb在调用API的一个Bug可能导致内存泄露
其实这个问题我很早很早以前就发现了,只是一直没留意,也不在意。今天无意中又用VB写了一个程序全部使用NTDLL导出函数编写,又出现了这样的情况,这样就由不得我不注意了,经过我的反复测试以及反汇编验证,证实了vb6_sp6在调用某些API(测试的应该有很多API存在这样的问题)存在内存泄露问题或者是函数调用失败。请大家先看我写的简单两段代码。

代码一:

Option Explicit
Private Const MEM_RELEASE = &H8000
Private Const MEM_COMMIT = &H1000
Private Const PAGE_EXECUTE_READWRITE = &H40
Private Declare Function VirtualAlloc Lib "kernel32" (lpAddress As Any, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
Private Declare Function VirtualFree Lib "kernel32" (lpAddress As Any, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long

Private Sub Code1()
Dim Base As Long
Base = VirtualAlloc(ByVal 0&, 100, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
If Base Then
MsgBox Hex(Base)
MsgBox VirtualFree(ByVal Base, 0, MEM_RELEASE)
MsgBox Hex(GetLastError)
End If
End Sub

这段代码是申请一段内存,然后马上释放它,代码没什么难以理解的非常简单。大家仔细看代码上好像也不存在有什么问题,那么问题在什么地方呢?

我一句一句来解析首先“Base = VirtualAlloc(ByVal 0&, 100, MEM_COMMIT, PAGE_EXECUTE_READWRITE)”
这行代码申请了一段长100字节的内存跟踪结果成功申请了得出某地址,然后我用msgbox把地址显示出来
这时这段地址是可用的,大家可以使用一些内存察看编辑工具,比如冰刃之类的。然后调用“VirtualFree ByVal Base, 0, MEM_RELEASE"释放内存,失败了。我检查了很久参数和API声明均没发现有什么问题那么问题出在什么地方呢?为了求证我写了代码二,也就是代码一最终在Ring3层调用的API函数

代码二:

Option Explicit
Private Const MEM_RELEASE = &H8000
Private Const MEM_COMMIT = &H1000
Private Const PAGE_EXECUTE_READWRITE = &H40
Private Declare Function VirtualAlloc Lib "kernel32" (lpAddress As Any, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
Private Declare Function VirtualFree Lib "kernel32" (lpAddress As Any, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long

Private Sub Code2()
Dim ntStatus As Long
Dim AllocationSize As Long
Dim ReturnLength As Long
Dim Base As Long

AllocationSize = 100
ntStatus = ZwAllocateVirtualMemory(-1, _
Base, _
0, _
AllocationSize, _
MEM_COMMIT, _
PAGE_EXECUTE_READWRITE _
)

If ntStatus >= 0 And Base Then
MsgBox Hex(Base)
AllocationSize = 0
ntStatus = ZwFreeVirtualMemory(-1, Base, AllocationSize, MEM_RELEASE)
MsgBox Hex(ntStatus)
End If
End Sub

同样出错在ZwFreeVirtualMemory这句上,这时函数有返回值,值为&HC00000F2查阅相关定义为
STATUS_INVALID_PARAMETER_4,说明第四个参数存在问题。这句话可就那纳闷了,好在前段时间在64位编程时也遇到这样的问题后来经过跟踪分析知道是传进去的数据按32位传进去的前面位补了FFFFFFFF所以出了错。我就猜测估计这里也是这样的问题然后我通过windbg进行双机调试断点下在nt!NtFreeVirtualMemory上发现了确实在第四个参数上进行了检测而这时我们传进去的值和我们本来想传进去的值有出入





[解决办法]
本来 long (32bit) 就需要 写成 &HFF00&
某些API其实是 short (16bit) integer( -32768 ~ 32767 (因为是 16位操作系统中留下来的API)
你输入37268 > 32767 超出其范围,溢出了.

http://forums.techguy.org/software-development/495522-vb6-h-constants.html&HFF00 is hex FF00 a short (16bit) integer
&HFF00& is hex 0000FF00 a long (32bit) integer
&HFF0000 is hex 00FF0000 a long (32bit) integer
65536 is hex 00010000 is a long (32bit) remember the max value for a short integer is 65535 or (FFFF).

例子:

VB code
Option ExplicitPrivate Const i = 32768Private Const j = &H8000 '<--这里其实是 -32768 为什么呢? 因为是short (16bit) integerPrivate Const m = 32768Private Const n = &H8000& '<--这个才是32768,因为是long (32bit) integerPrivate Sub Command1_Click()    If i = j Then        MsgBox ("True") '不会经过这里的.    End If        If m = n Then        MsgBox ("True")    End IfEnd Sub 


[解决办法]
估计这是从16位版的VB遗留下来的问题吧.
因为VB6在从255到255以上的操作时表现正常,只在32767到32767以上的数值自动转换时发生问题.
说明它在对待未指定类型整数的时候总是"想到"Integer,然后需要写代码的时候"提醒"一下,它才会当成32位的Long来看待,貌似后娘.
例:print 200*2 不会溢出, print 32000*2 溢出了.
[解决办法]
这与 API 没有关系,而是非常容易被人忽略的 VB 常识。

16进制值本身是无符号的,但是 VB 的 Integer/Long 都是有符号的。
在编译整数常数时,如果没有指定类型,总是优先用 Integer 类型来存放。

VB code
Private Const MEM_RELEASE = &H8000Private Const MEM_COMMIT = &H1000
[解决办法]
探讨

老虎说的有道理,我也怀疑并证实了是VB编译器识别的问题造成的,但是为什么32768到65536之间的数值才发生这样的处理呢??这点老虎解释下,我们这些半路出家的人基础的东西不牢啊。

[解决办法]
探讨
&H8000 和 &H1000 用两字节就能存放,所以是 Integer 类型。但是因为符号的关系,&H8000 成了负数。
然后在调用函数时,应为需要 Long 类型,就自动做 Integer->Long 的类型转换,扩充的高2字节按最高位填充,即负数扩充的是 &HFFFF0000,所以 &H8000 成了 &HFFFF8000。

[解决办法]
这不是 VB 的问题,而是程序员的问题。
要用 VB 的思想来思考,VB 的十六进制常数是可以有符号的,所以 &H8000 就是 -32768,要表示 +32768 就应该用 &H8000&,没有任何的二义性。
调用函数时将参数的值 +32768 写成了 -32768,就是程序员的问题。

就像车辆靠右行驶一样,不用疑问,遵守规则就行。
[解决办法]

在非 API 的代码中,我也遇到过 Long 型赋值需要加 & 后缀的情况。最初没有注意,在一些特殊情况下出现异常,跟踪调试才发现是这个问题。

[解决办法]
C程序里面
char c;
c=128;
printf("%02X",c);
输出地结果并不是80,而是前面补了一堆FF的东西。

如果
char c;
c=128;
printf("%02X",c & 0xFF);

或者
unsigned char c; //声明为无符号数
c=128;
printf("%02X",c);
就可以输出预期的结果。

应该同样是因为符号位引起的问题。
[解决办法]
探讨

其实我还没明白为什么Private Const MEM_RELEASE as long = &H8000 不行。
它与Private Const MEM_RELEASE =&H8000& 有什么不同。同样声明了类型为Long,为什么不同呢?

读书人网 >VB

热点推荐