基于python3的tkinter和scapy可视化报文构造工具(五)

本文详细介绍了如何利用Scapy库通过TCP和HTTP协议构造HTTP请求与响应,涉及三次握手过程中的seq和ack字段应用,以及HTTP报文的Host和User-Agent定制。还展示了四次挥手的实现,重点在于TCP flag的设置。
摘要由CSDN通过智能技术生成

前面详细介绍了界面和scapy的基本用法,本篇将根据TCP和HTTP协议来具体学习怎么构造自己想要的报文。本工具构造的HTTP报文主要关注host,UA等字段,所以留有相关的接口,但是对于一个完整的HTTP协议来说,下层还有TCP协议,IP协议以及网际接口层协议。一个完整的HTTP协议报文,包括三次握手,HTTP请求和响应,四次挥手。本文将根据这三个部分进行展开。

由于构造报文时用到了TCP的相关字段,有必要首先简单介绍一下TCP协议的规范,下图为TCP协议的首部字段

我们构造报文时主要关注的字段是seq和ack号,即上图中的序号和确认号。那这俩个字段是起什么作用呢?

序号(seq)用来标识从TCP发端向TCP收端发送的数据字节流,它表示在这个报文段中的的第一个数据字节。如果将字节流看作在两个应用程序间的单向流动,则TCP用序号对每个字节进行计数。序号是32bit的无符号数,序号到达232-1后又从0开始。

当建立一个新的连接时,SYN标志变1。序号字段包含由这个主机选择的该连接的初始序号ISN(InitialSequenceNumber)。该主机要发送数据的第一个字节序号为这个ISN加1,因为SYN标志消耗了一个序号。既然每个传输的字节都被计数,确认序号包含发送确认的一端所期望收到的下一个序号。因此,确认序号(ack)应当是上次已成功收到数据字节序号加1。只有ACK标志(下面介绍)为1时确认序号字段才有效。

发送ACK无需任何代价,因为32bit的确认序号字段和ACK标志一样,总是TCP首部的一部分。因此,我们看到一旦一个连接建立起来,这个字段总是被设置,ACK标志也总是被设置为1。

TCP为应用层提供全双工服务。这意味数据能在两个方向上独立地进行传输。因此,连接的每一端必须保持每个方向上的传输数据序号。

一:构造三次握手

开始的时候,客户端和服务器端分别创建自己的传输控制块(TCB),创建完后server进入Listen状态,此时准备接受client发送的请求。

第一次握手:客户端向服务器端发送连接请求。

    SYN=1,ACK=0表示请求连接。

    X表示本次字节流的初始序号。

    TCP规定:此时没有数据传输,但是消耗一个序号。

第二次握手:服务器向客户端发生应发响应。

    SYN=1,ACK=0表示同意连接的应答响应。

    Y表示本次字节流的初始序号。

    X+1表示希望收到的字节起始序号。

第三次握手:当客户端收到连接同意的应答后,还要向服务端发送一个确认报文段,表示:服务端发来的连接同意应答已经成功收到。 

    该报文段的头部为:ACK=1,seq=x+1,ack=y+1。 

 客户端发完这个报文段后便进入ESTABLISHED状态,服务端收到这个应答后也进入ESTABLISHED状态,此时连接的建立完成!

构造报文的时候,对于TCP层主要关注seq和ack的值,还有flag,不然构造出来的报文不符合规范。

 # 构造三次握手
    dport = 80
    userport = random.randint(10000, 65535)
    pktdump = PcapWriter(savefile, append=True, sync=True)

    srcSyn = Ether() / IP(src=srcIP, dst=desIP) / TCP(sport=userport, dport=dport, seq=1000, ack=0, flags='S')

    dseq = 0
    ethersrc = srcSyn['Ether'].src
    etherdst = srcSyn['Ether'].dst

    ansSyn = Ether(src=etherdst, dst=ethersrc) / IP(src=desIP, dst=srcIP) / TCP(sport=dport, dport=userport, \
                                                                                seq=2000, ack=1001, flags='SA')
    # ansseq = ansSyn['TCP'].seq + 1
    sack = Ether(src=ethersrc, dst=etherdst) / IP(src=srcIP, dst=desIP) / TCP(sport=userport, dport=dport, seq=1001,
                                                                              ack=2001, flags='A')
    list = [srcSyn, ansSyn, sack]
    for pcap in list:
        pktdump.write(pcap)
        time.sleep(0.1)

解释:
1:(A) –> [SYN] –> (B)
A向B发起连接请求,以一个随机数初始化A的seq,这里为1000,此时ACK=0

2:(A) <– [SYN/ACK] <–(B)
B收到A的连接请求后,也以一个随机数初始化B的seq,这里为2000,意思是:你的请求我已收到,我这方的数据流就从这个数开始。B的ACK是A的seq加1,即1000+1=1001

3:(A) –> [ACK] –> (B)
A收到B的回复后,它的seq是它的上个请求的seq加1,即1000+1=1001,意思也是:你的回复我收到了,我这方的数据流就从这个数开始。A此时的ACK是B的seq加1,即2000+1=2001

