標籤:

【20180410】- 為什麼說VBE的工程加密形同虛設?

其實公眾號早期就已經為大家分享了一篇關於如何儘可能的保護VBA代碼的文章,未學習的小夥伴可以移步【VBA技巧】- 如何儘可能的保護VBA代碼?進行學習哦。在該文章中,為大家介紹的第一個方法就是VBAProject工程保護,但在說明這樣做的缺點的時候,提到了此密碼很容易被破解或免密查看。

對於工程密碼破解,可能很多小夥伴都會了,此處就不在加以贅述。今天主要為大家介紹如何免密查看VBA工程代碼。其實該方法是一個叫tt.t的大神發現的,具體該大神叫啥,不得而知,有知道的小夥伴可以私聊告訴我,不勝感激。

原理描述引用自tt.t大神,具體如下:

DESCRIPTION: 在寫中文字元串轉換為拼音函數(HzToPy)過程中,第一次發現VBA功能的強大。於是這次嘗試將其他語言中比較好寫的API HOOK移植成VBA代碼,正好順便把VBA密碼保護去掉,喜歡加密碼的朋友不要生氣啊:) 總的來說VBA的寫法和其他語言區別不大,但VBA畢竟不太方便,代碼必須放在標準模塊中。再有就是對指針的支持實在有限,於是最後選擇了一種寫起來最簡單的API hook方法,就是所謂的陷阱法。如果你不太清楚什麼是API HOOK,請求助於google。Theory: 這裡就不說API hook的方法了,都是傳統方法沒什麼可說的,這裡只簡單說下VBA模塊密碼破解。其實這些我也不是很了解,畢竟知道加密過程用處不大,這個問題上我比較關心結果:) 判斷有密碼以及提示輸入密碼都是VBE6.dll幹得好事。如果有密碼,VBE6.dll會調用DialogBoxParamA顯示VB6INTL.dll資源中的第4070號對話框(就是那個輸入密碼的窗口),若DialogBoxParamA返回值非0,則VBE會認為密碼正確,然後乖乖展開加密模塊的資源。很顯然其中存在很大漏洞,就像給日記本加上了鎖,但裡面全是活頁,我們不需要打開鎖,只要從側面取出活頁就可以了。這個從側面取活頁的過程就是hook住DialogBoxParamA函數,若程序調用DialogBoxParamA裝入4070號對話框,我們就直接返回1,讓VBE以為密碼正確。

代碼如下圖所示:

Option Explicit#If VBA7 And Win64 Then 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#Else Private Declare Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" _ (Destination As LongPtr, Source As LongPtr, ByVal Length As LongPtr) Private Declare Function VirtualProtect Lib "kernel32" (lpAddress As LongPtr, _ ByVal dwSize As LongPtr, ByVal flNewProtect As LongPtr, lpflOldProtect As LongPtr) As LongPtr Private Declare Function GetModuleHandleA Lib "kernel32" (ByVal lpModuleName As String) As LongPtr Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As LongPtr, _ ByVal lpProcName As String) As LongPtr Private Declare 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#End IfDim HookBytes(0 To 5) As ByteDim OriginBytes(0 To 5) As ByteDim pFunc As LongPtrDim Flag As BooleanPrivate Function GetPtr(ByVal Value As LongPtr) As LongPtr 獲得函數的地址 GetPtr = ValueEnd FunctionPublic Sub RecoverBytes() 若已經hook,則恢復原API開頭的6位元組,也就是恢復原來函數的功能 If Flag Then MoveMemory ByVal pFunc, ByVal VarPtr(OriginBytes(0)), 6End SubPublic Function Hook() As Boolean Dim TmpBytes(0 To 5) As Byte Dim p As LongPtr Dim OriginProtect As LongPtr 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 IfEnd FunctionPrivate 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 有程序調用DialogBoxParamA裝入4070號對話框,這裡我們直接返回1,讓 VBE以為密碼正確了 MyDialogBoxParam = 1 Else 有程序調用DialogBoxParamA,但裝入的不是4070號對話框,這裡我們調用 RecoverBytes函數恢復原來函數的功能,在進行原來的函數 RecoverBytes MyDialogBoxParam = DialogBoxParam(hInstance, pTemplateName, _ hWndParent, lpDialogFunc, dwInitParam) 原來的函數執行完畢,再次hook Hook End IfEnd Function破解Sub Crack() If Hook Then MsgBox "破解成功"End Sub恢復Sub Recovery() RecoverBytes MsgBox "恢復成功"End Sub

把上面的代碼複製到一個模塊中,運行Crack宏即可。此時再打開具有工程密碼的文件,即可發現可以直接打開,無需輸入密碼。接下來我將動態為大家演示一下代碼是如何使用的。

完成後別忘了執行"call Recovery ",恢復密碼保護(恢復程序的密碼保護,已被破解的文件不受影響。當然當工作簿關閉後,Hook將自動失效。最後提醒一句,請勿用於非法途徑哦。

好了,今天的介紹就到此這裡了,大家如果在學習過程中遇到任何問題,歡迎諮詢或加入QQ群(群號:615356012)交流學習哦,期待你的進步^_^Written by Steven in 20180410^_^

微信公眾號:SaveUTime

SUT學習交流群:615356012,入群費用:5元,非誠勿擾~

關注公眾號,提高效率,節約您的時間!

推薦閱讀:

破解遊戲或軟體是個什麼樣的體驗?
如何評價《生化危機7》被破解?
為了CTF比賽,如何學習逆向和反彙編?
彙編調試技巧及簡單破解

TAG:VBA | 密碼 | 破解 |