网络上的一段代码,代码里运用APIHook来绕过VBA的密码保护机制,在VBE中可以直接查看加密的VBA工程而不需要密码验证。今天有空,对原作者代码做了一定的修改,使其可以运行于64位的Office之上,完美支持32和64位Office。

Office Excel 破解 VBA 密码保护_Office Excel 免密查看 VBA 代码

具体代码如下:

‘***************************************************************************

‘*

‘*MODULENAME:ProtectedVBAprojectPicklock(PVP)

‘*AUTHOR&DATE:tt.t

‘*23April2007

‘*ModifiedByMarsWang

‘*www.office26.com

‘*E-Mail:ttui(AT)163.com,sohu邮箱垃圾邮件太多已经不用了

‘*

‘*Usage:运行FrmHookMain窗口,点补丁,然后双击工程窗口中有密码保护的模块

‘*应该能够直接打开了:)

‘*

‘*

‘*DESCRIPTION:在写中文字符串转换为拼音函数(HzToPy)过程中,第一次发现VBA功能的强大.

‘*于是这次尝试将其他语言中比较好写的APIHOOK移植成VBA代码,

‘*正好顺便把VBA密码保护去掉,喜欢加密码的朋友不要生气啊:)

‘*总的来说VBA的写法和其他语言区别不大,但VBA毕竟不太方便,代码必须放在标准模块中.

‘*再有就是对指针的支持实在有限,于是最后选择了一种写起来最简单的APIhook方法,

‘*就是所谓的陷阱法.如果你不太清楚什么是APIHOOK,请求助于google.

‘*

‘*Theory:这里就不说APIhook的方法了,都是传统方法没什么可说的,这里只

‘*简单说下VBA模块密码破解.其实这些我也不是很了解,毕竟知道加密过程

‘*用处不大,这个问题上我比较关心结果:)

‘*判断有密码以及提示输入密码都是VBE6.dll干得好事.如果有密码,

‘*VBE6.dll会调用DialogBoxParamA显示VB6INTL.dll资源中的第4070号

‘*对话框(就是那个输入密码的窗口),若DialogBoxParamA返回值非0,

‘*则VBE会认为密码正确,然后乖乖展开加密模块的资源.很显然其中存在很大

‘*漏洞,就像给日记本加上了锁,但里面全是活页,我们不需要打开锁,只要从侧面

‘*取出活页就可以了.这个从侧面取活页的过程就是hook住DialogBoxParamA函数,

‘*若程序调用DialogBoxParamA装入4070号对话框,我们就直接返回1,让

‘*VBE以为密码正确.

‘*

‘*PS:PVP是在一个叫AdvancedVBAPasswordRecovery(AVPR)的软件启发下

‘*作出来的,AVPR提供了一个VBABackdoor功能就是跳过密码直接查看工程资源.

‘*它的原理和PVP一样,但用了通用性比较差的方法,适用系统比较有限,而PVP的方法

‘*理论上适用于所有采用第4070号对话框录入密码的Office系统.

‘*

‘*

‘***************************************************************************

OptionExplicit

DimHookBytes(0To5)AsByte

DimOriginBytes(0To5)AsByte

DimFlagAsBoolean

#IfWin64Then

DimpFuncAsLongPtr

PrivateDeclarePtrSafeSubMoveMemoryLib"kernel32"Alias"RtlMoveMemory"_

(DestinationAsAny,SourceAsAny,ByValLengthAsLongPtr)

PrivateDeclarePtrSafeFunctionVirtualProtectLib"kernel32"(lpAddressAsAny,_

ByValdwSizeAsLongPtr,ByValflNewProtectAsLong,lpflOldProtectAsLong)AsLong

PrivateDeclarePtrSafeFunctionGetModuleHandleALib"kernel32"(ByVallpModuleNameAsString)AsLongPtr

PrivateDeclarePtrSafeFunctionGetProcAddressLib"kernel32"(ByValhModuleAsLongPtr,_

ByVallpProcNameAsString)AsLongPtr

PrivateDeclarePtrSafeFunctionDialogBoxParamLib"user32"Alias"DialogBoxParamA"(ByValhInstanceAsLongPtr,_

ByValpTemplateNameAsLong,ByValhWndParentAsLongPtr,_

ByVallpDialogFuncAsLongPtr,ByValdwInitParamAsLongPtr)AsLong

PrivateFunctionGetPtr(ByValValueAsLongPtr)AsLongPtr

‘获得函数的地址

GetPtr=Value

EndFunction