另外对于flags字段,SYN报文,scapy库中用'S'来作为表示SYN报文的标志,同理'A'表示ACK报文的标志。构造出来的报文如下

二:构造HTTP请求和响应

构造http的请求与响应主要关注我们关注的字段,不关注的字段我们可以在代码中直接写死。

# 构造请求响应包
    if UserAgent:
        ua = f'User-Agent: {UserAgent}\r\n'
    else:
        ua = b'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36\r\n'

    if host:
        Host = f'Host: {host}\r\n'
    else:
        Host = b'Host: hpd.baidu.com\r\n'

    if retcode:
        retCode = retcode
    else:
        retCode = 200

    if server:
        serverD = f'Server: {server}\r\n'
    else:
        serverD = 'Server: Apache\r\n'
    httprequest = f'''POST /index.html HTTP/1.1\r\nAccept-Encoding: gzip\r\nContent-Type: application/json\r\nConnection: keep-alive\r\n{Host}{ua}\r\n'''
    
    httpresponse=f'''HTTP/1.1 {retcode} OK\r\n{serverD}Connection: keep-alive\r\nVary: Accept-Encoding\r\nContent-Encoding: json\r\n\r\n'''
    packetrequest = Ether(src=ethersrc, dst=etherdst) / IP(src=srcIP, dst=desIP) / TCP(sport=userport, dport=dport,
                                                                                       ack=2001, seq=1001,
                                                                                       flags=24) / httprequest

    httpack = Ether(src=etherdst, dst=ethersrc) / IP(src=desIP, dst=srcIP) / TCP(sport=dport, dport=userport, seq=2001,
                                                                                 ack=1001+len(packetrequest)-54, flags='A')

    packetreponse = Ether(src=etherdst, dst=ethersrc) / IP(src=desIP, dst=srcIP) / TCP(sport=dport, dport=userport,
                                                                                       flags=24, ack=1001+len(packetrequest)-54,
                                                                                       seq=2001) / httpresponse
    list2 = [packetrequest, httpack, packetreponse]
    for pcap in list2:
        pktdump.write(pcap)
        time.sleep(0.1)

构造出来的报文如下

看看http请求中报文的细节

 报文中的Host和UA字段均是我们作为传参进行输入的,达到了我们想要的效果

三:构造四次挥手

第一次挥手:A已经发送完数据,请求释放连接,只发送报文头,此时A进入FIN_WAIT状态。

    FIN=1,表示释放连接。

    seq=u,u-1表示确认的最后一个数据序号。

第二次挥手:B收到连接释放请求之后,会告诉它A到B这个方向已经释放。B向A发送应答响应,B进入CLOSE_WAIT状态。

   ACK=1:除TCP连接请求报文段以外,TCP通信过程中所有数据报的ACK都为1,表示应答。

   seq= v:v-1是B向A发送的最后一个字节的序号。

   ack=u+1表示希望收到从第u+1个字节开始的报文段,并且已经成功接收了前u个字节。

第三次挥手:当B向A发完所有数据后,向A发送连接释放请求,请求头:FIN=1,ACK=1,seq=w,ack=u+1。

    B便进入LAST-ACK状态。

第四次挥手:A收到释放请求后,向B发送确认应答,此时A进入TIME-WAIT状态。

该状态会持续2MSL时间,若该时间段内没有B的重发请求的话,就进入CLOSED状态,撤销TCB。当B收到确认应答后,也便进入CLOSED状态,撤销TCB。

知道了流程,那我们构造四次挥手的时候只需要关注flag字段和seq,ack即可

 # 四次挥手

    srcfin = Ether(src=etherdst, dst=ethersrc) / IP(src=srcIP, dst=desIP) / TCP(sport=userport, dport=dport,
                                                                                ack=2001+len(packetreponse)-54,
                                                                                seq=1001+len(packetrequest)-54, flags=17)
    srcfinack = Ether(src=ethersrc, dst=etherdst) / IP(src=desIP, dst=srcIP) / TCP(sport=dport, dport=userport, seq=2001+len(packetreponse)-54,
                                                                                 ack=1001+len(packetrequest)-53,
                                                                                 flags='A')
    desfinack = Ether(src=ethersrc, dst=etherdst) / IP(src=desIP, dst=srcIP) / TCP(sport=dport, dport=userport,
                                                                                   flags=17, ack=1001+len(packetrequest)-53,
                                                                                   seq=2001+len(packetreponse)-54)
    srcack = Ether(src=etherdst, dst=ethersrc) / IP(src=srcIP, dst=desIP) / TCP(sport=userport, dport=dport,
                                                                                ack=2001+len(packetreponse)-53,
                                                                                seq=1001+len(packetrequest)-53, flags=16)
    list3 = [srcfin, srcfinack,desfinack, srcack]
    for pcap in list3:
        pktdump.write(pcap)
        time.sleep(0.1)

构造出来的报文如下:

至此,一个完整的http报文即构造完成。

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ftzchina

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值