接下来,作者将讨论软件漏洞,并且介绍分析最简单的几种软件漏洞的分析方法。

有漏洞的软件存在各种 bug 或者程序错误,根据 bug 的类型,它们会被利用来执行各种恶意代码,用户身份认证也可能失效,导致用户非法操作、触发系统崩溃,以及提升权限等。

当然,在所有的内存破坏 bug 中,最简单的就是缓存区溢出,造成的原因是程序占用一块内存区域或者说缓存区用于存放数据,由于某些原因,最终的数据大小没有充分地检查,然后缓存区溢出超过声明占用的大小,导致内存中的局部变量、函数参数以及指针等被覆盖。

缓存区溢出当中最简单的就是栈内存溢出,也就是栈内存上占用的缓存区发生溢出。

包含缓存区的栈结构分析

Compilado_2.exe

看例子:

int main() 
{
	char buf[0x300];
	gets_s(buf, 0x300);
	
	return 0;
}

这个程序没有缓存区溢出,因为传入缓存区的字符串大小不会超过缓存区的大小。

程序汇编:

Buffer= byte ptr -304h
var_4= dword ptr -4
argc= dword ptr  8
argv= dword ptr  0Ch
envp= dword ptr  10h

push    ebp                     ; 保存调用者的基址指针到栈中
mov     ebp, esp                ; 将当前栈指针保存到基址指针中,建立新的栈帧
sub     esp, 304h               ; 为局部变量和缓冲区分配 304h 字节的空间
mov     eax, ___security_cookie ; 将全局安全cookie值加载到 eax 寄存器
xor     eax, ebp                ; 使用 ebp 对 eax 进行异或,生成新的cookie值
mov     [ebp+var_4], eax        ; 将生成的cookie值保存到栈中的变量 var_4
lea     eax, [ebp+Buffer]       ; 计算缓冲区的地址并存入 eax 寄存器
push    300h                    ; 将缓冲区大小 300h 字节压入栈中
push    eax                     ; 将缓冲区地址压入栈中
call    ds:gets_s               ; 调用 gets_s 函数读取输入到缓冲区
mov     ecx, [ebp+var_4]        ; 从栈中取出之前保存的cookie值到 ecx
add     esp, 8                  ; 调整栈指针,清理调用 gets_s 时压入的两个参数
xor     ecx, ebp                ; 再次用 ebp 对 ecx 进行异或,恢复原始cookie值
xor     eax, eax                ; 将 eax 清零,准备作为 __security_check_cookie 的参数
call    @__security_check_cookie@4 ; 调用 __security_check_cookie 函数,检测栈完整性
mov     esp, ebp                ; 恢复栈指针到基址指针的位置,清理栈帧
pop     ebp                     ; 弹出原始的基址指针,恢复调用者的栈帧
retn                            ; 返回调用者,结束函数

栈大小是 0x304h。

buffer 占了 0x300 大小。

var_4 占了 0x4 大小。

-00000304 ; Use data definition commands to create local variables and function arguments.
-00000304 ; Two special fields " r" and " s" represent return address and saved registers.
-00000304 ; Frame size: 304; Saved regs: 4; Purge: 0
-00000304 ;
-00000304
-00000304 Buffer db 768 dup(?)
-00000004 var_4 dd ?
+00000000  s db 4 dup(?)
+00000004  r db 4 dup(?)
+00000008 argc dd ?
+0000000C argv dd ?                               ; offset
+00000010 envp dd ?                               ; offset
+00000014
+00000014 ; end of stack variables

结构很清晰。

栈缓存区溢出实例

Compilado_3.exe

看下面的例子:

int main(int argc, char *argv[])
{
	char buf[0x10];
	int size;
	int c;
	
	printf("\\nPlease Enter Your Number of Choice: \\n");
	scanf_s("%d", &size);
	while((c = getchar()) != '\\n' && c != EOF);
	
	gets_s(buf, size);
	
	return 0;
}