1 个不稳定版本
0.1.0 | 2022年12月2日 |
---|
#8 in #writable
21KB
266 行
TPOM
此库劫持了 vDSO(虚拟动态共享对象)中的时间相关函数,并允许用用户提供的函数替换它们。
例如,嵌入到一个 Python 项目中
>>> import datetime
>>> datetime.datetime.now()
datetime.datetime(2022, 12, 1, 13, 47, 15, 574866)
>>> import tpom
>>> tpom.curse_me()
>>> datetime.datetime.now()
my clockgettime was called!
datetime.datetime(1970, 1, 1, 1, 1, 51)
受 libfaketime 启发,主要区别是不需要 LD_PRELOAD
任何共享对象(并在之后重新执行)。
工作原理
为了加快频繁调用的系统调用,内核通过 vDSO 暴露它们,这是一种将内核函数映射到用户内存空间的方法。
vDSO 内存区域包含一个 ELF 块,可以扫描动态符号,例如
DYNAMIC SYMBOL TABLE:
0000000000000c10 w DF .text 0000000000000005 LINUX_2.6 clock_gettime
0000000000000bd0 g DF .text 0000000000000005 LINUX_2.6 __vdso_gettimeofday
0000000000000c20 w DF .text 0000000000000060 LINUX_2.6 clock_getres
0000000000000c20 g DF .text 0000000000000060 LINUX_2.6 __vdso_clock_getres
0000000000000bd0 w DF .text 0000000000000005 LINUX_2.6 gettimeofday
0000000000000be0 g DF .text 0000000000000029 LINUX_2.6 __vdso_time
0000000000000be0 w DF .text 0000000000000029 LINUX_2.6 time
0000000000000c10 g DF .text 0000000000000005 LINUX_2.6 __vdso_clock_gettime
这些符号中的每一个都是一个可以调用的函数。
这是 用户空间 内存,每个进程都有 完全 控制权,这就是 TPOM 所做的
- 通过扫描 /proc/self/maps 找到 vDSO 内存范围
- 读取 vDSO ELF 块
- 使 vDSO 范围可写
- 在符号的地址上写入一个
jmp
作为第一条指令,指向最终调用用户提供的函数的 跳板
vDSO 之前
0000000000000c10 <__vdso_clock_gettime@@LINUX_2.6>:
c10: e9 9b fb ff ff jmp 7b0 <LINUX_2.6@@LINUX_2.6+0x7b0>
c15: 66 66 2e 0f 1f 84 00 data16 cs nop WORD PTR [rax+rax*1+0x0]
c1c: 00 00 00 00
之后
0000000000000c10 <__vdso_clock_gettime@@LINUX_2.6>:
c10: 48 b8 50 5b 7e 2c 37 movabs rax,0x56372c7e5b50
c17: 56 00 00
c1a: ff e0 jmp rax
c1c: 90 nop
c1d: 90 nop
c1e: 90 nop
c1f: 90 nop
注意
- 如果你的代码直接执行系统调用,则 此方法将不起作用。
- 仅适用于
x86_64
,在 Linux 上。 - 不需要
LD_PRELOAD
依赖关系
~1MB
~18K SLoC