【问题标题】:override libc open() library function覆盖 libc open() 库函数
【发布时间】:2014-05-05 07:21:42
【问题描述】:

我的库中的 glibc 提供了相同的覆盖 open() 并且我首先用我的库设置了 LD_PRELOAD,所以当进程调用 open() 时,打开是在我的库中定义的被调用。

问题:- glibc 中有几个其他函数调用 open() 一旦这样的例子是getpt(),当getpt() 调用open() 时,open() 被定义在 glibc 被调用时,我将如何让 getpt() 调用在我的 library() 中定义的 open()

约束: - 我没有编译 glibc 的选项。

【问题讨论】:

    标签: linux gcc overriding ld glibc


    【解决方案1】:

    正如 tmcguire 所说,从posix_openpt__open 的调用是对internal 符号的调用,不能插入。

    实际上,glibc 开发人员将此调用视为实现细节,您无权更改。

    我正在查看编译时解决方案

    你不能拥有它。

    比运行时解决方案导致运行时解决方案会对性能产生影响。

    运行时解决方案不需要对性能产生任何影响(除了调用open 而不是glibcs 的开销)。

    我只知道库插入glibc 内部调用的一种方法:运行时修补。这个想法是

    • 找到libc.so.6open的地址(这是__open的别名),
    • 在运行时定位glibc.text部分的边界
    • 扫描它以获取CALL __open 指令
    • 对于任何此类指令
      • mprotect 可写页面
      • 计算一条新指令 CALL my_open 并将其“修补”在原始指令的“顶部”
      • mprotect返回页面读取并执行

    这很难看,但它适用于 i*86(32 位)Linux,CALL 可以“访问”4GB 地址空间内的任何其他指令。它不适用于 x86_64,其中 CALL 仍限制为 +/- 2GB,但从您的库到 glibc 的距离可能不止于此。

    在这种情况下,您需要在libc.so.6 中找到合适的蹦床,您可以将原始CALL 重定向到该蹦床,并且您可以将注册间接JMP 放置到您的最终目的地。幸运的是,由于函数对齐,libc.so.6 通常有多个大小合适的未使用的NOP 区域。

    【讨论】:

    • 好的,在基于生产的环境中进行这种黑客攻击并不好,而且正如你所说的那样,它很丑。
    • @user3465381 FWIW,使用LD_PRELOAD 插入open 只是稍微不那么丑陋,可以说你也不应该在生产中这样做。
    • 好的,我正在提供一个可供内部应用程序和第三方应用程序使用的库。我没有编辑第三方代码的选项。 dlopen 也不是一个选项,原因与无法编辑第三方代码相同。鉴于这些限制,您将如何让第三方应用程序调用库中定义的开放函数。?这不是 LD_PRELOAD 的确切用例吗?
    • 我喜欢“实现细节”位,虽然正如你所说的修补东西很丑。
    【解决方案2】:

    只需在我的库中定义 getpt() 函数,我就能在编译时解决它。

    此解决方案不完整,因为 glibc [getpt() 除外] 中可能有其他函数可以调用 open,然后将调用 glibc 中的 open 调用。

    我现在可以接受这个解决方案,但我需要在将来完全修复它。

    【讨论】:

      【解决方案3】:

      我认为你不能用 LD_PRELOAD 做到这一点。

      如果你查看 libc 的反汇编(使用例如objdump --disassemble /lib64/libc.so.6 | grep -A20 "<getpt>:",你可以看到getpt() 调用__open(),这是open() 的别名。

      000000000011e9d0 <posix_openpt>:
        11e9d0:       53                      push   %rbx
        [...]
        11e9ee:       e8 dd d9 fb ff          callq  dc3d0 <__open>
      

      但是,对 __open 的调用是不通过 PLT 的 PC 相关调用 - 这意味着您可以使用 LD_PRELOAD 插入符号,因为 libc 中的所有调用都不会使用血小板。这可能是因为 libc 与 -BSymbolic 链接。

      剩下的唯一选择是做 strace 所做的事情,并使用 ptrace 附加到进程。请参阅 this question 了解其工作原理。

      【讨论】:

      • 虽然 ptrace 是一个不错的选择,但是要使用 ptrace,您需要一个父子关系,这是不可接受的,因为我提供了一个库,如果我必须使用 ptrace 执行此操作,然后我将不得不分叉然后对孩子进行跟踪。我不认为我可以有这个解决方案
      • 如果您不能使用 ptrace,那么我看不到拦截对 open() 的调用的方法,缺少像英特尔的 PIN 库那样的动态运行时检测。
      • hmm 好的,让我们看看其他人是否有想法,我正在查看 compile 时间解决方案而不是运行时解决方案,因为运行时解决方案将具有 性能 影响。
      猜你喜欢
      • 2015-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-29
      • 1970-01-01
      • 2010-10-25
      • 1970-01-01
      • 2021-08-26
      相关资源
      最近更新 更多