har*_*ngt 8 linux dll digital-signature
目标:加载.so或已经过验证签名的可执行文件(或针对任意算法进行验证).
我希望能够验证.so /可执行文件,然后使用dlopen/...加载/执行该.so /可执行文件
这里的扳手是,似乎没有编程方式来检查然后加载.可以手动检查文件,然后加载它.但是有一个机会窗口,有人可以将该文件替换为另一个文件.
我能想到的一个可能的解决方案是加载二进制文件,检查签名然后dlopen/execvt /proc/$PID/fd....但是我不知道这是否是一个可行的解决方案.
由于文件系统锁在Linux中是建议性的,因此它们对于此目的并不是那么有用......(好吧,有mount -o mand......但这是针对用户级别而不是root用户).
许多动态链接器(包括Glibc)支持将LD_AUDIT环境变量设置为以冒号分隔的共享库列表.允许这些库挂钩到动态库加载过程中的各个位置.
#define _GNU_SOURCE
#include <dlfcn.h>
#include <link.h>
unsigned int la_version(unsigned int v) { return v; }
unsigned int la_objopen(struct link_map *l, Lmid_t lmid, uintptr_t *cookie) {
if (!some_custom_check_on_name_and_contents(l->l_name, l->l_addr))
abort();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
用cc -shared -fPIC -o test.so test.c或类似编译.
你可以看到glibc/elf/tst-auditmod1.c或latrace更多的例子,或者阅读链接程序和库指南.
非常非常具体到Glibc的内部,但你仍然可以libdl在运行时挂钩.
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
extern struct dlfcn_hook {
void *(*dlopen)(const char *, int, void *);
int (*dlclose)(void *);
void *(*dlsym)(void *, const char *, void *);
void *(*dlvsym)(void *, const char *, const char *, void *);
char *(*dlerror)(void);
int (*dladdr)(const void *, Dl_info *);
int (*dladdr1)(const void *, Dl_info *, void **, int);
int (*dlinfo)(void *, int, void *, void *);
void *(*dlmopen)(Lmid_t, const char *, int, void *);
void *pad[4];
} *_dlfcn_hook;
static struct dlfcn_hook *old_dlfcn_hook, my_dlfcn_hook;
static int depth;
static void enter(void) { if (!depth++) _dlfcn_hook = old_dlfcn_hook; }
static void leave(void) { if (!--depth) _dlfcn_hook = &my_dlfcn_hook; }
void *my_dlopen(const char *file, int mode, void *dl_caller) {
void *result;
fprintf(stderr, "%s(%s, %d, %p)\n", __func__, file, mode, dl_caller);
enter();
result = dlopen(file, mode);
leave();
return result;
}
int my_dlclose(void *handle) {
int result;
fprintf(stderr, "%s(%p)\n", __func__, handle);
enter();
result = dlclose(handle);
leave();
return result;
}
void *my_dlsym(void *handle, const char *name, void *dl_caller) {
void *result;
fprintf(stderr, "%s(%p, %s, %p)\n", __func__, handle, name, dl_caller);
enter();
result = dlsym(handle, name);
leave();
return result;
}
void *my_dlvsym(void *handle, const char *name, const char *version, void *dl_caller) {
void *result;
fprintf(stderr, "%s(%p, %s, %s, %p)\n", __func__, handle, name, version, dl_caller);
enter();
result = dlvsym(handle, name, version);
leave();
return result;
}
char *my_dlerror(void) {
char *result;
fprintf(stderr, "%s()\n", __func__);
enter();
result = dlerror();
leave();
return result;
}
int my_dladdr(const void *address, Dl_info *info) {
int result;
fprintf(stderr, "%s(%p, %p)\n", __func__, address, info);
enter();
result = dladdr(address, info);
leave();
return result;
}
int my_dladdr1(const void *address, Dl_info *info, void **extra_info, int flags) {
int result;
fprintf(stderr, "%s(%p, %p, %p, %d)\n", __func__, address, info, extra_info, flags);
enter();
result = dladdr1(address, info, extra_info, flags);
leave();
return result;
}
int my_dlinfo(void *handle, int request, void *arg, void *dl_caller) {
int result;
fprintf(stderr, "%s(%p, %d, %p, %p)\n", __func__, handle, request, arg, dl_caller);
enter();
result = dlinfo(handle, request, arg);
leave();
return result;
}
void *my_dlmopen(Lmid_t nsid, const char *file, int mode, void *dl_caller) {
void *result;
fprintf(stderr, "%s(%lu, %s, %d, %p)\n", __func__, nsid, file, mode, dl_caller);
enter();
result = dlmopen(nsid, file, mode);
leave();
return result;
}
static struct dlfcn_hook my_dlfcn_hook = {
.dlopen = my_dlopen,
.dlclose = my_dlclose,
.dlsym = my_dlsym,
.dlvsym = my_dlvsym,
.dlerror = my_dlerror,
.dladdr = my_dladdr,
.dlinfo = my_dlinfo,
.dlmopen = my_dlmopen,
.pad = {0, 0, 0, 0},
};
__attribute__((constructor))
static void init(void) {
old_dlfcn_hook = _dlfcn_hook;
_dlfcn_hook = &my_dlfcn_hook;
}
__attribute__((destructor))
static void fini(void) {
_dlfcn_hook = old_dlfcn_hook;
}
Run Code Online (Sandbox Code Playgroud)
$ cc -shared -fPIC -o hook.so hook.c
$ cat > a.c
#include <dlfcn.h>
int main() { dlopen("./hook.so", RTLD_LAZY); dlopen("libm.so", RTLD_LAZY); }
^D
$ cc -ldl a.c
$ ./a.out
my_dlopen(libm.so, 1, 0x80484bd)
不幸的是,我的调查让我得出结论,即使你可以挂钩glibc/elf/dl-load.c:open_verify()(你做不到),也不可能让这个人在你的图书馆的某些部分进行无竞争.
以您给出的形式,该问题本质上是无法解决的,因为共享对象是通过 mmap() 加载来处理内存空间的。因此,即使您可以确保 dlopen() 操作的文件是您检查过并声明 OK 的文件,任何可以写入该文件的人都可以随时修改加载的对象加载后随时(这就是为什么您不通过写入正在运行的二进制文件来升级它们 - 而是先删除然后安装,因为写入它们可能会导致任何正在运行的实例崩溃)。
最好的选择是确保只有您正在运行的用户可以写入该文件,然后检查它,然后 dlopen() 它。您的用户(或 root)仍然可以潜入不同的代码,但是具有这些权限的进程无论如何都可以 ptrace() 您执行他们的命令。
| 归档时间: |
|
| 查看次数: |
3247 次 |
| 最近记录: |