当前位置:七道奇文章资讯安全技术网络技术
日期:2010-09-07 00:18:00  来源:本站整理

VB程序的跟踪与解析技术[网络技术]

赞助商链接



  本文“VB程序的跟踪与解析技术[网络技术]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:

文/图 laoxuetong
===================================
在从前的文章中我曾经说过,很多朋友都怕跟踪VB编写的软件,认为VB编写的程序代码太长,看不到所谓的关键代码,是垃圾代码等,究其缘由有以下几点.
1)        VB编写的代码被编译后,并没有将计算代码、事件处理代码以及属性设置代码等直接放在主程序之中,而是采取"拜托"的方法交给了库函数举行处理.
2)        VB在举行"拜托"之前,常需求做出大量的工作以保证"拜托"工作可以顺利举行下去.
3)        VB中广泛利用Unicode编码方法,造成对关键字符串的搜索艰难.
4)        VB编译的东西普通不直接调用核心API,若有需求,普通也是通过库函数举行的(间接调用).这样一来,很多熟习操作在VB中无法直接看到,造成心理惊惧.
5)        VB编写的代码经过编译后,很多的操作数据不能直接察看到,缘由是VB普通不是利用指针,而是利用构造保存数据.VB操作数据时给出的是这个构造的头部,而不是数据本身的地址.而OD对构造的解析并非很好,所以常常不知道函数操作的毕竟是什么、又是哪个对象.
但我的感受并非那么难,VB的每一步操作都有明确的标志,函数名称在OD中一目了然.这些函数名称绝大大都与用VB编写程序时利用的函数同名,识辨对比简单,你只需求搞清楚具体的操作对象就行了.
当然,要知道是对谁举行操作,也不是随便便可以知道的,有时需求跟踪到库函数里面去,但不是每次都需求这么做.库函数也是代码的汪洋大海,假如每一行代码都举行跟踪,那也不是一个劳顿的问题了.好在库函数的内部也有很明确的标志,肯定下一步的操作办法前,无妨转动看看,用以肯定什么时刻"步入",什么时刻"步过".
平常掌握记忆一些函数的情势和意义也是很重要的.没有记忆的学习是不成功的,读书亦如此.何况有很多的函数在我们编程时大概没用过,大概是根本没有.
当然,也不是全部的都需求记忆,假如你略微有一点点英文底子,并略知编程,能做到见字思意,不记忆什么照样可以"展开工作".不过,知识当然是越多越好的,这是颠簸不破的真理.

跟踪与解析技术
正因为有人认为难,所以本文不采取别人的软件作为例子举行讲授,而是采取按照故事情节,自导自演的办法来举行,并且采取源代码、编译历程中的代码和编译后的代码相结合展示的办法来研究VB的跟踪技术问题.
[例1]变量之间的数据传送
假定我们有三个变量,顺次命名为b1、b2、b3.变量范例按照需求设置为Long型和String型.
第一类,三个变量均为Long型,源代码以下.
Private Sub Command1_Click()
Dim b1, b2, b3 As Long
    
b1 = 12345678
b2 = b1
b3 = b2
Text1 = b3
End Sub

以上代码被编译为以下的汇编代码.

; 36 : Private Sub Command1_Click()
pushebp
movebp, esp
subesp, 12; 0000000cH
pushOFFSET FLAT:___vbaExceptHandler
moveax, DWORD PTR fs:__except_list
pusheax
movDWORD PTR fs:__except_list, esp
subesp, 80; 00000050H
pushebx
pushesi
pushedi
movDWORD PTR __$SEHRec$[ebp+8], esp
movDWORD PTR __$SEHRec$[ebp+12], OFFSET FLAT:$S33
movesi, DWORD PTR _Me$[ebp]
moveax, esi
andeax, 1
movDWORD PTR __$SEHRec$[ebp+16], eax
andesi, -2; fffffffeH
pushesi
movDWORD PTR _Me$[ebp], esi
movecx, DWORD PTR [esi]
callDWORD PTR [ecx+4]
xoredi, edi

看看,为了展开工作,做了不少的预备呢.实际上变量已经定义好了.在哪?我也没有看出来.

; 37 : Dim b1, b2, b3 As Long
; 38 :
; 39 : b1 = 12345678

再看下面的第一次赋值历程.

