原始套接字学习

原始套接字来两种类型:1)在IP头中使用预约义的协议,ICMPios

                                             2)  在IP头中使用自定义的协议算法


建立能够是 socket / WSASocket  只不过要将套接字设置为 SOCK_RAW服务器

注意 WIDNOWS XP SP2 已经再也不支持原始TCP数据的发送了网络


下面的内容MSDB没有显示彻底 ,小菜在各类网络搜索获得:并发

int setsockopt(
  __in  SOCKET s,   //<span style="color: rgb(51, 51, 51); font-family: Arial; font-size: 14.44444465637207px; line-height: 26px;">s(套接字): 指向一个打开的套接口描述字</span>
  __in  int level,
  __in  int optname,
  __in  const char *optval,
  __in  int optlen
);

level:(级别) 指定选项代码的类型。 4种类型:
SOL_SOCKET: 基本套接口

IPPROTO_IP: IPv4套接口app

IPPROTO_IPV6: IPv6套接口socket

IPPROTO_TCP: TCP套接口
tcp

optname(选项名):选项名称  每种level 对应几种选项名ide

SOL_SOCKET:
函数

SO_BROADCAST 容许发送广播数据 int
适用於 UDP socket。其意义是容许 UDP socket 「广播」(broadcast)讯息到网路上。

SO_DEBUG 容许调试 int

SO_DONTROUTE 不查找路由 int

SO_ERROR 得到套接字错误 int

SO_KEEPALIVE 保持链接 int

检测对方主机是否崩溃,避免(服务器)永远阻塞于TCP链接的输入。 设置该选项后,若是2小时内在此套接口的任一方向都没有数据交换,TCP就自动给对方 发一个保持存活探测分节(keepalive probe)。这是一个对方必须响应的TCP分节.它会致使如下三种状况: 对方接收一切正常:以指望的 ACK响应。2小时后,TCP将发出另外一个探测分节。 对方已崩溃且已从新启动:以RST响应。套接口的待处理错误被置为ECONNRESET,套接 口自己则被关闭。 对方无任何响应:源自berkeley的TCP发送另外8个探测分节,相隔75秒一个,试图获得 一个响应。在发出第一个探测分节11分钟15秒后若仍无响应就放弃。套接口的待处理错 误被置为ETIMEOUT,套接口自己则被关闭。如ICMP错误是“host unreachable (主机不 可达)”,说明对方主机并无崩溃,可是不可达,这种状况下待处理错误被置为 EHOSTUNREACH。
SO_DONTLINGER 若为真,则SO_LINGER选项被禁止。
SO_LINGER 延迟关闭链接 struct linger
上面这两个选项影响close行为
选项 间隔 关闭方式 等待关闭与否
SO_DONTLINGER 不关心 优雅 否
SO_LINGER 零 强制 否
SO_LINGER 非零 优雅 是
若 设置了SO_LINGER(亦即linger结构中的l_onoff域设为非零,参见2.4,4.1.7和4.1.21各节),并设置了零超时间隔,则 closesocket()不被阻塞当即执行,不管是否有排队数据未发送或未被确认。这种关闭方式称为“强制”或“失效”关闭,由于套接口的虚电路当即被 复位,且丢失了未发送的数据。在远端的recv()调用将以WSAECONNRESET出错。
若设置了SO_LINGER并肯定了非零的超时间 隔,则closesocket()调用阻塞进程,直到所剩数据发送完毕或超时。这种关闭称为“优雅的”关闭。请注意若是套接口置为非阻塞且 SO_LINGER设为非零超时,则closesocket()调用将以WSAEWOULDBLOCK错误返回。

