如果纯粹用文字来描述什么是动态注入,可能还是不太容易理解,所以本篇文章从如下一段代码开始:

// who.c

#include

#include



int main()

{

   while (1) {

       printf("who are you ?n");

       sleep(2);

   }



   return 0;

}

这段代码本身没有什么意思,执行”gcc who.c -o who -g -Wall”完成编译并启动who程序,每隔2s会向终端打印一句”who are you ?”动态ip修改方法,但有意思的是,可能会出现一种”诡异”的现象,比如屏幕上突然冒出一句”it’s me ~”。

1. 为什么可以出现这种“诡异”的现象?

静态ip和动态ip的区别_固定ip和动态ip的区别_动态ip修改方法

动态ip修改方法_静态ip和动态ip的区别_固定ip和动态ip的区别

2. 怎么获取打印”it’s me ~”的机器码?

动态ip修改方法_固定ip和动态ip的区别_静态ip和动态ip的区别

// isme32.c

int main()

{

__asm__(

   "jmp forwardnt"

   "backward:popl %esint"

   "movl $4, %eaxnt"

   "movl $2, %ebxnt"

   "movl %esi, %ecxnt"

   "movl $12, %edxnt"

   "int $0x80nt"

   "int3nt"

   "forward:call backwardnt"

   ".string "it's me ~\n""

   );



   return 0;

}

// isme64.c

int main()

{

__asm__(

   "jmp forwardnt"

   "backward:popq %rsint"

   "movq $1, %raxnt"

   "movq $2, %rdint"

   "movq $12, %rdxnt"

   "syscallnt"

   "int3nt"

   "forward:call backwardnt"

   ".string "it's me ~\n""

   );



   return 0;

}

固定ip和动态ip的区别_动态ip修改方法_静态ip和动态ip的区别

用蓝色、绿色标记出来的16进制内容,即为打印”it’s me ~”的机器码动态ip修改方法,但有2点需要说明:

3. 怎么实现注入?

本篇文章仅仅用于学习目的,注入的场景和方法都比较简单,思路在文章第1节已经介绍过了,就是”暂停被注入进程 -> 替换即将执行的指令块 -> 通知被注入进程继续执行”,由于有些场合还需要保证注入操作的隐蔽性,所以以下程序还在注入指令块完整执行后,将被注入进程恢复到了注入前的状态:

(理解这段代码,至少需要学习ptrace()系统调用的作用,如果有内核基础,也可以更深入的学习一下ptrace()的内部原理,另外,CODE宏对应的内容,即为利用文章第2节描述的方法,提取的机器码。)

动态ip修改方法_静态ip和动态ip的区别_固定ip和动态ip的区别

// inject.c
#include
#include
#include
#include
#include
#include
#include
#include


// 注入指令块(打印"it's me ~")
#ifdef ENV_I386
#define CODE
   "xebx15x5exb8x04x00x00x00"
   "xbbx02x00x00x00x89xf1xba"
   "x0cx00x00x00xcdx80xccxe8"
   "xe6xffxffxffx69x74x27x73"
   "x20x6dx65x20x7ex0ax00"

#define REG_IP  regs.eip
#else   // X_64
#define CODE
   "xebx19x5ex48xc7xc0x01x00"
   "x00x00x48xc7xc7x02x00x00"
   "x00x48xc7xc2x0cx00x00x00"
   "x0fx05xccxe8xe2xffxffxff"
   "x69x74x27x73x20x6dx65x20"
   "x7ex0ax00"

#define REG_IP  regs.rip
#endif

#define CODE_SIZE (sizeof(CODE)-1)


/* 往pid进程的addr地址处写数据 */
void putdata(pid_t pid, unsigned long addr, void *vptr, int len)
{
   int count = 0;
   long word;

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

       if (errno != 0)
           printf("putdata failed: %pn", (void *)(addr+count));
   }
}

/* 读pid进程addr地址处的数据 */
void getdata(pid_t pid, unsigned long addr, void *vptr, int len)
{
   int i = 0, count = 0;
   long word;
   unsigned long *ptr = (unsigned long*)vptr;

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

       if (errno != 0)
           printf("getdata failed: %pn", (void *)(addr+count));
   }
}


int main(int argc, char *argv[])
{
   pid_t pid;
   struct user_regs_struct regs;
   char backup[CODE_SIZE+1];

   if (argc != 2) {
       printf("Usage: %s {pid}n", argv[0]);
       return -1;
   }

   // 通过启动参数,获取被注入进程号,并attach
   pid = atoi(argv[1]);
   ptrace(PTRACE_ATTACH, pid, NULL, NULL);  // SIGTRAP
   if (errno != 0)
       printf("attach failed: %sn", strerror(errno));
   wait(NULL);  // 收到回复信号,保证后续过程在attach完成的情况下执行

   // 获取被注入进程当前寄存器值
   ptrace(PTRACE_GETREGS, pid, NULL, &regs);
   // 备份*ip寄存器指向的指令块
   getdata(pid, REG_IP, backup, CODE_SIZE);
   // 替换为CODE指令块
   putdata(pid, REG_IP, CODE, CODE_SIZE);
   // 恢复被注入进程的执行
   ptrace(PTRACE_CONT, pid, NULL, NULL);    // SIGCONT
   // 注入程序最后一条指令为int3,此为即为等待注入指令块执行完毕
   wait(NULL);

   // 等待用户输入,方便查看结果
   printf("continue to execute the orginal process, press any key ..n");
   getchar();

   // 将修改的指令块恢复为备份内容
   putdata(pid, REG_IP, backup, CODE_SIZE);
   // 恢复被注入进程寄存器值
   ptrace(PTRACE_SETREGS, pid, NULL, &regs);
   
   // detach,被注入进程恢复正常执行
   ptrace(PTRACE_DETACH, pid, NULL, NULL);

   return 0;
}

4. 视频演示

视频链接:

5. 参考

– End –

静态ip和动态ip的区别_固定ip和动态ip的区别_动态ip修改方法

看雪ID:xinpoo

本文由看雪论坛xinpoo原创

———END———
限 时 特 惠: 本站每日持续更新海量各大内部创业教程,一年会员只需98元,全站资源免费下载 点击网站首页每天更新
站 长 微 信: aiwo51889