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
答案 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*)==8
和sizeof(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()
给了我一个指向字符串的指针,当我修改它时,它表现为子进程环境的变化。
同样,究竟是什么问题?