TL;DR:您可以使用 .hook 跨越共享对象边界的常规函数调用bpftrace。然而,特别是对于我们所认为的 C++ 标准库,这只是实际 C++“标准库”功能的一小部分。但是,即使您可以跟踪所有这些函数调用,这也不能为您提供任何保证。
没有通用的方法可以知道哪些函数被调用:最终,您无法知道标准库的哪一部分被静态链接到程序中;根据设计,其中大部分甚至是内联的!在 C++ 中,大量标准功能位于 C++ 模板中,因此是程序编译单元的一部分,而不是 libstdc++。更糟糕的是,即使只使用绝对来自共享库的功能,“函数调用”也根本不是什么“特殊”的东西;它只是一个函数。它只是设置您需要传递参数并跳转到地址的寄存器。对于共享对象,该地址通常在启动时使用标准动态链接器进行解析,但绝对不能保证这是唯一发生的事情。特别是如果程序在运行时加载库(例如,任何执行插件的库,或脚本语言,或......),它并不像仅列出要导入的函数表那么简单。
您可以跟踪您的用户层程序并为 C++ 标准库中的每个函数插入一个钩子,并记录下来。bpftrace可能是这里选择的工具。安装 bpftrace,并查看其示例工具,通常位于 /usr/share/bpftrace/tools 中,尤其是bashreadline.bt:
#!/usr/bin/bpftrace
/*
* bashreadline Print entered bash commands from all running shells.
* For Linux, uses bpftrace and eBPF.
*
* This works by tracing the readline() function using a uretprobe (uprobes).
*
* USAGE: bashreadline.bt
*
* This is a bpftrace version of the bcc tool of the same name.
*
* Copyright 2018 Netflix, Inc.
* Licensed under the Apache License, Version 2.0 (the "License")
*
* 06-Sep-2018 Brendan Gregg Created this.
*/
BEGIN
{
printf("Tracing bash commands... Hit Ctrl-C to end.\n");
printf("%-9s %-6s %s\n", "TIME", "PID", "COMMAND");
}
uretprobe:/bin/bash:readline
{
time("%H:%M:%S ");
printf("%-6d %s\n", pid, str(retval));
}
TL;DR:您可以使用 .hook 跨越共享对象边界的常规函数调用
bpftrace
。然而,特别是对于我们所认为的 C++ 标准库,这只是实际 C++“标准库”功能的一小部分。但是,即使您可以跟踪所有这些函数调用,这也不能为您提供任何保证。没有通用的方法可以知道哪些函数被调用:最终,您无法知道标准库的哪一部分被静态链接到程序中;根据设计,其中大部分甚至是内联的!在 C++ 中,大量标准功能位于 C++ 模板中,因此是程序编译单元的一部分,而不是 libstdc++。更糟糕的是,即使只使用绝对来自共享库的功能,“函数调用”也根本不是什么“特殊”的东西;它只是一个函数。它只是设置您需要传递参数并跳转到地址的寄存器。对于共享对象,该地址通常在启动时使用标准动态链接器进行解析,但绝对不能保证这是唯一发生的事情。特别是如果程序在运行时加载库(例如,任何执行插件的库,或脚本语言,或......),它并不像仅列出要导入的函数表那么简单。
您可以跟踪您的用户层程序并为 C++ 标准库中的每个函数插入一个钩子,并记录下来。
bpftrace
可能是这里选择的工具。安装 bpftrace,并查看其示例工具,通常位于 /usr/share/bpftrace/tools 中,尤其是bashreadline.bt
:编写一个小型 awk、Python、PERL 或 PL1 程序,
uretprobe:executable name:function name
为其中的每个条目objdump -T /lib64/libstdc++.so
(或您认为该程序可能使用的任何库;您可以通过查找调用找出其中一个)生成这样的 a。这一切都非常适合脚本化!strace
open
是的,这不会给你任何保证。最好的情况是,您可以看到哪些地址被调用。程序是否准备了一个ROP蹦床来让外部库在正确的情况下为所欲为,是无法做到的。通常,您只能观察程序在正常运行期间的行为。但是,除非您阅读了它的所有反汇编内容,否则您无法判断它在 13 号星期五运行时、或者当 UID=1234、或者 CPUID 以 7 结尾时,或者……以及:任何外部库中的任何不需要的功能时是否会执行不同的操作如果您不希望程序调用,程序员可以将其包含在程序本身中(通过复制功能或仅通过静态链接)。
这就是为什么操作系统和 UNIXoid 特别跨操作系统/用户域边界划分特权:无法保证任何给定程序的行为,但 Linux 可以保证(以令人满意的确定性)不允许的程序访问文件无法访问它;而允许访问是程序本身不可能实现的。