变量的作用域指的是变量在源码中可以被访问到的范围。全局变量属于进程作用域,在整个进程中都可以访问到这个全局变量;静态变量属于文件作用域,在当前源码文件中可以访问;局部变量属于函数作用域,在函数内可以访问到;
运行环境:
- 操作系统: Windows 7家庭版
- 编译器:VC6 VS2013
变量识别
1 2 3 4 5 6 7 8 9 10 11 12 13
| static int g_nTest = 0x8765093;
int main(int argc, char* argv[]) { int n = 999; scanf("%d",&n); scanf("%d",&g_nTest); scanf("%d",&argc); printf("n = %d\n",n + argc); printf("g_nTest = %d\n",g_nTest + argc); printf("Hello World!\n"); return 0; }
|
全局变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| 00401010 | 55 | push ebp | 00401011 | 8BEC | mov ebp,esp | 00401013 | 83EC 44 | sub esp,44 | 00401016 | 53 | push ebx | 00401017 | 56 | push esi | 00401018 | 57 | push edi | 00401019 | 8D7D BC | lea edi,dword ptr ss:[ebp-44] | 0040101C | B9 11000000 | mov ecx,11 | 00401021 | B8 CCCCCCCC | mov eax,CCCCCCCC | 00401026 | F3:AB | rep stosd | 00401028 | C745 FC E7030000 | mov dword ptr ss:[ebp-4],3E7 |局部变量 0040102F | 8D45 FC | lea eax,dword ptr ss:[ebp-4] | 00401032 | 50 | push eax | 00401033 | 68 48504200 | push test.425048 | 425048:"%d" 00401038 | E8 23010000 | call test.401160 | printf 0040103D | 83C4 08 | add esp,8 | 00401040 | 68 307A4200 | push test.427A30 |全局变量 00401045 | 68 48504200 | push test.425048 | 425048:"%d" 0040104A | E8 11010000 | call test.401160 | 0040104F | 83C4 08 | add esp,8 | 00401052 | 8D4D 08 | lea ecx,dword ptr ss:[ebp+8] |参数访问 00401055 | 51 | push ecx | 00401056 | 68 48504200 | push test.425048 | 425048:"%d" 0040105B | E8 00010000 | call test.401160 | 00401060 | 83C4 08 | add esp,8 | 00401063 | 8B55 FC | mov edx,dword ptr ss:[ebp-4] | edx:EntryPoint 00401066 | 0355 08 | add edx,dword ptr ss:[ebp+8] | edx:EntryPoint 00401069 | 52 | push edx | edx:EntryPoint 0040106A | 68 3C504200 | push test.42503C | 42503C:"n = %d\n" 0040106F | E8 6C000000 | call test.4010E0 | 00401074 | 83C4 08 | add esp,8 | 00401077 | A1 307A4200 | mov eax,dword ptr ds:[427A30] | 0040107C | 0345 08 | add eax,dword ptr ss:[ebp+8] |
|
在IDA中识别 为这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| .text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp) .text:00401000 _main proc near ; CODE XREF: start+AF↓p .text:00401000 .text:00401000 var_4 = dword ptr -4 .text:00401000 argc = dword ptr 4 .text:00401000 argv = dword ptr 8 .text:00401000 envp = dword ptr 0Ch .text:00401000 .text:00401000 push ecx .text:00401001 lea eax, [esp+4+var_4] .text:00401005 mov [esp+4+var_4], 3E7h .text:0040100D push eax .text:0040100E push offset aD ; "%d" .text:00401013 call _scanf .text:00401018 push offset dword_408030 ;全局变量通过地址访问 .text:0040101D push offset aD ; "%d" .text:00401022 call _scanf .text:00401027 lea ecx, [esp+14h+argc] .text:0040102B push ecx .text:0040102C push offset aD ; "%d" .text:00401031 call _scanf .text:00401036 mov edx, [esp+1Ch+argc] .text:0040103A mov eax, [esp+1Ch+var_4] .text:0040103E add eax, edx .text:00401040 push eax .text:00401041 push offset aND ; "n = %d\n" .text:00401046 call sub_401080 .text:0040104B mov ecx, dword_408030 .text:00401051 mov edx, [esp+24h+argc] .text:00401055 add ecx, edx .text:00401057 push ecx .text:00401058 push offset aGNtestD ; "g_nTest = %d\n" .text:0040105D call sub_401080 .text:00401062 push offset aHelloWorld ; "Hello World!\n" .text:00401067 call sub_401080 .text:0040106C xor eax, eax .text:0040106E add esp, 30h .text:00401071 retn .text:00401071 _main endp .text:00401071
|
全局代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
|
#include <iostream> int GetInt() { return 3; } int GetInt1() { return 4; } int GetInt2() { return 5; } int GetInt3() { return 6; } int GetInt4() { return 7; } static int g_nTest = GetInt(); static int g_nTest1 = GetInt1() + g_nTest; static int g_nTest2 = GetInt2() / 7; static int g_nTest3 = GetInt3() % -8; static int g_nTest4 = GetInt4(); int main(int argc, char* argv[]) { int n = 999; scanf("%d", &n); scanf("%d", &g_nTest); scanf("%d", &argc); printf("n = %d\n", n + argc); printf("g_nTest = %d\n", g_nTest + argc); printf("Hello World!\n"); return 0; }
|
这里直接反汇编Release版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| start: .text:00401407 ; __unwind { // __SEH_prolog4 .text:00401407 call sub_4016AF .text:0040140C jmp loc_401285 .text:0040140C ; } // starts at 401407 .text:0040140C start endp ; sp-analysis failed
;进入loc_401285 .text:00401285 ; __unwind { // __SEH_prolog4 .text:00401285 push 14h .text:00401287 push offset unk_402560 .text:0040128C call __SEH_prolog4 .text:00401291 push 1 .text:00401293 call sub_401487 .text:00401298 pop ecx .text:00401299 test al, al .text:0040129B jz loc_4013F1 .text:004012A1 xor bl, bl .text:004012A3 mov [ebp-19h], bl .text:004012A6 ; __try { // __except at loc_4013C0 .text:004012A6 and dword ptr [ebp-4], 0 .text:004012AA call sub_401455 .text:004012AF mov [ebp-24h], al .text:004012B2 mov eax, dword_403044 .text:004012B7 xor ecx, ecx .text:004012B9 inc ecx .text:004012BA cmp eax, ecx .text:004012BC jz loc_4013F1 .text:004012C2 test eax, eax .text:004012C4 jnz short loc_40130F .text:004012C6 mov dword_403044, ecx .text:004012CC push offset unk_4020F8 .text:004012D1 push offset unk_4020EC .text:004012D6 call _initterm_e .text:004012DB pop ecx .text:004012DC pop ecx .text:004012DD test eax, eax .text:004012DF jz short loc_4012F2 .text:004012DF ; } // starts at 4012A6 .text:004012E1 mov dword ptr [ebp-4], 0FFFFFFFEh .text:004012E8 mov eax, 0FFh .text:004012ED jmp loc_4013E1 .text:004012F2 ; --------------------------------------------------------------------------- .text:004012F2 .text:004012F2 loc_4012F2: ; CODE XREF: start-128↑j .text:004012F2 push offset unk_4020E8 .text:004012F7 push offset unk_4020CC .text:004012FC call _initterm .text:00401301 pop ecx .text:00401302 pop ecx .text:00401303 mov dword_403044, 2 .text:0040130D jmp short loc_401314 .text:0040130F ; ---------------------------------------------------------------------------
;继续进入_initterm .rdata:004020D0 dd offset sub_401273 ; set_newMode 库函数 .rdata:004020D4 dd offset sub_401070 ;这几个函数分别代表我们写的函数 .rdata:004020D8 dd offset sub_401000 .rdata:004020DC dd offset sub_401020 .rdata:004020E0 dd offset sub_401040 .rdata:004020E4 dd offset sub_401060
|
静态局部变量
静态变量分为全局静态变量和局部静态变量,全局静态变量和全局变量类似,只是全局静态变量只能在本文件中使用;
局部静态便令比较特殊,它不会随作用域的结束而小时,并且在未进入作用域之前就已存在,其生命周期也和全局变量相同。局部静态变量会预先被当作全局变量处,而它的初始化部分只是在做赋值操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| void ShowStatic(int nNumber) { static int g_snNumber = nNumber; //定义局部静态变量,赋值为参数 printf("%d\n",g_snNumber); //打印静态变量 }
int main(int argc, char* argv[]) { for (int i = 0; i < 5; i++) { ShowStatic(i); } return 0; }
00401020 push ebp 00401021 mov ebp,esp ;保存寄存器环境 00401023 sub esp,40h 00401026 push ebx 00401027 push esi 00401028 push edi 00401029 lea edi,[ebp-40h] ; 0040102C mov ecx,10h 00401031 mov eax,0CCCCCCCCh 00401036 rep stos dword ptr [edi] 9: static int g_snNumber = nNumber; 00401038 xor eax,eax ;清空EAX寄存器 0040103A mov al,[`ShowStatic'::`2'::$S1 (00427c4c)] ;取00427c4c处1字节送入al中 0040103F and eax,1 ;将eax与数值1做与运算,eax最终结果只能是0或1 00401042 test eax,eax ;比较eax,不等于0则跳转,跳转到0040105e处 00401044 jne ShowStatic+3Eh (0040105e) 00401046 mov cl,byte ptr [`ShowStatic'::`2'::$S1 (00427c4c)] ;将之前比较是否为0的地址放入cl中 0040104C or cl,1 ;将cl与数值1做或运算,cl最低为将被置1,其他不变 0040104F mov byte ptr [`ShowStatic'::`2'::$S1 (00427c4c)],cl ;将cl值存入00427c4c中 00401055 mov edx,dword ptr [ebp+8] ;参数存入edx中 00401058 mov dword ptr [___decimal_point_length+0A9Ch (00427c48)],edx ;edx赋值到00427c48,将局部静态变量赋值为EDX寄存器中保存的数据 10: printf("%d\n",g_snNumber); 0040105E mov eax,[___decimal_point_length+0A9Ch (00427c48)] ;静态局部变量的访问,与全局变量一样 00401063 push eax 00401064 push offset string "%d\n" (0042201c) 00401069 call printf (00401110) 0040106E add esp,8 11: } 00401071 pop edi 00401072 pop esi 00401073 pop ebx 00401074 add esp,40h 00401077 cmp ebp,esp 00401079 call __chkesp (00401190) 0040107E mov esp,ebp 00401080 pop ebp 00401081 ret
|
cl中存储的是局部变量的标志,这个标志占1字节,通过位运算,将标志中的一个数据置为1,以此判断局部静态变量是否被初始化过。
当有两个静态局部变量,它们会公用一个标记位的内存;
堆
堆在C/C++中使用malloc和new实现堆空间申请,返回的数据便是申请的对空间的地址;保存堆空间首地址的变量大小为4字节的指针类型,其访问方式按作用域来划分;
堆识别在于:堆标志(全FD)、签名、地址、内存中的真相
地址特性
判断依据就是使用调试器装载程序,记住栈地址和内存地址首地址,然后运行到代码;
堆反调试
1 2 3 4 5 6 7
| //反调试代码 __asm { mov eax, fs:[18] mov eax, fs:[30] mov [eax + 0x2], 0 }
|
- Peb+0x68的地方调试时值非0,运行时值为0,,为堆类型
1 2 3 4 5 6
| __asm { mov eax, fs:[18] mov eax, fs:[30] mov [eax + 0x68], 0 }
|