2017-06-18 256 views
0

我最近在Linux中尝试共享库注入,并决定编写自己的程序来完成它(而不是使用GDB来注入库)。共享库注入:_dl_relocate_object段错误

我的程序使用pthread用程序集代码覆盖加载的程序程序(0x40000-0x400025)的第一个0x25字节,为文件名分配空间并调用dlopen。一旦完成所有这些,它将恢复程序状态并从中分离。

这里的组件:

global inject_library 
global nullsub 

section .data 
section .text 

inject_library: 
; rdi -> Pointer to malloc() 
; rsi -> Pointer to free() 
; rdx -> Pointer to dlopen() 
; rcx -> Size of the path to the .so to load 

; Create a new stack frame 
push rbp 

; Save rbx because we're using it as scratch space 
push rbx 
; Save addresses of free & dlopen on the stack 
push rsi 
push rdx 

; Move the pointer to malloc into rbx 
mov rbx, rdi 
; Move the size of the path as the first argument to malloc 
mov rdi, rcx 
; Call malloc(so_path_size) 
call rbx 
; Stop so that we can see what's happening from the injector process 
int 0x3 

; Move the pointer to dlopen into rbx 
pop rbx 
; Move the malloc'd space (now containing the path) to rdi for the first argument 
mov rdi, rax 
; Push rax because it'll be overwritten 
push rax 
; Second argument to dlopen (RTLD_NOW) 
mov rsi, 0x2 
; Call dlopen(path_to_library, RTLD_NOW) 
call rbx 
; Pass control to the injector 
int 0x3 

; Finally, begin free-ing the malloc'd area 
pop rdi 
; Get the address of free into rbx 
pop rbx 
; Call free(path_to_library) 
call rbx 

; Restore rbx 
pop rbx 

; Destory the stack frame 
pop rbp 

; We're done 
int 0x3 
retn 

nullsub: 
retn 

还有一个C程序调用该汇编程序,并使用并行线程来处理这些断点。

这种设置对于如下所示的小型单线程程序来说工作得很好。

#include <stdio.h> 
#include <sys/types.h> 
#include <unistd.h> 

int main(int argc, char* argv) { 
    pid_t my_pid = getpid(); 
    printf("PID: %ld\n", my_pid); 
    getchar(); 
    return 0; 
} 

我以前只是在它的构造做puts("Hi");简单的共享库。如上所述,这里的一切都完美无缺。但是,当我尝试将相同的库注入一个更大的(外部,闭源程序)时,我遇到了段错误。

这里的回溯:

#0 0x00007f6a7985d64d in _dl_relocate_object (scope=0x21fbc08, [email protected]=0, [email protected]=0) 
    at dl-reloc.c:259 
#1 0x00007f6a79865723 in dl_open_worker ([email protected]=0x7fff82d7cbf8) at dl-open.c:424 
#2 0x00007f6a793cf5d4 in __GI__dl_catch_error ([email protected]=0x7fff82d7cbe8, [email protected]=0x7fff82d7cbf0, 
    [email protected]=0x7fff82d7cbe7, [email protected]=0x7f6a798654c0 <dl_open_worker>, [email protected]=0x7fff82d7cbf8) 
    at dl-error-skeleton.c:198 
#3 0x00007f6a79865069 in _dl_open (file=0x21fb830 "/home/umang/code/insertion/test_library.so", mode=-2147483646, caller_dlopen=0x40001a, nsid=-2, 
    argc=<optimized out>, argv=<optimized out>, env=0x7fff82d7cfe8) at dl-open.c:649 
#4 0x00007f6a7964ef96 in dlopen_doit ([email protected]=0x7fff82d7ce08) at dlopen.c:66 
#5 0x00007f6a793cf5d4 in __GI__dl_catch_error ([email protected]=0x7f6a798510f0 <last_result+16>, 
    [email protected]=0x7f6a798510f8 <last_result+24>, [email protected]=0x7f6a798510e8 <last_result+8>, 
    [email protected]=0x7f6a7964ef40 <dlopen_doit>, [email protected]=0x7fff82d7ce08) at dl-error-skeleton.c:198 
