真假常量——const和define

常量,一般指在程序中出現的數字1,2,3,等,字符串Hello World,以及數組名稱等,他們都屬於常量。在程序中是不容許修改他們的值程序員

虛假常量const挑戰真正常量define數組

下面一段程序:函數

代碼前面定義了:工具

#define ZS 2234;測試

程序調試,反彙編以下:spa

如上圖所示:命令行

首先:程序是將當即數8BAh(當即數能夠做爲彙編的操做數)直接存儲到const修飾的常量cNum中,其地址爲ebp-0Ch指針

第一條cout指令輸出cNum的地址:ebp-0Ch,同時將其存入ecx並進棧,做爲cout函數調用的參數;調試

第二條cout指令輸出cNum的值:內存

能夠看到,程序中並無使用到cNum變量的地址[ebp-0Ch],而是經過直接將當即數:8BAh(=2234)直接壓棧,做爲cout函數的參數;

也就是說當程序中須要用到cNum的值得時候,系統已經用「真正的值」對其進行了替換。

綜上所述,能夠發現,所謂的const修飾的常量,其本質仍是變量,程序依然是在堆棧中進行空間分配。那麼程序又是經過什麼來阻止程序員修改const修飾的變量的呢?《C++反彙編與逆向分析技術揭祕》一書中曾提到,const關鍵字的做用域是在程序編譯過程當中,也就是說程序在編譯過程當中經過某種檢測可以阻止對const修飾的變量的修改。能夠嘗試對cNum變量進行修改,而後對文件進行編譯,發現編譯過程當中就會出現錯誤。那麼到底怎麼才能修改cNum內存中的數值呢?如今的限制條件只有const關鍵字一條,那麼咱們能夠將cNum的地址複製給一個沒有const修飾的指針,例如pNum,而後經過pNum來修改內存中的值。嘗試以下。

int *pNum=(int*)&cNum;

*pNum=1111;

cout<<(int*)pNum<<endl;

cout<<*pNum<<endl;

cout<<cNum<<endl;

能夠發現,輸出結果爲:

1111

2234

cNum所指向的內存空間中的數值已經修改,可是編譯過的程序中出現cNum的地方仍是維持原來的數值(2234

(既然在出現cNum的地方,編譯器已經用已知的預設值對其進行了替換,那麼修改變量內存中的值起始對程序應該是沒有任何影響的。)

真正的常量:數字、字符、字符串

程序中的數字、字符都是直接做爲當即數參與彙編指令運算。即其存儲位置是代碼段。結果以下圖:(利用VS2008自帶的反彙編窗口,選中「顯示代碼字節」)

可見,十六進制的數:0x12345678直接被編譯成了彙編指令,存儲在代碼段:0x00411554h位置。

也可利用微軟Visual Studio提供的命令行編譯工具:dumpbin.exe(一般存在的路徑是:C:\Program Files\Microsoft Visual Studio 9.0\VC\bin\

dumpbin /all  XXX.exe,結果截圖以下:

下面看一下字符串的存儲:

(測試代碼同上)

如上圖所示:

第一條指令:

將「Hello World」傳遞給str字符串的時候,直接從文件的只讀區00417810h讀取,從反彙編代碼能夠看出,程序利用eaxecxedx三個寄存器將00417810h只讀區域的字符串常量「Hello World」拷貝到str所表明的內存堆棧空間ebp-38h中。

第二條指令:定義了一個字符串指針,能夠看到其彙編指令只有短短的一行,只是將字符串在只讀區域的地址00417880h傳遞給了指針所指向的堆棧空間。

繼續查看利用字符串名和字符串指針來輸出的代碼:

二者指令徹底相同,都是將所指向的字符串的地址傳遞給了I/O流函數cin

一樣利用dumpbin.exe工具能夠看到只讀區域的原始代碼以下:

相關文章
相關標籤/搜索