rootkit for linux 18.tcp原理概述
2008-12-20 23:06
197 查看
前言
先说一个冷笑话缓解下气氛:
从前,有一个老师在发试卷,他说:“哪个学生叫林蛋大的?林蛋大上来拿试卷了!”
过了许久,一个学生站起来喊道:“老师,我叫楚中天!”
看懂了吗?
看不懂的话,把名字竖起来写一下就知道了。
当然今天我们不是要讨论谁的蛋大,我们来说一说tcp的状态机。
tcp状态机示意图
状态机呢,实际上是个很理论的东西,就是表示经过什么样的动作,它就到了什么样的状态。
跟人一样,人之初性本擅,你的猥琐习惯也不是一天两天养成的,是经过了一系列的事件,才到达了你今天这样一个状态。
先看看图:
看到这幅图,很多人又雷倒了。
当时我也被雷了一大下,但是我没有被雷倒。为什么呢?因为我被雷的太多了。
我们是服务器,只需要关注图的右边,你看看,少了一半了吧。
对于连接的关闭,我们只需要草草的处理,把重担全部留给客户端的os吧。你看看,又少了一半吧。
现在。我们需要关注的状态有三个 LISTEN,ESTABLISHED,SYN_SENT。
LISTEN:监听状态。我们是服务器,所以默认是处于监听状态的。现在顾客已经不是上帝了,服务器才是上帝。
SYN_SENT:三步握手的第一步。至于什么是三步握手,后面会说。
ESTABLISHED:连接建立,可以发送数据了。
这三个状态,是连接建立需要用到的,还是要认真处理的。
首先说说三步握手。什么是三步握手呢?
处理连接请求(三步握手)
我们是服务器,一开始处于LISTEN状态。一旦收到一个syn包(就是收到一个数据包,它的tcp头部的syn位置了1)。就给客户端发送一个syn,ack包,并且进入 SYN_SENT状态。这时候,如果收到一个 ack 包,就进入 ESTABLISHED 状态。
在ESTABLISHED状态,就是正常的数据传输状态了。
那幅图比较迷惑人。不用管它上面的那些连线上写了什么,自己抓几个包看看就清楚了。
我拿wireshark截获的包如下:
就是这样的。
我们代码要实现的,就是在收到第一个包后给客户端发送第二个包并从LISTEN状态进入SYN_SENT状态,在收到第三个包的时候从SYN_SENT进入ESTABLISHED状态。这还是比较简单的。
处理断开请求
除了连接请求之外,我们还要处理断开请求。
断开请求分两种,一种是异常断开,一种是正常断开。
正常断开的时候客户端会发送 FIN ACK包,从上面的图上也看得出来。
异常断开的时候客户端会发送 RST 包,上面的图就没写出来。但它的确很重要,RST是一定要处理的。所以现在的课本就是很混账,该写的你又不写。
我们看一下正常断开的情况:
客户端向服务端连续发送了4个FIN ACK包。在winsock的代码中,执行closesocket这个函数后,就是这个效果。
那我们的rootkit为什么不响应它呢?
我们收到FIN ACK包后,已经完成了状态切换,但就是不给客户端响应。因为我觉得没必要。客户端怎么处理这种断开到一半的socket是它自己的事,关我p事。
我们再看一下异常断开的情况:
我们的处理方法跟处理正常断开一样。代码也是比较简单的,就不说了。
ack与seq
tcp是一个可靠的传输协议。可靠之处就是它这样一种机制:
发送方发送了编号从13到20的包以后,不会马上把这些包所占的内存释放,也不会继续发后面的包,而是等待接收方的回应,如果接收方说“我收到编号为20包了”,那么发送方这个时候才把编号在20以前的包都释放,然后从编号为21的包开始继续发。如果等了很久都没有等到接收方的回应,那就把编号为13的包再发一遍。
这种机制课本里叫做“滑动窗口协议”。
其中编号13到20的包的总大小是接收方的“接收窗口”的大小。接收方告诉发送方“我收到了编号为xxx的包”是通过tcp数据报的ack字段。发送方告诉接收方“这个包的编号是xxx”是通过tcp数据报的seq字段。
注意,在我们的rootkit里,服务端可以作为接收方和发送方,是两面派。上面说的是我们作为发送方时候的情况,当我们作为接收方时,我们也要告诉客户端“我收到编号为xxx的包了”。
说到这里,又不得不提到tcp的“捎带确认”机制。
“捎带确认”机制是个啥呢。就是说,告诉客户端“我收到编号为xxx的包了”本来是要单独发一个包过去的。但是有了“捎带确认”机制以后,脚不疼了,腰不酸了,干活有劲了,以前要发一个包,现在不用发包了。为啥不用发包呢?当你往客户端发送数据包的时候,确认信息就顺带过去了,通过tcp数据报的ack字段。
所以每个tcp数据包都是带有确认信息的。也就是说,每次我们收到包的时候,客户端都在告诉我们“我收到编号为xxx的包了”。如果你没有发包给客户端,而客户端一直在发包给你,那每次的xxx都是一样的,客户端每次都会告诉你“我收到编号为xxx的包了”,“我收到编号为xxx的包了”,“我收到编号为xxx的包了”。。。
这东西只要用wireshark抓几个包看一下就行了。当时我看课本也看不懂,但是抓几个包看一下就马上懂了。
所以,seq字段和ack字段就是这么回事。
接收包
当我们收到数据报的时候,直接提交给上层(比如简单的网络文件议)处理,由上层负责分配内存空间,重组数据。我们并不需要接收队列。有的数据报的内容很少,完全没必要缓冲起来。就算要缓冲,由上层负责缓冲和重组也是合适的,因为只有上层才清楚当第一个数据包到的时候,要分配多少空间来缓冲后续的数据。
因此我们的接收包部分的代码还是很简单的。
发送包
当我们要发送数据报的时候。首先会对数据进行分段,每个段的大小为tcp的mss(最大分段长度)。然后把每个段放到发送队列中,那是一个循环队列。然后,我们会启动一个线程来发包,并且在发完包之后通过回调函数通知上层。实现了简单的异步发送。
为什么要这么麻烦呢?因为发送队列对于我们的rootkit是很有必要的。你放心,没有用的代码我不会费力气去写,你也应该清楚rootkit调试起来多麻烦。能省则省,我写代码的原则就是坚决不多写一行代码。
考虑一下前面所说的“滑动窗口协议”。
你想一下,如果没有发送队列,直接由上层协议控制超时重传,那基本没可能的嘛。
比如两个线程同时要发包,咋办?这种情况在我们的rookit里面会发生的,很可能几个线程一起发包。没有发送队列,你没法得知发包的顺序,重传的时候,你怎么知道传哪个包呢。
但是有了发送队列,一切就好办了。超时重传也比较容易实现,发的包都按顺序存放在队列里,反正一发包就有启动一个定时器,定时器过期的时候回调一个函数,那个函数检查是不是所有发出去的包客户端都响应了,如果没有的话,就重发。
总结
本篇文章,对rootkit的tcp部分实现是一个概述。记载下来主要是防止很久以后忘记了tcp是个咋回事。如果有讲的不对的地方,希望大家狂屌。
往后的几篇文章,将会分析代码。也相当与一份文档吧。因为我现在已经深刻地认识到,汇编写的东西。如果没有注释,没有文档,那过几天你再看自己的代码,真的想不起来是谁写的。
先说一个冷笑话缓解下气氛:
从前,有一个老师在发试卷,他说:“哪个学生叫林蛋大的?林蛋大上来拿试卷了!”
过了许久,一个学生站起来喊道:“老师,我叫楚中天!”
看懂了吗?
看不懂的话,把名字竖起来写一下就知道了。
当然今天我们不是要讨论谁的蛋大,我们来说一说tcp的状态机。
tcp状态机示意图
状态机呢,实际上是个很理论的东西,就是表示经过什么样的动作,它就到了什么样的状态。
跟人一样,人之初性本擅,你的猥琐习惯也不是一天两天养成的,是经过了一系列的事件,才到达了你今天这样一个状态。
先看看图:
看到这幅图,很多人又雷倒了。
当时我也被雷了一大下,但是我没有被雷倒。为什么呢?因为我被雷的太多了。
我们是服务器,只需要关注图的右边,你看看,少了一半了吧。
对于连接的关闭,我们只需要草草的处理,把重担全部留给客户端的os吧。你看看,又少了一半吧。
现在。我们需要关注的状态有三个 LISTEN,ESTABLISHED,SYN_SENT。
LISTEN:监听状态。我们是服务器,所以默认是处于监听状态的。现在顾客已经不是上帝了,服务器才是上帝。
SYN_SENT:三步握手的第一步。至于什么是三步握手,后面会说。
ESTABLISHED:连接建立,可以发送数据了。
这三个状态,是连接建立需要用到的,还是要认真处理的。
首先说说三步握手。什么是三步握手呢?
处理连接请求(三步握手)
我们是服务器,一开始处于LISTEN状态。一旦收到一个syn包(就是收到一个数据包,它的tcp头部的syn位置了1)。就给客户端发送一个syn,ack包,并且进入 SYN_SENT状态。这时候,如果收到一个 ack 包,就进入 ESTABLISHED 状态。
在ESTABLISHED状态,就是正常的数据传输状态了。
那幅图比较迷惑人。不用管它上面的那些连线上写了什么,自己抓几个包看看就清楚了。
我拿wireshark截获的包如下:
就是这样的。
我们代码要实现的,就是在收到第一个包后给客户端发送第二个包并从LISTEN状态进入SYN_SENT状态,在收到第三个包的时候从SYN_SENT进入ESTABLISHED状态。这还是比较简单的。
处理断开请求
除了连接请求之外,我们还要处理断开请求。
断开请求分两种,一种是异常断开,一种是正常断开。
正常断开的时候客户端会发送 FIN ACK包,从上面的图上也看得出来。
异常断开的时候客户端会发送 RST 包,上面的图就没写出来。但它的确很重要,RST是一定要处理的。所以现在的课本就是很混账,该写的你又不写。
我们看一下正常断开的情况:
客户端向服务端连续发送了4个FIN ACK包。在winsock的代码中,执行closesocket这个函数后,就是这个效果。
那我们的rootkit为什么不响应它呢?
我们收到FIN ACK包后,已经完成了状态切换,但就是不给客户端响应。因为我觉得没必要。客户端怎么处理这种断开到一半的socket是它自己的事,关我p事。
我们再看一下异常断开的情况:
我们的处理方法跟处理正常断开一样。代码也是比较简单的,就不说了。
ack与seq
tcp是一个可靠的传输协议。可靠之处就是它这样一种机制:
发送方发送了编号从13到20的包以后,不会马上把这些包所占的内存释放,也不会继续发后面的包,而是等待接收方的回应,如果接收方说“我收到编号为20包了”,那么发送方这个时候才把编号在20以前的包都释放,然后从编号为21的包开始继续发。如果等了很久都没有等到接收方的回应,那就把编号为13的包再发一遍。
这种机制课本里叫做“滑动窗口协议”。
其中编号13到20的包的总大小是接收方的“接收窗口”的大小。接收方告诉发送方“我收到了编号为xxx的包”是通过tcp数据报的ack字段。发送方告诉接收方“这个包的编号是xxx”是通过tcp数据报的seq字段。
注意,在我们的rootkit里,服务端可以作为接收方和发送方,是两面派。上面说的是我们作为发送方时候的情况,当我们作为接收方时,我们也要告诉客户端“我收到编号为xxx的包了”。
说到这里,又不得不提到tcp的“捎带确认”机制。
“捎带确认”机制是个啥呢。就是说,告诉客户端“我收到编号为xxx的包了”本来是要单独发一个包过去的。但是有了“捎带确认”机制以后,脚不疼了,腰不酸了,干活有劲了,以前要发一个包,现在不用发包了。为啥不用发包呢?当你往客户端发送数据包的时候,确认信息就顺带过去了,通过tcp数据报的ack字段。
所以每个tcp数据包都是带有确认信息的。也就是说,每次我们收到包的时候,客户端都在告诉我们“我收到编号为xxx的包了”。如果你没有发包给客户端,而客户端一直在发包给你,那每次的xxx都是一样的,客户端每次都会告诉你“我收到编号为xxx的包了”,“我收到编号为xxx的包了”,“我收到编号为xxx的包了”。。。
这东西只要用wireshark抓几个包看一下就行了。当时我看课本也看不懂,但是抓几个包看一下就马上懂了。
所以,seq字段和ack字段就是这么回事。
接收包
当我们收到数据报的时候,直接提交给上层(比如简单的网络文件议)处理,由上层负责分配内存空间,重组数据。我们并不需要接收队列。有的数据报的内容很少,完全没必要缓冲起来。就算要缓冲,由上层负责缓冲和重组也是合适的,因为只有上层才清楚当第一个数据包到的时候,要分配多少空间来缓冲后续的数据。
因此我们的接收包部分的代码还是很简单的。
发送包
当我们要发送数据报的时候。首先会对数据进行分段,每个段的大小为tcp的mss(最大分段长度)。然后把每个段放到发送队列中,那是一个循环队列。然后,我们会启动一个线程来发包,并且在发完包之后通过回调函数通知上层。实现了简单的异步发送。
为什么要这么麻烦呢?因为发送队列对于我们的rootkit是很有必要的。你放心,没有用的代码我不会费力气去写,你也应该清楚rootkit调试起来多麻烦。能省则省,我写代码的原则就是坚决不多写一行代码。
考虑一下前面所说的“滑动窗口协议”。
你想一下,如果没有发送队列,直接由上层协议控制超时重传,那基本没可能的嘛。
比如两个线程同时要发包,咋办?这种情况在我们的rookit里面会发生的,很可能几个线程一起发包。没有发送队列,你没法得知发包的顺序,重传的时候,你怎么知道传哪个包呢。
但是有了发送队列,一切就好办了。超时重传也比较容易实现,发的包都按顺序存放在队列里,反正一发包就有启动一个定时器,定时器过期的时候回调一个函数,那个函数检查是不是所有发出去的包客户端都响应了,如果没有的话,就重发。
总结
本篇文章,对rootkit的tcp部分实现是一个概述。记载下来主要是防止很久以后忘记了tcp是个咋回事。如果有讲的不对的地方,希望大家狂屌。
往后的几篇文章,将会分析代码。也相当与一份文档吧。因为我现在已经深刻地认识到,汇编写的东西。如果没有注释,没有文档,那过几天你再看自己的代码,真的想不起来是谁写的。
相关文章推荐
- rootkit for linux 17.ip、tcp数据报
- rootkit for linux 16.获取tcp可用端口号
- Linux 网络协议栈开发基础篇(八)—— VLAN原理概述
- rootkit for linux 11.“寻找入口点”的改进方法
- 18-TCP 协议(迟到的 ACK—— Linux)
- [zz]rootkit for linux 2.寻找入口点
- How to improve the performance networking (TCP & UDP) on Linux 2.4+ for high-bandwidth applications
- TCP/IP 原理 --概述
- rootkit for linux 1.ring0下的溢出
- rootkit for linux 8.大大杀器---“模拟法”获得字段偏移
- CrossOver for Mac/Linux 18 破解版
- TCP/IP原理、基础以及在Linux上的实现 (1)
- TCP/IP原理、基础以及在Linux上的实现 (5)
- Linux TCP/IP Tuning for Scalability
- rootkit for linux 14.file handle
- rootkit for linux 12.枚举目录
- Early Retransmit for TCP原理以及实现
- TCP/IP原理、基础以及在Linux上的实现
- TCP/IP原理、基础以及在Linux上的实现
- Linux之系统编程、TCP_IP通信原理、sock编程代码解析