本文假设你具备基本的 C 技能
Linux 完全在你的控制之中。虽然从每个人的角度来看似乎并不总是这样,但是高级用户喜欢去控制它。我将向你展示一个基本的诀窍,在很大程度上你可以去影响大多数程序的行为,它并不仅是好玩,在有时候也很有用。
一个让我们产生兴趣的示例让我们以一个简单的示例开始。先乐趣,后科学。
random_num.c:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main(){
srand(time(NULL));
int i =10;
while(i--)printf("%d\n",rand()%100);
return0;
}
我相信,它足够简单吧。我不使用任何参数来编译它,如下所示:
gcc random_num.c -o random_num
我希望它输出的结果是明确的:从 0-99 中选择的十个随机数字,希望每次你运行这个程序时它的输出都不相同。
现在,让我们假装真的不知道这个可执行程序的出处。甚至将它的源文件删除,或者把它移动到别的地方 —— 我们已不再需要它了。我们将对这个程序的行为进行重大的修改,而你并不需要接触到它的源代码,也不需要重新编译它。
因此,让我们来创建另外一个简单的 C 文件:
unrandom.c:
int rand(){
return42;//the most random number in the universe
}
我们将编译它进入一个共享库中。
gcc-shared -fPIC unrandom.c -o unrandom.so
因此,现在我们已经有了一个可以输出一些随机数的应用程序,和一个定制的库,它使用一个常数值 42 实现了一个 rand() 函数。现在 …… 就像运行 random_num 一样,然后再观察结果:
LD_PRELOAD=$PWD/unrandom.so ./random_nums
如果你想偷懒或者不想自动亲自动手(或者不知什么原因猜不出发生了什么),我来告诉你 —— 它输出了十次常数 42。
如果先这样执行
export LD_PRELOAD=$PWD/unrandom.so
然后再以正常方式运行这个程序,这个结果也许会更让你吃惊:一个未被改变过的应用程序在一个正常的运行方式中,看上去受到了我们做的一个极小的库的影响 ……
等等,什么?刚刚发生了什么?
是的,你说对了,我们的程序生成随机数失败了,因为它并没有使用 “真正的” rand(),而是使用了我们提供的的那个 —— 它每次都返回 42。
但是,我们告诉过它去使用真实的那个。我们编程让它去使用真实的那个。另外,在创建那个程序的时候,假冒的 rand() 甚至并不存在!
这句话并不完全正确。我们只能告诉它去使用 rand(),但是我们不能去选择哪个 rand() 是我们希望我们的程序去使用的。
当我们的程序启动后,(为程序提供所需要的函数的)某些库被加载。我们可以使用 ldd 去学习它是怎么工作的:
$ ldd random_nums
linux-vdso.so.1=>(0x00007fff4bdfe000)
libc.so.6=>/lib/x86_64-linux-gnu/libc.so.6(0x00007f48c03ec000)