基于堆栈的缓冲区的缓冲区溢出防止

时间:2016-04-22 08:55:03

标签: c arrays buffer-overflow

我正在编写一个C库,它为strcpy,strcat,gets等提供包装函数,以防止缓冲区溢出攻击。基本上我所做的是跟踪所有缓冲区的大小,并防止写入超出分配的内存。通过编写malloc,calloc和realloc的包装器,我能够跟踪动态分配的缓冲区。但是我无法在堆栈中获得缓冲区的大小。

char s[10];
char *s1 = "stackoverflow";
strcpy(s,s1);

上面代码中对'strcpy'的调用会调用我的包装函数,它看起来像

char *strcpy(char* s1, const char* s2)

由于函数将source作为char *,我无法知道堆栈中源数组的大小。我尝试重新分配基于堆栈的缓冲区,我的程序崩溃了。重新分配缓冲区是否可行?任何帮助表示赞赏。

4 个答案:

答案 0 :(得分:2)

  

由于该函数将source作为char *,我无法知道   堆栈中源数组的大小。

char *是一个通用指针,您可以将源代码转换为其他类型。

使用复合文字和struct

的示例
struct secure {
    char *s;
    size_t sz;
};

#define SECURE(xs, xsz) (char *)&((struct secure){.s = xs, .sz = xsz})

char *strcpy(char *s1, const char *s2)
{
    struct secure *sec = (struct secure *)s1;

    if (sec->sz < strlen(s2)) {
        printf("%s\n", "stackoverflow");
    } else {
        yourimplementation(sec->s, s2);
    }
    return sec->s;
}

int main(void)
{
    char s[10];
    char *s1 = "stackoverflow";

    strcpy(SECURE(s, sizeof s - 1), s1);
    return 0;
}

您可以通过以下方式避免在通话中使用SECURE

struct secure {
    char *s;
    size_t sz;
};

#define SECURE(ps, psz) (char *)&((struct secure){.s = ps, .sz = psz})
#define strcpy(s1, s2) strcpy(SECURE(s1, sizeof s1 - 1), s2)

char *(strcpy)(char *s1, const char *s2) /* () to distinguish from macro */
{
    struct secure *sec = (struct secure *)s1;

    if (sec->sz < strlen(s2)) {
        printf("%s\n", "stackoverflow");
    } else {
        yourimplementation(sec->s, s2);
    }
    return sec->s;
}

int main(void)
{
    char s[10];
    char *s1 = "stackoverflow";

    strcpy(s, s1);
    return 0;
}

但这不适用于动态内存(sizeof指针不是你想要的)

答案 1 :(得分:1)

此处s是自动存储,realloc是通过调用malloc或其他allocatin函数来重新分配动态分配的内存。因此,您无法在realloc上拨打s,调用它会导致UB,因此崩溃并不奇怪。

因此,即使您找到了获取自动变量大小的方法,也无法调整大小,您唯一能做的就是创建一个全新的缓冲区,然后将旧缓冲区复制到新缓冲区中。但在这种情况下,您需要一个标志来区分动态存储和自动存储。

答案 2 :(得分:0)

你不能仅仅通过char *指针知道buffersize。因此,许多库实现了自己的字符串对象。示例:glib中的GString,PHP有其内部String对象,pascal语言使用字符串对象。

答案 3 :(得分:0)

您正在以一种有趣且更加自动化的方式解决一个常见问题。

GCC本身就是一个堆栈保护程序,如果有人写入不允许的区域,无论是否故意,它都会使程序崩溃。它将某些变量放在具有已定义值的堆栈上,如果这些值发生更改,则可以检测到堆栈损坏。

在函数内处理char指针时,无法知道该区域指向的内存量。您可以设计自己的“char”类/结构,通过保持您的char指针和它的大小来为您处理。

struct char_helper {
     int size;
     char data[];
}

char buffer[10+sizeof(struct char_helper)]:
struct char_helper *mychar = (struct char_helper*)buffer;
mychar.size = 76;
your_strcpy(mychar, "hallo", 6);

您的函数your_strcpy现在可以检查大小。从技术上讲,我为这个大小添加了一个整数。另一种更灵活的方法是告诉strcpy缓冲区应该复制到哪个大小......

这就是strncpysnprintfstrnlen所做的事情。他们有一个小怪癖,终止0个字符。

相关问题