注:
本文的所有代碼是在我自己的VS2008中測試的,由於環境的差別,不能保證能在所有的編譯器上運行。
1.內嵌匯編介紹
在C++中,可以通過__asm關鍵字來嵌入匯編語言。
例如
int main(){ __asm{//匯編! mov eax,0 } return 0; }
2.匯編版本Hello, World!
我們知道,在C++中,可以使用printf函數來輸出。(如果使用cout,需要使用運算符重載等技術,在這里反而不方便)
提示:
匯編中,調用函數的指令叫做CALL。
函數的參數是保存在棧中的。
那么我們可以開始寫了。首先,先看看C++正常版本的:
#include<stdio.h> #include<stdlib.h> const char *s1="Hello, World\n",*s2="pause"; int main(){ printf(s1); system(s2); return 0; }
為了方便,我們先把正常版本反匯編一下,結果是:
printf(s1); 00BD13CE mov esi,esp 00BD13D0 mov eax,dword ptr [s1 (0BD7038h)] 00BD13D5 push eax 00BD13D6 call dword ptr [__imp__printf (0BD82C4h)] 00BD13DC add esp,4 00BD13DF cmp esi,esp 00BD13E1 call @ILT+315(__RTC_CheckEsp) (0BD1140h)
第一句,mov esi,esp 為了后面檢查棧是否正常用
第二句,mov eax,dword ptr[s1] 括號中的0BD7038h是地址,不要管他,意思是把地址放到eax中去
第三句,push eax 把剛才放進eax的地址放入棧, 實際就是把參數放入棧
第四句,call dword ptr [__imp__printf]
__imp__printf是printf函數編譯后的結果,下划線開頭表示這是一個函數
我們平時寫內聯匯編的時候直接寫printf即可
第五句,add esp,4
其實是手動平棧,之前往棧里面放了4字節的s1,現在把esp指針也就是棧頂指針下移(棧從高地址往低地址),平棧
最后兩句不管它,就是保證esi和esp相等,因為之前手動平了棧,結合第一句,這里應該是相等的,不寫應該也沒事
最終的內聯匯編應該是這樣:
#include<stdio.h> #include<stdlib.h> const char *s1="Hello, World\n",*s2="pause"; int main(){ _asm{ mov eax,dword ptr [s1] push eax call dword ptr [printf] add esp,4 mov eax,dword ptr[s2] push eax call dword ptr [system] add esp,4 } return 0; }
運行結果正常。
3.內聯匯編A+B
A+B問題,同時需要使用scanf和printf
首先注意一點,函數的參數在棧中是倒着存放的。(注:這個C標准沒有規定,但是匯編語言本身就是非常依賴環境的一個東西,所以暫且不管它)
例如
scanf("%d %d",&a,&b);
如果翻譯成匯編,應該是這樣(下面的是偽代碼)
push &b push &a push "%d %d" call scanf
然后我們就可以開始寫了。
scanf的部分,注意最前面兩個參數,由於放入的是地址,所以不能使用MOV指令而是要使用LEA指令
lea eax,[a]
表示把a的地址放入eax中。
其他部分沒有什么難度,注意最后平棧的時候,add esp到底加上多少,加上的是每個參數的大小相加。
例如scanf,每個都是4字節的地址,總共就是12字節。
完整代碼
#include<stdio.h> #include<stdlib.h> const char *s1="%d%d",*s2="%d\n",*s3="pause"; int a,b; int main(){ _asm{ lea eax,[b] push eax lea eax,[a] push eax mov eax,dword ptr [s1] push eax call dword ptr [scanf] add esp,12 mov eax,[a] add eax,[b] push eax mov eax,dword ptr [s2] push eax call dword ptr [printf] add esp,8 mov eax,dword ptr [s3] push eax call dword ptr [system] add esp,4 } return 0; }