-
-
[原创]elfspirit:Linux平台下的静态分析和注入框架
-
发表于: 2021-11-9 23:12 11494
-
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_finigef➤ 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_finimov r9, [rdx + _ld_catch_exception_got - _ld_fini]
sub r9, _ld_catch_exceptionmov r9, [rdx + _ld_catch_exception_got - _ld_fini]
sub r9, _ld_catch_exceptionadd r9, __libc_dlopen_modeadd r9, __libc_dlopen_modeuint8_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
[培训]传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!