- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我正在尝试修改 linux 系统调用的默认行为。目前,我正试图在它们实际被调用之前 Hook 并添加一个简单的打印语句。我知道 GCC 链接器的标准“wrap”选项以及它如何用于 Hook 包装器 Link to GCC Linker options .这非常适用于 open()、fstat()、fwrite() 等(我实际上是在挂接 libc 包装器)。
更新:
限制是并非所有系统调用都与这种方法 Hook 。为了说明这一点,让我们采用一个简单的静态编译二进制文件。当我们尝试添加包装器时,它们会受到我们在 main() 之后引入的调用的影响(请参阅下面显示的 strace 输出)
> strace ./sample
execve("./sample", ["./sample"], [/* 72 vars */]) = 0
uname({sys="Linux", node="kumar", ...}) = 0
brk(0) = 0x71f000
brk(0x7201c0) = 0x7201c0
arch_prctl(ARCH_SET_FS, 0x71f880) = 0
readlink("/proc/self/exe", "/home/admin/sample"..., 4096) = 41
brk(0x7411c0) = 0x7411c0
brk(0x742000) = 0x742000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 4), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fbcc54d1000
write(1, "Hello from the wrapped readlink "..., 36Hello from the wrapped readlink :з
) = 36
readlink("/usr/bin/gnome-www-browser", "/etc/alternatives/gnome-www-brow"..., 255) = 35
write(1, "/etc/alternatives/gnome-www-brow"..., 36/etc/alternatives/gnome-www-browser
) = 36
exit_group(36) = ?
+++ exited with 36 +++
如果我们仔细观察二进制文件,第一个“未拦截”调用 readlink()(系统调用 89,即 0x59)来自这些行——一些与链接器相关的代码部分(即 _dl_get_origin)确实readlink() 的功能。这些隐式系统调用(尽管以二进制代码形式存在)永远不会被我们的“包装”方法所吸引。
000000000051875c <_dl_get_origin>:
51875c: b8 59 00 00 00 mov $0x59,%eax
518761: 55 push %rbp
518762: 53 push %rbx
518763: 48 81 ec 00 10 00 00 sub $0x1000,%rsp
51876a: 48 89 e6 mov %rsp,%rsi
51876d: 0f 05 syscall
如何将包装思想扩展到像 readlink() 这样的系统调用(包括所有被调用的隐式调用)?
最佳答案
ld 有一个包装选项,引号 from manual :
--wrap symbol
Use a wrapper function for symbol. Any undefined reference to symbol will be resolved to __wrap_symbol. Any undefined reference to __real_symbol will be resolved to symbol. This can be used to provide a wrapper for a system function. The wrapper function should be called __wrap_symbol. If it wishes to call the system function, it should call __real_symbol.
它也适用于系统调用。下面是一个带有 readlink
的例子:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
ssize_t __real_readlink(const char *path, char *buf, size_t bufsiz);
ssize_t __wrap_readlink(const char *path, char *buf, size_t bufsiz) {
puts("Hello from the wrapped readlink :з");
__real_readlink(path, buf, bufsiz);
}
int main(void) {
const char testLink[] = "/usr/bin/gnome-www-browser";
char buf[256];
memset(buf, 0, sizeof(buf));
readlink(testLink, buf, sizeof(buf)-1);
puts(buf);
}
要将选项从编译器传递给链接器,请使用 -Wl
选项:
$ gcc test.c -o a -Wl,--wrap=readlink
$ ./a
Hello from the wrapped readlink :з
/etc/alternatives/gnome-www-browser
__wrap_func
是您的函数包装器。 __real_func
链接器将链接到真正的函数 func
。代码中对 func
的每次调用都将替换为 __wrap_func
。
UPD:人们可能会注意到静态编译的二进制文件会调用另一个未被拦截的readlink
。要了解原因,只需做一个小实验——将代码编译到目标文件,并列出符号,如:
$ gcc test.c -c -o a.o -Wl,--wrap=readlink
$ nm a.o
0000000000000037 T main
U memset
U puts
U readlink
U __real_readlink
U __stack_chk_fail
0000000000000000 T __wrap_readlink
这里有趣的是,您不会看到在进入 main 函数之前使用 strace 看到的一堆函数的引用——例如uname()
、brk()
、access()
等。这是因为 main 函数不是第一个被调用的代码在你的二进制文件中调用。 bit of research with objdump
将向您展示第一个名为 _start
的函数。
现在,让我们再举一个例子——覆盖 _start
函数:
$ cat test2.c
#include <stdio.h>
#include <unistd.h>
void _start() {
puts("Hello");
_exit(0);
}
$ gcc test2.c -o a -nostartfiles
$ strace ./a
execve("./a", ["./a"], [/* 69 vars */]) = 0
brk(0) = 0x150c000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3ece55d000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=177964, ...}) = 0
mmap(NULL, 177964, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f3ece531000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320\37\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1840928, ...}) = 0
mmap(NULL, 3949248, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f3ecdf78000
mprotect(0x7f3ece133000, 2093056, PROT_NONE) = 0
mmap(0x7f3ece332000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1ba000) = 0x7f3ece332000
mmap(0x7f3ece338000, 17088, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f3ece338000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3ece530000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3ece52e000
arch_prctl(ARCH_SET_FS, 0x7f3ece52e740) = 0
mprotect(0x7f3ece332000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ) = 0
mprotect(0x7f3ece55f000, 4096, PROT_READ) = 0
munmap(0x7f3ece531000, 177964) = 0
fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 10), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3ece55c000
write(1, "Hello\n", 6Hello
) = 6
exit_group(0) = ?
+++ exited with 0 +++
$
那是什么?!我们刚刚覆盖了二进制文件中的第一个函数,但仍然看到系统调用——为什么?
实际上这是因为调用不是由您的应用程序执行的,而是由内核在您的应用程序加载到内存并允许运行之前执行的。
UPD:如我们之前所见,您的应用程序不会调用这些函数。老实说,在 shell 为您的应用程序调用 execve
之后,我找不到 静态二进制文件 正在做什么,但是从列表中看,您看到的每个调用都是由内核本身 — 没有任何辅助应用程序,例如静态二进制文件不需要的动态链接器(并且因为有像 brk
这样的函数可以处理数据段)。
无论如何,你肯定不能那么容易地修改这个行为,你需要一些黑客攻击。因为如果您可以轻松地覆盖在二进制运行之前执行的代码的函数——即从其他二进制文件——这将是安全方面的一个大黑洞,试想一下:一旦您需要根权限,您就可以覆盖一个函数用一个来执行你的代码,稍等一下,一些具有 root 权限的守护进程恰好执行脚本,从而触发你的代码运行。
关于linux - 如何在二进制执行期间 Hook 所有 linux 系统调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31131163/
我有一个问题,但由于 this question 部分正在解决,但我想知道如何计算给定间隔之间的天数。 这是一个计算员工休假天数的查询。所以给定(或不给定)一个日期范围,我想计算给定间隔之间有多少假期
变量dateSubtract结果是 16,但我想找到这 2 天之间的总天数,应该是 165。没有 JODA TIME 我该如何做到这一点? String date = "06/17/2014"; Da
我想选择创建日期介于给定月份的第一天和最后一天之间的记录。我通过以下方式计算开始日期和结束日期的月份: 日期“月份”只是时间范围内的随机日期 Calendar cal = Calendar.getIn
我有一个对你们大多数人来说可能微不足道的问题。我尝试了很多,没有找到解决方案,所以如果有人能给我提示,我会很高兴。起点是每周 xts -时间序列。 月周值(value)目标 2011 年 12 月 W
我有一个 Facebook 应用程序,它将用户生日作为 varchar 存储在 mysql 数据库中。我正在尝试获取所有用户的生日 1周后推出,如果是在本周如果生日是上周。 在我的 php 中,我获取
我正在使用以下代码来获取年、月、日中的两个日期之间的差异 tenAppDTO.getTAP_PROPOSED_START_DATE()=2009-11-01 tenAppDTO.getTAP_PRO
我想检查当前时间(在 C++ 中)是否在一个时间范围内。 我想从元组 ("12:00", "17:30") 构造时间范围,即 (string, string) 并检查时间 now() 是否介于两者之间
gitlab 有一个功能,如果我在提交消息中放入票号,那么提交将与 gitlab.com 上的票相关联。 这在进行代码审查时非常方便。不幸的是,开发人员有时会忘记这样做。 我想指定 git hooks
我正在尝试制作使用SQLite数据库的简单注册/登录应用程序,到目前为止我得到了这段代码。这是我的“注册” Activity ,我猜它应该在按下注册按钮后将用户名和 pin(密码)实现到数据库,遗憾的
我正在尝试打开、关闭和写入文件。每当我尝试打开一个文件时,如果我提供的路径中不存在该文件,程序就会告诉我。如果存在,程序将读取其中的内容并显示它。如果用户不想查找文件,可以选择创建文件并用数据填充它。
我想要我的至slideToggle每当发生 react 性变化时,但到目前为止我还无法使其发生。我尝试在 rendered 中使用 JQuery和created模板的事件,但它没有触发。 触发此操作的
我们的 MySQL 遇到了神秘的网络问题。简单的更新查询(使用索引更新单行)通常会立即运行,然后有时(假设 1000 次中有 1 次)因超时而失败。与简单的插入查询相同。数据库没有过载。我们怀疑网络问
我正在使用 actionbarsherlock 的 ActionBar,第一次以横向或水平方向运行应用程序时,选项卡以 Tabs Mode 显示。将方向更改为纵向后,导航模式仍在 Tabs 中。第二次
每天晚上(太平洋标准时间晚上 8 点)我都会对生产数据库(innoDB 引擎)进行全局备份。 这是 mysqldump 命令: mysqldump -u$MYSQLUSER -p$MYSQLPWD -
当我的应用程序第一次启动时,它应该显示用户协议(protocol),这是一个 59kb 的 txt 文件。由于读取文件并将其附加到 TextView 需要一些时间,因此我决定在异步任务中执行此操作并在
如何只允许一个“.”在按键期间的javascript中? 我这里有一个代码: function allowOneDot(txt) { if ((txt.value.split(".")
我已经创建了像主页和用户这样的标题图标。在桌面 View 中,如果我单击用户图像,它会显示相应的重定向页面。如果我在选项卡或移动 View 中将其最小化, 它什么都不显示。此问题仅发生在用户图像上,而
下面的代码在 Release模式下工作,并且仅在 Debug模式下在 g_ItemList.push_back() 引发错误,我浏览了一些 SO 帖子和论坛。有人提到 "You can't itera
我遇到了一个我似乎无法解决的 mmap 问题。下面是设置:我使用 malloc 将一个巨大的多维数组分配到内存中,用我的值填充它,然后我想将它保存在一个文件中。该数组包含 3200000000 个字节
尝试加载共享库: handle = dlopen( "libaaa.so.2.5", RTLD_NOW ); if ( !handle ) { printf("Failed t
我是一名优秀的程序员,十分优秀!