有没有办法破解Excel VBA项目的密码?

我被要求更新一些Excel 2003的macros,但VBA项目是密码保护,似乎缺乏文档…没有人知道密码。

有没有办法在VBA项目上删除或破解密码?

你可以试试这个不需要HEX编辑的直接VBA方法。 它将适用于任何文件(* .xls,* .xlsm,* .xlam …)。

testing和工作

Excel 2007
Excel 2010
Excel 2013 – 32位版本
Excel 2016 – 32位版本

寻找64位版本? 请参阅https://stackoverflow.com/a/31005696/4342479

怎么运行的

我会尽我所能解释它是如何工作的 – 请原谅我的英语。

  1. VBE将调用一个系统函数来创build密码对话框。
  2. 如果用户input正确的密码并单击确定,此函数返回1.如果用户input了错误的密码或单击取消,此函数返回0。
  3. 对话框closures后,VBE检查系统函数的返回值
  4. 如果此值为1,VBE将“认为”密码正确,因此locking的VBA项目将被打开。
  5. 下面的代码将原来用来显示密码对话框的函数的内存与一个用户定义的函数交换,当被调用时它将总是返回1。

使用代码

  1. 打开包含已locking的VBA项目的文件
  2. 创build一个新的xlsm文件并将此代码存储在Module 1中

    code credited to Siwtom (nick name), a Vietnamese developer

     Option Explicit Private Const PAGE_EXECUTE_READWRITE = &H40 Private Declare Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" _ (Destination As Long, Source As Long, ByVal Length As Long) Private Declare Function VirtualProtect Lib "kernel32" (lpAddress As Long, _ ByVal dwSize As Long, ByVal flNewProtect As Long, lpflOldProtect As Long) As Long Private Declare Function GetModuleHandleA Lib "kernel32" (ByVal lpModuleName As String) As Long Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, _ ByVal lpProcName As String) As Long Private Declare Function DialogBoxParam Lib "user32" Alias "DialogBoxParamA" (ByVal hInstance As Long, _ ByVal pTemplateName As Long, ByVal hWndParent As Long, _ ByVal lpDialogFunc As Long, ByVal dwInitParam As Long) As Integer Dim HookBytes(0 To 5) As Byte Dim OriginBytes(0 To 5) As Byte Dim pFunc As Long Dim Flag As Boolean Private Function GetPtr(ByVal Value As Long) As Long GetPtr = Value End Function Public Sub RecoverBytes() If Flag Then MoveMemory ByVal pFunc, ByVal VarPtr(OriginBytes(0)), 6 End Sub Public Function Hook() As Boolean Dim TmpBytes(0 To 5) As Byte Dim p As Long Dim OriginProtect As Long Hook = False pFunc = GetProcAddress(GetModuleHandleA("user32.dll"), "DialogBoxParamA") If VirtualProtect(ByVal pFunc, 6, PAGE_EXECUTE_READWRITE, OriginProtect) <> 0 Then MoveMemory ByVal VarPtr(TmpBytes(0)), ByVal pFunc, 6 If TmpBytes(0) <> &H68 Then MoveMemory ByVal VarPtr(OriginBytes(0)), ByVal pFunc, 6 p = GetPtr(AddressOf MyDialogBoxParam) HookBytes(0) = &H68 MoveMemory ByVal VarPtr(HookBytes(1)), ByVal VarPtr(p), 4 HookBytes(5) = &HC3 MoveMemory ByVal pFunc, ByVal VarPtr(HookBytes(0)), 6 Flag = True Hook = True End If End If End Function Private Function MyDialogBoxParam(ByVal hInstance As Long, _ ByVal pTemplateName As Long, ByVal hWndParent As Long, _ ByVal lpDialogFunc As Long, ByVal dwInitParam As Long) As Integer If pTemplateName = 4070 Then MyDialogBoxParam = 1 Else RecoverBytes MyDialogBoxParam = DialogBoxParam(hInstance, pTemplateName, _ hWndParent, lpDialogFunc, dwInitParam) Hook End If End Function 
  3. 将此代码粘贴到Module2中并运行它

     Sub unprotected() If Hook Then MsgBox "VBA Project is unprotected!", vbInformation, "*****" End If End Sub 
  4. 回到您的VBA项目,享受。

是的,只要您使用.xls格式的电子表格(Excel到2003年的默认值)。 对于Excel 2007以后,默认是.xlsx ,这是一种相当安全的格式,并且这种方法不起作用。