若在一个流类套接口上设置了 SO_DONTLINGER(也就是说将linger结构的l_onoff域设为零,则 closesocket()调用当即返回。可是,若是可能,排队的数据将在套接口关闭前发送。请注意,在这种状况下WINDOWS套接口实现将在一段不确 定的时间内保留套接口以及其余资源,这对于想用因此套接口的应用程序来讲有必定影响。


IPPROTO_IP:

IP_HDRINCL 在数据包中包含IP首部 int
这个选项经常使用于黑客技术中,隐藏本身的IP地址

IP_OPTINOS IP首部选项 int
IP_TOS 服务类型
IP_TTL 生存时间 int


IPPROTO_TCP:

TCP_NODELAY

TCP_CORK

CP_NODELAY 和 TCP_CORK,
这 两个选项都对网络链接的行为具备重要的做用。许多UNIX系统都实现了TCP_NODELAY选项,可是,TCP_CORK则是Linux系统所独有的而 且相对较新;它首先在内核版本2.4上得以实现。此外,其余UNIX系统版本也有功能相似的选项,值得注意的是,在某种由BSD派生的系统上的 TCP_NOPUSH选项其实就是TCP_CORK的一部分具体实现。
TCP_NODELAY和TCP_CORK基本上控制了包的 “Nagle化”,Nagle化在这里的含义是采用Nagle算法把较小的包组装为更大的帧。John Nagle是Nagle算法的发明人,后者就是用他的名字来命名的,他在1984年首次用这种方法来尝试解决福特汽车公司的网络拥塞问题(欲了解详情请参 看IETF RFC 896)。他解决的问题就是所谓的silly window syndrome ,中文称“愚蠢窗口症候群”,具体含义是,由于广泛终端应用程序每产生一次击键操做就会发送一个包,而典型状况下一个包会拥有一个字节的数据载荷以及40 个字节长的包头,因而产生4000%的过载,很轻易地就能令网络发生拥塞,。 Nagle化后来成了一种标准而且当即在因特网上得以实现。它如今已经成为缺省配置了,但在咱们看来,有些场合下把这一选项关掉也是合乎须要的。
现 在让咱们假设某个应用程序发出了一个请求,但愿发送小块数据。咱们能够选择当即发送数据或者等待产生更多的数据而后再一次发送两种策略。若是咱们立刻发送 数据,那么交互性的以及客户/服务器型的应用程序将极大地受益。例如,当咱们正在发送一个较短的请求而且等候较大的响应时,相关过载与传输的数据总量相比 就会比较低,并且,若是请求当即发出那么响应时间也会快一些。以上操做能够经过设置套接字的TCP_NODELAY选项来完成,这样就禁用了Nagle算 法。
另一种状况则须要咱们等到数据量达到最大时才经过网络一次发送所有数据,这种数据传输方式有益于大量数据的通讯性能,典型的应用就是文件服 务器。应用 Nagle算法在这种状况下就会产生问题。可是,若是你正在发送大量数据,你能够设置TCP_CORK选项禁用Nagle化,其方式正好同 TCP_NODELAY相反(TCP_CORK 和 TCP_NODELAY 是互相排斥的)。下面就让咱们仔细分析下其工做原理。
假设应用程序 使用sendfile()函数来转移大量数据。应用协议一般要求发送某些信息来预先解释数据,这些信息其实就是报头内容。典型状况下报头很小,并且套接字 上设置了TCP_NODELAY。有报头的包将被当即传输,在某些状况下(取决于内部的包计数器),由于这个包成功地被对方收到后须要请求对方确认。这 样,大量数据的传输就会被推迟并且产生了没必要要的网络流量交换。
可是,若是咱们在套接字上设置了TCP_CORK(能够比喻为在管道上插入 “塞子”)选项,具备报头的包就会填补大量的数据,全部的数据都根据大小自动地经过包传输出去。当数据传输完成时,最好取消TCP_CORK 选项设置给链接“拔去塞子”以便任一部分的帧都能发送出去。这同“塞住”网络链接同等重要。
总而言之,若是你确定能一块儿发送多个数据集合(例如HTTP响应的头和正文),那么咱们建议你设置TCP_CORK选项,这样在这些数据之间不存在延迟。能极大地有益于WWW、FTP以及文件服务器的性能,同时也简化了你的工做。示例代码以下:

intfd, on = 1;
…
/* 此处是建立套接字等操做,出于篇幅的考虑省略*/
…
setsockopt (fd, SOL_TCP, TCP_CORK, &on, sizeof (on)); /* cork */
write (fd, …);
fprintf (fd, …);
sendfile (fd, …);
write (fd, …);
sendfile (fd, …);
…
on = 0;
setsockopt (fd, SOL_TCP, TCP_CORK, &on, sizeof (on)); /* 拔去塞子 */


optval(选项值):是一个指向变量的指针 类型:整形,套接口结构, 其余结构类型:linger{}, timeval{ }
optlen(选项长度) :optval 的大小


int WSAIoctl(
  __in   SOCKET s,
  __in   DWORD dwIoControlCode,
  __in   LPVOID lpvInBuffer,
  __in   DWORD cbInBuffer,
  __out  LPVOID lpvOutBuffer,
  __in   DWORD cbOutBuffer,
  __out  LPDWORD lpcbBytesReturned,
  __in   LPWSAOVERLAPPED lpOverlapped,
  __in   LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
)

dwIoControlCode:
SIO_RCVALL Enables a socket to receive all IPv4 or IPv6 packets passing throuigh a network interface

下面学习 IP TCP 结构:

版本号(Version):长度4比特。标识目前采用的IP协议的版本号。通常的值为0100(IPv4),IPv6的值(0110)。

IP包头长度(Header Length):长度4比特。这个字段的做用是为了描述IP包头的长度,由于在IP包头中有变长的可选部分。该部分占4个bit位,单位为32bit(4个字节),即本区域值= IP头部长度(单位为bit)/(8*4),所以,一个IP包头的长度最长为“1111”,即15*4=60个字节。IP包头最小长度为20字节。对于标准ipv4报头,这个字段的值确定是20/4=5(10进制)=0101(2进制)。

服务类型(Type of Service):长度8比特。这个子段能够拆分红两个部分:Precedence和TOS。TOS目前不太使用。而Precedence则用于QOS("Quality of Service",中文名为"服务质量")应用。(TOS字段的详细描述RFC 1340 1349)。

IP包总长(Total Length):长度16比特。以字节为单位计算的IP包的长度 (包括头部和数据),因此IP包最大长度65535字节。

标识符(Identifier):长度16比特。该字段和Flag和Fragment Offest字段联合使用,对大的上层数据包进行分段(fragment)操做。

标记(Flag):长度3比特。该字段第一位不使用。第二位是DF位,DF位设为1时代表路由器不能对该上层数据包分段。若是一个上层数据包没法在不分段的状况下进行转发,则路由器会丢弃该上层数据包并返回一个错误信息。第三位是MF位,当路由器对一个上层数据包分段,则路由器会在最后一个分段的IP包的包头中将MF位设为0,其他IP包的包头中将MF设为1。

Bit 0: reserved, must be zero
Bit 1: (DF) 0 = May Fragment, 1 = Don't Fragment.
Bit 2: (MF) 0 = Last Fragment, 1 = More Fragments.
 
分段序号(Fragment Offset):长度13比特。该字段对包含分段的上层数据包的IP包赋予序号。因为IP包在网络上传送的时候不必定能按顺序到达,这个字段保证了目标路由器在接受到IP包以后可以还原分段的上层数据包。到某个包含分段的上层数据包的IP包在传送是丢失,则整个一系列包含分段的上层数据包的IP包都会被要求重传。

生存时间(Time to Live):长度8比特。当IP包进行传送时,先会对该字段赋予某个特定的值。当IP包通过每个沿途的路由器的时候,每一个沿途的路由器会将IP包的TTL值减小1。若是TTL减小为0,则该IP包会被丢弃。这个字段能够防止因为故障而致使IP包在网络中不停被转发。

协议(Protocol):长度8比特。标识了上层所使用的协议。

头部校验(Header Checksum):长度16比特,因为IP包头是变长的,因此提供一个头部校验来保证IP包头中信息的正确性。

源IP地址(Source IP Addresses):长度16比特。标识了这个IP包的源IP地址。

目的IP地址(Destination IP Address):长度16比特。标识了这个IP包的目的IP地址。

可选项(Options):这是一个可变长的字段,长度为0或32bit的整倍数,最大320bit,若是不足则填充到满。该字段由起源设备根据须要改写。可选项目包含如下内容:

    松散源路由(Loose source routing):给出一连串路由器接口的IP地址。IP包必须沿着这些IP地址传送,可是容许在相继的两个IP地址之间跳过多个路由器。

    严格源路由(Strict source routing):给出一连串路由器接口的IP地址。IP包必须沿着这些IP地址传送,若是下一跳不在IP地址表中则表示发生错误。

    路由记录(Record route):当IP包离开每一个路由器的时候记录路由器的出站接口的IP地址。

    时间戳(Timestamps):当IP包离开每一个路由器的时候记录时间。

填充(Padding):由于IP包头长度(Header Length)部分的单位为32bit,因此IP包头的长度必须为32bit的整数倍。所以,在可选项后面,IP协议会填充若干个0,以达到32bit的整数倍。


源端口号(Source Port):16位,标识主机上发起传送的应用程序;

目的端口(Destonation Port):16位,标识主机上传送要到达的应用程序。

源端,目的端的端口号,用于寻找发端和收端应用进程。这两个值加上IP首部中的源端IP地址和目的端IP地址惟一肯定一个TCP链接。一个IP地址和一个端口号有时也称为一个插口(Socket),插口对(Socket Pair)(包含客户IP地址、客户端口号、服务器 IP地址和服务器端口号的四元组)可惟一肯定互联网络中每一个TCP链接的双方。IP+TCP端口惟一肯定一个TCP链接。

TCP协议经过使用"端口"来标识源端和目标端的应用进程。端口号可使用0到65535之间的任何数字。在收到服务请求时,操做系统动态地为客户端的应用程序分配端口号。在服务器端,每种服务在"众所周知的端口"(Well-Know Port)为用户提供服务。

序列码(Sequence Number):32位,当SYN出现,序列码其实是初始序列码(ISN),而第一个数据字节是ISN+1。用来标识从TCP源端向TCP目标端发送的数据字节流,它表示在这个报文段中的第一个数据字节。

确认码(Acknowledgment Numbwe):32位,若是设置了ACK控制位,这个值表示一个准备接收的包的序列码,只有ACK标志为1时,确认号字段才有效。它包含目标端所指望收到源端的下一个数据字节。

数据偏移量(Data Offset):4位,也就是头部长度,指出TCP负载(数据)的开始位置。以4字节为单位,如"0101"表示20字节位置的数据为负载开始,也就是头部长度为20字节。

保留(Reserved):6位,这些位必须是0;


控制标记(Contral Flag):6位,  2表示第一次握手   18表示  第二次握手

    URG(Urgent data):紧急指针(urgent pointer)有效。若是URG为1,表示这是一个携有紧急资料的封包。

    ACK(Acknowledgment field significant):确认序号有效。若是ACK为1,表示此封包属于一个要回应的封包。通常都会为1。

    PSH(Push function):接收方应该尽快将这个报文段交给应用层。若是PSH为1,此封包所携带的数据会直接上传给上层应用程序而无需通过TCP处理。

    RST(Reset):重建链接。若是RST为1,要求重传。表示要求从新设定封包再从新传递。

    SYN(Synchronize sequence number):发起一个链接。若是SYN为1,表示要求双方进行同步沟通。

    FIN(Finish-No more data for sender):释放一个链接。若是FIN为1,表示传送结束,然後双方发出结束回应进而正式终止一个TCP传送过程。

窗口(Window):16位,接收窗口大小。此字段用来进行流量控制,单位为字节数,这个值是本机指望一次接收的字节数。这里通常称为“滑动视窗(Sliding Window)”。

正如您刚才看到的TCP封包会经过SQN和ACK序号来确保传送的正确性,但若是每个封包都要等上一个封包的回应才被发送出去的话实在是太慢和难以接受的。这样咱们能够利用Sliding Window在传送两端划分出一个范围,规定出能够一次性发送的最大封包数目。 

当TCP传送创建起来之後两端都会将window的设定值还原到初始值比方说每次传送3个封包。而后发送端就一次发送三个封包出去,而后视窗则会往後移动三个封包填补发送出去之封包的空缺。若是接收端够顺利也能一次处理接收下来的三个封包的话,就会告诉发送端的window值为3,但若是接收端太忙或是其它因素影响暂时只能处理两个封包,那么在视窗里面就剩下一个封包,而后就会告诉发送端window值为2。这个时候发送端就只送出两个封包而视窗就会往後移动两个封包填补发送出去的空缺。

校验位(Checksum):16位,对整个TCP报文段,即TCP头部和TCP数据进行校验和计算,并由目标端进行验证。

当资料要传送出去的时候发送端会计算好封包资料大小然後得出这个检验值封包一块儿发送当接收端收到封包之後会再对资料大小进行计算看看是否和检验值一致若是结果不相称则被视为残缺封包会要求对方重发该个封包。

紧急指针(Urgent Pointer):16位,它是一个偏移量。指向后面是优先数据的字节,紧急指针指出在本报文段中的紧急数据的最后一个字节的序号,和序号字段中的值相加表示紧急数据最后一个字节的序号。

还记得刚才讲到Control Flag的时候咱们提到一个URG的标记吗若是URG被设定为1的时候,这里就会指示出紧急资料所在位置。不过这种情形很是少见,例如:当资料流量超出频宽的时候系统要求网路主机暂缓发送资料全部主机收到这样的信息都须要优先处理。

选项(Options):长度不定;但长度必须以字节记;选项的具体内容咱们结合具体命令来看。

这个选项也比较少用。当那些须要使用同步动做的程式如Telnet要处理好终端的交互模式就会使用到option来指定资料封包的大小由于telnet使用的资料封包都不多但又须要即时回应。

填充(Padding):不定长,填充的内容必须为0,它是为了保证包头的结合和数据的开始处偏移量可以被32整除。


        SYN(synchronous)是TCP/IP创建链接时使用的握手信号。在客户机和服务器之间创建正常的TCP网络链接时,客户机首先发出一个SYN消息,服务器使用SYN+ACK应答表示接收到了这个消息,最后客户机再以ACK消息响应。这样在客户机和服务器之间才能创建起可靠的TCP链接,数据才能够在客户机和服务器之间传递。


        SYN攻击属于DDoS攻击的一种,它利用TCP协议缺陷,经过发送大量的半链接请求,耗费CPU内存资源。SYN攻击除了能影响主机外,还能够危害路由器防火墙等网络系统,事实上SYN攻击并无论目标是什么系统,只要这些系统打开TCP服务就能够实施。服务器接收到链接请求(syn= j),将此信息加入未链接队列,并发送请求包给客户(syn=k,ack=j+1),此时进入SYN_RECV状态。当服务器未收到客户端的确认包时,重发请求包,一直到超时,才将此条目从未链接队列删除。配合IP欺骗,SYN攻击能达到很好的效果,一般,客户端在短期内伪造大量不存在的IP地址,向服务器不断地发送syn包,服务器回复确认包,并等待客户的确认,因为源地址是不存在的,服务器须要不断的重发直至超时,这些伪造的SYN包将长时间占用未链接队列,正常的SYN请求被丢弃,目标系统运行缓慢,严重者引发网络堵塞甚至系统瘫痪。


检测SYN攻击:

当你在服务器上看到大量的半链接状态时,特别是源IP地址是随机的,基本上能够判定这是一次SYN攻击。咱们使用系统自带的netstat 工具来检测SYN攻击:

# netstat -n -p TCP

 -n            以数字形式显示地址和端口号。
 -p proto      显示 proto 指定的协议的链接;proto 能够是下列任
               何一个: TCP、UDP、TCPv6 或 UDPv6。若是与 -s 选
               项一块儿用来显示每一个协议的统计,proto 能够是下列任
               何一个: IP、IPv六、ICMP、ICMPv六、TCP、TCPv六、UDP

  TCP    192.168.0.101:27309    192.168.0.101:168      SYN_SENT
  TCP    192.168.0.101:27310    192.168.0.101:169      SYN_SENT
  TCP    192.168.0.101:27311    192.168.0.101:170      SYN_SENT
  TCP    192.168.0.101:27312    192.168.0.101:171      SYN_SENT
  TCP    192.168.0.101:27313    192.168.0.101:172      SYN_SENT
  TCP    192.168.0.101:27314    192.168.0.101:173      SYN_SENT
  TCP    192.168.0.101:27315    192.168.0.101:174      SYN_SENT
  TCP    192.168.0.101:27316    192.168.0.101:175      SYN_SENT
  TCP    192.168.0.101:27317    192.168.0.101:176      SYN_SENT
  TCP    192.168.0.101:27318    192.168.0.101:177      SYN_SENT
  TCP    192.168.0.101:27319    192.168.0.101:178      SYN_SENT
  TCP    192.168.0.101:27320    192.168.0.101:179      SYN_SENT
  TCP    192.168.0.101:27321    192.168.0.101:180      SYN_SENT
  TCP    192.168.0.101:27322    192.168.0.101:181      SYN_SENT
  TCP    192.168.0.101:27323    192.168.0.101:182      SYN_SENT
  TCP    192.168.0.101:27324    192.168.0.101:183      SYN_SENT
  TCP    192.168.0.101:27325    192.168.0.101:184      SYN_SENT
  TCP    192.168.0.101:27326    192.168.0.101:185      SYN_SENT

在WINDOWS系统中是SYN_RECEIVED状态,源IP地址都是随机的,代表这是一种带有IP欺骗的SYN攻击。

netstat -n -p TCP | find "192.168.0.101"   查看所有连接

防范技术:


关于SYN攻击防范技术,人们研究得比较早。概括起来,主要有两大类,一类是经过防火墙、路由器等过滤网关防御,另外一类是经过加固TCP/IP协议栈防范
丰富带宽资源


不难看出syn攻击消耗带宽资源因此要想防护synflood一个丰富的带宽资源是必要的,一般的流量攻击,攻击者也是利用肉鸡的带宽资源来达到攻击堵死网络的,因此这个是一个前提
防火墙


利用防火墙来进行防御攻击是目前最有效的方法


SYN代码:

#include "stdafx.h"
#include <string.h>
#include <iostream>

#include <winsock2.h>
#pragma comment(lib,"WS2_32")

#include <WS2TCPIP.h>
#include <time.h>

// New WSAIoctl Options
#define SIO_RCVALL            _WSAIOW(IOC_VENDOR,1)
#define SIO_RCVALL_MCAST      _WSAIOW(IOC_VENDOR,2)
#define SIO_RCVALL_IGMPMCAST  _WSAIOW(IOC_VENDOR,3)
#define SIO_KEEPALIVE_VALS    _WSAIOW(IOC_VENDOR,4)
#define SIO_ABSORB_RTRALERT   _WSAIOW(IOC_VENDOR,5)
#define SIO_UCAST_IF          _WSAIOW(IOC_VENDOR,6)
#define SIO_LIMIT_BROADCASTS  _WSAIOW(IOC_VENDOR,7)
#define SIO_INDEX_BIND        _WSAIOW(IOC_VENDOR,8)
#define SIO_INDEX_MCASTIF     _WSAIOW(IOC_VENDOR,9)
#define SIO_INDEX_ADD_MCAST   _WSAIOW(IOC_VENDOR,10)
#define SIO_INDEX_DEL_MCAST   _WSAIOW(IOC_VENDOR,11) 


typedef struct _iphdr 
{
	unsigned char h_lenver; //4位首部长度+4位IP版本号
	unsigned char tos; //8位服务类型TOS
	unsigned short total_len; //16位总长度(字节)
	unsigned short ident; //16位标识
	unsigned short frag_and_flags; //3位标志位
	unsigned char ttl; //8位生存时间 TTL
	unsigned char proto; //8位协议 (TCP, UDP 或其余)
	unsigned short checksum; //16位IP首部校验和
	unsigned int sourceIP; //32位源IP地址
	unsigned int destIP; //32位目的IP地址
}IP_HEADER;

typedef struct _tcphdr //定义TCP首部
{
	USHORT th_sport; //16位源端口
	USHORT th_dport; //16位目的端口
	unsigned int th_seq; //32位序列号
	unsigned int th_ack; //32位确认号
	unsigned char th_lenres; //4位首部长度/6位保留字
	unsigned char th_flag; //6位标志位
	USHORT th_win; //16位窗口大小
	USHORT th_sum; //16位校验和
	USHORT th_urp; //16位紧急数据偏移量
}TCP_HEADER; 

struct //定义TCP伪首部
{
	unsigned long saddr; //源地址
	unsigned long daddr; //目的地址
	char mbz;
	char ptcl; //协议类型
	unsigned short tcpl; //TCP长度
}psd_header;

SOCKET sockRaw = INVALID_SOCKET,sockListen = INVALID_SOCKET;
int BeginPort,EndPort;
char *HOST;
int iErrorCode;
struct sockaddr_in dest;
BOOL StopScan = FALSE;

#define SEQ 0x28376839

void CheckSockError(int ierror,char *pErrorMsg)
{
	if (ierror == SOCKET_ERROR)
	{
		printf("%s ErrorCode:%d\n",pErrorMsg,ierror);
		closesocket(sockRaw);
		ExitProcess(-1);
	}
}


void meesage()
{
	printf("\t-------syn by panda--------------------------\n");
	printf("\t-------syn_test [ip] [port-port]-------------\n");
	printf("\t-------example: syn_test 127.0.0.1 1-1000----\n");
}
BOOL DecodeIPHeader(char *RecvBuf)
{
	IP_HEADER *iphdr = (IP_HEADER*)RecvBuf;
	unsigned short iphdrlen = sizeof(unsigned long) * (iphdr->h_lenver&0xf);
	TCP_HEADER* tcphdr = (TCP_HEADER*)(RecvBuf + iphdrlen);
	
	if (iphdr->sourceIP != dest.sin_addr.s_addr)
	{
		return FALSE;
	}
	if (ntohl(tcphdr->th_ack) != (SEQ+1) && ntohl(tcphdr->th_ack) != SEQ)
	{
		return FALSE;
	}
	if (tcphdr->th_flag == 18)
	{
		printf("\t%d open \n",ntohs(tcphdr->th_sport));
		return true;
	}
	return FALSE;
}


DWORD WINAPI RecvThread(LPVOID para)//接受数据线程
{
	//监听本机套接字
	sockListen = socket(AF_INET,SOCK_RAW,IPPROTO_IP);
	CheckSockError(sockListen,"RecvThread` socket");
	
	BOOL bOpt =true;
	iErrorCode = setsockopt(sockRaw,IPPROTO_IP,IP_HDRINCL,(char*)&bOpt,sizeof(bOpt));
	CheckSockError(iErrorCode,"RecvThread setsockopt");
	
	//得到本地IP
	char LocalName[256];
	gethostname(LocalName,sizeof(LocalName));
	struct hostent * my_hostent = gethostbyname(LocalName);
	
	SOCKADDR_IN sa;
	memcpy(&sa.sin_addr.S_un.S_addr,my_hostent->h_addr_list[0],my_hostent->h_length);
	sa.sin_family = AF_INET;
	sa.sin_port = htons(8000);
	
	iErrorCode = bind(sockListen,(sockaddr*)&sa,sizeof(sa));
	CheckSockError(iErrorCode,"bind");

	//设置SOCK_RAW为SIO_RCVALL,以便接收全部的IP包
	DWORD dwBufferInLen = 1;
	DWORD dwBufferLen[10];
	DWORD dwBytesReturned = 0;
	iErrorCode = WSAIoctl(sockListen,SIO_RCVALL,\
		&dwBufferInLen,sizeof(dwBufferInLen),\
		&dwBufferLen,sizeof(dwBufferLen),&dwBytesReturned,NULL,NULL);
	CheckSockError(iErrorCode,"RecvThread WSAIoctl");
	
	char RecvBuf[65535]={0};
	memset(RecvBuf,0,sizeof(RecvBuf));
	
	while (1)//循环监听  本地 数据包
	{
		iErrorCode = recv(sockListen,RecvBuf,sizeof(RecvBuf),0);
		//CheckSockError(iErrorCode,"RecvThread  recv");
		DecodeIPHeader(RecvBuf);
		if (StopScan == TRUE)
		{
			closesocket(sockListen);
			return 0;
		}
	}
	return 0;
}



USHORT CalcCheckSum(USHORT *buffer,int size)
{
	unsigned long cksum = 0;
	while (size > 1)
	{
		cksum += *buffer++;
		size -= sizeof(USHORT);
	}
	if (size)
	{
		cksum += *(USHORT*)buffer;
	}
	cksum = (cksum >> 16) + (cksum &0xffff);
	cksum += (cksum >>16);
	return (USHORT)(~cksum);
}
int play=0;
void progressbar(void)
{
	// 进度条 
	char *plays[12]= 
	{ 
		" | ", 
			" / ", 
			" - ", 
			" \\ ", 
			" | ", 
			" / ", 
			" - ", 
			" \\ ", 
			" | ", 
			" / ", 
			" - ", 
			" \\ ", 
	}; 
	printf(" =%s=\r", plays[play]);
	play = (play==11)?0:play+1;
	Sleep(2); 
}
int main(int argc, char* argv[])
{
	char *p;
	if (argc != 3)
	{
		meesage();
		return 0;
	}
	p = argv[2];
	if (strstr(argv[2],"-"))
	{
		BeginPort = atoi(argv[2]);
		while (*p)
		{
			if (*(p++) == '-')
			{
				break;
			}
		}
		EndPort = atoi(p);
		if (BeginPort <1 || BeginPort>65535 ||EndPort<1|| EndPort >65535|| EndPort<EndPort)
		{
			meesage();
			return 0;
		}
	}
	HOST = argv[1];
	meesage();

	WSADATA wsadata;
	iErrorCode = WSAStartup(MAKEWORD(2,2),&wsadata);
	CheckSockError(iErrorCode, "WsaStartup()");

	//////////////////////////////////////////////////////////////////////////////
	sockRaw = socket(AF_INET,SOCK_RAW,IPPROTO_IP);
	CheckSockError(sockRaw, "socket()");
	//设置IP头操做选项 是发送TCP报文的套接字
	BOOL bOpt = true;
	setsockopt(sockRaw,IPPROTO_IP,IP_HDRINCL,(char*)&bOpt,sizeof(bOpt));
	CheckSockError(sockRaw,"setsockopt()");

	//得到目标主机IP ,经过发送主机  第一次握手包
	memset(&dest,0,sizeof(dest));
	dest.sin_family = AF_INET;
	dest.sin_port = ntohs(BeginPort);

	struct hostent *my_hostent;
	if ((dest.sin_addr.s_addr = inet_addr(HOST)) == INADDR_NONE)
	{
		if ((my_hostent = gethostbyname(HOST)) == NULL)
		{
			memcpy(&(dest.sin_addr),my_hostent->h_addr_list[0],my_hostent->h_length);
			dest.sin_family = my_hostent->h_addrtype;
			printf("dest.sin_addr = %s",inet_ntoa(dest.sin_addr));
		}
		else
		{
			CheckSockError(SOCKET_ERROR,"gethostbyname");
		}
	}
	//////////////////////////////////////////////////////////////////////////////
	sockListen = socket(AF_INET , SOCK_RAW , IPPROTO_IP);
	CheckSockError(sockListen, "socket");
	
	//得到本地IP
	SOCKADDR_IN sa;
	unsigned char LocalName[256];
	struct hostent *hp;
	
	iErrorCode = gethostname((char*)LocalName, sizeof(LocalName)-1);
	CheckSockError(iErrorCode, "gethostname()");
	if((hp = gethostbyname((char*)LocalName)) == NULL)
	{
		CheckSockError(SOCKET_ERROR, "gethostbyname()");
	}
	memcpy(&sa.sin_addr.S_un.S_addr, hp->h_addr_list[0],hp->h_length);
	sa.sin_family = AF_INET;
	sa.sin_port = htons(8000);
	iErrorCode = bind(sockListen, (PSOCKADDR)&sa, sizeof(sa));
	CheckSockError(iErrorCode, "bind");

	//开启本地监听 第二次握手包 线程
	HANDLE Thread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)RecvThread,0,0,0);
	Sleep(1000);
    //////////////////////////////////////////////////////////////////////////////
	//发送第一次握手包
	
	IP_HEADER ip_header;
	TCP_HEADER tcp_header;
	//填充IP首部 一个IP包头的长度最长为“1111”,即15*4=60个字节。IP包头最小长度为20字节。对于标准ipv4报头,这个字段的值确定是20/4=5(10进制)=0101(2进制)。
	ip_header.h_lenver = (4<<4 | sizeof(ip_header)/sizeof(unsigned long));
	//高四位IP版本号,低四位首部长度
	ip_header.total_len=htons(sizeof(IP_HEADER)+sizeof(TCP_HEADER)); //16位总长度(字节)
	ip_header.ident=1; //16位标识
	ip_header.frag_and_flags=0; //3位标志位
	ip_header.ttl=128; //8位生存时间TTL
	ip_header.proto=IPPROTO_TCP; //8位协议(TCP,UDP…)
	ip_header.checksum=0; //16位IP首部校验和
	ip_header.sourceIP=sa.sin_addr.s_addr;  //32位源IP地址
	ip_header.destIP=dest.sin_addr.s_addr;  //32位目的IP地址
	
	//填充TCP首部
	tcp_header.th_sport = htons(8000);//源端口号
	tcp_header.th_lenres = (sizeof(TCP_HEADER)/4<<4 | 0);//TCP长度和保留位
	tcp_header.th_win=htons(16384); 

	//填充TCP伪首部(用于计算校验和,并不真正发送)
	psd_header.saddr=ip_header.sourceIP;
	psd_header.daddr=ip_header.destIP;
	psd_header.mbz=0;
	psd_header.ptcl=IPPROTO_TCP;
	psd_header.tcpl=htons(sizeof(tcp_header));

	
	printf("\n");
	printf("Scaning %s port : %d-%d\n",HOST,BeginPort,EndPort);
	clock_t start,end;//程序运行的起始和结束时间

	  start=clock();//开始计时
	//开始发包~~~~
	  char SendBuf[128] = {0};
	for (;BeginPort < EndPort;BeginPort++)
	{
		// 进度条 
		progressbar();
		
		tcp_header.th_dport = htons(BeginPort); //目的端口号
		tcp_header.th_ack=0;                  //ACK序列号置为0
		tcp_header.th_flag=2;                 //SYN 标志
		tcp_header.th_seq=htonl(SEQ);         //SYN序列号
		tcp_header.th_urp=0;                  //偏移
		tcp_header.th_sum=0;                  //校验和
		
		
		
		//计算TCP校验和 即TCP头部和TCP数据进行校验和计算,并由目标端进行验证。
		memcpy(SendBuf,&psd_header,sizeof(psd_header));
		memcpy(SendBuf+sizeof(psd_header), &tcp_header,sizeof(tcp_header));
		tcp_header.th_sum = CalcCheckSum((USHORT *)SendBuf,sizeof(psd_header)+sizeof(tcp_header));
		
		//计算IP校验和 IP包头是变长的,因此提供一个头部校验来保证IP包头中信息的正确性。
		memcpy(SendBuf,&ip_header,sizeof(ip_header));
		memcpy(SendBuf+sizeof(ip_header),&tcp_header,sizeof(tcp_header));
		memset(SendBuf+sizeof(ip_header)+sizeof(tcp_header),0,4);
		ip_header.checksum = CalcCheckSum((USHORT *)SendBuf,sizeof(ip_header)+sizeof(tcp_header));

		//填充发送缓冲区
		memcpy(SendBuf,&ip_header,sizeof(ip_header));

		//发送TCP报文
		iErrorCode=sendto(sockRaw,SendBuf,sizeof(ip_header)+sizeof(tcp_header),0,\
			(struct sockaddr*) &dest,
			sizeof(dest));
		CheckSockError(iErrorCode, "sendto()");
	}
	//结束发包~~~~
	end=clock();//计时结束
	StopScan = TRUE;
	printf("Closeing Scan.....\n");
	WaitForSingleObject(Thread,5000);
	CloseHandle(Thread);

	printf("Cost time: %f Sec",(float)(end-start) / CLOCKS_PER_SEC/*1000*/);
	
	if (sockRaw != INVALID_SOCKET)
	{
		closesocket(sockRaw);
	}
	if (sockListen!= INVALID_SOCKET)
	{
		closesocket(sockListen);
	}
	WSACleanup();
	return 0;
}