xbase-util 0.0.8__tar.gz → 0.1.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: xbase_util
3
- Version: 0.0.8
3
+ Version: 0.1.0
4
4
  Summary: 网络安全基础工具
5
+ Home-page: https://gitee.com/jimonik/xbase_util.git
5
6
  Author: xyt
6
7
  Author-email: 2506564278@qq.com
7
8
  License: <MIT License>
@@ -3,13 +3,14 @@ from distutils.core import setup
3
3
  from setuptools import find_packages
4
4
 
5
5
  setup(name="xbase_util",
6
- version="0.0.8",
6
+ version="0.1.0",
7
7
  description="网络安全基础工具",
8
8
  long_description="包含提取,预测,训练的基础工具",
9
9
  author="xyt",
10
10
  author_email="2506564278@qq.com",
11
11
  license="<MIT License>",
12
12
  packages=find_packages(),
13
+ url="https://gitee.com/jimonik/xbase_util.git",
13
14
  install_requires=[
14
15
  ],
15
16
  zip_safe=False,
@@ -0,0 +1,29 @@
1
+ import os.path
2
+
3
+
4
+ class EsDb:
5
+ def __init__(self, req):
6
+ self.req = req
7
+ self.internals = {}
8
+ print("初始化:Elasticsearch DB")
9
+
10
+ def get_file_by_file_id(self, node, num, prefix=None):
11
+ key = f'{node}!{num}'
12
+ if key in self.internals:
13
+ return self.internals[key]
14
+ res = self.req.search_file(f"{node}-{num}")
15
+ hits = res['hits']['hits']
16
+ if len(hits) > 0:
17
+ self.internals[key] = hits[0]['_source']
18
+ file = hits[0]['_source']
19
+ if prefix is None:
20
+ return file
21
+ prefix_res = prefix
22
+ if not prefix.endswith('/'):
23
+ prefix_res = f"{prefix}/"
24
+ origin_path = file['name']
25
+ basename = os.path.basename(origin_path)
26
+ result_path = f"{prefix_res}{basename}"
27
+ file['name'] = result_path
28
+ return file
29
+ return None
@@ -0,0 +1,26 @@
1
+ import requests
2
+
3
+
4
+ class EsReq:
5
+ def __init__(self, url,timeout=120):
6
+ self.es_url = url
7
+ self.timeout = timeout
8
+ print("初始化自定义es请求类")
9
+
10
+ def clear_all_scroll(self):
11
+ return requests.delete(self.es_url + "/_search/scroll", timeout=self.timeout, json={'scroll_id': '_all'})
12
+
13
+ def search(self, body, scroll):
14
+ requests.post(self.es_url + "/_search/scroll", data=body, timeout=self.timeout, json={'scroll_id': scroll})
15
+
16
+ def start_scroll(self, exp, scroll):
17
+ return requests.post(self.es_url + "/_search/scroll", timeout=self.timeout,
18
+ json=exp)
19
+
20
+ def scroll_by_id(self, scroll_id, scroll):
21
+ return requests.post(self.es_url + "/_search/scroll", timeout=self.timeout,
22
+ json={'scroll_id': scroll_id, 'scroll': scroll})
23
+
24
+ def search_file(self, id):
25
+ return requests.get(f"{self.es_url}/arkime_files_v30/_search", timeout=self.timeout,
26
+ json={"query": {"term": {"_id": id}}})
@@ -0,0 +1,161 @@
1
+ import json
2
+ import re
3
+ import traceback
4
+ from urllib.parse import unquote
5
+
6
+ import pandas as pd
7
+
8
+
9
+ def handle_uri(data):
10
+ print(f"处理URI:{len(data)}")
11
+ # 定义正则表达式,确保精确匹配各种攻击特征
12
+ regex_patterns = {
13
+ "sql": re.compile(
14
+ r"\b(select|union|insert|update|delete|drop|--|#| or |' or '|information_schema|database\(\)|version\(\))\b",
15
+ re.IGNORECASE),
16
+ "xss": re.compile(r"(<script\b|javascript:|onload=|onclick=|<iframe\b|src=)", re.IGNORECASE),
17
+ "cmd": re.compile(
18
+ r"(/etc/passwd\b|/etc/shadow\b|;|&&|\||\$\(.+\)|\bcurl\b|\bwget\b|\bexec\b|\bsystem\b|cmd=|proc/self/environ)",
19
+ re.IGNORECASE),
20
+ "path": re.compile(r"(\.\./|\.\.%2f|\.\.%5c|\.\.\\|\.\.;|%2f%2e%2e%2f)", re.IGNORECASE),
21
+ "redirect": re.compile(r"(redirect=|url=|next=|redirect_uri=|redirect:|RedirectTo=)", re.IGNORECASE),
22
+ "danger": re.compile(
23
+ r"(%3C|%3E|%27|%22|%00|%2F|%5C|%3B|%7C|%28|%29|%20|%3D|%3A|%3F|%26|%23|%2B|%25|file://|<foo|xmlns:|/etc/passwd|windows/win\.ini)",
24
+ re.IGNORECASE),
25
+ "suspicious_ext": re.compile(
26
+ r"\.(exe|sh|py|pl|bak|php5|jspx|bat|cmd|pif|js|vbs|vbe|sct|ini|inf|tmp|swp|jar|java|class|ps1)\b",
27
+ re.IGNORECASE)
28
+ }
29
+
30
+ # 定义多层解码函数,确保完全解码 URI
31
+ def fully_decode_uri(uri):
32
+ try:
33
+ decoded_uri = str(uri)
34
+ for _ in range(3): # 尝试多次解码嵌套的编码
35
+ decoded_uri = unquote(decoded_uri)
36
+ return decoded_uri
37
+ except Exception as e:
38
+ return uri
39
+
40
+ def process_row(row):
41
+ uris = row['http.uri']
42
+ if not isinstance(uris, list):
43
+ try:
44
+ uris = json.loads(uris)
45
+ if not isinstance(uris, list):
46
+ uris = [str(uris)]
47
+ except Exception:
48
+ uris = [str(uris)]
49
+ try:
50
+ decoded_uris = [fully_decode_uri(uri) for uri in uris]
51
+ except Exception as e:
52
+ traceback.print_exc()
53
+ exit(0)
54
+
55
+
56
+ # 初始化统计变量
57
+ param_count = 0
58
+ path_depth = 0
59
+ param_lengths = []
60
+ feature_flags = {key: False for key in regex_patterns.keys()}
61
+
62
+ # 遍历解码后的 URI
63
+ for uri in decoded_uris:
64
+ param_count += uri.count('&') + 1
65
+ path_depth += uri.count('/')
66
+
67
+ # 提取参数长度
68
+ if '?' in uri:
69
+ params = uri.split('?', 1)[-1].split('&')
70
+ for param in params:
71
+ if '=' in param:
72
+ _, value = param.split('=', 1)
73
+ param_lengths.append(len(value))
74
+
75
+ # 检查正则匹配特征
76
+ for key, pattern in regex_patterns.items():
77
+ if pattern.search(uri):
78
+ feature_flags[key] = True
79
+
80
+ # 计算参数长度的统计值
81
+ avg_length = sum(param_lengths) / len(param_lengths) if param_lengths else 0
82
+ max_length = max(param_lengths) if param_lengths else 0
83
+
84
+ # 创建返回结果字典
85
+ result = {
86
+ "URI_FEATURES_EXTRA_param_count": param_count,
87
+ "URI_FEATURES_EXTRA_path_depth": path_depth,
88
+ "URI_FEATURES_EXTRA_param_length_avg": avg_length,
89
+ "URI_FEATURES_EXTRA_param_length_max": max_length,
90
+ }
91
+
92
+ # 添加特征标志到结果
93
+ for key, value in feature_flags.items():
94
+ result[f"URI_FEATURES_EXTRA_contains_{key}"] = value
95
+
96
+ return result
97
+ feature_data = data.progress_apply(process_row, axis=1, result_type="expand")
98
+ data = pd.concat([data, feature_data], axis=1)
99
+ return data
100
+
101
+
102
+ def handle_ua(data):
103
+ print("处理UA")
104
+ data['http.useragent'] = data['http.useragent'].fillna('').astype(str)
105
+ # 处理换行符及多余空格
106
+ data['http.useragent'] = data['http.useragent'].str.replace(r'\s+', ' ', regex=True)
107
+ # 常见攻击的 User-Agent 字符串匹配模式,忽略大小写
108
+ attack_patterns = '|'.join([
109
+ r"\bselect\b", r"\bunion\b", r"\binsert\b", r"\bupdate\b", r"\bdelete\b", r"\bdrop\b", r"--", r"#", r" or ",
110
+ r"' or '",
111
+ r"information_schema", r"database\(\)", r"version\(\)", # SQL注入相关
112
+ r"<script>", r"javascript:", r"onload=", r"onclick=", r"<iframe>", r"src=", # XSS相关
113
+ r"/etc/passwd", r"/etc/shadow", r"\&\&", r"\|", r"\$\(\)", r"exec", r"system", # 命令执行相关
114
+ r"\.\./", r"\.\.%2f", r"\.\.%5c", r"%c0%af", r"%252e%252e%252f", # 路径遍历
115
+ r"\.php", r"\.asp", r"\.jsp", r"\.exe", r"\.sh", r"\.py", r"\.pl", # 文件扩展名
116
+ r"redirect=", r"url=", r"next=", # 重定向
117
+ r"%3C", r"%3E", r"%27", r"%22", r"%00", r"%2F", r"%5C", r"%3B", r"%7C", r"%2E", r"%28", r"%29", # 编码
118
+ r'Googlebot', r'Bingbot', r'Slurp', r'curl', r'wget', r'Nmap',
119
+ r'SQLMap', r'Nikto', r'Dirbuster', r'python-requests', r'Apache-HttpClient',
120
+ r'Postman', r'Burp Suite', r'Fuzzing', r'nessus'
121
+ ])
122
+ # 企业客户端 User-Agent 模式
123
+ enterprise_patterns = '|'.join([
124
+ r'MicroMessenger', r'wxwork', r'QQ/', r'QQBrowser', r'Alipay', r'UCWEB'
125
+ ])
126
+ # 批量检查是否为攻击的 User-Agent,忽略大小写
127
+ data['UserAgent_is_attack'] = data['http.useragent'].str.contains(attack_patterns, case=False, regex=True)
128
+ # 批量检查是否为企业客户端,忽略大小写
129
+ data['UserAgent_is_enterprise'] = data['http.useragent'].str.contains(enterprise_patterns, case=False)
130
+ # 提取浏览器和版本
131
+ data['UserAgent_browser'] = data['http.useragent'].str.extract(r'(Chrome|Firefox|Safari|MSIE|Edge|Opera|Trident)',
132
+ expand=False, flags=re.IGNORECASE).fillna("Unknown")
133
+ data['UserAgent_browser_version'] = data['http.useragent'].str.extract(
134
+ r'Chrome/([\d\.]+)|Firefox/([\d\.]+)|Version/([\d\.]+).*Safari|MSIE ([\d\.]+)|Edge/([\d\.]+)|Opera/([\d\.]+)|Trident/([\d\.]+)',
135
+ expand=False, flags=re.IGNORECASE).bfill(axis=1).fillna("Unknown").iloc[:, 0]
136
+ # 提取操作系统和版本
137
+ os_info = data['http.useragent'].str.extract(
138
+ r'(Windows NT [\d\.]+|Mac OS X [\d_\.]+|Linux|Android [\d\.]+|iOS [\d_\.]+|Ubuntu|Debian|CentOS|Red Hat)',
139
+ expand=False, flags=re.IGNORECASE)
140
+ data['UserAgent_os'] = os_info.str.extract(r'(Windows|Mac OS X|Linux|Android|iOS|Ubuntu|Debian|CentOS|Red Hat)',
141
+ expand=False, flags=re.IGNORECASE).fillna("Unknown")
142
+ data['UserAgent_os_version'] = os_info.str.extract(r'([\d\._]+)', expand=False).fillna("Unknown")
143
+ # 提取设备类型,忽略大小写
144
+ data['UserAgent_device_type'] = data['http.useragent'].str.contains('mobile|android|iphone', case=False).map(
145
+ {True: 'Mobile', False: 'Desktop'})
146
+ # 提取硬件平台,增加对 x64 的匹配
147
+ data['UserAgent_platform'] = data['http.useragent'].str.extract(r'(x86|x86_64|arm|arm64|x64)', expand=False,
148
+ flags=re.IGNORECASE).fillna('Unknown')
149
+ # 判断是否为爬虫,忽略大小写
150
+ data['UserAgent_is_bot'] = data['http.useragent'].str.contains('bot|crawler|spider|slurp|curl|wget|httpclient',
151
+ case=False)
152
+ # 提取语言偏好(如果存在),忽略大小写
153
+ data['UserAgent_language'] = data['http.useragent'].str.extract(r'\b([a-z]{2}-[A-Z]{2})\b', expand=False,
154
+ flags=re.IGNORECASE).fillna("Unknown")
155
+ # 统计 User-Agent 中的特殊字符个数
156
+ data['UserAgent_special_char_count'] = data['http.useragent'].progress_apply(
157
+ lambda x: len(re.findall(r'[!@#$%^&*\'=:|{}]', x, flags=re.IGNORECASE)))
158
+ # 更新 UserAgent_is_unknown 的计算逻辑
159
+ data['UserAgent_is_unknown'] = data[['UserAgent_browser', 'UserAgent_os', 'UserAgent_platform']].isna().any(
160
+ axis=1).fillna("Unknown")
161
+ return data
@@ -0,0 +1,247 @@
1
+ import math
2
+ import os
3
+ import struct
4
+ import time
5
+ import zlib
6
+ from datetime import datetime
7
+
8
+ from Crypto.Cipher import AES
9
+ from zstandard import ZstdDecompressor
10
+
11
+
12
+ def fix_pos(pos, packetPosEncoding):
13
+ if pos is None or len(pos) == 0:
14
+ return
15
+ if packetPosEncoding == "gap0":
16
+ last = 0
17
+ lastgap = 0
18
+ for i, pos_item in enumerate(pos):
19
+ if pos[i] < 0:
20
+ last = 0
21
+ else:
22
+ if pos[i] == 0:
23
+ pos[i] = last + lastgap
24
+ else:
25
+ lastgap = pos[i]
26
+ pos[i] += last
27
+ last = pos[i]
28
+
29
+
30
+ def group_numbers(nums):
31
+ result = []
32
+ for num in nums:
33
+ if num < 0:
34
+ result.append([num])
35
+ elif result:
36
+ result[-1].append(num)
37
+ return result
38
+
39
+
40
+ def decompress_streaming(compressed_data, id, fro):
41
+ try:
42
+ decompressor = ZstdDecompressor()
43
+ with decompressor.stream_reader(compressed_data) as reader:
44
+ decompressed_data = reader.read()
45
+ return decompressed_data
46
+ except Exception as e:
47
+ print(f"解码错误:{e} {id}")
48
+ return bytearray()
49
+
50
+
51
+ def read_header(param_map, id):
52
+ shortHeader = None
53
+ headBuffer = os.read(param_map['fd'], 64)
54
+ if param_map['encoding'] == 'aes-256-ctr':
55
+ if 'iv' in param_map:
56
+ param_map['iv'][12:16] = struct.pack('>I', 0)
57
+ headBuffer = bytearray(
58
+ AES.new(param_map['encKey'], AES.MODE_CTR, nonce=param_map['iv']).decrypt(bytes(headBuffer)))
59
+ else:
60
+ print("读取头部信息失败,iv向量为空")
61
+ elif param_map['encoding'] == 'xor-2048':
62
+ for i in range(len(headBuffer)):
63
+ headBuffer[i] ^= param_map['encKey'][i % 256]
64
+ if param_map['uncompressedBits']:
65
+ if param_map['compression'] == 'gzip':
66
+ headBuffer = zlib.decompress(bytes(headBuffer), zlib.MAX_WBITS | 16)
67
+ elif param_map['compression'] == 'zstd':
68
+ headBuffer = decompress_streaming(headBuffer, id, "header")
69
+ headBuffer = headBuffer[:24]
70
+ magic = struct.unpack('<I', headBuffer[:4])[0]
71
+ bigEndian = (magic == 0xd4c3b2a1 or magic == 0x4d3cb2a1)
72
+ # nanosecond = (magic == 0xa1b23c4d or magic == 0x4d3cb2a1)
73
+ if not bigEndian and magic not in {0xa1b2c3d4, 0xa1b23c4d, 0xa1b2c3d5}:
74
+ corrupt = True
75
+ # os.close(param_map['fd'])
76
+ raise ValueError("Corrupt PCAP header")
77
+ if magic == 0xa1b2c3d5:
78
+ shortHeader = struct.unpack('<I', headBuffer[8:12])[0]
79
+ headBuffer[0] = 0xd4 # Reset header to normal
80
+ linkType = struct.unpack('>I' if bigEndian else '<I', headBuffer[20:24])[0]
81
+ return headBuffer, shortHeader, bigEndian
82
+
83
+
84
+ def create_decipher(pos, param_map):
85
+ param_map['iv'][12:16] = struct.pack('>I', pos)
86
+ return AES.new(param_map['encKey'], AES.MODE_CTR, nonce=param_map['iv'])
87
+
88
+
89
+ def read_packet_internal(pos_arg, hp_len_arg, param_map, id):
90
+ pos = pos_arg
91
+ hp_len = hp_len_arg
92
+ if hp_len == -1:
93
+ if param_map['compression'] == "zstd":
94
+ hp_len = param_map['uncompressedBitsSize']
95
+ else:
96
+ hp_len = 2048
97
+ inside_offset = 0
98
+ if param_map['uncompressedBits']:
99
+ inside_offset = pos & param_map['uncompressedBitsSize'] - 1
100
+ pos = math.floor(pos / param_map['uncompressedBitsSize'])
101
+ pos_offset = 0
102
+ if param_map['encoding'] == 'aes-256-ctr':
103
+ pos_offset = pos % 16
104
+ pos = pos - pos_offset
105
+ elif param_map['encoding'] == 'xor-2048':
106
+ pos_offset = pos % 256
107
+ pos = pos - pos_offset
108
+
109
+ hp_len = 256 * math.ceil((hp_len + inside_offset + pos_offset) / 256)
110
+ buffer = bytearray(hp_len)
111
+ os.lseek(param_map['fd'], pos, os.SEEK_SET)
112
+ read_buffer = os.read(param_map['fd'], len(buffer))
113
+ if len(read_buffer) - pos_offset < 16:
114
+ return None
115
+ if param_map['encoding'] == 'aes-256-ctr':
116
+ decipher = create_decipher(pos // 16, param_map)
117
+ read_buffer = bytearray(decipher.decrypt(read_buffer))[pos_offset:]
118
+ elif param_map['encoding'] == 'xor-2048':
119
+ read_buffer = bytearray(b ^ param_map['encKey'][i % 256] for i, b in enumerate(read_buffer))[pos_offset:]
120
+ if param_map['uncompressedBits']:
121
+ try:
122
+ if param_map['compression'] == 'gzip':
123
+ read_buffer = zlib.decompress(read_buffer, zlib.MAX_WBITS | 16)
124
+ elif param_map['compression'] == 'zstd':
125
+ read_buffer = decompress_streaming(read_buffer, id, "packet")
126
+ except Exception as e:
127
+ print(f"PCAP uncompress issue: {pos} {len(buffer)} {read_buffer} {e}")
128
+ return None
129
+ if inside_offset:
130
+ read_buffer = read_buffer[inside_offset:]
131
+ header_len = 16 if param_map['shortHeader'] is None else 6
132
+ if len(read_buffer) < header_len:
133
+ if hp_len_arg == -1 and param_map['compression'] == 'zstd':
134
+ return read_packet_internal(pos_arg, param_map['uncompressedBitsSize'] * 2, param_map, id)
135
+ print(f"Not enough data {len(read_buffer)} for header {header_len}")
136
+ return None
137
+ packet_len = struct.unpack('>I' if param_map['bigEndian'] else '<I', read_buffer[8:12])[
138
+ 0] if param_map['shortHeader'] is None else \
139
+ struct.unpack('>H' if param_map['bigEndian'] else '<H', read_buffer[:2])[0]
140
+ if packet_len < 0 or packet_len > 0xffff:
141
+ return None
142
+ if header_len + packet_len <= len(read_buffer):
143
+ if param_map['shortHeader'] is not None:
144
+ t = struct.unpack('<I', read_buffer[2:6])[0]
145
+ sec = (t >> 20) + param_map['shortHeader']
146
+ usec = t & 0xfffff
147
+ new_buffer = bytearray(16 + packet_len)
148
+ struct.pack_into('<I', new_buffer, 0, sec)
149
+ struct.pack_into('<I', new_buffer, 4, usec)
150
+ struct.pack_into('<I', new_buffer, 8, packet_len)
151
+ struct.pack_into('<I', new_buffer, 12, packet_len)
152
+ new_buffer[16:] = read_buffer[6:packet_len + 6]
153
+ return new_buffer
154
+ return read_buffer[:header_len + packet_len]
155
+
156
+ if hp_len_arg != -1:
157
+ return None
158
+
159
+ return read_packet_internal(pos_arg, 16 + packet_len, param_map, id)
160
+
161
+
162
+ def read_packet(pos, param_map, id):
163
+ if 'fd' not in param_map or not param_map['fd']:
164
+ time.sleep(0.01)
165
+ return read_packet(pos, param_map['fd'], id)
166
+ return read_packet_internal(pos, -1, param_map, id)
167
+
168
+
169
+ def get_file_and_read_pos(id, file, pos_list):
170
+ filename = file['name']
171
+ if not os.path.isfile(filename):
172
+ print(f"文件不存在:{filename}")
173
+ return None
174
+ encoding = file.get('encoding', 'normal')
175
+ encKey = None
176
+ iv = None
177
+ compression = None
178
+ if 'dek' in file:
179
+ dek = bytes.fromhex(file['dek'])
180
+ encKey = AES.new(file['kek'].encode(), AES.MODE_CBC).decrypt(dek)
181
+
182
+ if 'uncompressedBits' in file:
183
+ uncompressedBits = file['uncompressedBits']
184
+ uncompressedBitsSize = 2 ** uncompressedBits
185
+ compression = 'gzip'
186
+ else:
187
+ uncompressedBits = None
188
+ uncompressedBitsSize = 0
189
+ if 'compression' in file:
190
+ compression = file['compression']
191
+
192
+ if 'iv' in file:
193
+ iv_ = bytes.fromhex(file['iv'])
194
+ iv = bytearray(16)
195
+ iv[:len(iv_)] = iv_
196
+ fd = os.open(filename, os.O_RDONLY)
197
+ param_map = {
198
+ "fd": fd,
199
+ "encoding": encoding,
200
+ "iv": iv,
201
+ "encKey": encKey,
202
+ "uncompressedBits": uncompressedBits,
203
+ "compression": compression,
204
+ "uncompressedBitsSize": uncompressedBitsSize
205
+ }
206
+ res = bytearray()
207
+ headBuffer, shortHeader, bigEndian = read_header(param_map, id)
208
+ res.extend(headBuffer)
209
+ param_map['shortHeader'] = shortHeader
210
+ param_map['bigEndian'] = bigEndian
211
+ # _________________________________
212
+ byte_array = bytearray(0xfffe)
213
+ next_packet = 0
214
+ b_offset = 0
215
+ packets = {}
216
+ i = 0
217
+ for pos in pos_list:
218
+ packet_bytes = read_packet(pos, param_map, id)
219
+ if not packet_bytes:
220
+ continue
221
+ packets[i] = packet_bytes
222
+ while next_packet in packets:
223
+ buffer = packets[next_packet]
224
+ del packets[next_packet]
225
+ next_packet = next_packet + 1
226
+ if b_offset + len(buffer) > len(byte_array):
227
+ res.extend(byte_array[:b_offset])
228
+ b_offset = 0
229
+ byte_array = bytearray(0xfffe)
230
+ byte_array[b_offset:b_offset + len(buffer)] = buffer
231
+ b_offset += len(buffer)
232
+ i = i + 1
233
+ os.close(fd)
234
+ res.extend(byte_array[:b_offset])
235
+ return res
236
+
237
+
238
+ def process_session_id_disk_simple(id, node, packet_pos, esdb, pcap_path_prefix):
239
+ packetPos = packet_pos
240
+ file = esdb.get_file_by_file_id(node=node, num=abs(packetPos[0]),
241
+ prefix=None if pcap_path_prefix == "origin" else pcap_path_prefix)
242
+ if file is None:
243
+ return None
244
+ fix_pos(packetPos, file['packetPosEncoding'])
245
+ pos_list = group_numbers(packetPos)[0]
246
+ pos_list.pop(0)
247
+ return get_file_and_read_pos(id, file, pos_list)
@@ -1,7 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: xbase-util
3
- Version: 0.0.8
3
+ Version: 0.1.0
4
4
  Summary: 网络安全基础工具
5
+ Home-page: https://gitee.com/jimonik/xbase_util.git
5
6
  Author: xyt
6
7
  Author-email: 2506564278@qq.com
7
8
  License: <MIT License>
@@ -1,6 +1,10 @@
1
1
  README.md
2
2
  setup.py
3
3
  xbase_util/__init__.py
4
+ xbase_util/es_db_util.py
5
+ xbase_util/esreq.py
6
+ xbase_util/handle_features_util.py
7
+ xbase_util/pcap_util.py
4
8
  xbase_util/xbase_util.py
5
9
  xbase_util.egg-info/PKG-INFO
6
10
  xbase_util.egg-info/SOURCES.txt
File without changes
File without changes