printf和getenv

时间:2011-08-22 21:41:17

标签: c

Ubuntu 10.04,C,gcc,x86 Intel proc:

当尝试使用printf打印环境变量的地址时,此玩具程序中打印的地址不正确(这是一个有意识的落后版本,我试图通过保存地址值来隔离问题的根源堆,不是直接使用指针,因为我读它可以被覆盖,并使用指针直接产生相同的问题):

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

int main(){

    const char* d = getenv("TEST");
    unsigned i = (unsigned*) malloc(sizeof(unsigned));
    i = (unsigned*) d;

    printf("%s at location 0x%8d.\n", "TEST", i);

}

编辑:与此(原始)版本相同的问题)

int main(){

    const char* d = getenv("TEST");
    printf("%s at location %p.\n", "TEST", d);

    return 0;
}

此问题与如何打印指针无关。请继续阅读。

如果我将程序更改为不打印TEST的地址,但将TEST更改为字符串(其值),则没问题。

但是,返回的地址不是环境变量在进入打印子程序之前实际所在的地址;并且应通过上述设置打印。在eax寄存器中使用main的反汇编和TEST的地址,我被称为TEST变量的正确位置,它比我的系统上大约0xbffffiii的main的堆栈帧稍微低一点(i任何十六进制值)。打印的地址值大约为0xbfeiiiii。

我不明白。这是一种安全预防措施,以防止环境变量被覆盖(例如,粉碎堆栈)?如果是的话,编译器是否真的像我一样跟踪传输地址,并默默调整其值?

或者我错过了什么? (可能)

非常感谢。

P.S。:为了消除可以想象的问题来源,我首先编译没有选项,然后: gcc -O0 -static -fno-stack-protector -g program.c

5 个答案:

答案 0 :(得分:3)

这不是打印地址的正确方法。只需使用%p格式打印(void*)d

printf("%s at location 0x%p.\n", "TEST", (void*)d);

答案 1 :(得分:1)

在R的评论之后我想出来了......在上面进一步指出我向ASLR的方向发展。

我不熟悉ASLR堆栈保护。我所描述的是由于ASLR扰乱内存地址空间以防止预测相对于它的变量的位置。 ASLR一直默认在它出现的LINUX内核中打开。

要查看我的意思,请获取su权限,然后输入(要发送的精确整数可能因您的发行版而异;在我的上它是0,默认为2.另外更好地确认文件首先只包含1个整数看着它,就像它在我的系统上一样):

cd /proc/sys/kernel
export OLD=$(less randomize_va_space) // save old value to restore after
echo 0 >  randomize_va_space  // 0 == turn off ASLR

然后再次运行我的小程序:它将报告我预期的地址(大约0xbffffiii)。显然,使用

恢复旧的保护
echo $OLD > randomize_va_space

非常感谢花费一些时间来帮助的每个人。

答案 2 :(得分:0)

char*指针存储在unsigned变量中可能适用于某些系统,但不可移植。例如,在我的64位Ubuntu框sizeof(char*)==8sizeof(unsigned)==4上。将指针存储在整数变量中的可移植方法是使用intptr_t类型。

或者,要简单地打印地址,请使用%p,如下所示:

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

int main() {
    const char* d = getenv("TEST");
    printf("%s at location %p.\n", "TEST", d);
}

在我的Ubuntu框中,打印出

TEST at location (nil).

TEST at location 0x7fff8cf34628.

取决于是否设置$TEST(地址可以变化)。

答案 3 :(得分:0)

  

但是,返回的地址不是地址   环境变量实际上是在进入印刷品之前定位的   子程序;并且应通过上述设置打印。运用   主要的反汇编和TEST在eax寄存器中的地址,我   提到TEST变量的正确位置,即   在主要的堆栈框架上比我的大约0xbffffiii更进一步   system(i任何十六进制值)。打印的地址值大约为   0xbfeiiiii。

请注意,此处有几个副本:程序中的文字"TEST",用于在程序环境中查找键"TEST",键{{1}的值字符串在该环境中以及指向"TEST"返回的值字符串的指针。我不知道你期望得到什么,但在我所知道的任何x86环境中,环境通常都不在堆栈附近。

您在反汇编中看到的是指向程序中getenv()字符串的指针,位于"TEST"的本地堆栈框架中的某个位置。那是环境变量,它是用于在环境中搜索环境变量的字符串,方法是将程序中的字符串与实际字符串进行比较。

所以我真的不知道你的期望,但看起来好像你期待错误,对不起。

文字main()的地址应该在程序中的某个位置,可能在只读段中。指向该字符串的指针的地址靠近堆栈指针。您环境中字符串"TEST"的地址根本不应靠近堆栈(可能位于比程序低的地址)。与"TEST"相关联的字符串应该非常靠近它,这应该是"TEST"返回的地址。

答案 4 :(得分:0)

您假设环境变量存储在某个特定位置,显然是基于使用gdb检查内存。我建议不是你的程序输出不正确,而是你对环境变量存储位置的假设。

getenv()返回一个指向字符串的指针,该字符串包含指定环境变量的值。如果它的地址与您的预期不同,那么这是一个什么问题呢?

考虑以下计划:

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

int main(void) {
    const char *test = getenv("TEST");
    if (test == NULL) {
        puts("test == NULL");
    }
    else {
        printf("test == %p, points to \"%s\"\n", (void*)test, test);
        /* WARNING: The following has undefined behavior! */
        *(char*)test = 'X';
        system("echo $TEST");
    }
    return 0;
}

当我在我的系统(Ubuntu 11.04,x86,gcc 4.3.4)上运行它并将$TEST设置为“foobar”时,它会打印出来:

test == 0x6125f299, points to "foobar"
Xoobar

所以getenv()给了我一个指向字符串的指针,当我修改它时,它表现为子进程环境的变化。

同样,究竟是什么问题?