Jinlong's Blog

小明的故事

一天早上,小明在家里无聊,打开编辑器写了一段程序:

1
2
3
4
5
6
7
#include <cstdio>
int main() {
printf("55555\n");
printf("Xiao Ming loves Xiao Dong.\n");
return 0;
}

小明喜欢着他的室友小东,可是他一直都不敢说,因为他怕说了之后,就连室友都做不了了,所以只能在无聊的时候写写这样的程序来发发牢骚。
编译,运行,命令行显示出以下结果:

1
2
3
4
5
xiaoming@ubuntu:~/code$ g++ Test.cpp
xiaoming@ubuntu:~/code$ ./a.out
55555
Xiao Dong loves Xiao Ming.
xiaoming@ubuntu:~/code$

咦,不对呀,我刚才要打印的明明是“Xiao Ming loves Xiao Dong”,为什么输出却是“Xiao Dong loves Xiao Ming.”呢?难道我整天做着白日梦做的我的手脚都不听脑子使唤了?唉。小明一边叹着气一边返回修改代码,想把字符串给改回来。可是当他打开Test.cpp的时候,无论怎么看,上面写着的都是:

1
printf("Xiao Ming loves Xiao Dong.\n");

为什么!!!为什么我明明打印的是“Xiao Ming loves Xiao Dong”,输出的却是“Xiao Dong loves Xiao Ming.”呢!!!难道…..小明脑子里想着各种狗血的可能性。不,不可能,小明把自己从思绪中拽了回来,叹了口气,肯定是上天看我可怜,用某种不可预知的力量让我的电脑发生了几个比特的错位,导致了现在这个结果,逗我开心吧。于是,小明在代码中加了一个for循环:

1
2
3
4
5
6
7
8
9
10
11
12
#include <cstdio>
int main() {
printf("55555\n");
int i;
/* 循环100次为了确认刚才发生的是意外 */
for(i = 0; i < 100; i++)
printf("Xiao Ming loves Xiao Dong.\n");
return 0;
}

这样总好了吧。这次命令行应该会输出100个“Xiao Ming loves Xiao Dong.”了吧。小明想着。
编译,运行。眼前的一幕让小明彻底傻眼了….屏幕出现是齐刷刷的100行“Xiao Dong loves Xiao Ming.”。为什么!!!到底是为什么!!!此时的小明又激动又崩溃又开心。突然,他想起了几天前看到的一篇文章:
How to wrap a system call (libc function) in Linux
然后又想起了前几天电脑借过给小东用。难道?!难道?!小明不敢相信自己的猜测,他用颤抖着的手打开家目录下的.bashrc,文件末尾的几行文字让他老脸一红。也许,他的猜测是正确的。

1
2
3
4
...
# modified by Xiao Dong on 2018-2-14
export LD_PRELOAD=/usr/lib/xiaodong.so
...

小明用nm命令查看了xiaodong.so里面实现了的API:

1
2
3
4
xiaoming@ubuntu:~/code$ nm /usr/lib/xiaodong.so
...
0000000000000760 T puts
...

这个xiaodong.so实现了一个跟libc的puts函数同名的puts,如果在程序执行时预先加载这个xiaodong.so,这个puts就会覆盖掉libc原先的puts,而printf函数内部实现刚好又是调用puts…所以说,小东肯定在xiaodong.so里面的puts函数上做了一些手脚。咦,说不定这个so的源代码还藏在这台电脑的某个角落里呢,看看:

1
2
3
4
xiaoming@ubuntu:~/code$ sudo updatedb
xiaoming@ubuntu:~/code$ locate xiaodong
/home/xiaoming/.test/xiaodong.cpp
...

原来,源代码藏在家目录下的一个隐藏的文件夹里,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef __USE_GNU
#define __USE_GNU
#endif
#include <cstring>
#include <sys/types.h>
#include <dlfcn.h>
typedef ssize_t (*puts_func_t)(const void*);
puts_func_t g_real_puts = (puts_func_t)dlsym(RTLD_NEXT, "puts");
extern "C" {
int puts(const char* buf) {
if(strcmp(buf, "Xiao Ming loves Xiao Dong.") == 0)
return g_real_puts("Xiao Dong loves Xiao Ming.");
return g_real_puts(buf);
}
};

至此,真相就大白了。
死鬼,原来你一直也喜欢着我呀,小明的脸变得更红了。怎么办?怎么办?是要去找他呢,还是先假装什么也不知道?小明的内心挣扎着…..(未完待续)