【 聲明:版權所有,歡迎轉載,請勿用於商業用途。 聯系信箱: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,那麼就要先修改屬性,後修改內容