上一篇文章讲解了环境变量LD_PRELOAD在故障注入中的用法。
下面进行一个实战,对open
进行故障注入
被注入程序
velscode@ubuntu:~/code$ cat open.c
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd = open("./file1", O_CREAT | O_RDWR);
printf("fd = %d, errno = %d(%s)\n", fd, errno, strerror(errno));
}
该程序试图创建并打开一个文件,在正常情况下,它似乎总是成功
velscode@ubuntu:~/code$ gcc open.c -o open
velscode@ubuntu:~/code$ ./open
fd = 3, errno = 0(Success)
velscode@ubuntu:~/code$ ./open
fd = 3, errno = 0(Success)
velscode@ubuntu:~/code$ ./open
fd = 3, errno = 0(Success)
velscode@ubuntu:~/code$ ./open
fd = 3, errno = 0(Success)
velscode@ubuntu:~/code$ ./open
fd = 3, errno = 0(Success)
故障注入程序
velscode@ubuntu:~/code$ cat hack.c
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>
int open(const char *pathname, int flags)
{
srand((unsigned int)time(NULL));
if (random() % 10 < 5) {
printf("Hack open!\n");
errno = 2; // No Such file
return -1;
} else {
printf("Not Hack open\n");
return openat(-100, pathname, flags, 0);
}
return 0;
}
一半概率返回-1(同时设置错误码为-2),一半概率正常放行open
编译 & 执行结果
velscode@ubuntu:~/code$ gcc -shared -o hack.so hack.c -fPIC
velscode@ubuntu:~/code$ export LD_PRELOAD=./hack.so
velscode@ubuntu:~/code$ ./open
Not Hack open
fd = 3, errno = 0(Success)
velscode@ubuntu:~/code$ ./open
Not Hack open
fd = 3, errno = 0(Success)
velscode@ubuntu:~/code$ ./open
Hack open!
fd = -1, errno = 2(No such file or directory)
velscode@ubuntu:~/code$ ./open
Hack open!
fd = -1, errno = 2(No such file or directory)
velscode@ubuntu:~/code$ ./open
Not Hack open
fd = 3, errno = 0(Success)
可以看到,目标程序概率性成功,概率性open
调用失败
补充
有人会问,拦截并返回 错误码的场景很好写,但是正常放行部分的代码该怎么写呢?因为此时你已经无法正常调用open
了,为什么上述代码里可以用openat
代替呢?
这个可以看下glibc
的源代码
先找下open
的入口
velscode@ubuntu:~/code/glibc$ grep " open(" -rn
intl/loadmsgcat.c:448:# define open(name, flags) __open_nocancel (name, flags)
这里可以看到,用宏做了一层转换,实际上是调用__open_nocancel
那我们再搜这个,看看里面是怎么实现的
int
__open_nocancel (const char *file, int oflag, ...)
{
int mode = 0;
if (__OPEN_NEEDS_MODE (oflag))
{
va_list arg;
va_start (arg, oflag);
mode = va_arg (arg, int);
va_end (arg);
}
return INLINE_SYSCALL_CALL (openat, AT_FDCWD, file, oflag, mode);
}
INLINE_SYSCALL_CALL
就不展开说了,这里已经很明显了。open
之下调用的就是openat
如果不使用mode
,直接赋0
这里这个AT_FDCWD
是一个常量,通过搜索可以知道值为-100