hitl_login.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. """Human-in-the-loop login: automate fill+click, user drags slider.
  2. Steps:
  3. 1. (capture running in background)
  4. 2. CDP: fill credentials + click login -> geetest pops up
  5. 3. Bring the 996box window to foreground (user drags slider)
  6. 4. Poll up to 240s: cq2.exe spawn? gateway connections? capture lines?
  7. 5. On traffic: report and exit (analysis happens after)
  8. """
  9. import asyncio
  10. import json
  11. import subprocess
  12. import sys
  13. import time
  14. import httpx
  15. import websockets
  16. USERNAME = "longwu988"
  17. PASSWORD = "Longwu1988"
  18. CDP = "http://127.0.0.1:9222"
  19. async def get_targets():
  20. async with httpx.AsyncClient(timeout=5) as c:
  21. r = await c.get(f"{CDP}/json/list")
  22. return r.json()
  23. async def cdp_eval(ws, expr, msg_id):
  24. await ws.send(json.dumps({"id": msg_id, "method": "Runtime.evaluate",
  25. "params": {"expression": expr, "returnByValue": True}}))
  26. while True:
  27. msg = json.loads(await ws.recv())
  28. if msg.get("id") == msg_id:
  29. return msg.get("result", {}).get("result", {}).get("value")
  30. def cq2_pid():
  31. try:
  32. out = subprocess.run(
  33. ["powershell", "-NoProfile", "-Command",
  34. "Get-Process cq2 -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Id"],
  35. capture_output=True, text=True, timeout=5)
  36. return out.stdout.strip() or None
  37. except Exception:
  38. return None
  39. def gateway_conns():
  40. try:
  41. out = subprocess.run(
  42. ["powershell", "-NoProfile", "-Command",
  43. "(netstat -ano | Select-String '47.118.|:12000' | Measure-Object).Count"],
  44. capture_output=True, text=True, timeout=5)
  45. return int(out.stdout.strip() or 0)
  46. except Exception:
  47. return 0
  48. def capture_lines():
  49. try:
  50. out = subprocess.run(
  51. ["powershell", "-NoProfile", "-Command",
  52. "(Get-Content 'D:\\传奇\\cq2_sdk\\real_login.jsonl' -ErrorAction SilentlyContinue | Measure-Object).Count"],
  53. capture_output=True, text=True, timeout=5)
  54. return int(out.stdout.strip() or 0)
  55. except Exception:
  56. return 0
  57. def focus_996box():
  58. subprocess.run(
  59. ["powershell", "-NoProfile", "-Command",
  60. "$p = Get-Process 996box -ErrorAction SilentlyContinue | Where-Object { $_.MainWindowTitle } | Select-Object -First 1;"
  61. "if ($p) { Add-Type '@\nusing System;using System.Runtime.InteropServices;\npublic class F{[DllImport(\"user32.dll\")] public static extern bool SetForegroundWindow(IntPtr h);}\n@'; [F]::SetForegroundWindow($p.MainWindowHandle) | Out-Null; 'focused: ' + $p.MainWindowTitle }"],
  62. capture_output=True, text=True, timeout=10)
  63. async def main():
  64. targets = await get_targets()
  65. login = next((t for t in targets if "login" in t.get("url", "") and t["type"] == "page"), None)
  66. if not login:
  67. print("no login page. Targets:")
  68. for t in targets:
  69. print(f" [{t['type']}] {t['url'][:70]}")
  70. return 1
  71. async with websockets.connect(login["webSocketDebuggerUrl"], max_size=10**7) as ws:
  72. # fill + click
  73. r = await cdp_eval(ws, f"""
  74. (function() {{
  75. const set = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
  76. const u = document.querySelector('#username');
  77. const p = document.querySelector('#password');
  78. if (!u || !p) return 'missing fields';
  79. set.call(u, '{USERNAME}'); u.dispatchEvent(new Event('input', {{bubbles:true}}));
  80. set.call(p, '{PASSWORD}'); p.dispatchEvent(new Event('input', {{bubbles:true}}));
  81. const btn = document.querySelector('button.ant-btn.login-btn, button.login-btn, button.ant-btn');
  82. if (!btn) return 'no button';
  83. btn.click();
  84. return 'clicked';
  85. }})()
  86. """, 1)
  87. print(f"fill+click: {r}")
  88. await asyncio.sleep(2.5)
  89. gt = await cdp_eval(ws, "!!document.querySelector('.geetest_box, [class*=geetest]')", 2)
  90. print(f"geetest visible: {gt}")
  91. # focus the window for the user
  92. focus_996box()
  93. print()
  94. print("*** 滑块已弹出 — 请在 996box 窗口里拖动滑块完成验证 ***")
  95. print("*** 后台抓包运行中,登录成功后自动进游戏,无需其他操作 ***")
  96. print()
  97. # monitor
  98. t0 = time.time()
  99. deadline = t0 + 300
  100. seen_traffic = False
  101. while time.time() < deadline:
  102. await asyncio.sleep(5)
  103. el = int(time.time() - t0)
  104. pid = cq2_pid()
  105. conns = gateway_conns()
  106. lines = capture_lines()
  107. flags = []
  108. if pid:
  109. flags.append(f"CQ2={pid}")
  110. if conns:
  111. flags.append(f"conns={conns}")
  112. if lines:
  113. flags.append(f"cap={lines}")
  114. print(f" t+{el:>3}s {' '.join(flags) or '...waiting'}")
  115. if lines > 0:
  116. seen_traffic = True
  117. # let the login frames accumulate
  118. if lines >= 3 or (lines >= 1 and el > 60):
  119. break
  120. if pid and conns and el > 30:
  121. break
  122. print()
  123. if seen_traffic:
  124. print("*** CAPTURED REAL TRAFFIC — analyze real_login.jsonl now ***")
  125. return 0
  126. print("no traffic captured within window")
  127. return 1
  128. sys.exit(asyncio.run(main()))