#Else

DimpFuncAsLong

PrivateDeclareSubMoveMemoryLib"kernel32"Alias"RtlMoveMemory"_

(DestinationAsLong,SourceAsLong,ByValLengthAsLong)

PrivateDeclareFunctionVirtualProtectLib"kernel32"(lpAddressAsLong,_

ByValdwSizeAsLong,ByValflNewProtectAsLong,lpflOldProtectAsLong)AsLong

PrivateDeclareFunctionGetModuleHandleALib"kernel32"(ByVallpModuleNameAsString)AsLong

PrivateDeclareFunctionGetProcAddressLib"kernel32"(ByValhModuleAsLong,_

ByVallpProcNameAsString)AsLong

PrivateDeclareFunctionDialogBoxParamLib"user32"Alias"DialogBoxParamA"(ByValhInstanceAsLong,_

ByValpTemplateNameAsLong,ByValhWndParentAsLong,_

ByVallpDialogFuncAsLong,ByValdwInitParamAsLong)AsInteger

PrivateFunctionGetPtr(ByValValueAsLong)AsLong

‘获得函数的地址

GetPtr=Value

EndFunction

#EndIf

PublicSubRecoverBytes()

‘若已经hook,则恢复原API开头的6字节,也就是恢复原来函数的功能

IfFlagThenMoveMemoryByValpFunc,ByValVarPtr(OriginBytes(0)),6

EndSub

PublicFunctionHook()AsBoolean

DimTmpBytes(0To5)AsByte

#IfWin64Then

DimpAsLongPtr

#Else

DimpAsLong

#EndIf

DimOriginProtectAsLong

Hook=False

‘VBE6.dll调用DialogBoxParamA显示VB6INTL.dll资源中的第4070号对话框(就是输入密码的窗口)

‘若DialogBoxParamA返回值非0,则VBE会认为密码正确,所以我们要hookDialogBoxParamA函数

pFunc=GetProcAddress(GetModuleHandleA("user32.dll"),"DialogBoxParamA")

‘标准apihook过程之一:修改内存属性,使其可写

IfVirtualProtect(ByValpFunc,6,&H40,OriginProtect)<>0Then

‘标准apihook过程之二:判断是否已经hook,看看API的第一个字节是否为&H68,

‘若是则说明已经Hook

MoveMemoryByValVarPtr(TmpBytes(0)),ByValpFunc,6

IfTmpBytes(0)<>&H68Then

‘标准apihook过程之三:保存原函数开头字节,这里是6个字节,以备后面恢复

MoveMemoryByValVarPtr(OriginBytes(0)),ByValpFunc,6

‘用AddressOf获取MyDialogBoxParam的地址

‘因为语法不允许写成p=AddressOfMyDialogBoxParam,这里我们写一个函数

‘GetPtr,作用仅仅是返回AddressOfMyDialogBoxParam的值,从而实现将

‘MyDialogBoxParam的地址付给p的目的

p=GetPtr(AddressOfMyDialogBoxParam)

‘标准apihook过程之四:组装API入口的新代码

‘HookBytes组成如下汇编

‘pushMyDialogBoxParam的地址

‘ret

‘作用是跳转到MyDialogBoxParam函数

HookBytes(0)=&H68

MoveMemoryByValVarPtr(HookBytes(1)),ByValVarPtr(p),4

HookBytes(5)=&HC3

‘标准apihook过程之五:用HookBytes的内容改写API前6个字节

MoveMemoryByValpFunc,ByValVarPtr(HookBytes(0)),6

‘设置hook成功标志

Flag=True

Hook=True

EndIf

EndIf

EndFunction

PrivateFunctionMyDialogBoxParam(ByValhInstanceAsLong,_

ByValpTemplateNameAsLong,ByValhWndParentAsLong,_

ByVallpDialogFuncAsLong,ByValdwInitParamAsLong)AsInteger

IfpTemplateName=4070Then

‘有程序调用DialogBoxParamA装入4070号对话框,这里我们直接返回1,让

‘VBE以为密码正确了

MyDialogBoxParam=1

Else

‘有程序调用DialogBoxParamA,但装入的不是4070号对话框,这里我们调用

‘RecoverBytes函数恢复原来函数的功能,在进行原来的函数

RecoverBytes

MyDialogBoxParam=DialogBoxParam(hInstance,pTemplateName,_

hWndParent,lpDialogFunc,dwInitParam)

‘原来的函数执行完毕,再次hook

Hook

EndIf

EndFunction

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注