一切正常:
#include <stdio.h>
int (* func)(const char * fmt, ...);
int main()
{
func = (void *)0x400420; /* printf pointer */
printf("printf address: %p\n", printf);
func("func() calling\n");
return 0;
}
但这不起作用:
#include <stdio.h>
int (* func)(const char * fmt, ...);
int main()
{
func = (void *)0x400420; /* printf pointer */
/* printf("printf address: %p\n", printf); */
func("func() calling\n");
return 0;
}
我做错了什么?请告诉我。我可以通过指针调用printf(或其他函数)吗?
答案 0 :(得分:7)
在第二个示例中,printf
未与您的计划相关联。
答案 1 :(得分:2)
更改您的代码以评估printf地址,而不是将其修复:
#include <stdio.h>
int (* func)(const char * fmt, ...);
int main()
{
func = printf; /* printf pointer */
func("func() calling\n");
return 0;
}
答案 2 :(得分:2)
两个可能性(实际上具有相同的主要原因):
您正在进行静态链接。这意味着链接器不需要从libc中提取printf,因此printf根本不会链接到您的程序中。
您正在进行动态链接,但在您的架构函数指针上实际上并没有指向函数本身,而是它的动态蹦床。这意味着您找到的魔术指针是一个只是因为您实际从程序中调用printf而创建的蹦床。
我很确定你是动态链接的。让我们通过这个简单的程序看看它在实践中是如何工作的:
#include <stdio.h>
int
main(int argc, char **argv)
{
void *foo = (void *)printf;
return 0;
}
我们编译并链接它,然后反汇编。这是将printf的地址加载到局部变量的相关指令。
4004cf: 48 c7 45 f8 c0 03 40 movq $0x4003c0,-0x8(%rbp)
请注意,指针是0x4003c0,与指针非常相似。但等一下。该指令的地址是0x4004cf。这非常接近printf指针所指向的位置,实际上它位于同一页面中,所以这是一个指向libc的指针是不可能的。让我们看看那个地址是什么:
00000000004003c0 <printf@plt>:
4003c0: ff 25 92 04 20 00 jmpq *0x200492(%rip)
4003c6: 68 00 00 00 00 pushq $0x0
4003cb: e9 e0 ff ff ff jmpq 4003b0 <_init+0x18>
这是程序的一部分,而不是libc。如果您更改程序以调用printf,它实际上将跳转到此地址。这是我正在谈论的动态蹦床。 “plt”在这里代表Procedure Linkage Table,它是动态链接器用来解析函数调用而不修改程序文本段的神奇之处。
您的问题是您在程序中删除了printf的任何链接。由于它不存在,链接器不会生成此蹦床,您只需将指针设置为指向恰好在该地址生成的随机内容。
顺便说一下。实际上,在你的程序中引用printf不足以制作像这样的技巧。调用另一个函数可能会将该函数的PLT条目放在生成printf的trampoline的相同位置。你绝对不能保证这些蹦床的最终结果,而且根据我对链接器如何生成代码的经验,它看起来是随机的(因为PLT条目的排序受到链接器中内部哈希表的排序的影响)。换句话说,当您的程序看起来与您用来打印地址的程序完全相同时,这只有很好的工作机会。
答案 3 :(得分:0)
在 Windows 7 上,编译器:mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0
int main()
{
int c = 5;
int (*pr)(const char *const _Format, ...);
pr = &printf;
(*pr)("%d\n", c);
return 0;
}
编译并运行。您可以按照此视频 Pointers in C / C++ [Full Course] 中的说明进行操作以获得更好的理解。大约 3 小时时,讲师开始谈论函数指针。