""" Standalone fake server + DNS redirect. Stays running until you press Ctrl+C. Launch Remcos/KeyGen yourself while this is running. Run as Administrator! """ import http.server import os import socket import socketserver import ssl import subprocess import sys import threading import time from datetime import datetime from urllib.parse import urlparse, parse_qs VERSION = "7.2.2" SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) CERT_DIR = os.path.join(SCRIPT_DIR, "certs") CA_CERT = os.path.join(CERT_DIR, "ca.crt") CA_KEY = os.path.join(CERT_DIR, "ca.key") SRV_CERT = os.path.join(CERT_DIR, "server.crt") SRV_KEY = os.path.join(CERT_DIR, "server.key") NRPT_BASE = r"HKLM:\System\CurrentControlSet\Services\Dnscache\Parameters\DnsPolicyConfig" NRPT_GUIDS = [ ("{b1a2c3d4-0001-aaaa-bbbb-000000000001}", [".breakingsec.io", "breakingsec.io"]), ("{b1a2c3d4-0001-aaaa-bbbb-000000000002}", [".ip-api.com", "pro.ip-api.com", "ip-api.com"]), ("{b1a2c3d4-0001-aaaa-bbbb-000000000003}", [".breakingsecurity.net", "breakingsecurity.net"]), ] REDIRECT_DOMAINS = {"breakingsec.io", "www.breakingsec.io", "breakingsecurity.net", "www.breakingsecurity.net", "pro.ip-api.com"} REAL_DNS = "8.8.8.8" GEO_RESPONSE = "\n".join([ "success", "US", "United States", "NA", "North America", "CA", "California", "Los Angeles", "Los Angeles", "90001", "34.0522", "-118.2437", "America/Los_Angeles", "ISP", "ISP Corp", "AS0000", "1.2.3.4", ]) # ============================================================ # SSL Certs # ============================================================ def generate_certs(): os.makedirs(CERT_DIR, exist_ok=True) if os.path.exists(SRV_CERT) and os.path.exists(CA_CERT): print("[*] Certs exist, reusing.") return True print("[*] Generating SSL certificates...") try: subprocess.run(["openssl", "req", "-x509", "-newkey", "rsa:2048", "-nodes", "-keyout", CA_KEY, "-out", CA_CERT, "-days", "3650", "-subj", "/CN=Remcos Test CA"], capture_output=True, check=True) csr = os.path.join(CERT_DIR, "server.csr") ext = os.path.join(CERT_DIR, "ext.cnf") with open(ext, "w") as f: f.write("[v3_req]\nsubjectAltName=DNS:breakingsec.io,DNS:*.breakingsec.io," "DNS:breakingsecurity.net,DNS:*.breakingsecurity.net," "DNS:pro.ip-api.com,DNS:*.ip-api.com,DNS:localhost\n" "basicConstraints=CA:FALSE\nkeyUsage=digitalSignature,keyEncipherment\n" "extendedKeyUsage=serverAuth\n") subprocess.run(["openssl", "req", "-newkey", "rsa:2048", "-nodes", "-keyout", SRV_KEY, "-out", csr, "-subj", "/CN=breakingsec.io"], capture_output=True, check=True) subprocess.run(["openssl", "x509", "-req", "-in", csr, "-CA", CA_CERT, "-CAkey", CA_KEY, "-CAcreateserial", "-out", SRV_CERT, "-days", "3650", "-extfile", ext, "-extensions", "v3_req"], capture_output=True, check=True) print("[+] Certificates generated.") return True except Exception as e: print(f"[-] Cert generation failed: {e}") return False def install_ca(): r = subprocess.run(["certutil", "-addstore", "-f", "Root", CA_CERT], capture_output=True, text=True) if r.returncode == 0: print("[+] CA cert installed in trust store.") else: print(f"[-] CA install failed: {r.stderr[:100]}") def remove_ca(): subprocess.run(["certutil", "-delstore", "Root", "Remcos Test CA"], capture_output=True) # ============================================================ # NRPT # ============================================================ def setup_nrpt(): print("[*] Setting NRPT rules...") for guid, domains in NRPT_GUIDS: names_str = ",".join(f"'{d}'" for d in domains) ps = f""" $p = '{NRPT_BASE}\\{guid}' New-Item -Path $p -Force | Out-Null Set-ItemProperty -Path $p -Name 'Name' -Value @({names_str}) -Type MultiString Set-ItemProperty -Path $p -Name 'GenericDNSServers' -Value '127.0.0.1' -Type String Set-ItemProperty -Path $p -Name 'ConfigOptions' -Value 8 -Type DWord Set-ItemProperty -Path $p -Name 'Version' -Value 2 -Type DWord """ r = subprocess.run(["powershell", "-Command", ps], capture_output=True, text=True, timeout=10) if r.returncode == 0: print(f" [+] {', '.join(domains)} -> 127.0.0.1") else: print(f" [-] {domains[0]}: {r.stderr.strip()[:80]}") subprocess.run(["ipconfig", "/flushdns"], capture_output=True, timeout=10) subprocess.run(["powershell", "-Command", "Clear-DnsClientCache; Register-DnsClient"], capture_output=True, timeout=10) def cleanup_nrpt(): for guid, _ in NRPT_GUIDS: ps = f"Remove-Item -Path '{NRPT_BASE}\\{guid}' -Recurse -Force -ErrorAction SilentlyContinue" subprocess.run(["powershell", "-Command", ps], capture_output=True, timeout=10) subprocess.run(["ipconfig", "/flushdns"], capture_output=True, timeout=10) # ============================================================ # DNS Server # ============================================================ class DNSHandler(socketserver.BaseRequestHandler): def handle(self): data = self.request[0] sock = self.request[1] try: domain = self._parse_domain(data).lower().rstrip(".") redirect = any(domain == d or domain.endswith("." + d) for d in REDIRECT_DOMAINS) if redirect: resp = self._build_response(data, "127.0.0.1") sock.sendto(resp, self.client_address) else: fwd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) fwd.settimeout(3) fwd.sendto(data, (REAL_DNS, 53)) try: r, _ = fwd.recvfrom(4096) sock.sendto(r, self.client_address) except socket.timeout: pass fwd.close() except: pass def _parse_domain(self, data): parts, idx = [], 12 while idx < len(data): l = data[idx] if l == 0: break idx += 1 parts.append(data[idx:idx+l].decode("ascii", errors="replace")) idx += l return ".".join(parts) def _build_response(self, query, ip): resp = bytearray(query[:2]) + b'\x81\x80' + query[4:6] + b'\x00\x01\x00\x00\x00\x00' idx = 12 while idx < len(query): if query[idx] == 0: idx += 5 break idx += 1 + query[idx] resp += query[12:idx] resp += b'\xc0\x0c\x00\x01\x00\x01\x00\x00\x0e\x10\x00\x04' resp += socket.inet_aton(ip) return bytes(resp) # ============================================================ # HTTP/HTTPS Server # ============================================================ class FakeHandler(http.server.BaseHTTPRequestHandler): def log_message(self, fmt, *args): ts = datetime.now().strftime("%H:%M:%S") print(f" [{ts}] {fmt % args}") def send_text(self, text, code=200): data = text.encode("utf-8") self.send_response(code) self.send_header("Content-Type", "text/plain") self.send_header("Content-Length", str(len(data))) self.send_header("Connection", "close") self.end_headers() self.wfile.write(data) def route(self): path = urlparse(self.path).path.lower() host = self.headers.get("Host", "") qs = parse_qs(urlparse(self.path).query) print(f" -> {self.command} {self.path} (Host: {host})") # Dump ALL headers for debugging for h_name, h_val in self.headers.items(): print(f" {h_name}: {h_val}") if "whitelist" in path: print(f" <- Whitelist: EMPTY") self.send_text("") return if "signalabuse" in path: print(f" <- Abuse absorbed") self.send_text("") return if "licpost" in path or ("keygen" in path and "lic" in path.lower()): lic_raw = qs.get("LIC", [""])[0] parts = lic_raw.split("|") print(f" ***** KeyGen LicPost! *****") print(f" Parts: {parts}") # Try response matching request format: pipe-delimited after "0\n" key = parts[0] if len(parts) > 0 else "" email = parts[1] if len(parts) > 1 else "" # Format: 0\nkey|email|product|expiry|type|version resp = f"0\n{key}|{email}|Remcos|20301231|Pro|7.2.2" print(f" <- Responding: '{resp}'") self.send_text(resp) return if "upd_pro" in path: print(f" <- Version: {VERSION}") self.send_text(VERSION + "\n") return # PeriodicCheck - license validation with hash + expiry if "periodiccheck" in path: lic = qs.get("LIC", ["?"])[0] resp = "49ef9592748fac8986f0d360454dbab0\n20301231\nRemcos" print(f" ***** PeriodicCheck (LIC={lic[:24]}) -> {repr(resp)} *****") self.send_text(resp) return # Auth/license check - sign with our private key if "auth" in path or "check" in path or "verify" in path or "validate" in path: print(f" ***** Auth endpoint: {self.path} *****") self.send_text("0\n20301231\nPro\n7.2.2") return if "onlinecheck" in path: print(f" <- OnlineCheck: {VERSION}") self.send_text(VERSION + "\n") return if "/line" in path or "ip-api" in host: print(f" <- GeoIP") self.send_text(GEO_RESPONSE + "\n") return print(f" *** UNKNOWN ENDPOINT: {self.path} ***") print(f" *** Host: {host} ***") # Return "0" instead of empty - empty might cause range check errors self.send_text("0") def do_GET(self): self.route() def do_POST(self): cl = int(self.headers.get("Content-Length", 0)) body = self.rfile.read(cl) if cl > 0 else b"" if body: try: print(f" POST body: {body[:300].decode('utf-8', errors='replace')}") except: print(f" POST body: {body[:100].hex()}") self.route() # ============================================================ # Main # ============================================================ def main(): print("=" * 60) print(" Fake Server (standalone) - stays running") print(" Launch Remcos/KeyGen yourself while this runs") print("=" * 60) if not ctypes.windll.shell32.IsUserAnAdmin(): print("[-] Need Administrator! Right-click -> Run as admin") return 1 # Setup generate_certs() install_ca() # CA bundle for OpenSSL apps ca_bundle = os.path.join(CERT_DIR, "ca_bundle.pem") try: import certifi system_ca = certifi.where() except ImportError: system_ca = None with open(ca_bundle, "w") as out: with open(CA_CERT) as f: out.write(f.read() + "\n") if system_ca and os.path.exists(system_ca): with open(system_ca) as f: out.write(f.read()) os.environ["SSL_CERT_FILE"] = ca_bundle os.environ["SSL_CERT_DIR"] = CERT_DIR # Start servers dns = socketserver.UDPServer(("127.0.0.1", 53), DNSHandler) threading.Thread(target=dns.serve_forever, daemon=True).start() print("[+] DNS on 127.0.0.1:53") http_srv = http.server.HTTPServer(("0.0.0.0", 80), FakeHandler) threading.Thread(target=http_srv.serve_forever, daemon=True).start() print("[+] HTTP on 0.0.0.0:80") try: class LoggingHTTPS(http.server.HTTPServer): def handle_error(self, request, client_address): print(f" [SSL ERROR] from {client_address}: {sys.exc_info()[1]}") https_srv = LoggingHTTPS(("0.0.0.0", 443), FakeHandler) ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) ctx.load_cert_chain(SRV_CERT, SRV_KEY) https_srv.socket = ctx.wrap_socket(https_srv.socket, server_side=True) threading.Thread(target=https_srv.serve_forever, daemon=True).start() print("[+] HTTPS on 0.0.0.0:443") except Exception as e: print(f"[-] HTTPS failed: {e}") # Port 45000 - Viotto packer license validation (raw TCP protocol!) def handle_viotto_client(conn, addr): print(f"\n *** PORT 45000 CONNECTION from {addr} ***") conn.settimeout(5) try: # Read whatever the client sends data = conn.recv(4096) print(f" *** RECEIVED ({len(data)} bytes): {data[:200].hex()}") try: print(f" *** AS TEXT: {data[:200].decode('ascii', errors='replace')}") except: pass # Echo back for now - we need to see the protocol first conn.sendall(data) print(f" *** Echoed {len(data)} bytes back") except socket.timeout: print(f" *** PORT 45000: recv timeout (client didn't send data)") except Exception as e: print(f" *** PORT 45000 error: {e}") finally: conn.close() def viotto_server(): srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) srv.bind(("0.0.0.0", 45000)) srv.listen(5) print("[+] Raw TCP on 0.0.0.0:45000 (Viotto license port!)") while True: conn, addr = srv.accept() threading.Thread(target=handle_viotto_client, args=(conn, addr), daemon=True).start() threading.Thread(target=viotto_server, daemon=True).start() # NRPT setup_nrpt() # Verify time.sleep(1) for domain in ["breakingsec.io", "breakingsecurity.net"]: try: result = socket.getaddrinfo(domain, 443) ip = result[0][4][0] status = "OK" if ip == "127.0.0.1" else f"WRONG ({ip})" print(f" {domain} -> {ip} [{status}]") except Exception as e: print(f" {domain} -> ERROR: {e}") print() print("=" * 60) print(" RUNNING. Now launch KeyGen.exe or Remcos manually.") print(" Press Ctrl+C to stop and clean up.") print("=" * 60) print() try: while True: time.sleep(1) except KeyboardInterrupt: pass finally: print("\n[*] Cleaning up...") cleanup_nrpt() print("[+] NRPT removed") remove_ca() print("[+] CA removed") import ctypes if __name__ == "__main__": sys.exit(main())