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的地址辨别为:
b10012F4E4
b20012F4D4
结论:变量在内存中的安置办法是按照从右到左的次序举行的.即按照先定义的后安置,后定义的先安置的情势举行,所今后定义的地址较小.
当给变量赋值之后,我们注意一下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程序的跟踪与解析技术[网络技术]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |