My Blog

Unprotecting VBA Project Password (Using a password that you know)

21

First things first. This is NOT about hacking the VBA Project Password. If you have landed on this page to expect to see how it is hacked then I would recommend you to search somewhere else. This article is about unlocking the VBA Project password if you KNOW the password.

Recently while answering a question in stackoverflow, I came across this interesting question. I know many people in the past have been using Sendkeys to achieve this. I have been thinking about a different approach but could never find a time for this. Finally, yesterday, I took the plunge!

Below are VBA/VB.Net examples on how to unlock the VBA Password. VB6 example is same as VBA example. Before we get into the actual code, let’s understand the logic of the code.

Let’s say we have a workbook whose VBA project looks like this currently.

LOGIC:

  • Find the Handle of the VBAProject Password window using FindWindow
  • Once that is found, find the handle of the Edit Box in that window using FindWindowEx
  • Once the handle of the Edit Box is found, simply use SendMessage to write to it.
  • Find the handle of the Buttons in that window using FindWindowEx
  • Once the handle of the OK button is found, simply use SendMessage to click it.

RECOMMENDATION:

1. For API’s THIS is the best link I can recommend.

2. If you wish to become good at API’s like FindWindow, FindWindowEx and SendMessage then get a tool that gives you a graphical view of the system’s processes, threads, windows, and window messages. For Ex: uuSpy or Spy++.

Here is what Spy++ will show you for VBAProject Password window

VBA

