summaryrefslogtreecommitdiff
path: root/PcapNGFormatAnalys.py
blob: ccb3837a11096b5de77c4e8a4761b3cb08bedbf8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
#!/usr/bin/evn python
# -*- coding:utf-8 -*-

import binascii  
import sys
import os
import subprocess
import json
import struct

class SectionHeaderBlock_a: # SHB

    def __init__(self, data):

        self._Block_Type = data[0:4]
        self._Block_Length = data[4:8]
        self._Byte_Order_Magic = data[8:12]
        self._Major_Version = data[12:14]
        self._Minor_Version = data[14:16]
        self._Section_Length = data[16:24]
        self._decimal_number = int.from_bytes(self._Block_Length, byteorder='little')
    
    def display_content(self):
        print(binascii.b2a_hex(self._Block_Type))
        print(binascii.b2a_hex(self._Block_Length))
        print(binascii.b2a_hex(self._Byte_Order_Magic))
        print(binascii.b2a_hex(self._Major_Version))
        print(binascii.b2a_hex(self._Minor_Version))
        print(binascii.b2a_hex(self._Section_Length))
        print(self._decimal_number)

class SectionHeaderBlock_b: # SHB
    def __init__(self, data, Block_Length):
        self._Options = data[0:Block_Length - 24 - 4 ]
        self._Block_Length_redundant = data[Block_Length - 24 - 4 : Block_Length - 24]
    
    def display_content(self):
        print(binascii.b2a_hex(self._Options))
        print(binascii.b2a_hex(self._Block_Length_redundant))

class InterfaceDescriptionBlock: # IDB
    def __init__(self, data):
        self._Block_Type = data[0:4]
        self._Block_Length = data[4:8]
        self._decimal_number = int.from_bytes(self._Block_Length, byteorder='little')

    def display_content(self):
        print(binascii.b2a_hex(self._Block_Type))
        print(binascii.b2a_hex(self._Block_Length))    
        print(self._decimal_number)    

class PacketBlocks_a: # PB
    def __init__(self, data):
        self._Block_Type = data[0:4]
        self._Block_Length = data[4:8]
        self._Block_Length_int = int.from_bytes(self._Block_Length, byteorder='little')
        self._Interface_ID = data[8:12]
        self._Timestamp_Upper = data[12:16]
        self._Timestamp_Lower = data[16:20]
        self._Captured_Packet_Length = data[20:24]
        self._Captured_Packet_Length_int = int.from_bytes(self._Captured_Packet_Length, byteorder='little')
        self._Original_Packet_Length = data[24:28]
        self._Original_Packet_Length_int = int.from_bytes(self._Original_Packet_Length, byteorder='little')

    def display_content(self):
        print('Block_Type', binascii.b2a_hex(self._Block_Type))
        print('Block_Length', binascii.b2a_hex(self._Block_Length))    
        print('Block_Length_int', self._Block_Length_int)    
        print('Interface_ID', binascii.b2a_hex(self._Interface_ID))
        print('Timestamp_Upper', binascii.b2a_hex(self._Timestamp_Upper))
        print('Timestamp_Lower', binascii.b2a_hex(self._Timestamp_Lower))
        print('Captured_Packet_Length', binascii.b2a_hex(self._Captured_Packet_Length))
        print('Captured_Packet_Length_int', self._Captured_Packet_Length_int) 
        print('Original_Packet_Length', binascii.b2a_hex(self._Original_Packet_Length))
        print('Original_Packet_Length_int', self._Original_Packet_Length_int) 

class PacketBlocks_b: # PB
    def __init__(self, data, Block_Length, Packet_Length):
        self._Packet_Data= data[0:Packet_Length]
        self._Options = data[Packet_Length:Block_Length - 32]
        self._Block_Total_Length_redundant  = data[Block_Length - 32: Block_Length - 28]

    def display_content(self):
        print('Packet_Data', binascii.b2a_hex(self._Packet_Data))
        print('Options', binascii.b2a_hex(self._Options))      
        print('Block_Total_Length_redundant', binascii.b2a_hex(self._Block_Total_Length_redundant))

