C:如何检查是否已写入内存地址?

时间:2020-04-17 08:11:38

标签: c stack memory-address buffer-overflow

在C中是否有一种方法可以检查我自己的进程的内存地址是否被写入?
例如:如果我的程序接受放置在缓冲区中的输入,是否可以通过检查是否已写入缓冲区的下一个地址来检查输入是否溢出了缓冲区?
我知道我可以检查缓冲区下一个地址的内容并验证它是否已被修改,但是这样对我的工作不利。

我正在研究一种保护措施,以防缓冲区溢出和格式字符串错误同时存在

2 个答案:

答案 0 :(得分:0)

我知道我可以检查缓冲区的下一个地址的内容并验证它是否已被修改,但是这样对我的工作不利。

不幸的是,当使用不带平台和实现特定扩展的C语言时,这是 only 的可能性。

但是,有些C编译器可以生成机器码,这些机器码将在这种情况下检测许多缓冲区溢出-例如,GCC具有开关-fsanitize=undefined-fsanitize=address可以一起使用,当缓冲区溢出时,它将诊断输出到终端。它们使程序运行慢得多。另外,您可以使用valgrind运行程序,它也可以调试许多情况,其中分配的缓冲区被溢出。

答案 1 :(得分:0)

要添加到您的研究中:

如果在支持mprotect调用的linux平台上进行编程,并且仅处理动态分配,则可以将内存总体分配为至少一页以上,并在返回的内存之后至少mprotect个内存动态分配。

例如:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <stdalign.h>
#include <sys/mman.h>
#include <stddef.h>
#include <signal.h>

#if PROTECT_ME
void *malloc2(size_t size) {
    const size_t maxalign = _Alignof(max_align_t);
    assert(size < getpagesize() - sizeof(size_t) - maxalign); // roughly, could be TODO

    // allocate memory
    char * const pnt = mmap(NULL, getpagesize() + 1,
        PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    assert(pnt != (void*)-1);
    assert((uintptr_t)pnt % getpagesize() == 0);

    // calculate a pointer right after the returned pointer that has
    // to be aligned
    char * const end = pnt + getpagesize();
    char * const beg = end - size - (size % maxalign);

    // I store size in the memory before the data
    memcpy(beg - sizeof(size), &size, sizeof(size));

    // protect the data behind the pointer
    assert((uintptr_t)end % getpagesize() == 0);
    const int err = mprotect(end, 1, 0);
    assert(err == 0);

    return beg;
}

void free2(void *beg) {
    // extract size for memory preceding the data
    size_t size;
    memcpy(&size, beg - sizeof(size), sizeof(size));

    // calculate mmap returned pointer
    const size_t maxalign = _Alignof(max_align_t);
    char * const end = beg + size + (size % maxalign);
    char * const pnt = end - getpagesize();

    assert((uintptr_t)pnt % getpagesize() == 0);
    int err = munmap(pnt, getpagesize() * 2);
    assert(err == 0);
}

void signal_segv(int a) {
    fprintf(stderr, "The process had written to a bad place\n");
    abort();
}

__attribute__((__constructor__))
void _init_me(void) {
    signal(SIGSEGV, signal_segv);
}

#define malloc malloc2
#define free free2

#endif


int main() {
    int *a = malloc(5 * sizeof(*a));
    for (int i = 0; i < 10; ++i) {
        a[i] = i;  // will write out-of-bounds for a array when i == 5
        fprintf(stderr, "a[%d]=%d\n", i, a[i]);
    }
    free(a);
}

使用gcc进行编译并在没有保护的情况下运行:

$ gcc 1.c && ./a.out
a[0]=0
a[1]=1
a[2]=2
a[3]=3
a[4]=4
a[5]=5
a[6]=6
a[7]=7
a[8]=8
a[9]=9

但是在平台上启用保护的情况下,将“足够”(对齐)边界越界写入数组时应生成SIGSEGV:

$ gcc -DPROTECT_ME=1 1.c && ./a.out
a[0]=0
a[1]=1
a[2]=2
a[3]=3
a[4]=4
a[5]=5
The process had written to a bad place
Aborted (core dumped)