-
-
[原创]elfspirit:Linux平台下的静态分析和注入框架
-
发表于: 2021-11-9 23:12 10848
-
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直播授课