程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 用匯編的眼光看C++(之特殊函數)

用匯編的眼光看C++(之特殊函數)

編輯:C++入門知識

 

【 聲明:版權所有,歡迎轉載,請勿用於商業用途。  聯系信箱:feixiaoxing @163.com】 

 

    這裡說的函數主要指的是inline函數、static函數。inline函數比較特殊,它既具有宏的性質,同時也能讓編譯器對它進行函數檢查。static函數同樣也比較特殊,它只可以被同文件的函數使用。如果static函數在include文件中,那麼這個頭文件只要被使用一次,那麼這個函數就要在exec文件中重新出現一次。現在大家可能理解起來有點困難,但是請大家稍微等待一下,下面我們將會用示例進行說明。最後,我們用一個替換的技巧對函數指針進行修改,讓你調用的函數發生修改,這樣給大家都函數的定義加深一下印象。

 

 

 

 

    (1)內聯函數

 

 

copy to clipboardprint?inline int add(int a, int b) 

    return a + b; 

inline int add(int a, int b)

{

       return a + b;

}    那麼這個函數在應用的時候,會怎麼編譯呢,可以看一下?

 

 

copy to clipboardprint?0040114A   mov         eax,1 

0040114F   add         eax,2 

00401152   mov         dword ptr [ebp-4],eax 

0040114A   mov         eax,1

0040114F   add         eax,2

00401152   mov         dword ptr [ebp-4],eax

 

 

    inline函數是一種特殊的函數。在進行函數編譯的時候,編譯器會對內聯函數這段代碼按照函數的要求進行格式檢查。但是編譯生成執行代碼的過程中,編譯器會把這段代碼按照宏的性質復制到call的函數當中。所以在call函數中,我們發現這段調用代碼並不是call的形式,而是直接按照語句的形式。但是這種inline函數中的代碼行數不能過多,因為我們內聯的目的就是就是減少call的機會。

 

注意:

 

    a) 函數在編譯的時候需要打開INLINE優化開關,【PROJECT】->【setting】->【C/C++】->【optimizations】,在內聯擴展中選擇第二項

 

    b)編譯的時候會生成錯誤,那麼刪除編譯指令/ZI即可,結果是源碼無法單步調試,只能匯編級單步調試

 

 

    (2)static函數是什麼屬性

 

copy to clipboardprint?static int add(int a, int b) 

    return a + b; 

static int add(int a, int b)

{

       return a + b;

}    a) 如果在不同的源文件都有這樣一個add函數呢 ?

 

    如果在不同的文件裡面函數聲明為static函數,那麼沒有關系,各個static函數只為各個文件使用,不存在multi definition的問題。

 

    b)如果頭文件有這樣一個static函數聲明和定義?

 

    頭文件中有一個static函數的話,那麼調用這個函數的每個文件都為這個static函數重新編譯一下。結果和a)的結果是一樣的,大家可以自己試試看一下,對static函數地址打印一下,看看是不是add函數的地址是一樣的。

 

   

 

    (3)一個修改函數地址的范例

 

 

copy to clipboardprint?#include <windows.h>  

 

int add(int a, int b) 

    return a + b; 

 

int sub(int a, int b) 

    return a - b; 

 

void set() 

    HANDLE hProcess = GetCurrentProcess(); 

    DWORD pOldFlag = 0; 

    BOOL result = 0; 

    result = VirtualProtectEx(hProcess, (LPVOID)add, 0x10, PAGE_EXECUTE_READWRITE, &pOldFlag); 

    if(result != 0) 

    { 

        printf("%d\n", GetLastError()); 

    } 

 

void process() 

    char* n = (char*) add; 

    char* t = (char*) sub; 

    *n  =  0xFF; 

    *(n+1) = 0x25; 

    *(int*)(n +2) = (int)&t; 

    int data = add(3,2); 

    assert(1 == data); 

    return; 

#include <windows.h>

 

int add(int a, int b)

{

       return a + b;

}

 

int sub(int a, int b)

{

       return a - b;

}

 

void set()

{

       HANDLE hProcess = GetCurrentProcess();

       DWORD pOldFlag = 0;

       BOOL result = 0;

       result = VirtualProtectEx(hProcess, (LPVOID)add, 0x10, PAGE_EXECUTE_READWRITE, &pOldFlag);

       if(result != 0)

       {

              printf("%d\n", GetLastError());

       }

}

 

void process()

{

       char* n = (char*) add;

       char* t = (char*) sub;

       *n  =  0xFF;

       *(n+1) = 0x25;

       *(int*)(n +2) = (int)&t;

       int data = add(3,2);

       assert(1 == data);

       return;

}    簡單介紹一下,上面的代碼包括四個函數,add函數和sub函數主要為了替換測試使用,set函數是修改代碼段訪問屬性的一段代碼,而process函數就是我們測試使用的一段代碼。其實這段代碼的意思不難,目的在於你在call add函數,發現實際上在call的是sub函數。那麼我們是怎麼做到的呢,關鍵在兩個方面:(1)修改add 函數代碼段的訪問屬性;(2)修改add函數第一個字節的內容,那麼我們需要把函數add處地內容修改為jmp sub,那麼就要先修改屬性,後修改內容

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved