首页
社区
课程
招聘
[原创]elfspirit:Linux平台下的静态分析和注入框架
发表于: 2021-11-9 23:12 10818

[原创]elfspirit:Linux平台下的静态分析和注入框架

2021-11-9 23:12
10818

elfspirit 是一个实验性但是有用的程序,可以解析和修改ELF文件。它提供了多种功能,包括增加或删除一个section,为二进制静态注入动态链接库,删除节头表以增加逆向难度等功能。

如果你对Linux二进制分析有所了解的话,那么肯定知道readelf与ERESI,readelf用来对ELF文件进行解析,ERESI则是一个ELF逆向框架,可以用来动态注入和调试ELF,它们提供了足够强大的能力,辅助ELF文件的逆向,但是无法覆盖所有场景,比如如何通过给ELF打patch的方式注入so?我们不是重复造轮子,而是基于需求,编写了一个名为elfspirit的静态分析框架。

用一句通俗的话来解释elfspirit提供的创新功能就是

你给我一个Linux二进制可执行程序,我用elfspirit注入一段汇编代码(基于此,才将这种方式称之为静态注入),即可使其在后续运行过程中加载任意so

有人可能会说,一般的病毒或者蠕虫不都已经做到这一点了么?其实,无论是病毒还是恶意程序,他们往往都是针对特定的二进制进行感染,我们在这里更想使用一种通用的方式,向任意二进制注入寄生代码,而这些代码只是用于加载一个so。

对于静态注入,由于依赖于 libc 中某些函数的偏移地址,因此当前版本仅仅在libc-2.31/2.31上通过验证测试。不过没关系,我们也考虑到后续的兼容性,为此特意提供了一个json文件,用于加载其他版本的libc的相关偏移地址。

静态注入原理细节请参考个人博客:ELF Static Injection to Load Malicious Dynamic Link Library

这里做一下简单陈述

修改.eh_frame(当然你也可以选择其他的部分,.eh_frame是恶意软件常用的一个节),写入汇编代码(一般称为寄生码),由于不是很多二进制没有使用 dl 库,因此无法直接调用 dlopen,但是可以使用 libc 提供的__libc_dlopen_mode作为替代。用该函数即可实现加载动态库。该函数的基本用法如下

我们通过修改程序的入口点(如果想躲避侦察,也可以修改入口函数的前几行汇编),跳转到寄生代码。寄生代码要做的事情就是

难点在于

项目及博客给出了答案,即利用内核加载二进制并运行之前,某些寄存器的值会关联装载器的符号,利用这些符号,最终定位 libc 的基地址。

_start 运行之前,查看寄存器值,在 rdx 寄存器发现了一个较为特殊的值

这个值存放 ld-2.32.so 符号 _dl_fini 的值

ld-2.32.so

libc-2.32.so

libc base address = [rdx + (0x2B018 - 0x102f0)] - 0x137A90

转化为通用公式

如果还想调用 __libc_dlopen_mode

因此,我们需要知道的偏移如下

其他非难点部分不在这里展示。

最终静态注入部分的框架如下

amd64 示例寄生代码

这里只列出与常用工具不太一样的功能。

请注意,如果增加的节位于ELF中间地段,可能会影响.text里的偏移地址

输出

我们已在hello_x86_new增加了一个新的section

用法

输出

当我们运行 hello_x86_new 时,就会自动加载 libdemo_x32.so

静态注入受限于不同的Linux发行版,已适配的libc/ld版本相关偏移地址已放在json文件中

文章涉及的所有代码已在 elfspirit 中实现,限于篇幅,还有很多细节并没有展开讲述。如果你想了解更多,可以参考项目中提供的其他博客,也可以细读源码。笔者能力有限,源码很难避免一些bug或者漏洞,欢迎斧正。如果可能,我们未来还可以做更多事情,这里只做一次简单分享。我们想强调的不是工具本身,而是提出的这种静态注入的概念及思想。

 
 
 
 
int main(int argc, char const *argv[])
{
    char lib_name[] = "libpatchdemo.so";
    void * handle = __libc_dlopen_mode(lib_name, RTLD_LAZY);
    func = dlsym(handle, "hello_world");
    func();
    return 0;
}
int main(int argc, char const *argv[])
{
    char lib_name[] = "libpatchdemo.so";
    void * handle = __libc_dlopen_mode(lib_name, RTLD_LAZY);
    func = dlsym(handle, "hello_world");
    func();
    return 0;
}
 
$rdx   : 0x00007ffff7fe21b0  →  <_dl_fini+0> push rbp
$rdx   : 0x00007ffff7fe21b0  →  <_dl_fini+0> push rbp
gef➤  xinfo 0x00007ffff7fe22f0
─────────────────────────────────── xinfo: 0x7ffff7fe22f0 ───────────────────────────────────
Page: 0x00007ffff7fd3000  →  0x00007ffff7ff3000 (size=0x20000)
Permissions: r-x
Pathname: /usr/lib/x86_64-linux-gnu/ld-2.32.so
Offset (from page): 0xf2f0
Inode: 3151771
Segment: .text (0x00007ffff7fd3050-0x00007ffff7ff2cbe)
Offset (from segment): 0xf2a0
Symbol: _dl_fini
gef➤  xinfo 0x00007ffff7fe22f0
─────────────────────────────────── xinfo: 0x7ffff7fe22f0 ───────────────────────────────────
Page: 0x00007ffff7fd3000  →  0x00007ffff7ff3000 (size=0x20000)
Permissions: r-x
Pathname: /usr/lib/x86_64-linux-gnu/ld-2.32.so
Offset (from page): 0xf2f0
Inode: 3151771
Segment: .text (0x00007ffff7fd3050-0x00007ffff7ff2cbe)
Offset (from segment): 0xf2a0
Symbol: _dl_fini
 
mov r9, [rdx + _ld_catch_exception_got - _ld_fini]
sub r9, _ld_catch_exception
mov r9, [rdx + _ld_catch_exception_got - _ld_fini]
sub r9, _ld_catch_exception
add r9, __libc_dlopen_mode
add r9, __libc_dlopen_mode
 
uint8_t sc_x86_64[] = \
    /* start */
    "\x55"                                      // push   rbp
    "\x48\x89\xe5"                              // mov    rbp, rsp
    "\x48\x83\xec\x30"                          // sub    rsp, 30h
    "\x48\xb8\x6c\x69\x62\x70\x61\x74\x63\x68"  // movabs rax,0x686374617062696c
    "\x48\xbb\x64\x65\x6d\x6f\x2e\x73\x6f\x00"  // movabs rbx,0x6f732e6f6d6564
    "\x48\x89\x45\xe0"                          // mov    QWORD PTR [rbp-0x20],rax
    "\x48\x89\x5d\xe8"                          // mov    QWORD PTR [rbp-0x18],rbx
    "\x48\x8d\x45\xe0"                          // lea    rax,[rbp-0x20]
    "\xbe\x01\x00\x00\x00"                      // mov    esi,0x1
    "\x48\x89\xc7"                              // mov    rdi,rax
    "\x4c\x8b\x8a\x68\xae\x01\x00"              // mov    r9, [rdx + 0x1ae68]
    "\x49\x81\xe9\xe0\x81\x13\x00"              // sub    r9, 0x0000000001381E0
    "\x49\x81\xc1\x00\x78\x13\x00"              // add    r9, 0x000000000137800
    "\x41\xff\xd1"                              // call   r9
    "\xc9"                                      // leave
    /* end */
    "\xe8\x0b\x00\x00\x00";                     // call   <_start>
uint8_t sc_x86_64[] = \
    /* start */
    "\x55"                                      // push   rbp
    "\x48\x89\xe5"                              // mov    rbp, rsp
    "\x48\x83\xec\x30"                          // sub    rsp, 30h
    "\x48\xb8\x6c\x69\x62\x70\x61\x74\x63\x68"  // movabs rax,0x686374617062696c
    "\x48\xbb\x64\x65\x6d\x6f\x2e\x73\x6f\x00"  // movabs rbx,0x6f732e6f6d6564
    "\x48\x89\x45\xe0"                          // mov    QWORD PTR [rbp-0x20],rax
    "\x48\x89\x5d\xe8"                          // mov    QWORD PTR [rbp-0x18],rbx
    "\x48\x8d\x45\xe0"                          // lea    rax,[rbp-0x20]
    "\xbe\x01\x00\x00\x00"                      // mov    esi,0x1
    "\x48\x89\xc7"                              // mov    rdi,rax
    "\x4c\x8b\x8a\x68\xae\x01\x00"              // mov    r9, [rdx + 0x1ae68]
    "\x49\x81\xe9\xe0\x81\x13\x00"              // sub    r9, 0x0000000001381E0
    "\x49\x81\xc1\x00\x78\x13\x00"              // add    r9, 0x000000000137800
    "\x41\xff\xd1"                              // call   r9
    "\xc9"                                      // leave
    /* end */
    "\xe8\x0b\x00\x00\x00";                     // call   <_start>
elfspirit addsec   [-n]<节名> [-z]<要增加的大小> [-o]<存放地址> ELF
elfspirit addsec   [-n]<节名> [-z]<要增加的大小> [-o]<存放地址> ELF
$ ./elfspirit addsec -n .testsection -o 0x3340 -z 0x100 ./testcase/hello_x86                                                                                                                                                         1
 [+] offset to insert: 0x3340
 [+] create ./testcase/hello_x86_new
$ ./elfspirit addsec -n .testsection -o 0x3340 -z 0x100 ./testcase/hello_x86                                                                                                                                                         1
 [+] offset to insert: 0x3340
 [+] create ./testcase/hello_x86_new

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2021-11-9 23:39 被magicsong编辑 ,原因: 修改错别字
收藏
免费 4
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//