c - 如何从 C 中的堆栈指针获取返回地址?
问题描述
对不起,我的 C 有点生锈了。
我想仅使用堆栈指针的地址来获取返回地址作为练习。我知道我可以使用该__builtin_return_address
函数直接获取返回地址,但我想使用堆栈指针手动完成。
现在我只有指向返回地址的指针的地址,但我想获得实际地址。
void a() {
void *sp = __builtin_frame_address(0);
printf("%p\n", (*(&sp) + 0x8));
}
int main() {
a();
}
解决方案
来自此扩展的GNU 文档:
6.51 获取函数的返回或帧地址
这些函数可用于获取有关函数调用者的信息。
内置功能:
void * __builtin_return_address (unsigned int level)
此函数返回当前函数或其调用者之一的返回地址。level 参数是向上扫描调用堆栈的帧数。一个值
0
产生当前函数的返回地址,一个值1
产生当前函数调用者的返回地址,等等。内联时的预期行为是函数返回返回的函数的地址。要解决此问题,请使用noinline函数属性。level 参数必须是一个常量整数。
在某些机器上,可能无法确定除当前函数之外的任何函数的返回地址;在这种情况下,或者当到达栈顶时,这个函数返回一个未指定的值。此外,
__builtin_frame_address
可用于确定是否已到达堆栈顶部。可能需要对返回值进行额外的后处理,请参阅
__builtin_extract_return_addr
。返回地址在内存中的存储表示可能与返回的地址不同
__builtin_return_address
。例如,在 AArch64 上,存储的地址可能会被返回地址签名破坏,而返回的地址__builtin_return_address
则不会。使用非零参数调用此函数可能会产生不可预知的影响,包括使调用程序崩溃。结果,当该
-Wframe-address
选项有效时,诊断出被视为不安全的呼叫。此类调用仅应在调试情况下进行。在代码地址可表示为的目标上
void *
,
void *addr = __builtin_extract_return_addr(__builtin_return_address(0));
给出当前函数将返回的代码地址。例如,这样的地址可以与 dladdr 或其他使用代码地址的接口一起使用。内置功能:
void * __builtin_extract_return_addr(void *addr)
返回的地址
__builtin_return_address
可能必须通过此函数提供才能获得实际的编码地址。例如,在 31 位 S/390 平台上,最高位必须被屏蔽,或者在 SPARC 平台上,必须添加偏移量才能执行真正的下一条指令。如果不需要修复,此函数只需通过
addr
.内置功能:
void * __builtin_frob_return_addr(void *addr)
此函数与
__builtin_extract_return_addr
.内置功能:
void * __builtin_frame_address(unsigned int level)
该函数与 类似
__builtin_return_address
,但它返回的是函数帧的地址,而不是函数的返回地址。__builtin_frame_address
使用 的值调用会0
产生当前函数的帧地址,使用 的值会1
产生当前函数的调用者的帧地址,依此类推。框架是堆栈上保存局部变量和保存寄存器的区域。帧地址通常是函数压入堆栈的第一个字的地址。但是,确切的定义取决于处理器和调用约定。如果处理器有一个专用的帧指针寄存器,并且函数有一个帧,则
__builtin_frame_address
返回帧指针寄存器的值。在某些机器上,可能无法确定除当前函数之外的任何函数的帧地址;在这种情况下,或者当到达堆栈顶部时,如果启动代码正确初始化了第一帧指针,则此函数返回 0。
使用非零参数调用此函数可能会产生不可预知的影响,包括使调用程序崩溃。结果,当该
-Wframe-address
选项有效时,诊断出被视为不安全的呼叫。此类调用仅应在调试情况下进行。
这是修改后的代码:
#include <stdio.h>
void a() {
printf("return address: %p\n", __builtin_extract_return_addr(__builtin_return_address(0)));
}
int main() {
a();
return 0;
}
从帧地址获取返回地址并不总是可能的,并且是非常系统特定的,它还取决于函数调用约定。编译器在编译内置函数时会考虑这些因素__builtin_return_address
,尝试从__builtin_frame_address
.
如果您的目标是构建自省系统,您可能必须分别存储框架和返回地址。您将需要解析调试信息以尝试理解可通过这些指针访问的数据。
推荐阅读
- wso2is - 如何使用 Salt 和 SHA-256 解密存储在 WSO2 身份服务器中的密码
- c++ - 使用 libc++ 构建后 OSX 10.9 上的 Xalan-C 运行时 malloc 错误
- python - 了解 Python 字符串中的重复计数
- reactjs - 带有 React.js 的 TinyMCE 在前端显示 html 标签
- spring - Spring dblink 并在另一个数据库上调用触发器
- security - 我有一个解密密码哈希的任务?
- r - 按特定长度和值组合增长向量
- powershell - 使用 Invoke-VMScript 查询 wmi 磁盘未得到预期结果
- .net - 是否可以使用 powershell 在 zip 中提取/读取文件的一部分?
- python - 等待来自 asyncio.subprocess 模块的 subprocesss.stdout.drain() 后出现异常“ConnectionResetError”