使用LD_PRELOAD保护个人信息
使用LD_PRELOAD保护个人信息
引言
在现在这个时代,想要个人信息完全不暴露,几乎是不可能的。
不管用的哪个软件,只要TA跑起来,TA就可以读取电脑上的很多文件。
那么在Uubntu上有没有什么办法,可以控制程序一些文件能读而另一些文件不能读呢?
其实是有的。比如:文件权限,但文件权限控制太僵硬了。有时不给读,程序就不起来。
我们这个文章就介绍一种比较温柔的方法:允许程序读,但是对于某些文件读取的内容是脱敏后的。
LD_PRELOAD
在ubuntu系统,程序读取文件一般都是先调用(直接或间接)open或fopen函数来打开文件。
int open(const char *pathname, int flags, ... /* mode_t mode */ );
FILE *fopen(const char *restrict pathname, const char *restrict mode);
只要我们想办法,让在程序调用open或fopen前,
修改一下pathname的路径,就可以把程序要读取的文件改成其它文件。
从而保护原来文件不被读取。
而ld.so正好提供环境变量LD_PRELOAD,可以帮助我们实现上述功能。
LD_PRELOAD列的共享库文件(.so)中的函数,将被程序优先调用。
所以我们需要作的就是,自己实现一个版本open和fopen函数。TA们的功能是:
当被调用时,检查一下pathname是否为敏感文件,如果是则修改pathname为不敏感版本的文件,
然后再调用原始版本open和fopen打开文件返回。
代码(path_rename.c)参考如下:
#include <dlfcn.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
extern int vsnprintf(char *str, size_t size, const char *format, va_list ap);
extern int snprintf(char *str, size_t size,
const char *restrict format, ...);
static inline ssize_t print(const char*fmt,...){
char buf[4096] = {0};
va_list ap;
va_start(ap, fmt);
int n = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
return write(2,buf,n);
}
static const char *PATH_MAP[][2] = {
/**
{"需要保护的文件路径","实际上允许读取的文件路径"}
**/
{ "/etc/lsb-release", "/etc/lsb-release.pub" },
{ "/etc/issue", NULL},/** NULL -> /etc/issue.pricacy **/
{ "for_test", NULL}, /** NULL -> for_test.pricacy **/
};
static const int PATH_MAP_LEN = sizeof(PATH_MAP) / sizeof(PATH_MAP[0]);
static inline void fix_path(const char*path,char *out_path,size_t out_len)
{
if(out_len<0)return;
out_path[0] = 0;
if(path == NULL)return;
//默认:维持pathname路径不变
strncpy(out_path,path,out_len);
for(int i=0;i<PATH_MAP_LEN;i++){
const char*o = PATH_MAP[i][0];
if(o == NULL)continue;
const char*n = PATH_MAP[i][1];
if(strcmp(path,o) == 0){
if(n == NULL){
snprintf(out_path,out_len,"%s.privacy",path);
}else{
strncpy(out_path,n,out_len);
}
print("%s -> %s\n",o,out_path);
break;
}
}
}
int open (const char * pathname, int flags, ...)
{
print("%s:%s(%s,%d)\n",__FILE__,__func__,pathname,flags);
char path[4096] = {0};
fix_path(pathname,path,sizeof(path));
static int (*open_ptr)(const char* , int) = NULL;
if(open_ptr == NULL){
open_ptr = dlsym(RTLD_NEXT,__func__);
}
int fd = open_ptr(path,flags);
return fd;
}
void *fopen(const char *pathname, const char *mode){
print("%s:%s(%s,%d)\n",__FILE__,__func__,pathname,mode);
char path[4096] = {0};
fix_path(pathname,path,sizeof(path));
static void* (*open_ptr)(const char* , const char *) = NULL;
if(open_ptr == NULL){
open_ptr = dlsym(RTLD_NEXT,__func__);
}
void* fd = open_ptr(path,mode);
return fd;
}
我们把新实现open和fopen编译成共享库(path_rename.so):
gcc -shared -fpic path_rename.c -o path_rename.so -ldl -D_GNU_SOURCE
写个简单代码(main.c),用来测试:
#include <stdio.h>
int main(int argc,char **argv)
{
if(argc>1){
FILE *fp = fopen(argv[1],"rb");
if(fp){
char buf[4097] = {0};
size_t n = 0;
while(!feof(fp)){
n = fread(buf,1,sizeof(buf)-1,fp);
//printf("n: %ld\n",n);
buf[n] = 0;
printf("%s",buf);
}
fclose(fp);
}
}
return 0;
}
再写个简单Makefile:
main:main.c
$(CC) $< -o $@ -g3 -Wall
path_rename.so:path_rename.c
$(CC) -shared -fpic $< -o $@ -ldl -D_GNU_SOURCE
clean:
rm -rf *.so main
test:main path_rename.so Makefile
LD_PRELOAD=./path_rename.so ./main main.c || true
LD_PRELOAD=./path_rename.so cat for_test /etc/lsb-release || true
然后
make test
就可以立马看到效果了。
用法
修改path_rename.c中的PATH_MAP,再
make path_rename.so
然后
LD_PRELOAD=dir_of_so/path_rename.so xxxx
这样程序xxxx,能读什么文件就随我们拿捏了。
注意
函数open和fopen,只是比较常见的用于打开文件的函数。
打开文件还有其TA函数可选择,具体可自行查抄。
但针对其TA函数,实现文件保护的实现套路是一样一样的。