[TOC]
程序
局部变量
1 2 3 4 5 6 7 8
| #include<stdio.h> int main(){ int x; printf("Enter X:\n"); scanf("%d",&x); printf("You entered %d...\n",x); return 0; }
|
全局变量
1 2 3 4 5 6 7 8 9
| #include<stdio.h> //now,x is global variable int x; int main(){ printf("Enter X:\n"); scanf("%d",&x); printf("You entered %d...\n",x); return 0; }
|
scanf()函数的状态监测
1 2 3 4 5 6 7 8 9 10
| #include <stdio.h> int main(){ int x; printf ("Enter X:\n"); if (scanf ("%d", &x)==1) printf ("You entered %d...\n", x); else printf ("What you entered? Huh?\n"); return 0; };
|
x86
x86局部变量
MSVC(2010)
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
| CONST SEGMENT $SG3831 DB 'Enter X:',0aH,00H $SG3832 DB '%d',00H $SG3833 DB 'You entered %d...',0aH,00H CONST ENDS PUBLIC _main EXTRN _scanf:PROC EXTRN _printf:PROC ;Function compile flags:/Odtp _TEXT SEGMENT _x$=-4 ;size=4 _main PROC push ebp mov ebp,esp push ecx push OFFSET $SG3831 ;'Enter X:' call _printf add esp,4 lea eax,DWORD PTR _x$[ebp] push eax push OFFSET $SG3832 ;'%d' call _scanf add esp,8 mov ecx,DWORD PTR _x$[ebp] push ecx push OFFSET $SG3833 ;'You entered %d...' call _printf add esp,8
;rentuen 0 xor eax,eax mov esp,ebp pop ebp ret 0 _main ENDP _TEXT ENDS
|
####OllyDbg调试
现在使用 OllyDbg 调试,加载程序之后,一直按 F8 键单步执行,等待程序退出 ntdll.dll、进入我们程序的主文件。然后向下翻滚滚轴,查找 main()主函数。在 main()里面点中第一条指令PUSH EBP
并在此处按下 F2 键设置断点。接着按 F9 键,运行断点之前的指令。
在这个界面里,我们在寄存器的区域内用右键单击 EAX 寄存器,然后选择“Follow in stack”。如此一来,OllyDbg 就会在栈窗口里显示栈地址和栈内数据,以便我们清楚地观察栈里的局部变量。图中红箭头所示的就是栈里的数据。其中,在地址 0x6E494714 处的数据就是脏数据。在下一时刻,PUSH 指令会把数据存储到栈里的下一个地址。接下来,在程序执行完 scanf()函数之前,我们一直按 F8 键。在执行 scanf()函数的时候,我们要在运行程序的终端窗口里输入数据,例如 123,如下图:
scanf()函数的执行之后的情形如下图所示。EAX 寄存器里存有函数的返回值 1。这表示它成功地读取了 1 个值。我们可以在栈里找到局部变量的地址,其数值为 0x7B(即数字 123)。
这个值将通过栈传递给 ECX 寄存器,然后再次通过栈传递给 printf()函数,如下图
x86全局变量
MSVC
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
| _DATA SEGMENT COMM _x:DWORD $SG2456 DB 'Enter X: ', 0aH, 00H $SG2457 DB '%d', 00H $SG2458 DB 'You entered %d... ', 0aH, 00H _DATA ENDS PUBLIC _main EXTRN __scanf:PROC EXTRN __printf:PROC ; Function compile flags: /Odtp _TEXT SEGMENT _main PROC push ebp mov ebp,esp push OFFSET $SG2456 call _printf add esp,4 push OFFSET _x push OFFSET $SG2457 call _scanf add esp,8 mov eax,DWORD PTR_x push eax push OFFSET $SG2458 call _printf add esp,8 xor eax,eax pop ebp ret 0 _main ENDP _TEXT ENDS
|
x 变量的存储空间是数据段(_data 域),反而没有使用数据栈。因此整个程序的所有指令都可以直接访问全局变量 x。在可执行文件中,未经初始化的变量不会占用任何存储空间
OllyDbg调试
我们可以在 OllyDbg 观察程序的数据段里的变量:
全局变量 x 出现在数据段里。在调试器执行完 PUSH 指令之后,变量 x 的指针推即被推送入栈,我们就可在栈里右键单击 x 的地址并选择“Follow in dump”,并在左侧的内存窗口观察它。在控制台输入 123 之后,栈里的数据将会变成0x7B。若考虑到数权,此处应该是 00 00 00 7B。可见,这是 x86 系统低位优先的“小端字节序/LITTLE-ENDIAN”的典型特征。小端字节序属于“字节(顺)序/endianness”的一种,它的第一个字节是数权最低的字节,数权最高的字节会排列在最后。
此后,EAX 寄存器将存储这个地址里的 32 位值,并将之传递给 printf()函数。本例中,变量 x 的内存地址是011F7138.
在 OllyDbg 里,按下 Alt+M 组合键可查看这个进程的内存映射(process memory map),如下图所示,这个地址位于程序 PE 段的.data 域:
x86scanf()函数的状态监测
MSVC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| lea eax,DOWORD PTR_x$[ebp] push eax push OFFSET $SG3833 ;'%d',00H call _scanf add esp, 8 cmp eax, 1 jne SHORT $LN2@main mov ecx, DWORD PTR _x$[ebp] push ecx push OFFSET $SG3834 ; 'You entered %d... ', 0aH, 00H call _printf add esp, 8 jmp SHORT $LN1@main $LN2@main: push OFFSET $SG3836 ; 'What you entered? Huh? ', 0aH, 00H call _printf add esp, 4 $LN1@main: eax, eax
|
不仅是 CMP
指令所有的“数学/算术计算”指令都会设置标志位。如果将 1 与 1 进行比较,1−1=0,ZF 标志位(“零”标识位,最终运算结果是 0)将被计算指令设定为 1。将两个不同的数值进行 CMP 比较时,ZF 标志位的值绝不会是 1。JNE 指令会依据 ZF 标志位的状态判断是否需要进行跳转,实际上此两者(Jump if Not Zero)的同义指令。JNE 和 JNZ 的 opcode 都相同。所以,即使使用减法运算操作指令 SUB 替换 CMP指令,Jcc 指令也可以进行正常的跳转。不过在使用 SUB 指令时,我们还需要分配一个寄存器保存运算结果,而 CMP 则不需要使用寄存器保存运算结果。
x64
在编译面向 x64 平台的可执行程序时,由于这个程序的参数较少,编译器会直接使用寄存器传递参数。除此之外,编译过程和 x86 的编译过程没有太大的区别。
x64局部变量
msvc 2010
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
| _DATA SEGMENT $SG3831 DB 'Enter X:',0aH,00H $SG3832 DB '%d',00H $SG3833 DB 'You entered %d...',0aH,00H _DATA ENDS
_TEXT SEGMENT x$=32 mian PROC $LN3: sub rsp,56 lea rcx, OFFSET FLAT:$SG1289 ; 'Enter X: ' call printf lea rdx, QWORD PTR x$[rsp] lea rcx, OFFSET FLAT:$SG1291 ; '%d' call scanf lea edx, DWORD PTR x$[rsp] lea rcx, OFFSET FLAT:$SG1292 ; 'You entered %d... ' call printf ;return 0 xor eax, eax add rsp, 56 ret 0 main ENDP _TEXT ENDS
|
使用radare2调试
使用gcc编译源代码,之后使用radare2进行调试,结果如下:
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
| root@kali:~# r2 a.out -- Press 'C' in visual mode to toggle colors [0x000005f0]> aaa [x] Analyze all flags starting with sym. and entry0 (aa) [x] Analyze len bytes of instructions for references (aar) [x] Analyze function calls (aac) [x] Use -AA or aaaa to perform additional experimental analysis. [x] Constructing a function name for fcn.* and sym.func.* functions (aan) [0x000005f0]> s main [0x000006fa]> pdf ;-- main: / (fcn) sym.main 73 | sym.main (); | ; var int local_4h @ rbp-0x4 | ; DATA XREF from 0x0000060d (entry0) | 0x000006fa 55 push rbp | 0x000006fb 4889e5 mov rbp, rsp | 0x000006fe 4883ec10 sub rsp, 0x10 | 0x00000702 488d3dcb0000. lea rdi, str.Enter_X: ; 0x7d4 ; "Enter X:" | 0x00000709 e8a2feffff call sym.imp.puts ; int puts(const char *s) | 0x0000070e 488d45fc lea rax, [local_4h] | 0x00000712 4889c6 mov rsi, rax | 0x00000715 488d3dc10000. lea rdi, 0x000007dd ; "%d" | 0x0000071c b800000000 mov eax, 0 | 0x00000721 e8aafeffff call sym.imp.__isoc99_scanf | 0x00000726 8b45fc mov eax, dword [local_4h] | 0x00000729 89c6 mov esi, eax | 0x0000072b 488d3dae0000. lea rdi, str.You_entered__d..._n ; 0x7e0 ; "You entered %d...\n" | 0x00000732 b800000000 mov eax, 0 | 0x00000737 e884feffff call sym.imp.printf ; int printf(const char *format) | 0x0000073c b800000000 mov eax, 0 | 0x00000741 c9 leave \ 0x00000742 c3 ret [0x000006fa]>
|
x64全局变量
GCC编译,使用radare2调试:
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
| r2 b.out -- Use 'rabin2 -ris' to get the import/export symbols of any binary. [0x000005f0]> aaa [x] Analyze all flags starting with sym. and entry0 (aa) [x] Analyze len bytes of instructions for references (aar) [x] Analyze function calls (aac) [x] Use -AA or aaaa to perform additional experimental analysis. [x] Constructing a function name for fcn.* and sym.func.* functions (aan) [0x000005f0]> s main [0x000006fa]> pdf ;-- main: / (fcn) sym.main 72 | sym.main (); | ; DATA XREF from 0x0000060d (entry0) | 0x000006fa 55 push rbp | 0x000006fb 4889e5 mov rbp, rsp | 0x000006fe 488d3dcf0000. lea rdi, str.Enter_X: ; 0x7d4 ; "Enter X:" | 0x00000705 e8a6feffff call sym.imp.puts ; int puts(const char *s) | 0x0000070a 488d35330920. lea rsi, obj.x ; 0x201044 | 0x00000711 488d3dc50000. lea rdi, 0x000007dd ; "%d" | 0x00000718 b800000000 mov eax, 0 | 0x0000071d e8aefeffff call sym.imp.__isoc99_scanf | 0x00000722 8b051c092000 mov eax, dword [obj.x] ; [0x201044:4]=0 | 0x00000728 89c6 mov esi, eax | 0x0000072a 488d3daf0000. lea rdi, str.You_entered__d..._n ; 0x7e0 ; "You entered %d...\n" | 0x00000731 b800000000 mov eax, 0 | 0x00000736 e885feffff call sym.imp.printf ; int printf(const char *format) | 0x0000073b b800000000 mov eax, 0 | 0x00000740 5d pop rbp \ 0x00000741 c3 ret [0x000006fa]>
|
x64: scanf()函数的状态监测
:GCC编译,使用radare2调试:
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
| root@kali:~# r2 c.out -- You can debug a program from the graph view ('ag') using standard radare2 commands [0x000005f0]> aaa [x] Analyze all flags starting with sym. and entry0 (aa) [x] Analyze len bytes of instructions for references (aar) [x] Analyze function calls (aac) [x] Use -AA or aaaa to perform additional experimental analysis. [x] Constructing a function name for fcn.* and sym.func.* functions (aan) [0x000005f0]> s main [0x000006fa]> pdf ;-- main: / (fcn) main 92 | main (); | ; var int local_4h @ rbp-0x4 | ; DATA XREF from 0x0000060d (entry0) | 0x000006fa 55 push rbp | 0x000006fb 4889e5 mov rbp, rsp | 0x000006fe 4883ec10 sub rsp, 0x10 | 0x00000702 488d3ddb0000. lea rdi, str.Enter_X: ; 0x7e4 ; "Enter X:" | 0x00000709 e8a2feffff call sym.imp.puts ; int puts(const char *s) | 0x0000070e 488d45fc lea rax, [local_4h] | 0x00000712 4889c6 mov rsi, rax | 0x00000715 488d3dd10000. lea rdi, 0x000007ed ; "%d" | 0x0000071c b800000000 mov eax, 0 | 0x00000721 e8aafeffff call sym.imp.__isoc99_scanf | 0x00000726 83f801 cmp eax, 1 | ,=< 0x00000729 7518 jne 0x743 | | 0x0000072b 8b45fc mov eax, dword [local_4h] | | 0x0000072e 89c6 mov esi, eax | | 0x00000730 488d3db90000. lea rdi, str.You_entered__d..._n ; 0x7f0 ; "You entered %d...\n" | | 0x00000737 b800000000 mov eax, 0 | | 0x0000073c e87ffeffff call sym.imp.printf ; int printf(const char *format) | ,==< 0x00000741 eb0c jmp 0x74f | || ; JMP XREF from 0x00000729 (main) | |`-> 0x00000743 488d3db90000. lea rdi, str.What_you_entered__Huh_ ; 0x803 ; "What you entered? Huh?" | | 0x0000074a e861feffff call sym.imp.puts ; int puts(const char *s) | | ; JMP XREF from 0x00000741 (main) | `--> 0x0000074f b800000000 mov eax, 0 | 0x00000754 c9 leave \ 0x00000755 c3 ret [0x000006fa]>
|
其他系统
关于ARM、MIPS系统对于printf()函数的处理,请各位同学参考《RE4B》一书
#作业