十六进制文本码流转pcap(text2pcap)

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <base href="/">
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <title>Hex to PCAP Converter</title>

    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            padding-top: 0;
            min-height: 100vh;
            background: rgb(251, 250, 250);
            display: flex;
            justify-content: center;
            align-items: flex-start;
        }

        .container {
            width: 80%;
            margin: 5px 0;
            min-height: 100vh;
            padding: 20px;
            background: white;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }

        textarea {
            width: 80%;
            min-height: 250px;
            margin: 10px 0;
            font-family: monospace;
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        button {
            background: #007bff;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            cursor: pointer;
            margin: 5px;
        }
        button:hover {
            background: #0056b3;
        }
        .output-container {
            margin-top: 20px;
        }
        .title {
            font-weight: bold;
            margin-bottom: 5px;
            color: #333;
        }
        .checkbox-container {
            margin: 10px 0;
        }
        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.5);
        }
        .modal-content {
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            width: 400px;
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }
        .modal input[type="text"] {
            width: 100%;
            padding: 8px;
            margin: 10px 0;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        .modal-buttons {
            display: flex;
            justify-content: space-between;
            margin-top: 15px;
        }
        .modal-buttons button {
            width: 170px;
        }
		
		@media (max-width: 768px) {
            .container {
                padding: 1rem;
                margin: 70px 1rem 1rem;
            }

            .button-container {
                flex-direction: column;
            }

            .modal-content {
                width: 95%;
                padding: 1.5rem;
            }
        }
    </style>
</head>
<body>

    <div class="container">
        <h1>Hex to PCAP Converter</h1>
        <div class="checkbox-container">
            <input type="checkbox" id="addHeaders" checked>
            <label for="addHeaders">Add MAC/IP/UDP Headers (GTP-C Port: 2123)</label>
        </div>

        <div class="input-container">
            <div class="title">Input Hex Stream:</div>
            <textarea id="inputHex" placeholder="Enter hex stream (space or newline separated)"></textarea>
            <div class="button-container">
                <button onclick="convertHex()">Convert to Wireshark Format</button>
                <button onclick="showExportDialog()">Export PCAP</button>
            </div>
        </div>
        
        <div class="output-container">
            <div class="title">Wireshark Compatible Format:</div>
            <textarea id="outputHex" readonly></textarea>
        </div>
    </div>

    <!-- Export Dialog -->
    <div id="exportDialog" class="modal">
        <div class="modal-content">
            <h2>Export PCAP</h2>
            <div>
                <label for="filename">FileName:</label>
                <input type="text" id="filename" value="packet.pcap">
            </div>
            <div class="modal-buttons">
                <button onclick="closeExportDialog()">Cancel</button>
                <button onclick="confirmExport()">Export</button>
            </div>
        </div>
    </div>

    <script>
        const MAC_IP_HEADER_LENGTH = 34;  // MAC + IP header length (不含UDP头)
        const UDP_HEADER_LENGTH = 8;      // UDP header length
        
        function getHeaderTemplate() {
            return [
                'FA', '16', '3E', '3C', 'F5', '10', 'FA', '16',
                '3E', '9F', 'B1', 'B4', '08', '00', '45', '00',
                '00', '00', // IP总长度 (占位)
                '15', '81', '00', '00', '80', '11',
                '00', '00', // IP校验和 (占位)
                '65', '26', '03', '6F', '65', '26',
                '03', 'D7', '08', '4B', '08', '4B',
                '00', '00', // UDP长度 (占位)
                '00', '00'  // UDP校验和 (占位,可选)
            ];
        }

        function calculateIPChecksum(header) {
            let sum = 0;
            for (let i = 14; i < 34; i += 2) {
                const value = (parseInt(header[i], 16) << 8) + parseInt(header[i + 1], 16);
                sum += value;
            }
            
            while (sum >> 16) {
                sum = (sum & 0xFFFF) + (sum >> 16);
            }
            
            return (~sum & 0xFFFF).toString(16).padStart(4, '0');
        }

        function processHexInput(input) {
            const cleaned = input.replace(/[\s\n\r]+/g, '').toUpperCase();
            const bytes = [];
            for (let i = 0; i < cleaned.length; i += 2) {
                bytes.push(cleaned.substr(i, 2));
            }
            return bytes;
        }

        function updateHeaders(headers, dataLength) {
            // 更新IP总长度 (IP头20字节 + UDP头8字节 + 数据长度)
            const ipTotalLength = (20 + 8 + dataLength).toString(16).padStart(4, '0');
            headers[16] = ipTotalLength.slice(0, 2);
            headers[17] = ipTotalLength.slice(2, 4);
            
            // 更新UDP长度 (UDP头8字节 + 数据长度)
            const udpLength = (8 + dataLength).toString(16).padStart(4, '0');
            headers[38] = udpLength.slice(0, 2);
            headers[39] = udpLength.slice(2, 4);
            
            // 更新IP校验和
            const ipChecksum = calculateIPChecksum(headers);
            headers[24] = ipChecksum.slice(0, 2);
            headers[25] = ipChecksum.slice(2, 4);
            
            return headers;
        }

        function convertHex() {
            let input = document.getElementById('inputHex').value;
            let bytes = processHexInput(input);
            
            if (document.getElementById('addHeaders').checked) {
                let headers = getHeaderTemplate();
                headers = updateHeaders(headers, bytes.length);
                bytes = [...headers, ...bytes];
            }
            
            let output = '';
            for (let i = 0; i < bytes.length; i += 16) {
                const offset = (i).toString(16).padStart(4, '0');
                output += offset + '   ';
                
                const lineBytes = bytes.slice(i, i + 16);
                output += lineBytes.join(' ');
                
                if (lineBytes.length < 16) {
                    output += '   '.repeat(16 - lineBytes.length);
                }
                
                output += '\n';
            }
            
            document.getElementById('outputHex').value = output;
        }

        function createPcapHeader() {
            const buffer = new ArrayBuffer(24);
            const view = new DataView(buffer);
            
            view.setUint32(0, 0xa1b2c3d4, false);
            view.setUint16(4, 2, false);
            view.setUint16(6, 4, false);
            view.setInt32(8, 0, false);
            view.setUint32(12, 0, false);
            view.setUint32(16, 65535, false);
            view.setUint32(20, 1, false);
            
            return new Uint8Array(buffer);
        }

        function createPacketHeader(length) {
            const buffer = new ArrayBuffer(16);
            const view = new DataView(buffer);
            const now = Math.floor(Date.now() / 1000);
            
            view.setUint32(0, now, false);
            view.setUint32(4, 0, false);
            view.setUint32(8, length, false);
            view.setUint32(12, length, false);
            
            return new Uint8Array(buffer);
        }

        function showExportDialog() {
            const now = new Date();
            const year = now.getFullYear();
            const month = String(now.getMonth() + 1).padStart(2, '0');
            const day = String(now.getDate()).padStart(2, '0');
            const hours = String(now.getHours()).padStart(2, '0');
            const minutes = String(now.getMinutes()).padStart(2, '0');
            
            const defaultFilename = `packet-${year}-${month}-${day}-${hours}-${minutes}.pcap`;
            document.getElementById('filename').value = defaultFilename;
            document.getElementById('exportDialog').style.display = 'block';
        }

        function closeExportDialog() {
            document.getElementById('exportDialog').style.display = 'none';
        }

        function confirmExport() {
            let filename = document.getElementById('filename').value.trim();
            if (!filename.endsWith('.pcap')) {
                filename += '.pcap';
            }
            exportPcap(filename);
            closeExportDialog();
        }

        function exportPcap(filename) {
            let input = document.getElementById('inputHex').value;
            let hexBytes = processHexInput(input);
            
            if (document.getElementById('addHeaders').checked) {
                let headers = getHeaderTemplate();
                headers = updateHeaders(headers, hexBytes.length);
                hexBytes = [...headers, ...hexBytes];
            }
            
            const packetData = new Uint8Array(hexBytes.map(hex => parseInt(hex, 16)));
            const pcapHeader = createPcapHeader();
            const packetHeader = createPacketHeader(packetData.length);
            
            const finalBuffer = new Uint8Array([
                ...pcapHeader,
                ...packetHeader,
                ...packetData
            ]);
            
            const blob = new Blob([finalBuffer], { type: 'application/octet-stream' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        }

        window.onload = function() {
            document.getElementById('inputHex').value = '';
        }
    </script>
</body>
</html>
相关推荐
deli0071 小时前
手机拍灯为什么有一条条黑纹?PWM 240Hz 实测
前端
wangruofeng6 小时前
AI Models 1.1.0 发布,查大模型价格、对比模型和找 API 地址
前端·aigc·ai编程
wangruofeng6 小时前
给前端开发者的 100 个设计参考网站
前端·设计
星栈9 小时前
视频即代码,Remotion 能做什么、不能做什么
前端·音视频开发
广州华水科技9 小时前
单北斗GNSS位移监测系统的应用与技术解析
前端
用户47949283569159 小时前
从一句话或代码仓到可交互 HTML:开源 Agent 画图技能 Archify v3.2 实测与选型边界
前端·后端·面试
风骏时光牛马12 小时前
生成一个AI多模态标题,仅输出一条标题,无额外文字、问句、备选、配图、文案等任何附加内容,不需要其他的任务服务 只要标题。
前端
wangruofeng12 小时前
从 GIF 到 AVIF:网页图片格式演进与 2026 年选型指南
前端·架构
清晨Coding13 小时前
为什么用 React Developer Tools 查看组件树这么乱?
前端·react.js
鶴哥只手遮天13 小时前
《Electron 45.2.3 内核解剖》第 4 章 补丁体系:380 个补丁改了什么
前端·electron