class PB_Options: 
    def __init__(self, data):
        self._Option_Code= data[0:2]
        self._Option_Length = data[2:4]
        self._Option_Length_int = int.from_bytes(self._Option_Length, byteorder='little')
        self._Option_Value = data[4:self._Option_Length_int + 4]
        self._End_of_Options = data[self._Option_Length_int + 4 : ]

    def display_content(self):
        print(binascii.b2a_hex(self._Option_Code))
        print(binascii.b2a_hex(self._Option_Length))    
        print(self._Option_Length_int)  
        print(binascii.b2a_hex(self._Option_Value))
        print(binascii.b2a_hex(self._End_of_Options))

def ExtractPacketsCommentStr(input_file_path, appsketch_api, pcap_file_id):
    # Use tshark to extract the five_tuple info
    tshark_cmd = ['tshark', '-r', input_file_path, '-T', 'fields', '-e', 'frame.number', '-e', 'tcp.stream', '-e', 'udp.stream']
    ret_str = subprocess.run(tshark_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    decoded_result = ret_str.stdout
    list_1 = decoded_result.splitlines()
    list_output = []
    for i in range(0, len(list_1)) :
        list_2 = list_1[i].split("\t")
        list_output.append(list_2)

    comment_str_list = []
    for i in range(len(list_output)):
        # frame_id  = list_output[i][0]
        session_dict = {}
        if list_output[i][1] != '':
            proto = 'tcp'
            stream_id = list_output[i][1]
        elif list_output[i][1] == '' and list_output[i][2] != '':
            proto = 'udp'
            stream_id = list_output[i][2]          
        else:
            proto = 'other'
            stream_id = ''
        session_dict['session'] = appsketch_api + '/session/' + pcap_file_id + '/' + proto + '/' + stream_id
        comment_str_list.append(session_dict)
        
    return comment_str_list

def TransferPcapNG(input_file_path):
    with open(input_file_path, 'rb') as f:
        first_four_bytes = f.read(4)
    if first_four_bytes == bytes.fromhex('0a0d0d0a') or first_four_bytes == bytes.fromhex('0A0D0D0A'):
        return input_file_path
    else:
        # Get the file name (including extension)
        file_name_with_extension = os.path.basename(input_file_path)
        # Remove the extension
        transfer_file_path = os.path.splitext(file_name_with_extension)[0] + '.pcapng'
        # Get the directory path without the file name
        directory_path = os.path.dirname(input_file_path)

        tshark_cmd = ['tshark', '-r', input_file_path, '-w', directory_path + '/' + transfer_file_path]
        subprocess.run(tshark_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        input_file_path = './output/Transfer.pcapng'
        return directory_path + '/' + transfer_file_path

if __name__=="__main__":

    input_file_path = TransferPcapNG(sys.argv[1])
    output_file_path = sys.argv[2]
    appsketch_api = sys.argv[3]
    pcap_file_id = sys.argv[4]

    comment_str_list = ExtractPacketsCommentStr(input_file_path, appsketch_api, pcap_file_id)
    # for i, pkt in enumerate(comment_str_list):
    #     print(comment_str_list[i])

    binfile_r = open(input_file_path, 'rb') 
    binfile_w = open(input_file_path, 'rb') 
    binfile_output = open(output_file_path, 'wb') 

    size = os.path.getsize(input_file_path)

    # Read Pcapng SHB
    section_header_block_a = SectionHeaderBlock_a(binfile_r.read(24))
    # section_header_block_a.display_content()
    SHB_length = section_header_block_a._decimal_number
    # Remaining bytes read
    binfile_r.read(SHB_length - 24)

    # Read Pcapng IDB
    interface_description_block = InterfaceDescriptionBlock(binfile_r.read(8))
    # interface_description_block.display_content()
    IDB_length = interface_description_block._decimal_number
    # Remaining bytes read
    binfile_r.read(IDB_length - 8 )

    # 4Byte alignment
    # Get the number of bytes currently read
    current_position = binfile_r.tell()
    # Calculate the next 4-byte aligned position
    aligned_position = (current_position + 3) & ~3

    align_null_bytes_SHB = bytes.fromhex('')
    for i in range(aligned_position - current_position):
        align_null_bytes_SHB +=  bytes.fromhex('00')

    write_data = binfile_w.read(current_position)    
    binfile_output.write(write_data)
    binfile_output.write(align_null_bytes_SHB)
    binfile_w.close()

    # Packets Traversal Add Option
    for i in range(len(comment_str_list)):
        # Read Pcapng Packet Blocks 
        packet_block_a = PacketBlocks_a(binfile_r.read(28))   
        # packet_block_a.display_content()

        packet_block_data = binfile_r.read(packet_block_a._Captured_Packet_Length_int)
        # print('Packet_Data', binascii.b2a_hex(packet_block_data))

        # 4Byte alignment
        # Get the number of bytes currently read
        current_position = binfile_r.tell()
        # Calculate the next 4-byte aligned position
        aligned_position = (current_position + 3) & ~3
        align_null_bytes_PB_a = bytes.fromhex('')
        for j in range(aligned_position - current_position):
            align_null_bytes_PB_a +=  bytes.fromhex('00')

        # 使用 `json.dumps` 方法将 JSON 对象格式化为带缩进的字符串
        remark_str = json.dumps(comment_str_list[i], indent=4)
        
        for j in range(4 - len(remark_str.encode('utf-8')) % 4):
            binary_str = '00'
            char = chr(int(binary_str, 2))
            remark_str += char

        Block_Type  =  packet_block_a._Block_Type 
        # Modify Block_Length
        # Block_Length = packet_block_a._Block_Length
        Block_Length = struct.pack('<I', packet_block_a._Block_Length_int + 8 + len(remark_str))

        Interface_ID = packet_block_a._Interface_ID
        Timestamp_Upper = packet_block_a._Timestamp_Upper
        Timestamp_Lower = packet_block_a._Timestamp_Lower
        Captured_Packet_Length = packet_block_a._Captured_Packet_Length
        Original_Packet_Length = packet_block_a._Original_Packet_Length

        # Modify Option_Length
        Option_Code = bytes.fromhex('0100')
        Option_Length = struct.pack('<H', len(remark_str))
        Option_Value = remark_str.encode('utf-8')
        End_of_Options = bytes.fromhex('00000000')

        current_position = 2 + 2 + len(remark_str) + 4
        aligned_position = (current_position + 3) & ~3
        align_null_bytes_PB_b = bytes.fromhex('')
        for j in range(aligned_position - current_position):
            align_null_bytes_PB_b +=  bytes.fromhex('00')
        
        # Modify Block_Total_Length_Redundant
        Block_Total_Length_Redundant = struct.pack('<I', packet_block_a._Block_Length_int + 8 + len(remark_str))
        
        write_data = Block_Type + Block_Length + Interface_ID + Timestamp_Upper + Timestamp_Lower + Captured_Packet_Length + Original_Packet_Length + \
                    packet_block_data + align_null_bytes_PB_a + \
                    Option_Code + Option_Length + Option_Value + End_of_Options + align_null_bytes_PB_b + \
                    Block_Total_Length_Redundant 

        binfile_output.write(write_data)
        binfile_r.read(4)

        if binfile_r.tell() == size:
            break

        # Get the number of bytes currently read
        current_position = binfile_r.tell()
        # Calculate the next 4-byte aligned position
        aligned_position = (current_position + 3) & ~3
        # Move the file pointer to a 4-byte aligned position
        binfile_r.seek(aligned_position)
    
    try:
        os.remove(input_file_path)
        print(f"File '{input_file_path}' has been deleted successfully.")
    except FileNotFoundError:
        print(f"File '{input_file_path}' does not exist.")
    except PermissionError:
        print(f"Permission denied: '{input_file_path}'.")
    except Exception as e:
        print(f"An error occurred: {e}")