leaedx, DWORD PTR _unnamed_var1$[ebp]
movDWORD PTR _unnamed_var1$[ebp], edi
leaecx, DWORD PTR _b1$[ebp]
movDWORD PTR _b1$[ebp], edi
movDWORD PTR _b2$[ebp], edi
movDWORD PTR _unnamed_var1$[ebp], edi
movDWORD PTR _unnamed_var1$[ebp], edi
movDWORD PTR _unnamed_var1$[ebp+8], 12345678 ; 00bc614eH
movDWORD PTR _unnamed_var1$[ebp], 3
callDWORD PTR __imp_@__vbaVarMove

赋值之后干了什么,看出来了吗?

; 40 : b2 = b1

下面是变量之间的传送历程.

lea        edx, DWORD PTR _b1$[ebp]
lea        ecx, DWORD PTR _b2$[ebp]
call DWORD PTR __imp_@__vbaVarCopy

看出来采取了什么办法了吗?

; 41 : b3 = b2

下面是第二次变量之间的传送历程.

lea        edx, DWORD PTR _b2$[ebp]
push        edx
call        DWORD PTR __imp____vbaI4Var
mov        ebx, eax

怎么实现的?

; 42 : Text1 = b3

再看看整数怎么被转换成字符了,谁最值得猜疑?

moveax, DWORD PTR [esi]
pushesi
callDWORD PTR [eax+764]
leaecx, DWORD PTR _unnamed_var1$[ebp]
pusheax
pushecx
callDWORD PTR __imp____vbaObjSet
movesi, eax
pushebx
movedx, DWORD PTR [esi]
movDWORD PTR -100+[ebp], edx
callDWORD PTR __imp____vbaStrI4
movedx, eax
leaecx, DWORD PTR _unnamed_var1$[ebp]
callDWORD PTR __imp_@__vbaStrMove
movedx, DWORD PTR -100+[ebp]
pusheax
pushesi
callDWORD PTR [edx+164]
cmpeax, edi
fnclex
jgeSHORT $L59
push164; 000000a4H
pushOFFSET FLAT:___vba@001E08B4
pushesi
pusheax
callDWORD PTR __imp____vbaHresultCheckObj
$L59:
leaecx, DWORD PTR _unnamed_var1$[ebp]
callDWORD PTR __imp_@__vbaFreeStr
leaecx, DWORD PTR _unnamed_var1$[ebp]
callDWORD PTR __imp_@__vbaFreeObj
; 43 : End Sub

上面就是经过编译,但还没有来得及链接的原始代码.假如需求,可以利用OD找到它.比方:

00401BD7   MOV DWORD PTR SS:[EBP-48],0BC614E

不过,将前后的代码对比一下,你会看到已经发生了变异,因为这是链接之后的代码.完好地将上述的代码复制出来,并举行以下对比.

