PC
设置为0
。我错过了什么还是做错了什么?该过程进入ARM模式下的
0x1708
。下面是我从ELF文件中反汇编的奇怪的asm代码块。 。值为0x170c
(此文件存储在little-endian中)。然后继续。; section: .plt
; function: function_1708 at 0x1708 -- 0x1718
0x1708: 04 e0 2d e5 str lr, [sp, #-4]!
0x170c: 04 e0 9f e5 ldr lr, [pc, #4]
0x1710: 0e e0 8f e0 add lr, pc, lr
0x1714: 08 f0 be e5 ldr pc, [lr, #8]!
; data inside code section at 0x1718 -- 0x171c
0x1718: b4 77 00 00 |.w.. |
而0x8ed4位于
LR
部分。在0x1718
处为零。我从0x77b4
追溯到这个奇怪的块,因此在执行该块之前不应修改任何数据。我做错了什么,或者这是ARM体系结构的特定行为吗? />
#1 楼
实际上,您描述的行为来自GOT / PLT部分的通常行为。它们用于在运行时动态地将程序调用链接到共享库函数。实际上,共享库可以在进程内存中的任何位置加载,无法静态预测其弹出位置。因此,GOT / PLT在运行时动态加载每个库函数的地址,并将其缓存以备将来使用。
其工作方式非常简单,PLT(过程链接表)只是一堆小代码小工具(对于程序中调用的每个库函数,一个函数,再加上位于PLT节开头的一个通用代码)。库函数的地址。
首先,由于尚未解析函数地址,因此GOT都设置为零。整个程序中执行的GOT填充都将被执行并调用新函数(某些功能可能很少被调用,而GOT中的地址可能很少被填充)。 PLT(请参见下图)并执行专门为此功能编写的代码(每个代码小工具都有其自己的偏移量,以便从GOT获取正确的地址)。但是,起初,GOT仅包含零,因此您需要对其进行初始化。
要对其进行初始化,请跳到PLT的最开始,找到程序要调用的库函数的地址。您执行此代码,然后在GOT中写入该函数的地址。
一旦缓存了该函数的地址,如果您调用了函数,然后直接跳转到它(如下图所示)。
对于ARM,您有两种类型的GOT / PLT模式:
直接PLT
间接PLT
在您的情况下,这是直接PLT(但是它似乎是很旧的代码,我怀疑一个旧的编译器左右,因为新的编译器最好使用
ip
代替lr
)。 >请注意,PLT是静态部分,因此0x170c
将始终保持不变(因此,pc
将始终在0x170c
处加载相同的值)。参考文献
Eli Bendersky在共享库中的位置独立代码(PIC)。
Raspberry Pi中的ARM汇编程序–第27章,由Roger FerrerIbáñez编写。 />
用于Cortex-M和Cortex-R的ARM FDPIC工具集,内核和库。
评论
因此,在我的情况下,function_1708是PLT [0],这是调用解析器的代码,对吗?链接器如何知道将其内存地址填充到共享库的正确地址的位置?就我而言,0x8ed4不是.got中的第一个地址。第一个是0x8eac。填充解析器地址的策略取决于ELF文件的格式,运行的操作系统或体系结构或CPU?
– IvanaGyro
18-09-18在7:41
在程序每次运行的开始,链接器都会设置共享库在PLT中的地址,并且程序所使用的每个函数在当前共享库中都具有一个偏移量。这样,如果您知道共享库在内存中的地址以及特定函数的偏移量,则可以获取虚拟内存中该函数的地址。多亏了GOT(可缓存结果),您只需计算一次。
–恐怖
18-09-18在8:02
我想我问错了问题。但是,我在某些ELF规范中得到了答案。与@Igor Skochinsky♦回答的相同,链接器选择放置解析器地址的位置在ELF规范中定义。该定义取决于文件ELF的类型。无论如何,您对我有很大帮助。谢谢!
– IvanaGyro
18-09-18在16:26
#2 楼
虽然另一个答案没有错,但实际上并没有涵盖真正的问题:调用零地址不会导致崩溃的原因是什么? ,但实际上前几个GOT条目是特殊的/保留的,并由动态加载器/链接器(有时也称为解释器)用于其自身目的:当前模块的符号,它是标签列表(
GOT[0]
条目)的开始,其中包含正确解析动态符号所需的各种信息。请参阅ELF规范。 在运行时使用指向
_DYNAMIC
结构的指针填充Elf32_Dyn
,该结构包含该进程中存在的所有动态图像的列表。此列表位于动态链接器中(在Linux系统上为GOT[1]
,在Android系统上为link_map
二进制文件)。 br /> 您引用的代码段是在首次调用外部符号时调用的解析程序存根(您可以看到,所有GOT条目均已初始化为其地址
ld.so
)。它获取linker
并跳转到它,它应该最终进入动态链接器,该链接器将查找符号,修补GOT条目并作为最后一步跳转到该函数。尚无(AFAIK)官方文档,仅是ELF ABI规范,glibc源代码和各种博客文章的随机片段。我建议您在调试器中单步执行此代码以查看实际发生的情况,并将其与源代码进行匹配。除了来自@perror的链接之外,我还找到了这篇文章,其中解释了一些保留的GOT条目(尽管对于x64 Linux而非ARM Android)。评论
我试图找到ELF的官方规格。根据维基百科,有很多不同的规范。该组织开发的ELF,即UNIX系统实验室,已经不存在了。第一个规范由AT&T发布。我很好奇为什么这么多公司发布其ELF规范或将ELF规范包含在其ABI规范中,以及历史背景是什么。挺有趣的。
– IvanaGyro
18-09-18在16:14
@IgorSkochinsky:我不知道这一切!实际上,我假设IvenCJ7所做的分析是静态完成的,因此GOT最初设置为零,然后设置为PLT的开始。我现在看到我很可能错了。很好的解释伊戈尔(再次)和良好的链接,谢谢!
–恐怖
18-09-18在16:45
评论
您是否熟悉其他汇编语言的GOT / PLT? (通常,x86更为人所知)。因为实际上您的问题与GOT / PLT行为密切相关,特别是与ARM体系结构有关。不。我是逆向工程的新手。我正在搜索相关信息。有什么好读的书吗?
好吧,那我就写一个完整的答案。
请注意,这并非微不足道!如果您一开始不太了解,那是完全正常的。尝试获取调试器并逐步执行程序以更好地理解(并保持信心!)。