如何在C语言中在运行时访问解释器路径地址?

11 浏览
0 Comments

如何在C语言中在运行时访问解释器路径地址?

通过使用objdump命令,我发现内存中的地址0x02a8包含了路径/lib64/ld-linux-x86-64.so.2,而且由于C标准的原因,这个路径以0x00字节结尾。

因此,我尝试编写一个简单的C程序来打印这行代码(我使用了书籍《RE for beginners》中的示例-第24页):

#include 
int main(){
    printf(0x02a8);
    return 0;
}

但是令我失望的是,我得到的是一个段错误,而不是预期的/lib64/ld-linux-x86-64.so.2输出。

我觉得使用这种没有指定符号或至少指针转换的"快速"调用printf很奇怪,所以我试图让代码更自然一些:

#include 
int main(){
    char *p = (char*)0x02a8;
    printf(p);
    printf("\n");
    return 0;
}

但是即使运行这段代码,我仍然得到了一个段错误。

我不相信这是由于受限制的内存区域造成的,因为在书中,第一次尝试就一切正常。我不确定,也许还有其他没在书中提到的东西。

所以我需要一些清晰的解释,为什么每次运行程序时都会出现段错误。

我正在使用最新的完全升级的Kali Linux版本。

0
0 Comments

问题的原因是在Linux上使用GCC编译生成的ELF可执行文件是位置无关的。这是为了安全考虑,当程序运行时,操作系统可以将其放置在内存中的任何位置,并且程序仍然可以正常工作。这种技术被称为地址空间布局随机化(ASLR),是操作系统的一个特性,默认情况下启用。

通常,ELF程序会有一个“基地址”,并且必须加载到该地址才能正常工作。然而,在位置无关的ELF中,“基地址”被设置为0x0,操作系统和解释器会在运行时决定将程序放置在何处。

当对位置无关的可执行文件使用objdump时,你看到的每个地址都不是真实地址,而是相对于程序基地址的偏移量(只有在运行时才能知道基地址)。因此,只有在运行时才能知道字符串(或任何其他变量)的位置。

解决方法是编译一个非位置无关的ELF。可以通过以下方式实现:

gcc -no-pie -fno-pie prog.c -o prog

编译方式更改后,通过objdump可以看到全新的0x4002a8地址,与之前的0x02a8地址类似,看起来更像是真实的地址。但是仍然会出现段错误。解决方法是以与objdump参数程序相同的方式编译“打印路径”程序,加上-no-pie选项。

0
0 Comments

在C语言中,我们如何在运行时访问解释器路径地址?

问题的出现原因是由于现代64位Linux操作系统中的可执行文件是位置无关的,并且它们会在内存中加载到任意地址。因此,ELF文件不包含任何固定的基地址。

解决方法之一是创建一个位置相关的可执行文件,但这并不适用于现代64位Linux操作系统中的任意可执行文件。因此,更值得学习如何处理位置无关的可执行文件,但这可能有点棘手。

以下代码可以用于打印ELF即ELF文件头的magic值和解释器字符串。这种方法可能只适用于较小的可执行文件。

#include 
#include 
#include 
int main(){
    // 将main函数转换为uintptr_t类型
    uintptr_t main_addr = (uintptr_t)main;
    // 清除低12位,使其指向页面的开始
    main_addr &= ~0xFFFLLU;
    // 减去一页,使其位于elf头部...
    main_addr -= 0x1000;
    // 打印ELF的magic值
    puts((char *)main_addr);
    // 打印解释器字符串,从hexdump偏移量开始
    puts((char *)main_addr + 0x318);
}

还有另一种方法可以找到内存中ELF可执行文件的起始位置,即所谓的"辅助向量"和getauxval函数:

#include 
#include 
#include 
int main(){
    char *elf_header = (char *)getauxval(AT_PHDR) - 0x40;
    puts(elf_header + 0x318); // 或者使用你的可执行文件中的偏移量
}

最后,我们可以根据ELF头部的信息独立找到解释器的位置,前提是你有一个64位的ELF文件,使用Wikipedia中的魔术数:

#include 
#include 
#include 
int main() {
    // 获取第一个程序头部的指针
    char *ph = (char *)getauxval(AT_PHDR);
    // ELF头部在此位置
    char *elfh = ph - 0x40;
    // 段类型0x3是解释器;64位可执行文件中的程序头部项目长度为0x38
    while (*(uint32_t *)ph != 3) ph += 0x38;
    // 偏移量是从可执行文件的开头开始的64位,偏移量为0x8
    uint64_t offset = *(uint64_t *)(ph + 0x8);
    // 打印解释器路径...
    puts(elfh + offset);
}

我在答案中提供了Marco的链接,让他因为他在昨天比我更快地给出了有效的`-no-pie`解决方案而得到更多的赞。

0
0 Comments

在C语言中,有一个问题是如何在运行时访问解释器路径地址。根据下面的内容,我们可以了解到这个问题的原因是由于在使用printf函数时,没有正确使用格式参数。

在使用printf函数时,第一个参数是一个字符串,用于指定显示的格式,而后面的参数是根据格式字符串来显示的数据。如果要打印一个字符串,应该使用%s格式参数。示例代码如下:

printf("%s\n", pointer_to_beginning_of_string);

如果这个解决方法不起作用,可能是因为尝试访问了不允许访问的内存。可以尝试在编译器中添加额外的标志“-Werror -Wextra -Wall -pedantic”来查看错误信息。

以上内容解释了在C语言中如何在运行时访问解释器路径地址的问题的原因和解决方法。

0