VisualBasic內嵌匯編語言解決方案
VB簡單易用,但功能有時候受限制。VC、Delphi都可以直接在程序中寫匯編代碼,可惱的是,VB不行。我在網上也看過許多有關于VB嵌入匯編的方法,不過有些方法,過于復雜,而且也沒相應的介紹。我這里提供一種方法,也許大家以后可能有用!
基本思路:匯編代碼,可以存在一個byte類型的數組中,然后通過某種手段,把系統控制權,轉交給這段匯編代碼,我們的匯編代碼段,就得到了執行。但如何讓這段匯編代碼,獲得系統的控制權限呢?查查WIN API手冊,就可以知道有CallWindowProc這個函數。這個函數本是用于調用用戶自己定義的窗口過程的,其原形如下:
它有5個參數。lpPrevWnFunc是一個long型,等于用戶自己窗口過程的地址,其余3個都是窗口過程所必須的參數,詳見MSDN。
我們只需要關心第一個參數:lpPrevWndFunc,窗口過程地址。如果,我們把自己的匯編代碼地址,傳進去會怎么樣?當然,CallWindowProc就把這個地址,當成窗口過程地址,然后,調用這段匯編代碼了。我們的匯編代碼便得到執。。
當然,也得裝摸做樣的吧,將其余4個參數傳進去,就傳4個0算了,因為這4個參數,我們更本不用,但又是CallWindowProc必須的,不要忘了,我們傳進去的lpPrevWndFunc,并非真正的窗口過程地址,而是自己的匯編碼地址。
具體一點,比如,我們要嵌入一段什么也不干的匯編代碼:
然后:
VarPtr函數,用于取變量地址。返回一個long 型值。
為什么前面要執行幾個pop和一個push呢?因為我們是以一段匯編代碼首地址,偽裝成一個窗口過程的,系統調用CallWindowProc時,實際上除lpPrevWndFunc,我們還傳入了4個參數,就是上面的的4個0,而CallWindoProc函數在調用lpPrevWndFunc這段匯編代碼程序時,把其余4個參數是壓入了堆棧的。相當于執行了以下代碼:
因為我們根本沒有用到這4個參數,所以我們只需要將它彈出。所以,我們執行了4個POP ECX,就是把這4個不用的參數彈出,以保持堆棧指針的正確性。但為什么還要,第一句的:POP EAX,還是因為CallWindowProc把lpPrevWndFunc當成一個窗口過程的原故,因為作為一個正常的窗口過程,在執行Call語句的時候,得把Call語句的下一條指令地址push到堆棧中,用于子程序ret。在上面這段代碼就是執行了:push xxxx00afh。事實上,在CallWindowProc中,實際上隱含執行這么幾句,我們必須關心的代碼:
為了能讓窗口過程執行結束后堆棧指針保持平衡,當然要執行相應的pop指令,第一個pop eax是把子程序返回的地址暫時保存在寄存器eax中,然后彈出4個不用的參數。
接著把保存在eax中返回地址,壓回堆棧。當執行ret時,就能正確返回到CallWindowProc中了。
基本思路:匯編代碼,可以存在一個byte類型的數組中,然后通過某種手段,把系統控制權,轉交給這段匯編代碼,我們的匯編代碼段,就得到了執行。但如何讓這段匯編代碼,獲得系統的控制權限呢?查查WIN API手冊,就可以知道有CallWindowProc這個函數。這個函數本是用于調用用戶自己定義的窗口過程的,其原形如下:
| Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long |
它有5個參數。lpPrevWnFunc是一個long型,等于用戶自己窗口過程的地址,其余3個都是窗口過程所必須的參數,詳見MSDN。
我們只需要關心第一個參數:lpPrevWndFunc,窗口過程地址。如果,我們把自己的匯編代碼地址,傳進去會怎么樣?當然,CallWindowProc就把這個地址,當成窗口過程地址,然后,調用這段匯編代碼了。我們的匯編代碼便得到執。。
當然,也得裝摸做樣的吧,將其余4個參數傳進去,就傳4個0算了,因為這4個參數,我們更本不用,但又是CallWindowProc必須的,不要忘了,我們傳進去的lpPrevWndFunc,并非真正的窗口過程地址,而是自己的匯編碼地址。
具體一點,比如,我們要嵌入一段什么也不干的匯編代碼:
| Dim AsmCode() as byte redim AsmCode(8) '生成機器代碼 AsmCode(0) = &H58 'POP EAX AsmCode(1) = &H59 'POP ECX AsmCode(2) = &H59 'POP ECX AsmCode(3) = &H59 'POP ECX AsmCode(4) = &H59 'POP ECX AsmCode(5) = &H50 'PUSH EAX '你可以在這里添加你想執行的Asm代碼... '.....如果添加的話,后面的數組偏移需要做相應改動 '你添加的代碼在這里結束 '將控制權交還主程序 AsmCode(6) = &HC3 'RET '..... |
然后:
| CallDllFunction = CallWindowProc(VarPtr(AsmCode(0), 0, 0, 0, 0) |
VarPtr函數,用于取變量地址。返回一個long 型值。
為什么前面要執行幾個pop和一個push呢?因為我們是以一段匯編代碼首地址,偽裝成一個窗口過程的,系統調用CallWindowProc時,實際上除lpPrevWndFunc,我們還傳入了4個參數,就是上面的的4個0,而CallWindoProc函數在調用lpPrevWndFunc這段匯編代碼程序時,把其余4個參數是壓入了堆棧的。相當于執行了以下代碼:
| xxxx00A4H: push 0 xxxx00A6H: push 0 xxxx00A8H: push 0 xxxx00AAH: push 0 xxxx00ACH: call VarPtr(AsmCode(0))(這段代碼我們是看不見的,是CallWindoProc在內部做的處理) xxxx00AFH: ...... |
因為我們根本沒有用到這4個參數,所以我們只需要將它彈出。所以,我們執行了4個POP ECX,就是把這4個不用的參數彈出,以保持堆棧指針的正確性。但為什么還要,第一句的:POP EAX,還是因為CallWindowProc把lpPrevWndFunc當成一個窗口過程的原故,因為作為一個正常的窗口過程,在執行Call語句的時候,得把Call語句的下一條指令地址push到堆棧中,用于子程序ret。在上面這段代碼就是執行了:push xxxx00afh。事實上,在CallWindowProc中,實際上隱含執行這么幾句,我們必須關心的代碼:
| push 0;參數入棧 push 0 push 0 push 0 push xxxx00afh;(當執行call 時,自動執行) |
為了能讓窗口過程執行結束后堆棧指針保持平衡,當然要執行相應的pop指令,第一個pop eax是把子程序返回的地址暫時保存在寄存器eax中,然后彈出4個不用的參數。
接著把保存在eax中返回地址,壓回堆棧。當執行ret時,就能正確返回到CallWindowProc中了。