【问题标题】:How to identify an array defined in a shared library?如何识别共享库中定义的数组?
【发布时间】:2021-02-27 21:17:34
【问题描述】:

我正在处理一个旧的专有共享库——没有源代码。

二进制文件包含很多符号(数千个),其中包括我需要的字符串数组(数百个字符串)。我知道,它就在那里,因为strings(1) 按顺序列出了它们。不幸的是,库附带的头文件没有声明这个列表......

我如何找出库中的哪个符号指的是数组?然后我会添加我自己的声明并能够从我的代码中访问它...

【问题讨论】:

    标签: shared-libraries objdump binutils nm readelf


    【解决方案1】:

    简单的方法...

    gdb 中尝试find 命令。

    例如,我猜字符串在共享库的.rodata 部分,所以我们将使用info target 来查找该部分的地址边界,并在该范围内进行搜索:

    $ gdb a.out
    (gdb) start
    Temporary breakpoint 1 at 0x401136
    Starting program: /scratch/a.out 
    Temporary breakpoint 1, 0x0000000000401136 in main ()
    (gdb) info target
        ...
        0x00007f5049f0c000 - 0x00007f5049f0c020 is .rodata in ./lib.so
        ...
    (gdb) find 0x00007f5049f0c000,0x00007f5049f0c020,"test string"
    0x7f5049f0c000 <foo1>
    0x7f5049f0c014
    2 patterns found.
    

    如果你的字符串被声明为初始化的字符数组,那应该可以工作,比如

    static const char foo1[] = "test string";
    

    如果它们被声明为指针,比如

    static const char *foo2 = "another test string";
    

    这会有点困难。字符串本身不会被标记,您要查找的符号将是指向该字符串的指针。然后你需要再次使用find 命令来搜索那个指针值,我猜它在.data 部分。

    (gdb) find 0x00007f5049f0c000,0x00007f5049f0c020,"another test string"
    0x7f5049f0c00c
    1 pattern found.
    (gdb) info target
        ...
        0x00007f5049f0e018 - 0x00007f5049f0e028 is .data in ./foo.so
        ...
    (gdb) find 0x00007f5049f0e018,0x00007f5049f0e028,0x7f5049f0c00c
    0x7f5049f0e020 <foo2>
    1 pattern found.
    

    学习经历...

    使用strings --radix=x,找到你的字符串。将列出文件中的偏移量(以十六进制表示);例如:

    $ strings --radix=x lib.so | grep "test string" 
    5ad740 test string
    

    现在您需要将该文件偏移量转换为虚拟地址。

    使用readelf -lW列出库的段;你会看到这样的东西:

    $ readelf -lW lib.so | grep -e Type -e LOAD
      Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
      LOAD           0x000000 0x0000000000400000 0x0000000000400000 0x004dc0 0x004dc0 R   0x1000
      LOAD           0x005000 0x0000000000405000 0x0000000000405000 0x53a989 0x53a989 R E 0x1000
      LOAD           0x540000 0x0000000000940000 0x0000000000940000 0x28f7c2 0x28f7c2 R   0x1000
      LOAD           0x7cfc70 0x0000000000bd0c70 0x0000000000bd0c70 0x0014b0 0x00d518 RW  0x1000
    

    您对OffsetVirtAddrFileSiz 列感兴趣。找到包含文件偏移量X 的 LOAD 段,其中Offset &lt;= X &lt; Offset + FileSiz。在此示例中,偏移量 0x5ad740 位于第三段(从偏移量 0x540000 开始)。通过减去段的起始偏移量并添加段的起始虚拟地址,将您的偏移量转换为虚拟地址:

    offset - starting offset + starting virtual address = virtual address
    0x5ad740 - 0x540000 + 0x940000 = 0x9ad740
    

    现在使用nm -n 按地址顺序扫描符号表。如果幸运的话,您会找到完全匹配的:

    $ nm -n lib.so | grep 9ad740
    00000000009ad740 R foo1
    

    否则,您将需要查找最近的具有较低地址的符号。应该就是这样,如果字符串被声明为数组

    如果字符串被声明为指针,那么这些指针将需要动态重定位(我们正在查看共享库,对吗?)——很可能是R_xxx_RELATIVE 重定位。寻找一个 RELATIVE 重定位,它的加数与你的字符串的虚拟地址匹配:

    $ readelf -rW lib.so
    Relocation section '.rela.dyn' at offset 0xc00000 contains 2 entries:
        Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
    0000000000c00018  0000000000000008 R_X86_64_RELATIVE                      9ad740
    

    这表明您的指针位于 0xc00018。再次使用nm,您可以找到该虚拟地址的符号:

    $ nm -n lib.so | grep c00018
    0000000000c00018 R foo2
    

    【讨论】:

    • 对于分数如此之低的人来说,这是一个非常有用的答案 :) 你应该发布更多,非常感谢!
    猜你喜欢
    • 1970-01-01
    • 2019-10-21
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 2011-02-28
    • 1970-01-01
    • 2016-06-15
    相关资源
    最近更新 更多