00401B80   PUSH EBP
00401B81   MOV EBP,ESP
00401B83   SUB ESP,0C
00401B86   PUSH <JMP.&MSVBVM60.__vbaExceptHandler>
;SE handler installation
00401B8B   MOV EAX,DWORD PTR FS:[0]
00401B91   PUSH EAX
00401B92   MOV DWORD PTR FS:[0],ESP
00401B99   SUB ESP,50
00401B9C   PUSH EBX
00401B9D   PUSH ESI
00401B9E   PUSH EDI
00401B9F   MOV DWORD PTR SS:[EBP-C],ESP
00401BA2   MOV DWORD PTR SS:[EBP-8],典范1.004010A0
00401BA9   MOV ESI,DWORD PTR SS:[EBP+8]
00401BAC   MOV EAX,ESI
00401BAE   AND EAX,1
00401BB1   MOV DWORD PTR SS:[EBP-4],EAX
00401BB4   AND ESI,FFFFFFFE
00401BB7   PUSH ESI
00401BB8   MOV DWORD PTR SS:[EBP+8],ESI
00401BBB   MOV ECX,DWORD PTR DS:[ESI]
00401BBD   CALL DWORD PTR DS:[ECX+4]
00401BC0   XOR EDI,EDI
00401BC2   LEA EDX,DWORD PTR SS:[EBP-50]
00401BC5   MOV DWORD PTR SS:[EBP-50],EDI
00401BC8   LEA ECX,DWORD PTR SS:[EBP-24]
00401BCB   MOV DWORD PTR SS:[EBP-24],EDI
00401BCE   MOV DWORD PTR SS:[EBP-34],EDI
00401BD1   MOV DWORD PTR SS:[EBP-3C],EDI
00401BD4   MOV DWORD PTR SS:[EBP-40],EDI
00401BD7   MOV DWORD PTR SS:[EBP-48],0BC614E
;这里就是给变量b1赋值.
00401BDE   MOV DWORD PTR SS:[EBP-50],3
00401BE5   CALL DWORD PTR DS:[<&MSVBVM60.__vbaVarMove>>
;MSVBVM60.__vbaVarMove

赋值之后,做了这项工作.记着这个函数名,它但是VB跟踪的重要断点(听我背面的注释).

00401BEB   LEA EDX,DWORD PTR SS:[EBP-24]
00401BEE   LEA ECX,DWORD PTR SS:[EBP-34]
00401BF1   CALL DWORD PTR DS:[<&MSVBVM60.__vbaVarCopy>>
;MSVBVM60.__vbaVarCopy

玩了一点新玩意,换了一个函数作传送,将b1的值传送给了b2.见到常数12345678了吗?

00401BF7   LEA EDX,DWORD PTR SS:[EBP-34]
00401BFA   PUSH EDX
00401BFB   CALL DWORD PTR DS:[<&MSVBVM60.__vbaI4Var>]
;MSVBVM60.__vbaI4Var

b2又传送给b3了.见到常数12345678了吗?

00401C01   MOV EBX,EAX
00401C03   MOV EAX,DWORD PTR DS:[ESI]
00401C05   PUSH ESI
00401C06   CALL DWORD PTR DS:[EAX+2FC]
00401C0C   LEA ECX,DWORD PTR SS:[EBP-40]
00401C0F   PUSH EAX
00401C10   PUSH ECX
00401C11   CALL DWORD PTR DS:[<&MSVBVM60.__vbaObjSet>]
;MSVBVM60.__vbaObjSet
00401C17   MOV ESI,EAX
00401C19   PUSH EBX
00401C1A   MOV EDX,DWORD PTR DS:[ESI]
00401C1C   MOV DWORD PTR SS:[EBP-64],EDX
00401C1F   CALL DWORD PTR DS:[<&MSVBVM60.__vbaStrI4>]
;MSVBVM60.__vbaStrI4

将整数转换成字符.见到常数12345678了吗?

00401C25   MOV EDX,EAX
00401C27   LEA ECX,DWORD PTR SS:[EBP-3C]
00401C2A   CALL DWORD PTR DS:[<&MSVBVM60.__vbaStrMove>>
; MSVBVM60.__vbaStrMove

完成转换之后又作了这个行动.见到字符串"12345678了吗?"

00401C30   MOV EDX,DWORD PTR SS:[EBP-64]
00401C33   PUSH EAX
00401C34   PUSH ESI
00401C35   CALL DWORD PTR DS:[EDX+A4]
00401C3B   CMP EAX,EDI
00401C3D   FCLEX
00401C3F   JGE SHORT 典范1.00401C53
00401C41   PUSH 0A4
00401C46   PUSH 典范1.004017A8
00401C4B   PUSH ESI
00401C4C   PUSH EAX
00401C4D   CALL DWORD PTR DS:[<&MSVBVM60.__vbaHresultC>
;MSVBVM60.__vbaHresultCheckObj
00401C53   LEA ECX,DWORD PTR SS:[EBP-3C]
00401C56   CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeStr>>
;MSVBVM60.__vbaFreeStr
00401C5C   LEA ECX,DWORD PTR SS:[EBP-40]
00401C5F   CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeObj>>
;MSVBVM60.__vbaFreeObj
00401C65   MOV DWORD PTR SS:[EBP-4],EDI
00401C68   PUSH 典范1.00401C93
00401C6D   JMP SHORT 典范1.00401C82
00401C6F   LEA ECX,DWORD PTR SS:[EBP-3C]
00401C72   CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeStr>>
;MSVBVM60.__vbaFreeStr
00401C78   LEA ECX,DWORD PTR SS:[EBP-40]
00401C7B   CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeObj>>
;MSVBVM60.__vbaFreeObj
00401C81   RETN

要深化理解VB的操作步骤、操作目标和操作情势,我们需求作细心跟踪才能懂得此中的原理.目前实际跟踪一下,先在"00401BD7 MOV DWORD PTR SS:[EBP-48],0BC614E"处下一个断点,当履行完上面的号令后,数据保存在"0012F4C0 4E 61 BC 00"处.那么0012F4C0处就是b1吗?答复是No,这只不过算作是一个缓存,真正将常数12345678赋值给变量的是下面的语句:

00401BDE   MOV DWORD PTR SS:[EBP-50],3
00401BE5   CALL DWORD PTR DS:[<&MSVBVM60.__vbaVarMove>>
;MSVBVM60.__vbaVarMove

何故见得?我们可以跟入看看.跟入前按照EBP-50=0012F508-50=0012F4B8这个大概定位,将这一部份的内存内容复制出来备考,如图1所示.再看履行完"00401BDE MOV DWORD PTR SS:[EBP-50],3"后的效果,如图2所示.再履行下一号令时采取F7(步入)的办法可来到下面的代码处.
 
图1
  
图2

734998ED   MOV EDX,DWORD PTR DS:[EDI+4]
734998F0   MOV ECX,DWORD PTR DS:[EDI+C]
734998F3   MOV DWORD PTR DS:[ESI],EAX
734998F5   MOV EAX,DWORD PTR DS:[EDI+8]
734998F8   MOV DWORD PTR DS:[ESI+8],EAX
734998FB   MOV DWORD PTR DS:[EDI],0
73499901   MOV DWORD PTR DS:[ESI+4],EDX
73499904   MOV DWORD PTR DS:[ESI+C],ECX

这部份代码就在MSVBVM60(库函数)中.这时可以看到,EDI=0012F4B8,也就是指向3的位置(见图2).所以EDI+4=0012F4BC,指向970108BF.EDI+8=0012F4C0,指向00BC614E,也就是12345678.EDI+C=0012F4C4,指向0.而之前已经将EDI=0012F4B8处的内容读了出来,这个数据的格局为WORD,并保存在EAX中.别的,ESI= 0012F4E4.
目前我们解析一下,函数顺次读取[EDI]、[EDI+4]、[EDI+8]和[EDI+C],并辨别保存到[ESI]、[ESI+4]、[ESI+8]和[ESI+C],并且还将[EDI]清0.这就是基本的操作,这个"变量移动"函数所作的工作不但仅是移动了我们的数据,还作了一些"不该做的"事情.还是看看后果吧,如图3所示.从图中可以看到开始存入的3没有了,在0012F4E4处呈现了新的3,0012F4EC处保存的就是我们的数据.同时也可以看到,这里的数据是按照以下的构造情势举行组织的.
  
图3

Type
Flag1 As Integer
Flag2 As Integer
Data1 AS Long
Data2 As Long
Data3 As Long
End Type

变量的标志放在Flag1中,数据放在Data2中.而今后对数据举行操作,其指针指向的是Flag1,自然也就看不到数据了.而没有了这个标志,VB就会认为这个变量不存在了.我们胡涂,但VB懂得.
有人说,你这恐怕是凭空猜想吧?呵呵,有点儿.VB开辟平台不是我开辟的,我只可以这么看了.这个我们先放下不谈论,看看背面的情形.
b2也是一个变量,履行b2=b1后,b2也应当为这样的构造才对.那就看看吧.
目前预备履行下列指令了:

00401BEB   LEA EDX,DWORD PTR SS:[EBP-24]
00401BEE   LEA ECX,DWORD PTR SS:[EBP-34]
00401BF1   CALL DWORD PTR DS:[<&MSVBVM60.__vbaVarCopy>>
;MSVBVM60.__vbaVarCopy

到00401BF1时,EDX=0012F4E4,ECX=0012F4D4.EDX=0012F4E4?不就是b1的构造吗?莫非函数VarCopy要将0012F4E4中的内容复制给0012F4D4处?也就是说0012F4D4处就是b2?看看履行后的情形,如图4所示.看看两个对应的地方,是不是完好一样?
 
图4
所以我们可以得出结论:VB存储数据是按照构造方法保存的,普通我们见到的地址不是直接指向数据的地址,而是指向标志的地址.这就是VB中普通看不到数据的真正缘由.要想看到这个数据,必须追入这个构造中才可以.
仿佛懂得了点,但是又在犯胡涂.你绕了这么半天,到底想阐明什么事?当然,面包会有的……说走嘴了!
我要说的是,VB在运行中不一定存在加、减、乘、除运算,但一定存在给变量赋值或变量转移的问题.固然我们不能看到后果怎样被转移,但VB却给出了明确的标志MSVBVM60.__vbaVarMove或MSVBVM60.__vbaVarCopy.它就是我在跟踪VB编写的软件时的常用断点;并且,利用了函数MSVBVM60.__vbaVarMove之后,背面会跟随着呈现变量所在地址,如"00401BEB LEA EDX,DWORD PTR SS:[EBP-24]"处.假如你很关心这个数据,要看看也就不是难事了.
这个例子的关键词:变量构造,断点挑选.

[例2]算术与逻辑运算
关于用VB编写的软件,其注册考证因受数据范例的限制,要末都对比简单,要末跟踪就很艰难.因为作为算术运算,VB都试图举行"高保真",也就是计算后果都是精确的(这点与别的编程平台的计算是差别的).假如数的范围超越VB的约定,不是举行溢出丧失处理,而是直接报警,回绝持续往后履行.并且逻辑运算对数据构造的要求更高,只能在正数的范围内举行.所以VB不能安闲地举行数据计算,从而限制了难度.
那为什么又说要末很难呢?因为作者假如要增添计算的难度,必定要编制超越范围的数据计算的模块,这个模块内的事情就不好说了.仅就两个较大的整数相加,其算法你也未必可以看得懂得(耗时吃力啊!).
下面我们来设置三个变量,其构造为Integer(为的是不至于出问题),并举行加、减、除算术运算(乘法简单超范围)和与、或、异或逻辑运算,代码以下.
Private Sub Command1_Click()
Dim b1, b2, b3 As Integer
    
b1 = 12345
b2 = 56789
b3 = b2 + b1
b3 = b3 - b1
b3 = b3 \ b1
    
b3 = b1 And b2
b3 = b1 Or b2
b3 = b1 Xor b2
End Sub

很简单吧?看看编译后的代码.

00401BA9   MOV DWORD PTR SS:[EBP-50],3039
;常数12345
00401BB0   MOV DWORD PTR SS:[EBP-58],2
;标志
00401BB7   CALL ESI
;<&MSVBVM60.__vbaVarMove>

注意这个函数名,今后相同.

00401BB9   LEA EDX,DWORD PTR SS:[EBP-58]
00401BBC   LEA ECX,DWORD PTR SS:[EBP-34]
00401BBF   MOV DWORD PTR SS:[EBP-50],0DDD5
;常数56789
00401BC6   MOV DWORD PTR SS:[EBP-58],3
;标志
00401BCD   CALL ESI

同上.但这里要注意一个问题,b1的标志为2,b2的标志为3,这个标志仿佛与上例中的标志相同.
00401BCF   LEA EAX,DWORD PTR SS:[EBP-34]
00401BD2   LEA ECX,DWORD PTR SS:[EBP-24]
00401BD5   PUSH EAX
00401BD6   LEA EDX,DWORD PTR SS:[EBP-48]
00401BD9   PUSH ECX
00401BDA   PUSH EDX
00401BDB   CALL DWORD PTR DS:[<&MSVBVM60.__vbaVarAdd>]
;MSVBVM60.__vbaVarAdd

以上代码举行变量相加.

00401BE1   MOV ESI,DWORD PTR DS:[<&MSVBVM60.__vbaI2Var>
;MSVBVM60.__vbaI2Var
00401BE7   PUSH EAX
00401BE8   CALL ESI
;<&MSVBVM60.__vbaI2Var>

后果返回给变量b3,以下同.

00401BEA   LEA ECX,DWORD PTR SS:[EBP-48]
00401BED   MOV EBX,EAX
00401BEF   CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeVar>>
;MSVBVM60.__vbaFreeVar
00401BF5   LEA EAX,DWORD PTR SS:[EBP-58]
00401BF8   LEA ECX,DWORD PTR SS:[EBP-24]
00401BFB   MOV WORD PTR SS:[EBP-50],BX
00401BFF   PUSH EAX
00401C00   LEA EDX,DWORD PTR SS:[EBP-48]
00401C03   MOV EBX,2
00401C08   PUSH ECX
00401C09   PUSH EDX
00401C0A   MOV DWORD PTR SS:[EBP-58],EBX
00401C0D   CALL DWORD PTR DS:[<&MSVBVM60.__vbaVarSub>]
;MSVBVM60.__vbaVarSub

以上代码举行变量相减运算.

00401C13   PUSH EAX
00401C14   CALL ESI

后果返回给b3.

00401C16   MOV WORD PTR SS:[EBP-50],AX
00401C1A   LEA EAX,DWORD PTR SS:[EBP-58]
00401C1D   LEA ECX,DWORD PTR SS:[EBP-24]
00401C20   PUSH EAX
00401C21   LEA EDX,DWORD PTR SS:[EBP-48]
00401C24   PUSH ECX
00401C25   PUSH EDX
00401C26   MOV DWORD PTR SS:[EBP-58],EBX
00401C29   CALL DWORD PTR DS:[<&MSVBVM60.__vbaVarIdiv>>
;MSVBVM60.__vbaVarIdiv

以上代码举行变量相除运算.

00401C2F   PUSH EAX
00401C30   CALL ESI

后果返回给b3.

00401C32   LEA EAX,DWORD PTR SS:[EBP-24]
00401C35   LEA ECX,DWORD PTR SS:[EBP-34]
00401C38   PUSH EAX
00401C39   LEA EDX,DWORD PTR SS:[EBP-48]
00401C3C   PUSH ECX
00401C3D   PUSH EDX
00401C3E   CALL DWORD PTR DS:[<&MSVBVM60.__vbaVarAnd>]
;MSVBVM60.__vbaVarAnd

以上代码举行变量and运算.

00401C44   PUSH EAX
00401C45   CALL ESI

后果返回给b3.

00401C47   LEA EAX,DWORD PTR SS:[EBP-24]
00401C4A   LEA ECX,DWORD PTR SS:[EBP-34]
00401C4D   PUSH EAX
00401C4E   LEA EDX,DWORD PTR SS:[EBP-48]
00401C51   PUSH ECX
00401C52   PUSH EDX
00401C53   CALL DWORD PTR DS:[<&MSVBVM60.__vbaVarOr>]
;MSVBVM60.__vbaVarOr

以上代码举行变量or运算.

00401C59   PUSH EAX
00401C5A   CALL ESI

后果返回给b3.

00401C5C   LEA EAX,DWORD PTR SS:[EBP-24]
00401C5F   LEA ECX,DWORD PTR SS:[EBP-34]
00401C62   PUSH EAX
00401C63   LEA EDX,DWORD PTR SS:[EBP-48]
00401C66   PUSH ECX
00401C67   PUSH EDX
00401C68   CALL DWORD PTR DS:[<&MSVBVM60.__vbaVarXor>]
;MSVBVM60.__vbaVarXor

以上代码举行变量xor运算.

00401C6E   PUSH EAX
00401C6F   CALL ESI

后果返回给b3.

00401C71   MOV DWORD PTR SS:[EBP-4],EDI
00401C74   PUSH 典范2_算.00401C96
00401C79   JMP SHORT 典范2_算.00401C85

目前我们来扼要的归纳一下.
1)        上面的每一步运算前,都有"PUSH ECX"和"PUSH EDX"的行动,ECX和EDX中的内容是什么?
2)        为什么b1和b2的标志不一样?
3)这段代码能正常运行吗?
第三个问题很简单答复,在VB环境下调试可知,不能运行.缘由很简单,Integer范例的数据中最大的正整数为32767.而此时b2=56789,明显大于32767,计算时必定会有溢出报警.故可将56789改正成5678,再加以察看.同时,背面的逻辑运算都不能举行,缘由相同.
第二个问题中,因为56789已经超越了Integer的范围,所以b2当作Long型数据来保存了.改正数据后再看代码.

00401B97   MOV EBX,2 ;标志,已集合管理了
00401B9C   MOV DWORD PTR SS:[EBP-58],EDI
00401B9F   LEA EDX,DWORD PTR SS:[EBP-58]
00401BA2   LEA ECX,DWORD PTR SS:[EBP-24]
00401B7747.net   MOV DWORD PTR SS:[EBP-24],EDI
00401BA8   MOV DWORD PTR SS:[EBP-34],EDI
00401BAB   MOV DWORD PTR SS:[EBP-48],EDI
00401BAE   MOV DWORD PTR SS:[EBP-50],3039
;12345
00401BB5   MOV DWORD PTR SS:[EBP-58],EBX
;标志
00401BB8   CALL ESI
;<&MSVBVM60.__vbaVarMove>
00401BBA   LEA EDX,DWORD PTR SS:[EBP-58]
00401BBD   LEA ECX,DWORD PTR SS:[EBP-34]
00401BC0   MOV DWORD PTR SS:[EBP-50],162E
;5678
00401BC7   MOV DWORD PTR SS:[EBP-58],EBX
;标志
00401BCA   CALL ESI

看来,VB还是蛮细心的.第一个问题不好直接答复,那就跟踪一下.当给变量b1、b2赋值之后,b1、b2的地址辨别为:

b10012F4E4
b20012F4D4

结论:变量在内存中的安置办法是按照从右到左的次序举行的.即按照先定义的后安置,后定义的先安置的情势举行,所今后定义的地址较小.
当给变量赋值之后,我们注意一下ECX和EDX中装的是什么.
作加法时:ECX=0012F4E4,b1的地址.EDX=0012F4C0(猜想为b3的地址).EAX=0012F4D4,b2的地址(已入栈).实行证明0012F4C0确切是b3的地址.所以函数的参数就是(b2,b1,b3).有了这样的解析,你还愁VB的代码看不懂吗?
我想,别的的就不用再解析了吧!
本例要掌握的关键词:参与运算的数据在那边,数据格局标志,常见算术逻辑运算函数辨认.
表示:你可曾注意到MSVBVM60.__vbaI4Var与MSVBVM60.__vbaI2Var有何差别?我的理解,4-2是指的数据在内存中的长度,4字节或两字节.
结论:VB程序在举行算术或逻辑运算时,传送给库函数的参数不是直接数,也不是数据的指针,而是构造的指针.故而,大大都情形下不能直接察看到运算前后的数据,但可以追索到后果的存放位置.

[例3]数组与数组重定义
定义数组与重新定义数组是编程人员常常作的事情,怎么辨认呢?看源代码.

Private Sub Command1_Click()
Dim b2(), b1(4) As Integer
    
b1(0) = 5: b1(1) = 4: b1(2) = 3: b1(3) = 2: b1(4) = 1
ReDim b2(4)
b2(0) = 1234
End Sub

我事前定义了5个元素的数组b1,再给它们赋值;然后将数组b2扩大至5个元素.为便于跟踪,仅对第一个元素赋值.编译后的有关代码以下.

00401AAF   PUSH EAX  ;注意这个数据
00401AB0   MOV DWORD PTR SS:[EBP-34],EDI
00401AB3   MOV DWORD PTR SS:[EBP-44],EDI
00401AB6   MOV DWORD PTR SS:[EBP-48],EDI
00401AB9   CALL DWORD PTR DS:[<&MSVBVM60.__vbaAryConst>;

函数 vbaAryConstruct2奉告我们定义了一个数组构造.为什么不是数组,而是数组构造?看看图5就会懂得了吧?
  
图5
注意几个地方:0012F4E0处,Integer数据范例标志.0012F4EC,数组长度标志.0012F4DC处表示什么不清楚.0012F4E8处是关键,它就是存放数组的元素的地方.履行完函数之后的代码以下.

00401ABF   MOV ECX,DWORD PTR SS:[EBP-20]
;数组指针
00401AC2   PUSH EDI
00401AC3   PUSH 4
00401AC5   PUSH 1
00401AC7   MOV WORD PTR DS:[ECX],5
;填充第一个单元
00401ACC   MOV EDX,DWORD PTR SS:[EBP-20]
00401ACF   PUSH 0C
00401AD1   MOV WORD PTR DS:[EDX+2],4
;填充第二个单元
00401AD7   MOV EAX,DWORD PTR SS:[EBP-20]
00401ADA   MOV WORD PTR DS:[EAX+4],3
;填充第三个单元
00401AE0   MOV ECX,DWORD PTR SS:[EBP-20]
00401AE3   LEA EAX,DWORD PTR SS:[EBP-34]
00401AE6   MOV WORD PTR DS:[ECX+6],SI
;填充第四个单元
00401AEA   MOV EDX,DWORD PTR SS:[EBP-20]
00401AED   PUSH EAX
00401AEE   PUSH 10
00401AF0   PUSH 880
00401AF5   MOV WORD PTR DS:[EDX+8],1
;填充第五个单元

跟入00150818看看,如图6所示,可谓是一目了然.
  
图6

接下来的这个函数好眼熟,哦,重新定义数组.

00401AFB   CALL DWORD PTR DS:[<&MSVBVM60.__vbaRedim>]
;MSVBVM60.__vbaRedim
00401B01   MOV ECX,DWORD PTR SS:[EBP-34]
00401B04   ADD ESP,1C
00401B07   CMP ECX,EDI
00401B09   MOV DWORD PTR SS:[EBP-3C],4D2
;这不就是1234吗?

这里怎么不像上面那样给数组赋值,重定义都干了些什么?
任何编译平台大概都有些烦心事,VB也不例外.实际上,VB将给数组赋值和重新定义数组两件事混合着办,搅乱了我们的视野.重新组织一下代码.

00401AC3   PUSH 4
00401AC5   PUSH 1
00401ACF   PUSH 0C
00401AE3   LEA EAX,DWORD PTR SS:[EBP-34]
00401AED   PUSH EAX
00401AEE   PUSH 10
00401AF0   PUSH 880
00401AFB   CALL DWORD PTR DS:[<&MSVBVM60.__vbaRedim>]
;MSVBVM60.__vbaRedim

注意到只有EAX中存放的大概是地址,莫非它就是数组的地址?履行函数vbaRedim后我们发现,[0012F4D4]=001528B8.来到001528B8处,等候后续的代码的履行.当我们履行完毕后,情形没有像我们预想的那样,在001528B8处见到我们的数组元素,反而是通事背面代码.

00401B20   MOV ECX,DWORD PTR DS:[ECX+C]
00401B23   LEA EDX,DWORD PTR SS:[EBP-44]
00401B26   ADD ECX,EAX
00401B28   CALL DWORD PTR DS:[<&MSVBVM60.__vbaVarMove>>
;MSVBVM60.__vbaVarMove

完成数组的组建.后果如图7所示.这是一种构造型数据组织方法,与图3非常类似.
  
图7
假如全部赋值,情形又若何?改正代码代码略)后看看内存状况,如图8所示.这回算是看清楚了,重新定义后的数组,其元素在内存中不是次序存放的,而是按照构造来举行存放的.因为我安置的值辨别是1234、2234、3234、4234、5234,辨别对应着042D、08BA、0CA2、108A、1472.呵呵,你到那边去找数据啊!
  
图8
问题:动态数组是若何组织的?
为理解答这个问题,首先看看函数vbaRedim必须的参数:(0x880,0x10,EAX,0xC,1,4).此中4与数组的大小有关,0xC是构造的某个标志,EAX部份是一个指针(不一定为EAX),别的的我也说不清参数的意义.怎么去查看这个动态数组的构造呢?
第一步,跟踪EAX.
履行函数vbaRedim前(参数已传送),在转存中跟随EAX.比方,目前EAX=0012F4D4,则跟随后如图9所示.接着履行函数vbaRedim,这个位置的后果变成了图10中的内容.很明显,被00150640填充.紧随着这个函数背面的语句是"00401B01 MOV ECX,DWORD PTR SS:[EBP-34]",刚好要传送给ECX的参数就是00150640,不会那么刚巧吧?好,跟随到00150640,后果如图11所示.
  
图9
  
图10
  
图11
这里的一些数据是不是有些眼熟?对,就是履行函数前传送的部份参数.而在这些参数的背面又有一个数据:00150670,所指的位置就在背面不远处.这个数占据效吗?看看数组被填充后的情形,如图12所示.这就是我们给数组赋值的后果.
  
图12
目前我们来理理眉目.重新定义数组时,EAX给出的是一个指针1,这个指针1指向另一个指针2,指针2指向一个构造,这个构造中包含了真正的数组的指针.故指针1是一个指针的指针,指针2是构造的指针,构造中包含的指针指向动态数组.
知道了这些,在VB中看不到数据你不冤枉,因为没人奉告我们怎么去看VB中的数据.假如你还没有看懂得,那就看看图13中的对应关系吧.假如还不懂得,那就自己弄一个VB的东西跟踪看看就简单学会了.
  
图13
结论:静态数组中的元素是次序存放的,但数组的地址在一个构造中;动态数组采取了多级指针系统,数组中的数据不是次序存放的,各安闲一个构造中,而这个构造元素是次序存放的.

完毕语
其实,VB的东西跟踪不艰难,因为VB的东西也就起着管理的作用,具体事物普通都是交给库函数来完成,操作对比显眼.探求符合的断点对比关键,我就常用VarMove函数作断点.艰难的是VB中数据的察看,没有一定的侦查本领(因为没有现成的教程)是不简单做到的.有时为了搞清楚参与计算的是哪些数据,不得不跟入库函数,但库函数中的标志也是很明显的,简单辨认.关于这点,别的平台编译的东西以及API反倒不如它简单看得懂得.
好了,我该歇息一下了,咱们回见吧


  以上是“VB程序的跟踪与解析技术[网络技术]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
  • VB程序的跟踪与解析技术
  • 本文地址: 与您的QQ/BBS好友分享!
    • 好的评价 如果您觉得此文章好,就请您
        0%(0)
    • 差的评价 如果您觉得此文章差,就请您
        0%(0)

    文章评论评论内容只代表网友观点,与本站立场无关!

       评论摘要(共 0 条,得分 0 分,平均 0 分) 查看完整评论
    Copyright © 2020-2022 www.xiamiku.com. All Rights Reserved .