Files
2026-08-27 11:22:37 -06:00

249 lines
11 KiB
Python

"""
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()