作為程序員,我們幾乎每天都在和變量打交道,但你真的瞭解這些變量在內存裏的"藏身之處"嗎?今天從逆向分析的角度,帶大家扒開普通局部變量、靜態局部變量、全局變量、靜態全局變量和const全局變量的底層存儲邏輯,看完讓你對變量的理解再上一個台階。
先看一段"找茬"代碼
為了把問題説透,先上一段包含5種變量的測試代碼:
// 全局變量家族
int g_global_int = 0x01;
static int g_static_global_int = 0x02;
const int g_const_global_int = 0x03;
// 局部變量家族展示
void local_variable_demo()
{
int local_int = 0x04; // 普通局部變量
static int static_local_int = 0x05; // 靜態局部變量
const int const_local_int = 0x06; // const局部變量
printf("%d, %d, %d\n", local_int, static_local_int, const_local_int);
}
// 全局變量展示
void global_variable_demo()
{
printf("%d, %d, %d\n", g_global_int, g_static_global_int, g_const_global_int);
}
int main()
{
local_variable_demo();
global_variable_demo();
return 0;
}
這段代碼看起來平平無奇,但當我們用x64dbg調試器看它的反彙編時,各種變量的"小動作"就藏不住了。
局部變量:棧上的"臨時住户"
先看local_variable_demo函數的反彙編,重點觀察三種局部變量的存儲差異。
普通局部變量:棧空間的"臨時租客"
反彙編裏有這樣幾條關鍵指令:
sub esp, 0xD8:給棧頂指針減0xD8,相當於在棧上開闢一塊內存空間mov dword ptr ss:[ebp-0x8], 0x4:把0x04(初始值)寫入棧上ebp-0x8的位置mov edx, dword ptr ss:[ebp-0x8]:從棧上讀取值到寄存器,再作為參數壓棧
結論:普通局部變量存放在棧上,函數調用時創建,函數結束後自動釋放,生命週期僅限函數內部。
靜態局部變量:數據段的"常住民"
靜態局部變量的反彙編很簡單,只有一條讀取指令:mov ecx, dword ptr ds:[0xBAA028]。
找不到初始化的代碼?因為靜態局部變量在編譯時就完成了初始化:
- 非零初始值存放在.data段(可寫數據段)
- 零初始值存放在.bss段(未初始化數據段)
結論:靜態局部變量雖然定義在函數內,但存儲在全局數據區,生命週期和程序一樣長,只會初始化一次。
const局部變量:棧上的"帶鎖抽屜"
反彙編裏const局部變量和普通局部變量的存儲方式完全一樣,都是存在棧上。
那為什麼不能修改?其實是編譯器在編譯階段加了"保護鎖"——當你嘗試修改const局部變量時,編譯器會直接報錯,但從內存層面看,這些數據和普通局部變量沒有本質區別。
全局變量:數據段的"固定產權房"
再看global_variable_demo函數的反彙編,三種全局變量都直接從內存地址讀取數據,但地址位置大有講究。
通過內存佈局分析發現:
- 普通全局變量(
g_global_int)和靜態全局變量(g_static_global_int)擠在.data段(可寫) - const全局變量(
g_const_global_int)單獨住在.rdata段(只讀)
關鍵差異:
- 靜態全局變量和普通全局變量的區別不在存儲區域,而在作用域——靜態全局變量僅限當前文件訪問
- const全局變量因為只讀特性,被編譯器分配到更安全的只讀數據段,防止意外修改
變量存儲總結:一張表看懂本質
| 變量類型 | 存儲區域 | 生命週期 | 初始化時機 | 訪問限制 |
|---|---|---|---|---|
| 普通局部變量 | 棧 | 函數調用期間 | 運行時初始化 | 函數內部 |
| 靜態局部變量 | .data/.bss | 整個程序運行期間 | 編譯時初始化 | 函數內部 |
| 普通全局變量 | .data/.bss | 整個程序運行期間 | 編譯時初始化 | 所有文件(extern可見) |
| 靜態全局變量 | .data/.bss | 整個程序運行期間 | 編譯時初始化 | 當前文件 |
| const全局變量 | .rdata | 整個程序運行期間 | 編譯時初始化 | 所有文件(extern可見) |
額外思考:變量安全防護
知道了變量的存儲規律,反過來想:既然內存中的數據能被輕易查找和修改,如何保護程序的關鍵數據?
可以藉助專業的程序保護工具,通過調試保護、內存校驗等技術,增加逆向分析的難度,讓核心數據更安全。
理解變量的底層存儲,不僅能幫我們寫出更高效的代碼,還能從逆向角度思考程序的安全性——這大概就是深入底層帶來的雙重收穫吧。