使用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函数,实现文件保护的实现套路是一样一样的。

标签: none

添加新评论