【问题标题】:show strings in compiled binary在编译后的二进制文件中显示字符串
【发布时间】:2015-03-09 02:31:48
【问题描述】:

假设我有一个简单的 C 程序,我用 gcc -o hello hello.c 编译它:

#include<stdio.h>

main()
{
    printf("hello");
}

现在我想用strings 实用程序显示“字符串”:

$ strings hello
/lib64/ld-linux-x86-64.so.2
__gmon_start__
libc.so.6
printf
__libc_start_main
GLIBC_2.2.5
fffff.
l$ L
t$(L
|$0H
hello
;*3$"

正如预期的那样,我可以在二进制文件中看到字符串“hello”。

但是,当我修改 C 程序并将“hello”作为常量时:

#include<stdio.h>

char s[6] = {'h','e','l','l','o','\0' } ;

main()
{
    printf("%s\n", s);
}

我在二进制文件中看不到字符串“hello”了。

有人能解释一下原因吗?

【问题讨论】:

  • 检查你的可执行文件的sectionsman strings 建议“它只打印来自目标文件的初始化和加载部分的字符串”。
  • 需要更多详细信息。它在 64 位 Linux 上使用 gcc 4.9.2 为我工作 (tm)。您还可以通过查看汇编程序输出 (gcc -S) 而不是编译后的二进制文件来更好地了解原因。
  • 试试strings -a hello
  • OT:至少是int main(void)
  • "但是,当我修改我的 C 程序并将 "hello" 作为常量时:" - 仅供参考,char s[6] = .... 不是常量。出于兴趣,将s inside 移至main() 并重复您的测试。如果您按要求添加装配输出,这将是您的问题的弹药,这将表示段名称。

标签: c string gcc


【解决方案1】:

来自man 1 strings(强调我的):

对于给定的每个文件,GNU 字符串打印可打印字符 至少 4 个字符长的序列(或给定的数字 使用以下选项),然后是不可打印的字符。 默认情况下,它只打印初始化和 加载的目标文件部分;对于其他类型的文件,它会打印 整个文件中的字符串。

C 语言没有将字符串定义为一等公民。它们被表达字符串数组字符串字面量。例如,在这样的基本程序中:

#include <stdio.h>

int main(void)
{
    char s[] = "my string";

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

    return 0;
}

我们可以合理地说s 数组包含一个字符串。请注意,这是在堆栈上分配的。它具有自动存储持续时间,与您问题中的示例相反,其中smain(和任何)函数之外明确定义。

现在,回到您的问题,您的两个程序中的两个底层 对象 具有相同的特征:

  • 它们的类型为 char[6] 并且具有相同的内容 (C11 §6.2.5/p20),
  • 它们具有静态存储持续时间,这意味着它们必须在程序执行之前进行概念初始化(C11 §5.1.2/p1)。

唯一的区别是修改字符串文字会调用未定义的行为,因此编译器可能会选择将它们放置到单独的(例如只读)内存位置。

C11 §6.2.5/p20 类型

数组类型描述了一个连续分配的非空集合 具有特定成员对象类型的对象,称为元素类型。

C11 §5.1.2/p1 执行环境

所有具有静态存储持续时间的对象都应被初始化(设置为 它们的初始值)在程序启动之前。

从更实际的角度来看,除了strings 命令之外,您还可以使用gdb 调试器分析您的程序,更具体地说,使用x/s 命令。这是基本的说明:

$ gcc -g hello.c -o hello
$ gdb -q hello
Reading symbols from /home/grzegorz/hello...done.
(gdb) disas /m main
Dump of assembler code for function main:
6   {
   0x00000000004004c4 <+0>: push   %rbp
   0x00000000004004c5 <+1>: mov    %rsp,%rbp

7       printf("%s\n", s);
   0x00000000004004c8 <+4>: mov    $0x60086c,%edi
   0x00000000004004cd <+9>: callq  0x4003b8 <puts@plt>

8   }
   0x00000000004004d2 <+14>:    leaveq 
   0x00000000004004d3 <+15>:    retq   

End of assembler dump.
(gdb) x/s 0x60086c
0x60086c <s>:    "hello"

您可能想为您的程序比较disas 命令的结果,看看它们之间是否存在一些差异。

【讨论】:

  • 如果我没记错的话,由于 BFD 库中最近的一个漏洞,strings 的默认值在许多发行版中已经从 -d 更改为 -a,所以现在整个默认情况下会扫描文件,而不仅仅是初始化、加载的部分。
  • @rodrigo:回复我自己,这里是vulnerability description的链接。
【解决方案2】:

我在二进制文件中看不到字符串“hello”了。

我认为这是 expected 有效的行为,因为您不再提供文字 "hello",而是提供 6 个单独的 char 文字。

【讨论】:

  • 但是除了他之外,我们所有人都看到了。即使它们是char,它们也会连续存储。在strings 中可见
  • 我也看到了它们(使用最近的 Debian 稳定版)。但是没有规定它们需要由编译器连续存储。 @Milind
  • @alk 实际上,编译器很可能会生成一系列单独的立即存储操作(每个字符一个)。如果没有 ASM 输出,我们无法判断。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-21
  • 1970-01-01
  • 2016-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多