最新消息

[公告2014/05/30] 如有需要將部落格中,任何一篇文章的程式碼使用在商業用途,請與我聯繫。

[公告2015/04/26] Line版的 iInfo程式與投資應用 群組已上線想加入的朋友們,請先查看 "入群須知" 再與我聯繫 Line : aminwhite5168,加入請告知身分與回答 "入群須知" 的問題。

[公告2018/04/22] 台北 Python + Excel VBA 金融資訊爬蟲課程,課程如網頁內容 金融資訊爬蟲班:台北班 Python 金融資訊爬蟲、EXCEL VBA 金融資訊爬蟲

[公告2019/01/08] 請注意:我再次重申,部落格文章的程式碼,是要提供各位參考與學習,一旦網頁改版請自行修改,別要求東要求西要我主動修改,你們用我寫東西賺錢了、交差了,請問有分我一杯羹嗎?既然賺錢沒分我,請問有什麼理由要求我修改,如果沒能力改,就花錢來找我上課。

[公告2019/12/01] 若各位有 Excel VBA 案子開發需求,歡迎與我聯繫,可接案處理。

[公告2020/05/22] 頁面載入速度慢,起因為部分JS來源(alexgorbatchev.com)失效導致頁面載入變慢,目前已做調整,請多見諒。

2016年1月17日 星期日

破解Excel VBA巨集含有密碼的Excel VBA檔案 (3)

延續前面 破解含有密碼的Excel VBA檔案 (2) 文章,這裡使用API Hook方式來處理有加密的Excel檔案。
依據下面步驟操作即可,但請注意以下程式碼僅適用於32位元環境的Office,64位元須另行處理
1. 在Excel VBE環境中新增一個模組,將以下程式碼複製到裡面執行。
2. 開啟含有加密的Excel 檔案即可。
Option Explicit

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 Gettr(ByVal Value As Long) As Long
    '獲得函數的位址
    GetPtr = Value
End Function

Public Sub RecoverBytes()
    '若已經hook,則恢復原API開頭的6位元組,也就是恢復原來函數的功能
    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
    
    'VBE6.dll調用DialogBoxParamA顯示VB6INTL.dll資源中的第4070號對話方塊(就是輸入密碼的視窗)
    '若DialogBoxParamA返回值非0,則VBE會認為密碼正確,所以我們要hook DialogBoxParamA函數
    pFunc = GetProcAddress(GetModuleHandleA("user32.dll"), "DialogBoxParamA")
    
    '標準api hook過程之一: 修改記憶體屬性,使其可寫
    If VirtualProtect(ByVal pFunc, 6, &H40, OriginProtect) <> 0 Then
        '標準api hook過程之二: 判斷是否已經hook,看看API的第一個位元組是否為&H68,
        '若是則說明已經Hook
        MoveMemory ByVal VarPtr(TmpBytes(0)), ByVal pFunc, 6
        If TmpBytes(0) <> &H68 Then
            '標準api hook過程之三: 保存原函數開頭位元組,這裡是6個位元組,以備後面恢復
            MoveMemory ByVal VarPtr(OriginBytes(0)), ByVal pFunc, 6
            
            '用AddressOf獲取MyDialogBoxParam的地址
            '因為語法不允許寫成p = AddressOf MyDialogBoxParam,這裡我們寫一個函數
            'GetPtr,作用僅僅是返回AddressOf MyDialogBoxParam的值,從而實現將
            'MyDialogBoxParam的位址付給p的目的
            p = GetPtr(AddressOf MyDialogBoxParam)
            
            '標準api hook過程之四: 組裝API入口的新代碼
            'HookBytes 組成如下彙編
            'push MyDialogBoxParam的地址
            'ret
            '作用是跳轉到MyDialogBoxParam函數
            HookBytes(0) = &H68
            MoveMemory ByVal VarPtr(HookBytes(1)), ByVal VarPtr(p), 4
            HookBytes(5) = &HC3
            
            '標準api hook過程之五: 用HookBytes的內容改寫API前6個位元組
            MoveMemory ByVal pFunc, ByVal VarPtr(HookBytes(0)), 6
            '設置hook成功標誌
            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
        '有程式調用DialogBoxParamA裝入4070號對話方塊,這裡我們直接返回1,讓
        'VBE以為密碼正確了
        MyDialogBoxParam = 1
    Else
        '有程式調用DialogBoxParamA,但裝入的不是4070號對話方塊,這裡我們調用
        'RecoverBytes函數恢復原來函數的功能,在進行原來的函數
        RecoverBytes
        MyDialogBoxParam = DialogBoxParam(hInstance, pTemplateName, _
        hWndParent, lpDialogFunc, dwInitParam)
        '原來的函數執行完畢,再次hook
        Hook
    End If
End Function

Sub 破解()
    If Hook Then
        MsgBox "VBA Project 密碼破解成功!", vbInformation, "*****"
    End If
End Sub

Sub 恢復()
    RecoverBytes
    MsgBox "VBA Project 密碼恢復成功!", vbInformation, "*****"
End Sub

參考資料