#6 0x00007f6a7964f665 in _dlerror_run ([email protected]=0x7f6a7964ef40 <dlopen_doit>, [email protected]=0x7fff82d7ce08) at dlerror.c:163 
#7 0x00007f6a7964f021 in __dlopen (file=<optimized out>, mode=<optimized out>) at dlopen.c:87 
#8 0x000000000040001a in ??() 
#9 0x00000000021fb830 in ??() 
#10 0x00007f6a79326a90 in ??() at malloc.c:3071 from /lib64/libc.so.6 
#11 0x00007f6a796488a0 in ??() from /lib64/libc.so.6 
#12 0x0000000000000d68 in ??() 
#13 0x00007f6a7931e938 in _IO_new_file_underflow (fp=0x7f6a7964efe0 <__dlopen>) at fileops.c:600 
#14 0x00007f6a7931fa72 in __GI__IO_default_uflow (fp=0x7f6a796488a0 <_IO_2_1_stdin_>) at genops.c:404 
#15 0x00007f6a7931a20d in getchar() at getchar.c:37 
#16 0x00000000004005d7 in main() 

这回溯告诉我出事(可怕的)错误的dlopen通话。具体来说,错误在于glibc dl-reloc.c:259

下面是可疑的glibc代码。

254   l->l_lookup_cache.value = _lr; }))     \ 
255  : l) 
256 
257 #include "dynamic-link.h" 
258 
259  ELF_DYNAMIC_RELOCATE (l, lazy, consider_profiling, skip_ifunc); 
260 
261 #ifndef PROF 
262  if (__glibc_unlikely (consider_profiling) 
263  && l->l_info[DT_PLTRELSZ] != NULL) 

ELF_DYNAMIC_RELOCATEdynamic-link.h定义如下宏 -

/* This can't just be an inline function because GCC is too dumb 
    to inline functions containing inlines themselves. */ 
# define ELF_DYNAMIC_RELOCATE(map, lazy, consider_profile, skip_ifunc) \ 
    do {          \ 
    int edr_lazy = elf_machine_runtime_setup ((map), (lazy),    \ 
          (consider_profile));  \ 
    ELF_DYNAMIC_DO_REL ((map), edr_lazy, skip_ifunc);     \ 
    ELF_DYNAMIC_DO_RELA ((map), edr_lazy, skip_ifunc);    \ 
    } while (0) 

#endif 

elf_machine_runtime_setup回报就好了,所以我假设问题在于ELF_DYNAMIC_DO_REL。这是提到的宏的source。这里的问题在于被调用的方法是内联的,因此GDB只显示宏名称而不显示底层的源代码。

在GDB使用ni,我看到以下后elf_machine_runtime_setup回报:

ELF_DYNAMIC_RELOCATE (l, lazy, consider_profiling, skip_ifunc); 
ELF_DYNAMIC_RELOCATE (l, lazy, consider_profiling, skip_ifunc); 
ELF_DYNAMIC_RELOCATE (l, lazy, consider_profiling, skip_ifunc); 

步进通过装配,段错误下面的指令后会发生:movaps %xmm0,-0x70(%rbp)

info local是没有多大帮助:

(gdb) info local 
ranges = {{start = 140072440991568, size = 0, nrelative = 0, lazy = 670467104}, {start = 0, size = 140072438891376, nrelative = 140072441065920, 
    lazy = 672664367}} 
textrels = 0x0 
errstring = 0x0 
lazy = <optimized out> 
skip_ifunc = 0 

有趣的是,当我用GDB注入共享库(使用此代码,我发现某处净),该库加载完美。

sudo gdb -n -q -batch \ 
    -ex "attach $pid" \ 
    -ex "set \$dlopen = (void*(*)(char*, int)) dlopen" \ 
    -ex "call \$dlopen(\"$(pwd)/libexample.so\", 1)" \ 
    -ex "detach" \ 
    -ex "quit" 
)" 

在此先感谢!

+0

有没有可能GDB正在做的事情(说停止所有其他线程),我不是? –

回答

0

经过几天抓我的头和剥去我的头发,我决定谷歌“MOVAPS段错误”。

MOVAPS是一个SIMD指令(在这里,它用于快速清除四字)。这里的some more info差不多。

在左看右看,我注意到以下段落:

当源或目标操作数是内存操作,操作数必须在16字节边界或一般保护性异常上对齐(#GP)生成。

嗯。所以我读了违规地址的价值。

(gdb) print $rbp - 0x70 
$2 = (void *) 0x7ffecd32e838 

那里。地址未对齐到16字节的边界,因此发生段错误。

解决这个问题很简单。

; Create a new stack frame 
push rbp 
sub rsp, 0x8 
; Do stuff 
; Fix the stack pointer 
add rsp, 0x8 
; Destroy stack frame, return, etc. 

我仍然怀疑如果这是正确的方式来做到这一点,但它的工作原理。

哦,GDB在整个​​过程中都做得很对 - 它确保堆栈对齐。