博客園IBinary原創 QQ:2510908331 博客鏈接:http://www.cnblogs.com/iBinary/c++
轉載請註明出處,謝謝編程
混合編程的概念,有時候咱們會想,C語言可不能夠調用匯編的函數,或者反過來調用windows
或者說C語言裏面內部直接內聯彙編去編寫.函數
能夠實現,靜看怎麼實現工具
FILE(文件) - NEW (新建)測試
而後咱們打開源文件,一級一級展開,找到咱們的main函數優化
那麼咱們如今要調用匯編寫的,那麼咱們用匯編寫一個代碼spa
建立彙編程序,這個比較簡單,咱們新建個文件夾,裏面新建一個文本文檔,後綴名改成ASM,而後用RadAsm打開,開始編寫代碼插件
使用RadAsm編寫,這樣比較快命令行
編寫咱們的彙編代碼
,注意下方的end結束符號,咱們並無指明開始位置是MyAdd,也就是說這個彙編程序,只能編譯
編譯出的OBJ 和咱們上邊寫的程序的OBJ 一塊兒鏈接(上面的程序也是編譯,不鏈接)
看下彙編代碼
.386 .model flat,stdcall option casemap:none .const .data .code MyAdd proc c ,n1:DWORD,n2:DWORD mov eax,n1 add eax,n2 ret MyAdd endp End
博客園IBinary原創 QQ:2510908331 博客鏈接:http://www.cnblogs.com/iBinary/
1.先編譯彙編程序,產生obj文件
將此obj文件複製到咱們的C/c++的目錄下
2.修改C/c++程序,調用咱們的增長函數
C/C++代碼以下,注意這樣寫你只能編譯,不能鏈接,只能先生成OBJ
博客園IBinary原創 QQ:2510908331 博客鏈接:http://www.cnblogs.com/iBinary/
咱們要想使用上面幾個程序,有多中方式去掉用,分別是
編譯咱們的.cpp文件,產生.boj文件
命令是 cl /c 文件名
,而後把上面的MyAdd.obj(彙編程序編譯的)
彙編程序的編譯能夠經過RadAsm,若是配置好了就直接F5編譯便可,若是沒配置好,能夠手工用命令行編譯,這裏不講解了,之前課程都有講怎麼編譯,還有配置RadAsm
如今咱們拿到了兩個.obj文件
開始鏈接成一個.exe文件
手工鏈接
Link C/C++文件名.obj 彙編文件名.obj
理論上來講連個obj文件誰在前誰在後都沒有區別,可是如今是咱們的C/C++程序要調用 MyAdd,因此C/C++在前
打開CMD定位到咱們的目錄下(怎麼打開百度搜索)
你能夠直接文件夾上面輸入CMD 回車,則會在當前的目錄下,注意這裏爲了演示命令的截圖
把兩個obj文件拷貝了出來
開始鏈接
成功生成
打開程序校驗一下
能夠調用了
咱們能夠把obj放到VC++中,這樣咱們能夠直接編譯鏈接使用,不用手工編譯鏈接了
由於VC++6.0的Bug,我使用了一個插件修復,原本能夠直接在 File(文件) - > Open(打開)的
解決VC的Bug,這裏我直接提供一個Dll,把Dll放在VC++的目錄的上一層,Addins中
操做步驟
1.右鍵屬性打開文件位置
2.返回上一層目錄
3.進入Addins文件夾下,把FileTool.dll拷貝進去
拷貝FileTool.dll
4.從新打開VC++6.0(注意管理員權限打開)
在菜單中點擊 Tools(工具) -> Customize(定製) -> Add-ins And Macro Files
選中便可,若是再有問題,能夠百度搜索,DLL會打包
上面解決了一個BUG,那麼如今看下咱們的工程中是否有了MyAdd.obj
如今編譯鏈接則能夠執行
上面咱們直接使用的obj,可是這樣不太好,由於obj一多,工程文件就多了,很差維護(固然目的再也不這裏)
那麼咱們把obj定義爲lib
怎麼定義?,可使用vc++自帶的lib工具,若是配置了環境變量,則直接輸入cmd,跳轉到目錄下,把MyAdd.obj生成爲lib
輸入lib 則會出現這個幫助,若是沒有配置環境變量,那麼輸出lib則會出錯,不過通常默認配置了,若是不會配置,請看前邊的配置環境,RadAsm IDE的配置,裏面內容同樣
先介紹一下Lib工具的使用把
這個工具很簡單, lib 選項(可選) 文件名(可選)
例如咱們要把MyAdd.obj變爲lib,則語法是
Lib MyAdd.obj...... (...表明了有多個obj 依次後面填寫便可,注意中間不要加逗號,隔開便可)
生成了
遍歷lib 看下有多少obj,着用list語法
語法
Lib /list lib名稱
,,爲了測試C2ASM我也打包了
如今咱們可使用Lib去編程了
如今只須要咱們的工程中包含這個lib則可使用,不用再把MyAdd.obj添加到工程中了
博客園IBinary原創 QQ:2510908331 博客鏈接:http://www.cnblogs.com/iBinary/
彙編調用C寫的函數,那麼是同樣的,由於.obj都是同樣的
首先看下彙編代碼,彙編代碼應該挺熟悉了,(不熟悉,看下之前的)
.386 .model flat,stdcall option casemap:none include msvcrt.inc includelib msvcrt.lib ;C庫的動態的靜態使用 includelib MyAdd.lib ;lib調用,加載本身的lib MyAdd proto c n1:dword, n2:dword .const g_szFmt db "1+2=%d", 0dh, 0ah, 0 g_szPause db "pause", 0 .data .code main proc c argc:dword, argv:dword invoke MyAdd, 1, 2 invoke crt_printf, offset g_szFmt, eax invoke crt_system, offset g_szPause mov eax, 0 ret main endp end main
注意,由於咱們這裏使用printf了,咱們是動態的靜態使用,什麼是靜態的動態使用,前邊已經說過了,不會的請看下前邊的文章
那麼如今咱們新建個.c文件,裏面單獨寫一個函數(不用工程了)
編譯這個文件,生成.obj,而後和彙編程序的.obj鏈接,可是注意如今是彙編程序的.obj在前
由於彙編調用這個的obj
固然這兩個obj咱們也能夠打包成lib使用,上面的彙編程序就是用的打包好的lib
因此這幾個步驟就不寫了,生成lib給彙編程序使用,至於手工的編譯彙編程序,鏈接彙編程序其實不建議去用了,隨着編譯器的提高,之後加的選項愈來愈多
手工生成lib
Lib MyAdd.obj (MyAdd.obj 是手工編譯的MyAdd.c的文件)
使用RadAsm編譯鏈接1.asm程序
博客園IBinary原創 QQ:2510908331 博客鏈接:http://www.cnblogs.com/iBinary/
像咱們上面的生成的lib只能給C/C++使用,可是別的程序不見得能使用
因此咱們寫一個彙編的DLL,給C/C++程序使用
至於C/C++調用dll,那麼有兩種方式
一種是使用靜態方式,就是加載dll的靜態lib庫(這個lib庫中保存的是dll名稱)
而後添加個頭文件去使用
另外一種是動態的loadlibrary,和GetprocessAddress去使用,動態的加載dll去使用
這裏簡單說下第一種,至於動態使用有開發知識的應該會調用,若是沒開發知識也沒有關係
由於我們這個是彙編,不是再講開發,(雖然開發很重要),可是我們今天的主要內容不是上面全部的,壓軸的在最下面
1.首先利用RadAsm新建一個dll的空工程,填寫如下代碼
彙編代碼:
.386 .model flat,stdcall option casemap:none include windows.inc .const .data .code MyAdd Proc n1:DWORD, n2:DWORD mov eax, n1 add eax, n2 ;寫一個咱們的MyAdd函數 ret MyAdd endp DllMain proc hModule:HINSTANCE , dwReason:DWORD, lpvReserved:LPVOID mov eax, TRUE ret DllMain endp end DllMain ;注意DLL的入口點要指定
而後在DEF文件導出咱們的定義的代碼
編譯鏈接以後則會生成DLL,和保存DLL信息的lib
那麼咱們的工程可使用了
靜態使用
結果
至於代碼,會上傳課堂資料中
博客園IBinary原創 QQ:2510908331 博客鏈接:http://www.cnblogs.com/iBinary/
首先咱們會想,上面雖然完成的 彙編和C的互相調用,也解決的跨語言的DLL調用
可是覺着仍是很差,爲何,由於可能我想寫的彙編代碼就那麼一點,我還得生成DLL
或者生成lib
那麼咱們突發奇想,可不能夠在C/C++中寫彙編代碼
好比咱們寫個int 3的中斷指令
C/C++代碼
#include "stdafx.h" typedef int (*PFN)(int n1,int n2); //定義函數指針 int main(int argc, char* argv[]) { //寫二進制代碼 unsigned char Code[] = {0xCC, 0x55, 0x8B, 0xEC, 0x8B, 0x45, 0x08, 0x03, 0x45, 0x0c, 0xc9, 0xC3}; int result = ((PFN)(void*)Code)(1, 2); printf("%d",result); return 0; }
看到這個代碼是否是暈了,不要緊,誰叫咱們是學彙編的,用OD調試看下
由於是Dbg程序,因此int 3指令對齊了,咱們發現確實是斷點到這裏中止了,咱們須要價格ret
直接打開int 3.exe看看是否會崩潰,若是崩潰則用OD調試,看下到底出現了什麼狀況
調試看看
發現是int3斷點斷下來了,咱們發現,剛在咱們寫入的代碼實際上是二進制代碼咱們把它當作函數執行,也就是Call一下,咱們寫入的是一個加法的函數
難道彙編代碼都要這樣寫嗎
因此VC++6.0爲咱們提供了一個語法,叫作
_asm 注意前邊是一個下劃線
也能夠加塊語句去寫
可是通常咱們不這樣寫,由於這樣會破壞寄存器環境因此開始和結束咱們要保存一下寄存器的環境
Pushad 和push s是保存全部寄存器環境,和全部標誌寄存器標誌
咱們看下VC++6.0的彙編到底作了什麼
(在VC++6.0中內聯彙編,能夠下短點,而後ALT +8跳轉到VC的彙編中查看)
是同樣的
上面咱們知道的怎麼寫內聯彙編了,那麼下邊咱們則能夠把這個內聯彙編定位爲函數
寫個ADD函數把
首先咱們工程封裝成一個函數
咱們能夠直接這樣寫,由於編譯器內部已經幫咱們壓棧,平棧...各類東西都幫咱們作了
咱們一會ALT + 8看下
如今咱們要調用了,由於返回值問題,是怎麼返回咱們不知道,雖然咱們知道是放在eax中
可是若是你改爲int,那麼咱們要寫爲 return eax?顯然是不能夠的,而若是在_asm中
寫ret,那麼這個函數不知道你返回了因此先定義爲void,咱們一會解決返回值問題
調用:
咱們要本身push,本身Call,又由於MyAdd是C調用約定,因此咱們要本身平棧,
咱們看下彙編代碼
這個是咱們調用的代碼
咱們看下MyAdd的時候裏面作了什麼
咱們發現其實咱們的核心代碼就是兩句,可是編譯器幫咱們作了不少事
從第一個循環申請局部變量上面就不說了,前邊講過了
(保存棧底,開闢局部空間,保存環境.....)
主要看下面,恢復完寄存器信息以後就開始釋放局部變量空間,而後在Debug版本下會檢測棧
是否平衡,若是不平衡,就彈個錯誤框,最後ret的時候,由於壓入了兩個參數尚未平棧
因此上面咱們須要本身平棧,一個參數4個字節,因此+8
看下結果
上面咱們若是調用,那麼就要本身內聯,本身調用,可是很不方便,因此咱們加個返回值
直接調用也能夠,編譯器智慧給警告,由於編譯器支持這個語法
調用
直接調用便可
看下結果
那麼就完美解決了
博客園IBinary原創 QQ:2510908331 博客鏈接:http://www.cnblogs.com/iBinary/
咱們這樣想,上面編譯器幫咱們作了不少事情,咱們不知道,咱們有的時候這個函數就行本身用咱們的寫的代碼
那麼這個時候就能夠用裸函數了,有關鍵字
_declspec(naked) ....
看下反彙編是什麼
能夠看到,明顯的編譯器沒有幫咱們作申請局部變量空間....等等一系列的操做
可是咱們就要本身去寫了
看下結果
若是內聯了,那麼就不支持invoke這種僞指令去操做了,都是真實的去寫彙編代碼
調用其實挺簡單,加上數據段,和函數名就能夠的,可是注意函數的頭文件要包含(Windows.h)
咱們上面調用一個Add函數,本身還要計算
mov eax,[ebp +8]
Sub eax,[ebp + 0ch]
可是其實這些咱們的函數有參數了,咱們可使用參數來弄
好比
Mov eax,n1
Sub eax,n2 這樣去寫就行
反正怎麼像僞指令怎麼寫,不支持也要想辦法優化.
否則參數多了就容易混亂
博客園IBinary原創 QQ:2510908331 博客鏈接:http://www.cnblogs.com/iBinary/
課堂資料:
連接:http://pan.baidu.com/s/1jIJzsyE 密碼:dtu4