是否可以识别地址引用是否属于进程地址空间中的静态/堆/堆栈

Har*_*ald 3 c heap-memory memory-address

我们有一种机制可以监控捕获引用地址的加载和存储指令.我想对地址进行分类,无论它们属于堆栈,堆还是分配静态变量的区域.有没有办法以编程方式进行此分类?

我最初的想法是在进程开始运行时立即执行一个带有小内存请求(1?)的malloc(),这样我就可以捕获堆的"基地址"(或起始地址).这样,我可以区分静态分配的那些变量和其余的变量.对于那些不属于静态区域的引用(那些是堆和堆栈),我怎么能区分它们?

一些小测试显示以下简单代码(在Linux 3.18/x86-64中运行,使用gcc 4.8.4编译)

#include <stdio.h>
#include <stdlib.h>

int x;

int foo (void)
{
    int s;
    int *h = malloc (sizeof(int));

    printf ("x = %p, *s = %p, h = %p\n", &x, &s, h);
}

int main (int argc, char *argv[])
{
    foo();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

显示了地址空间的一些随机化(不是在静态变量中,而是在剩余部分 - 堆和堆栈中),这可能会增加一些不确定性,但可能是一种找到这些地区的限制的方法.

kfx*_*kfx 9

没有标准的C API,这意味着所有可能的解决方案都将基于特定于平台的黑客攻击.此外,这个答案仅限于单线程应用程序.

  1. 如何识别堆栈地址?

堆栈是连续的存储区域.因此,您需要知道的只有两个数字:堆栈的顶部和堆栈的底部.堆栈的顶部基本上受当前函数的堆栈帧的限制.但是,由于无法从C代码访问当前堆栈帧的大小,因此很难确定当前帧的确切位置.这里的技巧是从当前调用另一个函数并使用被调用函数堆栈框架中的地址作为边界值stack_top.

学习堆栈的底部更简单 - 它的值在程序执行期间保持不变,并且受入口点函数的堆栈帧(main()在C程序中)的限制.因此,在main()函数中获取某个局部变量的地址就足够了.

还有一点需要注意,x86堆栈会向后增长,这意味着堆栈顶部的地址小于底部.这段代码总结了一下:

void *stack_bottom;

bool IS_IN_STACK(void *x) __attribute__((noinline));
bool IS_IN_STACK(void *x) {
    void *stack_top = &stack_top;
    return x <= stack_bottom && x >= stack_top;
}

int main (int argc, char *argv[]) {
   int x;
   stack_bottom = &x;
   ...
Run Code Online (Sandbox Code Playgroud)
  1. 如何识别静态变量的地址?

这里的逻辑更简单.静态变量在固定的,特定于平台的地址开始的内存区域中分配.通常该区域位于存储器中的所有其他区域之前.因此,唯一需要学习的是这个静态存储区的结束地址.

幸运的是,GCC接头提供符号 end,edata以及etext该表示的端部.bss,.data.text分别段.静态变量分配在.bss.data分段中,因此在大多数平台上进行此检查应该足够了:

#define IS_STATIC(x) ((void*)(x) <= (void*)&end || (void*)(x) <= (void*)&edata)
Run Code Online (Sandbox Code Playgroud)

这个宏会检查edataend避免做出假设,关于这些.bss.data至上在内存中.

  1. 堆地址.

堆变量通常直接在地址.data.bss区域中的地址中分配.但是,有时堆地址可能属于非连续的内存范围.因此,您可以在此处执行的最佳操作是读取Linux进程文件以查找其他答案中建议的内存映射.或者,只是检查是否都IS_IN_STACKIS_STATIC返回false.

使用这些宏的完整程序:

int x;
extern int end, edata;

void *stack_bottom;

bool IS_IN_STACK(void *x) __attribute__((noinline));
bool IS_IN_STACK(void *x) {
    void *stack_top = &stack_top;
    return x <= stack_bottom && x >= stack_top;
}

#define IS_STATIC(x) ((void*)(x) <= (void*)&end || (void*)(x) <= (void*)&edata)

int foo (void)
{
    int s;
    int *h = malloc (sizeof(int));

    printf ("x = %p, *s = %p, h = %p\n", &x, &s, h);
    // prints 0 1 0
    printf ("%d %d %d\n", IS_IN_STACK(&x), IS_IN_STACK(&s), IS_IN_STACK(h));
    // prints 1 0 0
    printf ("%d %d %d\n", IS_STATIC(&x), IS_STATIC(&s), IS_STATIC(h));
}

int main (int argc, char *argv[])
{
    int x;
    stack_bottom = &x;
    foo();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)