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