栈溢出原理与实践


系统栈的工作原理

内存的不同用途

进程使用内存的大致分类:

  1. 代码区:存储着被装入执行的二进制机器码,处理器会到这个区域取值并执行
  2. 数据区:用于存储全局变量等
  3. 堆区:进程可以在堆区动态的申请一定大小的内存,并在用完之后归还给堆区,即动态分配与回收
  4. 栈区:用于动态的存储函数之间的调用关系,以保证调用函数在返回时恢复到母函数中继续执行

PE文件代码段中包含的二进制级别的机器代码会被装入内存的代码区(.text),处理器将到内存的这个区域一条一条的取出指令和操作数,并送入算数逻辑单元进行运算;如果代码中请求开辟动态内存,则会在内存的堆区分配一块大小合适的区域返回给代码区的代码使用;当函数调用发生时,函数的调用关系等信息会动态的保存在内存的栈区,以供处理器在执行完被调用函数的代码时,返回母函数.

Alt

程序所使用的缓冲区可以是堆区、栈区和存放静态变量的数据区

栈与系统栈

内存的栈区实际上指的是系统栈,系统栈由系统自动维护,它用于实现高级语言中函数的调用

当函数被调用时,系统栈会为这个函数开辟一个新的栈帧,并把它压入栈中。这个栈帧中的内存空间被它所属的函数独占,正常情况下是不会和别的函数共享的。当函数返回时,系统栈会弹出该函数所对应的栈帧

系统栈在函数调用时发生的变化如下:

Alt

寄存器与函数栈帧

win32系统提供两个特殊的寄存器用于标识位于系统栈顶端的栈帧:

  1. ESP:栈指针寄存器(extended stack pointer),存放永远指向系统栈最上面的一个栈帧的栈顶的指针
  2. EBP:基址指针寄存器(extended base pointer),存放一个指向系统栈最上面的一个栈帧的底部的指针

注意:EBP指向当前位于系统栈最上面的一个栈帧的底部,而不是系统栈的底部

代码植入

因为ASCII表示范围有限,很多值无法直接通过键盘输入,所以下面的代码通过从文件中读取数据来完成栈溢出的攻击

原理图:

Alt

#include<stdio.h>
#include<windows.h>

#define PASSWORD "1234567"

int verify_password(char *password)
{
    int authenticated;
    char buffer[44];
    authenticated = strcmp(password, PASSWORD);
    strcpy(buffer, password);
    return authenticated;
}

int main(void)
{
    int valid_flag = 0;
    char password[1024];
    FILE * fp;
    LoadLibrary("user32.dll");  // prepare for messagebox
    if(!(fp=fopen("password.txt","rw+")))
        exit(0);
    fscanf(fp, "%s", password);
    valid_flag = verify_password(password);
    if(valid_flag)
        printf("incorrect password!\n");
    else
        printf("Congratulation! You have passed the verification!\n");
    fclose(fp);
    return 0;
}

实际上这里是可以直接看汇编代码得到偏移的:

Alt

载入od之后,可以先在loadlibrary执行之后断下,得到user32.dll的加载基址:

Alt

之后就需要得到Messagebox的偏移地址:

Alt

这里使用VC6的工具DEPENDES查看函数偏移,只需要打开任意有视窗的程序,就可以在左边侧栏离找到user32.dll,然后就可以找到Messagebox的偏移了。

之后计算得到MessageboxA的地址:0x76af0000 + 0x81060 = 0x76b71060

然后在OD中找到栈上的变量地址:

Alt

我都电脑并没有刻意去关闭ALSR,这里我是在调试过程中修改password.txt的,所以这里地址与关闭ALSR的情况会有所出入。

接着在winhex中编辑shellcode:

Alt

这里的shellcode思路比较常规,即覆盖返回地址到变量地址,进而使程序执行事先写入的机器码。这个函数执行结束后,即跳转到我们预期的位置:

Alt Alt

接着继续运行程序就可以得到预期的弹框了:

Alt

这里对应的机器码解释如下:

Alt

其实不难发现,实际上windows上的栈溢出执行shellcode与linux下过程基本无异。