Open a new Excel instance and paste the below code in a module. I have commented the code so you shouldn’t have any problem understanding it.

Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
(ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, _
ByVal lpsz2 As String) As Long

Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
(ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long

Private Declare Function GetWindowTextLength Lib "user32" Alias _
"GetWindowTextLengthA" (ByVal hwnd As Long) As Long

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long

Dim Ret As Long, ChildRet As Long, OpenRet As Long
Dim strBuff As String, ButCap As String
Dim MyPassword As String

Const WM_SETTEXT = &HC
Const BM_CLICK = &HF5

Sub UnlockVBA()
    Dim xlAp As Object, oWb As Object

    Set xlAp = CreateObject("Excel.Application")

    xlAp.Visible = True

    '~~> Open the workbook in a separate instance
    Set oWb = xlAp.Workbooks.Open("C:\Sample.xlsm")

    '~~> Launch the VBA Project Password window
    '~~> I am assuming that it is protected. If not then
    '~~> put a check here.
    xlAp.VBE.CommandBars(1).FindControl(ID:=2578, recursive:=True).Execute

    '~~> Your passwword to open then VBA Project
    MyPassword = "Blah Blah"

    '~~> Get the handle of the "VBAProject Password" Window
    Ret = FindWindow(vbNullString, "VBAProject Password")

    If Ret <> 0 Then
        'MsgBox "VBAProject Password Window Found"

        '~~> Get the handle of the TextBox Window where we need to type the password
        ChildRet = FindWindowEx(Ret, ByVal 0&, "Edit", vbNullString)

        If ChildRet <> 0 Then
            'MsgBox "TextBox's Window Found"
            '~~> This is where we send the password to the Text Window
            SendMess MyPassword, ChildRet

            DoEvents

            '~~> Get the handle of the Button's "Window"
            ChildRet = FindWindowEx(Ret, ByVal 0&, "Button", vbNullString)

            '~~> Check if we found it or not
            If ChildRet <> 0 Then
                'MsgBox "Button's Window Found"

                '~~> Get the caption of the child window
                strBuff = String(GetWindowTextLength(ChildRet) + 1, Chr$(0))
                GetWindowText ChildRet, strBuff, Len(strBuff)
                ButCap = strBuff

                '~~> Loop through all child windows
                Do While ChildRet <> 0
                    '~~> Check if the caption has the word "OK"
                    If InStr(1, ButCap, "OK") Then
                        '~~> If this is the button we are looking for then exit
                        OpenRet = ChildRet
                        Exit Do
                    End If

                    '~~> Get the handle of the next child window
                    ChildRet = FindWindowEx(Ret, ChildRet, "Button", vbNullString)
                    '~~> Get the caption of the child window
                    strBuff = String(GetWindowTextLength(ChildRet) + 1, Chr$(0))
                    GetWindowText ChildRet, strBuff, Len(strBuff)
                    ButCap = strBuff
                Loop

                '~~> Check if we found it or not
                If OpenRet <> 0 Then
                    '~~> Click the OK Button
                    SendMessage ChildRet, BM_CLICK, 0, vbNullString
                Else
                    MsgBox "The Handle of OK Button was not found"
                End If
            Else
                 MsgBox "Button's Window Not Found"
            End If
        Else
            MsgBox "The Edit Box was not found"
        End If
    Else
        MsgBox "VBAProject Password Window was not Found"
    End If
End Sub

Sub SendMess(Message As String, hwnd As Long)
    Call SendMessage(hwnd, WM_SETTEXT, False, ByVal Message)
End Sub
VB.Net
Imports Excel = Microsoft.Office.Interop.Excel

Public Class Form1
    Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
    (ByVal lpClassName As String, ByVal lpWindowName As String) As Integer

    Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
    (ByVal hWnd1 As Integer, ByVal hWnd2 As Integer, ByVal lpsz1 As String, _
    ByVal lpsz2 As String) As Integer

    Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
    (ByVal hwnd As Integer, ByVal lpString As String, ByVal cch As Integer) As Integer

    Private Declare Function GetWindowTextLength Lib "user32" Alias _
    "GetWindowTextLengthA" (ByVal hwnd As Integer) As Integer

    Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
    (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As String) As Integer

    Const WM_SETTEXT = &HC
    Const BM_CLICK = &HF5

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        '~~> Define your Excel Objects
        Dim xlApp As New Excel.Application
        Dim xlWorkBook As Excel.Workbook
        Dim Ret As Integer, ChildRet As Integer, OpenRet As Integer
        Dim strBuff As String, ButCap As String
        Dim MyPassword As String

        xlApp.Visible = True

        '~~> Open the workbook in a separate instance
        xlWorkBook = xlApp.Workbooks.Open("C:\Sample.xlsm")

        '~~> Launch the VBA Project Password window
        '~~> I am assuming that it is protected. If not then
        '~~> put a check here.
        xlApp.VBE.CommandBars(1).FindControl(Id:=2578, Recursive:=True).Execute()

        Threading.Thread.Sleep(500) '<~~ We need this so that there ample time for the VBA window to launch

        '~~> Your passwword to open then VBA Project
        MyPassword = "Blah Blah"

        '~~> Get the handle of the "VBAProject Password" Window
        Ret = FindWindow(vbNullString, "VBAProject Password")

        If Ret <> 0 Then
            'MsgBox "VBAProject Password Window Found"

            '~~> Get the handle of the TextBox Window where we need to type the password
            ChildRet = FindWindowEx(Ret, 0, "Edit", vbNullString)

            If ChildRet <> 0 Then
                'MsgBox "TextBox's Window Found"
                '~~> This is where we send the password to the Text Window
                SendMess(MyPassword, ChildRet)

                '~~> Get the handle of the Button's "Window"
                ChildRet = FindWindowEx(Ret, 0, "Button", vbNullString)

                '~~> Check if we found it or not
                If ChildRet <> 0 Then
                    'MsgBox "Button's Window Found"

                    '~~> Get the caption of the child window
                    strBuff = New String(Chr(0), GetWindowTextLength(ChildRet) + 1)
                    GetWindowText(ChildRet, strBuff, Len(strBuff))
                    ButCap = strBuff

                    '~~> Loop through all child windows
                    Do While ChildRet <> 0
                        '~~> Check if the caption has the word "OK"
                        If InStr(1, ButCap, "OK") Then
                            '~~> If this is the button we are looking for then exit
                            OpenRet = ChildRet
                            Exit Do
                        End If

                        '~~> Get the handle of the next child window
                        ChildRet = FindWindowEx(Ret, ChildRet, "Button", vbNullString)
                        '~~> Get the caption of the child window
                        strBuff = New String(Chr(0), GetWindowTextLength(ChildRet) + 1)
                        GetWindowText(ChildRet, strBuff, Len(strBuff))
                        ButCap = strBuff
                    Loop

                    '~~> Check if we found it or not
                    If OpenRet <> 0 Then
                        '~~> Click the OK Button
                        SendMessage(ChildRet, BM_CLICK, 0, vbNullString)
                    Else
                        MsgBox("The Handle of OK Button was not found")
                    End If
                Else
                    MsgBox("Button's Window Not Found")
                End If
            Else
                MsgBox("The Edit Box was not found")
            End If
        Else
            MsgBox("VBAProject Password Window was not Found")
        End If
    End Sub

    Sub SendMess(ByVal Message As String, ByVal hwnd As Integer)
        Call SendMessage(hwnd, WM_SETTEXT, False, Message)
    End Sub
End Class
  1. Eric
    Eric05-02-2013

    Sid,

    Great post!

    How did you determine that the ID of the VBE Project Password window was 2578? Is there a list of these somewhere that I couldn’t find?

    Cheers,

    Eric

  2. Siddharth Rout
    Siddharth Rout05-02-2013

    Eric, I am not sure if there is a list somewhere but I wrote a code couple of years ago which gives me that list.

    • Eric
      Eric05-02-2013

      So you have an idea for the next blog post? You’re welcome!

      That’s my not-so-subtle way of asking you to share…

      Cheers,

      Eric

      • Siddharth Rout
        Siddharth Rout05-02-2013

        lol ok.. will do it in the next 1 hour :)

        • Eric
          Eric05-02-2013

          Thanks, but I don’t want to push THAT hard.

          A couple of days would be fine.

          Cheers,

          Eric

          • Siddharth Rout
            Siddharth Rout05-02-2013

            1 hour it is ;)

  3. Ivan
    Ivan05-03-2013

    Thank you for posting this solution. I have been looking a solution for this for about 3 years. It has helped me a lot.

    I am trying to creating a patch file that updates the code in another workbook (modules, forms). I have no problem using your solution if the file doing the patching is unlocked. Problem occurs if the patch file is locked. I have tried unlocking the project within the file but it does not seem to allow it (cannot find the textbox to enter the VBA password). I’m not sure if it is possible or if I am just doing something wrong?

    • Siddharth Rout
      Siddharth Rout05-03-2013

      How is the patch file locked? What kind of file is it?

      • Ivan
        Ivan05-03-2013

        Both files are Excel 97-2003 compatible files. VBA Projects are both locked in each file. I can unlock the file that needs to be patched but not the file that does the patching.

        • Siddharth Rout
          Siddharth Rout05-04-2013

          If the patch files are Excel 97-2003 compatible files then my above post will unlock it if you have the password…

    • Jens
      Jens05-18-2013

      Perhaps you do not use an English version of EXCEL, but a localized one (where the names of windows are different)? For me Siddharth’s solution works perfectly.

      • Ivan
        Ivan05-23-2013

        No, I am using an English version of Excel. I can unlock the VBA Project password in a new instance but I can’t do the same for the file that actually does the unlocking.

  4. Philip
    Philip06-04-2013

    exceptional work Mr Sid,

    I used to work in a much larger bank, and they had thousands of Excel macro workbook files, all with the same VBA Project Password…

    Needless to say, it became an Audit issue over time, and there was aperiodic question – how can we llop through 15000 workbooks, and change all the VB Projecct ppasswords?

    So, now I am thinking I should share this with my former colleagues… hopefully they can extend your admiral work in order to automate updating all those Excel 2003-2007 workbooks :)

    thanks again

    Philip

  5. Philip
    Philip06-04-2013

    how difficult is it to click the ‘Protection’ tab in the VB Project Properties dialog, set the password, then save it?

    • Siddharth Rout
      Siddharth Rout06-04-2013

      If you are comfortable with APIs then it is a simple job :)

    • Jens
      Jens06-05-2013

      If you are not so comfortable with APIs (like me) it might take an hour or two longer. The code is different from what Sid is using (you first have to change the TAB and to move the focus before entering data, also there is more than one button you could click) but everything you need you can find here: http://hp.vector.co.jp/authors/VA016119/excel/lockprj.txt

      What I forgot to write earlier to Sid: this is really great stuff. After so many comments in the web (each one saying “without SENDKEYS this is not possible”) you really found a unique solution, which can be replicated easily.

      • Siddharth Rout
        Siddharth Rout06-05-2013

        Yes Jen, you are right. My approach would be the same :) To protect the project, one has to change the tab using APIs first and then move focus to the relevant windows (textboxes) before entering data/clicking buttons.

  6. Philip
    Philip06-05-2013

    Thanks Je3ns, I’ll check that out tonight at home…

  7. Glenn
    Glenn04-29-2014

    everytime I run the code it prompts me here
    ” xlAp.VBE.CommandBars(1).FindControl(ID:=2578, Recursive:=True).Execute”

    Whats the meaning of this?

  8. Joe
    Joe05-01-2014

    I was able to implement your solution, but I do not want the user to see any windows. I tried setting this setting:

    xlAp.Visible = False

    But I still see the Project Properties window you initially test to call the Password entry window.

    How can you hide all the windows in this process?