正如特雷布所说,这是一个简单的比较。 一种方法是使用hex编辑器简单地换出文件中的密码条目(请参阅Windows的hex编辑器 )。 一步一步的例子:

  1. 创build一个新的简单的Excel文件。
  2. 在VBA部分,设置一个简单的密码(如 – 1234)。
  3. 保存该文件并退出。 然后检查文件大小 – 请参阅Stewbob的陷阱
  4. 用hex编辑器打开刚刚创build的文件。
  5. 复制以下键开始的行:

     CMG=.... DPB=... GC=... 
  6. 第一次备份 excel文件,你不知道的VBA密码,然后用你的hex编辑器打开它,并从虚拟文件粘贴上述复制的行。

  7. 保存excel文件并退出。
  8. 现在,打开需要查看VBA代码的excel文件。VBA代码的密码将简单地为1234(如我在这里展示的示例)。

如果您需要使用Excel 2007或2010,下面还有一些其他答案可能有所帮助,特别是: 1,2,3 。

编辑 2015年2月:另一种看起来很有希望的方法,请看ĐứcThanhNguyễn的这个新的答案

还有一个(稍微简单些)解决scheme,没有大小的问题。 今天我使用这种方法(在2003 XLS文件上,使用Excel 2007),并且成功了。

  1. 备份xls文件
  2. 在HEX编辑器中打开文件并findDPB=...部分
  3. DPB=...string更改为DPx=...
  4. 在Excel中打开xls文件
  5. 打开VBA编辑器( ALT + F11
  6. 魔法: Excel发现一个无效的键(DPx),并询问是否要继续加载项目(基本上忽略保护)
  7. 您将可以覆盖密码,因此请将其更改为您可以记住的内容
  8. 保存xls文件*
  9. closures并重新打开文档,并使用VBA魔法!

*注意:确保您已将密码更改为新值,否则下次打开电子表格时,Excel将报告错误(意外错误),然后当您访问VBA模块列表时,您将看到源模块,但尝试打开表单/代码/等时收到另一个错误。 要解决这个问题,请返回到VBA项目属性并将密码设置为新值。 保存并重新打开Excel文档,你应该很好去!

我build立在ĐứcThanhNguyễn的梦幻般的答案,允许此方法与64位版本的Excel一起工作。 我在64位Windows 7上运行Excel 2010 64位。

  1. 打开包含已locking的VBA项目的文件。
  2. 创build一个新的xlsm文件并将此代码存储在Module 1中

     Option Explicit Private Const PAGE_EXECUTE_READWRITE = &H40 Private Declare PtrSafe Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" _ (Destination As LongPtr, Source As LongPtr, ByVal Length As LongPtr) Private Declare PtrSafe Function VirtualProtect Lib "kernel32" (lpAddress As LongPtr, _ ByVal dwSize As LongPtr, ByVal flNewProtect As LongPtr, lpflOldProtect As LongPtr) As LongPtr Private Declare PtrSafe Function GetModuleHandleA Lib "kernel32" (ByVal lpModuleName As String) As LongPtr Private Declare PtrSafe Function GetProcAddress Lib "kernel32" (ByVal hModule As LongPtr, _ ByVal lpProcName As String) As LongPtr Private Declare PtrSafe Function DialogBoxParam Lib "user32" Alias "DialogBoxParamA" (ByVal hInstance As LongPtr, _ ByVal pTemplateName As LongPtr, ByVal hWndParent As LongPtr, _ ByVal lpDialogFunc As LongPtr, ByVal dwInitParam As LongPtr) As Integer Dim HookBytes(0 To 5) As Byte Dim OriginBytes(0 To 5) As Byte Dim pFunc As LongPtr Dim Flag As Boolean Private Function GetPtr(ByVal Value As LongPtr) As LongPtr GetPtr = Value End Function Public Sub RecoverBytes() If Flag Then MoveMemory ByVal pFunc, ByVal VarPtr(OriginBytes(0)), 6 End Sub Public Function Hook() As Boolean Dim TmpBytes(0 To 5) As Byte Dim p As LongPtr Dim OriginProtect As LongPtr Hook = False pFunc = GetProcAddress(GetModuleHandleA("user32.dll"), "DialogBoxParamA") If VirtualProtect(ByVal pFunc, 6, PAGE_EXECUTE_READWRITE, OriginProtect) <> 0 Then MoveMemory ByVal VarPtr(TmpBytes(0)), ByVal pFunc, 6 If TmpBytes(0) <> &H68 Then MoveMemory ByVal VarPtr(OriginBytes(0)), ByVal pFunc, 6 p = GetPtr(AddressOf MyDialogBoxParam) HookBytes(0) = &H68 MoveMemory ByVal VarPtr(HookBytes(1)), ByVal VarPtr(p), 4 HookBytes(5) = &HC3 MoveMemory ByVal pFunc, ByVal VarPtr(HookBytes(0)), 6 Flag = True Hook = True End If End If End Function Private Function MyDialogBoxParam(ByVal hInstance As LongPtr, _ ByVal pTemplateName As LongPtr, ByVal hWndParent As LongPtr, _ ByVal lpDialogFunc As LongPtr, ByVal dwInitParam As LongPtr) As Integer If pTemplateName = 4070 Then MyDialogBoxParam = 1 Else RecoverBytes MyDialogBoxParam = DialogBoxParam(hInstance, pTemplateName, _ hWndParent, lpDialogFunc, dwInitParam) Hook End If End Function 
  3. 将此代码粘贴到Module2中并运行它

     Sub unprotected() If Hook Then MsgBox "VBA Project is unprotected!", vbInformation, "*****" End If End Sub 

免责声明这为我工作,我已经logging在这里,希望它可以帮助别人。 我还没有完全testing它 。 在继续此选项之前,请确保保存所有打开的文件。

科林·皮卡德(Colin Pickard)有一个很好的答案,但是有一个“小心”的。 有一些实例(我还没有找出原因),文件中“CMG = …….. GC = ….”条目的总长度与从一个excel文件到下一个。 在某些情况下,这个条目将是137个字节,而在其他情况下,它将是143个字节。 137字节的长度是奇数,如果用'1234'密码创build文件时发生这种情况,只需创build另一个文件,并跳到143字节长度。

如果您尝试将错误数量的字节粘贴到文件中,则当您尝试使用Excel打开文件时,将会丢失VBA项目。

编辑

这对Excel 2007/2010文件无效。 标准的.xlsx文件格式实际上是一个.zip文件,其中包含许多具有格式,布局,内容等的子文件夹,存储为xml数据。 对于不受保护的Excel 2007文件,只需将.xlsx扩展名更改为.zip,然后打开zip文件并查看所有xml数据即可。 这非常简单。

但是,当您使用密码保护Excel 2007文件时,整个.zip(.xlsx)文件实际上是使用RSAencryption进行encryption的。 不再可以将扩展名更改为.zip并浏览文件内容。

值得指出的是,如果您有一个Excel 2007(xlsm)文件,那么您可以简单地将其保存为Excel 2003(xls)文件,并使用其他答案中概述的方法。

对于.xlsm你需要做一个稍微不同的方法。

  1. .xlsm文件的扩展名更改为.zip
  2. 打开.zip文件(使用WinZip或WinRar等)并转到xl文件夹。
  3. 提取vbaProject.bin文件并在hex编辑器中打开它。
  4. searchDPB并用DPxreplace并保存该文件。
  5. 用压缩文件中的这个新的replace旧的vbaProject.bin文件。
  6. 将文件扩展名更改回.xlsm
  7. 打开工作簿跳过警告消息。
  8. 在Excel中打开Visual Basic。
  9. 转到工具> VBAProject属性>保护选项卡。
  10. input新的密码并保存.xlsm文件。
  11. closures并重新打开,您的新密码将工作。

你有没有尝试过在OpenOffice.org中打开它们?

前段时间我也有类似的问题,发现Excel和Calc不了解对方的encryption,所以可以直接访问所有的东西。

这是一段时间以前,所以如果这不仅仅是我的侥幸,它也可能已经修补。

对于Excel 2007以后,您需要将您的文件扩展名更改为.zip。在存档中有一个子文件夹xl,在那里您会findvbaProject.bin。 按照上面的步骤使用vbaProject.bin,然后将其保存回档案中。 修改你的扩展名和瞧! (意思是按照上面的步骤)

Colin Pickard大部分都是正确的,但不要混淆整个文件的“密码打开”保护与VBA密码保护,这是与前者完全不同的,对于Office 2003和2007是一样的(对于Office 2007,重新命名该文件为.zip并查找zip中的vbaProject.bin)。 而从技术上讲,编辑文件的正确方法是使用像CFX这样的OLE复合文档查看器来打开正确的stream。 当然,如果你只是replace字节,普通的旧二进制编辑器可能会工作。

顺便说一句,如果你想知道这些字段的确切格式,他们现在logging下来:

http://msdn.microsoft.com/en-us/library/dd926151%28v=office.12%29.aspx

如果您的“已知密码”文件中的CMG="XXXX"\r\nDPB="XXXXX"\r\nGC="XXXXXX"的块比“未知密码”文件中的现有块短,你的hexstring与尾部零到达正确的长度。

例如

CMG="xxxxxx"\r\nDPB="xxxxxxxx"\r\nGC="xxxxxxxxxx"

在未知的密码文件中,应该设置为

CMG="XXXX00"\r\nDPB="XXXXX000"\r\nGC="XXXXXX0000"以保留文件长度。

我也有办公室2007年与.XLA(97/2003格式)文件的工作。

如果该文件是一个有效的zip文件(前几个字节是50 4B – 用于.xlsm格式),则解压该文件并查找子文件xl/vbaProject.bin 。 这是一个CFB文件,就像.xls文件一样。 按照XLS格式(应用于子文件)的说明进行操作,然后将内容压缩。

对于XLS格式,您可以按照这篇文章中的其他一些方法。 我个人更喜欢searchDPB=块并replace文本

 CMG="..." DPB="..." GC="..." 

与空白空间。 这消除了CFB容器尺寸问题。

汤姆 – 我最初犯了一个小学生错误,因为我没有看字节大小,而是从“CMG”复制并粘贴到后面的条目。 尽pipe这两个文件之间有两种不同的文本大小,但正如Stewbob所警告的那样,我失去了VBA项目。

使用HxD,有一个计数器跟踪你select了多less文件。 复制从CMG开始,直到计数器读取8F(hex为143),同样当粘贴到locking的文件 – 我结束了两倍的数量的“…”在贴的末尾,看起来奇怪,感觉差不多不自然,但它的工作。

我不知道这是否是至关重要的,但是我确保在Excel中重新打开文件之前,我closures了hex编辑器和Excel。 然后,我不得不通过菜单打开VB编辑器,进入VBProject属性,并input“新”密码来解锁代码。

我希望这有帮助。

只要文档是在Office 2007或以前版本中创build的, ElcomSoft就可以生成适用于此案例的高级Office密码破解程序高级Office密码恢复产品。

我的工具VbaDiff直接从文件中读取VBA,因此您可以使用它从大多数Office文档中恢复受保护的VBA代码,而无需使用hex编辑器。

Excel中的保护是一个简单的文本比较。 在你最喜欢的debugging器( Ollydbg是我select的工具)中加载Excel,find执行比较的代码,并修复它始终返回true,这应该让你访问macros。

你的Excel文件的扩展名更改为XML。 并在记事本中打开它。 在xml文件中find密码文本。

你看到下面的线;

 Sheets("Sheet1").Unprotect Password:="blabla" 

(对不起,我的英语不好)

我尝试了上面的一些解决scheme,没有一个适用于我(excel 2007 xlsm文件)。 然后,我发现了另一个解决scheme,即使是检索密码,不只是破解它。

将此代码插入模块,运行并给它一些时间。 它会通过蛮力恢复你的密码。

 Sub PasswordBreaker() 'Breaks worksheet password protection. Dim i As Integer, j As Integer, k As Integer Dim l As Integer, m As Integer, n As Integer Dim i1 As Integer, i2 As Integer, i3 As Integer Dim i4 As Integer, i5 As Integer, i6 As Integer On Error Resume Next For i = 65 To 66: For j = 65 To 66: For k = 65 To 66 For l = 65 To 66: For m = 65 To 66: For i1 = 65 To 66 For i2 = 65 To 66: For i3 = 65 To 66: For i4 = 65 To 66 For i5 = 65 To 66: For i6 = 65 To 66: For n = 32 To 126 ActiveSheet.Unprotect Chr(i) & Chr(j) & Chr(k) & _ Chr(l) & Chr(m) & Chr(i1) & Chr(i2) & Chr(i3) & _ Chr(i4) & Chr(i5) & Chr(i6) & Chr(n) If ActiveSheet.ProtectContents = False Then MsgBox "One usable password is " & Chr(i) & Chr(j) & _ Chr(k) & Chr(l) & Chr(m) & Chr(i1) & Chr(i2) & _ Chr(i3) & Chr(i4) & Chr(i5) & Chr(i6) & Chr(n) Exit Sub End If Next: Next: Next: Next: Next: Next Next: Next: Next: Next: Next: Next End Sub