""" AnyDesk Process Recon Enumerates loaded modules, TLS-related exports, and interesting functions. Run this first to understand AnyDesk's internals. """ import frida import sys import json import time AGENT_CODE = r""" 'use strict'; // ============================================================ // Phase 1: Enumerate all loaded modules // ============================================================ function enumModules() { const mods = Process.enumerateModules(); const result = []; for (const m of mods) { result.push({ name: m.name, base: m.base.toString(), size: m.size, path: m.path }); } return result; } // ============================================================ // Phase 2: Find TLS/crypto related exports across all modules // ============================================================ function findTlsExports() { const patterns = [ 'SSL_read', 'SSL_write', 'SSL_new', 'SSL_connect', 'SSL_accept', 'SSL_free', 'SSL_CTX_new', 'EncryptMessage', 'DecryptMessage', 'SslEncryptPacket', 'SslDecryptPacket', 'BCryptEncrypt', 'BCryptDecrypt', 'CryptEncrypt', 'CryptDecrypt', 'mbedtls_ssl_read', 'mbedtls_ssl_write', // Winsock 'send', 'recv', 'WSASend', 'WSARecv', 'sendto', 'recvfrom', // File operations (for file transfer) 'CreateFileW', 'CreateFileA', 'WriteFile', 'ReadFile', 'MoveFileW', 'MoveFileExW', 'CopyFileW', 'CreateDirectoryW', 'RemoveDirectoryW', // Memory alloc (for heap overflow detection) 'HeapAlloc', 'HeapFree', 'HeapReAlloc', 'VirtualAlloc', 'VirtualFree', 'malloc', 'free', 'realloc', 'calloc', // Image/bitmap 'CreateDIBSection', 'SetDIBits', 'GetDIBits', 'CreateBitmap', 'CreateCompatibleBitmap', // Clipboard 'SetClipboardData', 'GetClipboardData', 'OpenClipboard', 'CloseClipboard', 'EmptyClipboard', ]; const found = []; const mods = Process.enumerateModules(); for (const m of mods) { try { const exports = m.enumerateExports(); for (const exp of exports) { for (const pat of patterns) { if (exp.name && exp.name.includes(pat)) { found.push({ module: m.name, name: exp.name, address: exp.address.toString(), type: exp.type }); } } } } catch (e) { // Some modules can't be enumerated } } return found; } // ============================================================ // Phase 3: Look for AnyDesk-specific strings in memory // ============================================================ function findInterestingStrings() { const mainModule = Process.enumerateModules()[0]; // AnyDesk.exe const found = []; const patterns = [ 'DeskRT', 'file_transfer', 'FileTransfer', 'clipboard', 'Clipboard', 'identity', 'Identity', 'user_image', 'UserImage', 'avatar', 'codec', 'Codec', 'decompress', 'Decompress', 'decode_frame', 'DecodeFrame', 'bitmap', 'Bitmap', 'PNG', 'png_decode', 'traversal', 'path_sanitize', 'sanitize', 'validate_path', 'ad.security', 'ssl_ctx', 'TLS', 'handshake', 'Handshake', ]; for (const pat of patterns) { try { const matches = Memory.scanSync(mainModule.base, mainModule.size, stringToPattern(pat)); if (matches.length > 0) { found.push({ pattern: pat, count: matches.length, first_addr: matches[0].address.toString(), // Read surrounding context context: safeReadUtf8(matches[0].address.sub(16), 64) }); } } catch (e) { // Scan failed for this pattern } } return found; } function stringToPattern(str) { let pat = ''; for (let i = 0; i < str.length; i++) { if (i > 0) pat += ' '; pat += str.charCodeAt(i).toString(16).padStart(2, '0'); } return pat; } function safeReadUtf8(addr, len) { try { const buf = addr.readByteArray(len); if (!buf) return ''; const arr = new Uint8Array(buf); let s = ''; for (let i = 0; i < arr.length; i++) { const c = arr[i]; if (c >= 0x20 && c < 0x7f) s += String.fromCharCode(c); else s += '.'; } return s; } catch (e) { return ''; } } // ============================================================ // Phase 4: Scan for OpenSSL-style function tables // ============================================================ function findOpenSSLIndicators() { const mods = Process.enumerateModules(); const indicators = []; for (const m of mods) { const nameLower = m.name.toLowerCase(); if (nameLower.includes('ssl') || nameLower.includes('crypto') || nameLower.includes('tls') || nameLower.includes('openssl') || nameLower.includes('mbedtls') || nameLower.includes('boringssl') || nameLower.includes('libcrypto') || nameLower.includes('libssl') || nameLower.includes('schannel') || nameLower.includes('ncrypt') || nameLower.includes('bcrypt') || nameLower.includes('sspicli')) { indicators.push({ name: m.name, base: m.base.toString(), size: m.size, path: m.path }); } } return indicators; } // ============================================================ // Run all phases // ============================================================ const report = {}; send({type: 'status', msg: 'Phase 1: Enumerating modules...'}); report.modules = enumModules(); send({type: 'status', msg: `Found ${report.modules.length} modules`}); send({type: 'status', msg: 'Phase 2: Finding TLS/crypto exports...'}); report.tls_exports = findTlsExports(); send({type: 'status', msg: `Found ${report.tls_exports.length} interesting exports`}); send({type: 'status', msg: 'Phase 3: Scanning for interesting strings...'}); report.strings = findInterestingStrings(); send({type: 'status', msg: `Found ${report.strings.length} string patterns`}); send({type: 'status', msg: 'Phase 4: Looking for TLS library indicators...'}); report.tls_indicators = findOpenSSLIndicators(); send({type: 'status', msg: `Found ${report.tls_indicators.length} TLS-related modules`}); send({type: 'result', data: report}); """ def on_message(message, data): if message['type'] == 'send': payload = message['payload'] if payload.get('type') == 'status': print(f" [*] {payload['msg']}") elif payload.get('type') == 'result': global result_data result_data = payload['data'] elif message['type'] == 'error': print(f" [!] ERROR: {message['description']}") def find_anydesk_pid(): """Find AnyDesk process ID""" import subprocess try: output = subprocess.check_output( ['tasklist', '/FI', 'IMAGENAME eq AnyDesk.exe', '/FO', 'CSV', '/NH'], text=True, stderr=subprocess.DEVNULL ) for line in output.strip().split('\n'): if 'AnyDesk' in line: parts = line.strip().strip('"').split('","') if len(parts) >= 2: return int(parts[1].strip('"')) except: pass return None def main(): global result_data result_data = None print("=" * 60) print(" AnyDesk Process Recon") print("=" * 60) pid = find_anydesk_pid() if not pid: print("[!] AnyDesk.exe not found. Make sure it's running.") sys.exit(1) print(f"[+] Found AnyDesk.exe (PID: {pid})") print(f"[+] Attaching Frida...") try: session = frida.attach(pid) except frida.ProcessNotFoundError: print("[!] Could not attach to AnyDesk. Run as Administrator.") sys.exit(1) except Exception as e: print(f"[!] Frida attach failed: {e}") print("[!] Make sure to run as Administrator.") sys.exit(1) print("[+] Injecting recon agent...") script = session.create_script(AGENT_CODE, runtime='v8') script.on('message', on_message) script.load() # Wait for results timeout = 30 for i in range(timeout * 10): if result_data is not None: break time.sleep(0.1) if result_data is None: print("[!] Timed out waiting for results") session.detach() sys.exit(1) # Save and display results output_path = "recon_results.json" with open(output_path, 'w') as f: json.dump(result_data, f, indent=2) print(f"\n[+] Full results saved to {output_path}") # Summary print(f"\n{'=' * 60}") print(" SUMMARY") print(f"{'=' * 60}") print(f"\n[Modules] {len(result_data['modules'])} loaded") for m in result_data['modules'][:10]: print(f" {m['name']:30s} @ {m['base']} ({m['size']:>10,} bytes)") if len(result_data['modules']) > 10: print(f" ... and {len(result_data['modules'])-10} more (see JSON)") print(f"\n[TLS/Crypto Related Modules]") if result_data['tls_indicators']: for m in result_data['tls_indicators']: print(f" {m['name']:30s} @ {m['base']} {m['path']}") else: print(" None found (AnyDesk may use statically linked TLS)") print(f"\n[Interesting Exports] {len(result_data['tls_exports'])} found") # Group by category tls_funcs = [e for e in result_data['tls_exports'] if any(k in e['name'] for k in ['SSL_', 'Encrypt', 'Decrypt', 'mbedtls', 'Crypt'])] net_funcs = [e for e in result_data['tls_exports'] if any(k in e['name'] for k in ['send', 'recv', 'WSA'])] file_funcs = [e for e in result_data['tls_exports'] if any(k in e['name'] for k in ['CreateFile', 'WriteFile', 'ReadFile', 'MoveFile', 'CopyFile', 'Directory'])] clip_funcs = [e for e in result_data['tls_exports'] if 'Clipboard' in e['name'] or 'clipboard' in e['name']] img_funcs = [e for e in result_data['tls_exports'] if any(k in e['name'] for k in ['DIB', 'Bitmap', 'bitmap'])] if tls_funcs: print(f"\n TLS/Crypto ({len(tls_funcs)}):") for e in tls_funcs[:15]: print(f" {e['module']:20s} :: {e['name']:40s} @ {e['address']}") if net_funcs: print(f"\n Network ({len(net_funcs)}):") for e in net_funcs[:10]: print(f" {e['module']:20s} :: {e['name']:40s} @ {e['address']}") if file_funcs: print(f"\n File I/O ({len(file_funcs)}):") for e in file_funcs[:10]: print(f" {e['module']:20s} :: {e['name']:40s} @ {e['address']}") if clip_funcs: print(f"\n Clipboard ({len(clip_funcs)}):") for e in clip_funcs: print(f" {e['module']:20s} :: {e['name']:40s} @ {e['address']}") if img_funcs: print(f"\n Image/Bitmap ({len(img_funcs)}):") for e in img_funcs: print(f" {e['module']:20s} :: {e['name']:40s} @ {e['address']}") print(f"\n[String Patterns in AnyDesk.exe]") if result_data['strings']: for s in result_data['strings']: print(f" '{s['pattern']}' — {s['count']} hits, first @ {s['first_addr']}") if s['context']: print(f" context: {s['context']}") else: print(" No patterns found in main module") session.detach() print(f"\n[+] Done. Review {output_path} for full details.") print("[+] Next step: run 02_sniffer.py to capture protocol traffic") if __name__ == '__main__': main()