通过指针调用printf()

时间:2013-10-09 12:06:45

标签: c pointers printf

一切正常:

#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(或其他函数)吗?

4 个答案:

答案 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)

两个可能性(实际上具有相同的主要原因):

  1. 您正在进行静态链接。这意味着链接器不需要从libc中提取printf,因此printf根本不会链接到您的程序中。

  2. 您正在进行动态链接,但在您的架构函数指针上实际上并没有指向函数本身,而是它的动态蹦床。这意味着您找到的魔术指针是一个只是因为您实际从程序中调用printf而创建的蹦床。

  3. 我很确定你是动态链接的。让我们通过这个简单的程序看看它在实践中是如何工作的:

    #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 小时时,讲师开始谈论函数指针。