Telemetria i reakcje mechanizmów obronnych VM
Raportuj wykrycia mechanizmów obronnych VM do swojego backendu za pomocą vmDefenseHook i dostosuj reakcję na każdą kategorię wykryć przy użyciu vmDefenseReaction — od wersji wyłącznie telemetrycznej, która niczego nie przerywa, po twarde przerwanie działania na skradzionej paczce.
Problem
Mechanizmy obronne VM — vmSelfDefending, vmDebugProtection i vmDomainLock — działają lokalnie: gdy zostanie wykryty debugger, narzędzie automatyzujące, zmodyfikowane środowisko lub nieautoryzowana domena, chroniony kod przerywa działanie albo po cichu zatruwa własne wyniki. To zatrzymuje atakującego, ale domyślnie nigdy się o tym nie dowiesz. Nie sposób stwierdzić, jak często paczka jest sondowana, który detektor zadziałał ani czy mechanizm obronny nie przerywa działania legalnemu użytkownikowi.
Od wersji v7.1.0 tę lukę wypełniają dwie opcje. Żadna z nich nie włącza mechanizmów obronnych — jedynie obserwują i sterują tymi, które zostały już włączone:
vmDefenseHook— globalny callback otrzymujący obiekt sygnału za każdym razem, gdy mechanizm obronny coś wykryje. Służy do wysyłania telemetrii do backendu.vmDefenseReaction— mapa per kategoria, która wybiera, jak reaguje włączony mechanizm obronny: przerwać działanie, zatruć wyniki albo nie robić nic lokalnie.
Przepis 1 — raportowanie wykryć do backendu
Krok 1 — zarejestruj globalną funkcję hooka, zanim wczyta się zobfuskowana paczka
Środowisko uruchomieniowe VM i jego mechanizmy obronne działają przed chronionym programem, więc wiele wykryć następuje już podczas startu. Hook należy zdefiniować jako zwykłą zmienną globalną na stronie hosta, przed tagiem zobfuskowanego skryptu:
<script>
// In your page, BEFORE the obfuscated script:
window.__vmDetection = function (signal) {
navigator.sendBeacon('/api/vm-defense', JSON.stringify(signal));
};
</script>
<script src="/app.obfuscated.js"></script>
Krok 2 — wskaż go w vmDefenseHook
Opcja jest obiektem, w którym name to nazwa globalnej funkcji do wywołania (aliases jest opcjonalne — patrz niżej):
JavaScriptObfuscator.obfuscate(source, {
vmObfuscation: true,
// the hook alone enables nothing - a defense must be on for detectors to run:
vmSelfDefending: true,
vmDebugProtection: true,
vmDefenseHook: { name: '__vmDetection' }
});
Forma z samym łańcuchem znaków (vmDefenseHook: '__vmDetection') jest nadal akceptowana jako skrót od { name: '__vmDetection' }, ale jest przestarzała — lepiej używać formy obiektowej.
W panelu pole VM Defense Hook pojawia się w sekcji opcji VM, gdy włączony jest co najmniej jeden mechanizm obronny (vmSelfDefending, vmDebugProtection lub vmDomainLock).
Krok 3 — odbierz sygnał na backendzie
Każde wykrycie wywołuje hook z jednym obiektem signal:
source— konkretny detektor:headless,node,agent,domain,debugger,sandbox,nativeHook,timinglubintegrity. Od wersji v7.4.0 dawne detektoryenviinspectorraportują jakosource: 'debugger'.category—automation,debugger,sandbox,domain,tamperlubintegrity. Źródłonoderaportuje jakocategory: 'debugger'(v7.4.0+).score,threshold— wynik wykrycia i przekroczony próg
Minimalny endpoint odbierający (pokazano Express; zadziała dowolny backend przyjmujący POST). Normalizuje ciało żądania do tablicy, dzięki czemu obsługuje też formę zbiorczą wysyłaną przez opisany niżej wzorzec z buforem:
app.post('/api/vm-defense', express.text({ type: '*/*' }), (req, res) => {
// a signal: { source: 'headless', category: 'automation', score: 7, threshold: 4 }
const signals = [].concat(JSON.parse(req.body));
for (const signal of signals) {
console.warn('vm-defense', { ...signal, ip: req.ip, ua: req.get('user-agent') });
}
res.sendStatus(204);
});
Zmiana nazw pól sygnału (aliasy) v7.4.0+
Domyślne wartości source / category to nazwy opisowe, więc każdy, kto podepnie się pod callback (lub przeczyta kod wynikowy), rozpozna zabezpieczenie i to, który detektor zadziałał. aliases zmienia nazwy pól sygnału na wybrane nieprzejrzyste tokeny, stosowane wewnątrz VM przed wyemitowaniem sygnału, dzięki czemu te nazwy nigdy nie pojawiają się w kodzie wynikowym ani nie docierają do callbacku. Aplikacja zna własne mapowanie i przekazuje tokeny do backendu.
Aliasy działają per pole: każde przyjmuje key (nazwę właściwości, którą otrzyma callback); tekstowe pola nazw source i category przyjmują dodatkowo mapę values, natomiast score / threshold są liczbami i przyjmują wyłącznie key. Nieustawione wpisy zachowują nazwy domyślne.
vmDefenseHook: {
name: '__vmDetection',
aliases: {
source: { key: 'a8Qm', values: { headless: 'xP4m9Q' } },
category: { key: 'p3Tx', values: { automation: 'bQ7s1M' } },
score: { key: 's1' },
threshold: { key: 't1' }
}
// the callback now receives e.g. { a8Qm: 'xP4m9Q', p3Tx: 'bQ7s1M', s1: <score>, t1: <threshold> }
}
W panelu sekcja Aliasy sygnałów znajduje się pod polem VM Defense Hook.
Przepis 2 — dostosowanie domyślnych reakcji
vmDefenseReaction konfiguruje sposób reakcji każdej kategorii wykryć. Niczego nie włącza — same mechanizmy obronne uruchamiają vmSelfDefending, vmDebugProtection i vmDomainLock; ta opcja wybiera jedynie, jak reaguje włączony mechanizm obronny. Jednostką kontroli jest kategoria: każdy detektor w danej kategorii wykonuje reakcję tej kategorii, a reakcja ustawiona dla kategorii, której opcja jest wyłączona, po prostu nie ma efektu.
| Kategoria | Włączana przez | Reaguje, gdy |
|---|---|---|
automation | vmSelfDefending lub vmDebugProtection | Kodem steruje oprogramowanie, a nie człowiek: przeglądarka headless lub automatyzowana, framework do scrapowania / testowania albo agent AI do kodowania przechodzący krok po kroku przez stronę. |
debugger | vmDebugProtection lub vmSelfDefending | Ktoś ma otwarty debugger lub inspektor narzędzi deweloperskich przeglądarki i krok po kroku analizuje działający kod, aby go zrozumieć. |
sandbox | vmDebugProtection | Kod w ogóle nie działa w prawdziwej przeglądarce — został przeniesiony do emulowanego lub skryptowego środowiska JavaScript, aby uruchomić go i zbadać offline. |
domain | vmDomainLock | Kod działa w witrynie, która nie została autoryzowana: na hoście spoza listy dozwolonych vmDomainLock (na przykład paczka skopiowana na cudzą domenę). |
tamper | vmSelfDefending | Środowisko JavaScript wokół VM zostało zmodyfikowane, aby ją obserwować lub przejąć — na przykład natywne mechanizmy wbudowane przeglądarki podmieniono na wersje z instrumentacją. |
integrity | vmSelfDefending | Własny kod chronionej paczki został zmieniony lub załatany od czasu jej wygenerowania. |
Klucze to sześć powyższych nazw kategorii lub default (wartość zapasowa dla kategorii nieokreślonych). Wartości to:
break— natychmiast przerwać działaniedecoy— kontynuować działanie na zatrutym stanie, po cichu produkując błędne wynikinone— nie robić nic lokalnie (wyłącznie telemetria)
Kategoria, która nie została ustawiona, wraca do wbudowanych wartości domyślnych:
// built-in defaults
vmDefenseReaction: {
automation: 'break',
debugger: 'decoy',
sandbox: 'decoy',
domain: 'break',
tamper: 'break',
integrity: 'break'
}
default obejmuje każdą kategorię, w tym te poprawne z założenia (integrity, tamper), więc { default: 'none' } daje naprawdę nieprzerywającą działania wersję wyłącznie z telemetrią:
vmDefenseReaction: { default: 'none' } // never break - pair with vmDefenseHook
vmDefenseReaction: { automation: 'none' } // tolerate automation FPs; the rest keep their defaults (a bad domain still breaks)
W panelu listy wyboru VM Defense Reactions pojawiają się w sekcji opcji VM po włączeniu mechanizmu obronnego; każda kategoria jest edytowalna tylko wtedy, gdy włączony jest mechanizm obronny emitujący jej detektory.
Od telemetrii do egzekwowania
Nie trzeba już pierwszego dnia wybierać między widocznością a egzekwowaniem. Mechanizmy obronne warto wdrożyć w dwóch wersjach: najpierw takiej, która tylko raportuje, a potem — gdy telemetria wygląda czysto — takiej, która reaguje.
Krok 1 — wypuść wersję wyłącznie obserwującą
Włącz wszystkie mechanizmy obronne, których planujesz używać, wskaż w vmDefenseHook swój endpoint i wyłącz wszystkie reakcje. Każdy detektor nadal działa i raportuje każde trafienie do backendu — po prostu niczego nie przerywa:
JavaScriptObfuscator.obfuscate(source, {
vmObfuscation: true,
vmSelfDefending: true,
vmDebugProtection: true,
vmDomainLock: ['example.com'],
vmDefenseHook: '__vmDetection',
vmDefenseReaction: { default: 'none' } // observe only
});
Krok 2 — przejrzyj zebrane sygnały
Gdy wersja zobaczy już prawdziwy ruch, poszukaj wykryć wywołanych przez legalne użycie. Dwa najczęstsze:
- trafienia
automationz własnych testów end-to-end lub monitoringu dostępności — te artefakty należy budować bez mechanizmów obronnych, zamiast tolerować całą kategorię na produkcji. - trafienia
domainz hosta staging lub podglądowego, którego nie uwzględniono na liście dozwolonychvmDomainLock— należy dodać ten host.
Lepiej usunąć przyczynę, niż złagodzić reakcję: każda kategoria pozostawiona na none to detektor, o który atakujący nie musi się już martwić.
Krok 3 — włącz reakcje
Wystarczy usunąć nadpisanie default: 'none', aby zaczęły obowiązywać wbudowane reakcje per kategoria — cała zmiana to ta jedna linia. Jeśli któraś kategoria nadal generuje fałszywe alarmy, których nie da się wyeliminować, należy pozostawić na none tylko ją (np. vmDefenseReaction: { automation: 'none' }), a resztę egzekwować.
