C++內嵌彙編 教程1

計算機知識雜談發表於2022-01-20

注:
本文的所有程式碼是在我自己的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;
}

 

相關文章