Telemetry và phản ứng phòng thủ VM
Báo cáo các lần phát hiện của phòng thủ VM về backend của bạn bằng vmDefenseHook, và điều chỉnh cách mỗi danh mục phát hiện phản ứng bằng vmDefenseReaction - từ một bản dựng chỉ đo lường, hoàn toàn không phá vỡ đến một bản dựng phá vỡ ngay lập tức trên một bundle bị đánh cắp.
Xem
Obfuscator.io Defense Reactions: Break, Decoy, and the VM Defense Hook
Vấn đề
Các cơ chế phòng thủ VM - vmSelfDefending, vmDebugProtection và vmDomainLock - hoạt động cục bộ: khi phát hiện một trình gỡ lỗi, công cụ tự động hóa, môi trường bị can thiệp, hoặc một miền không được phép, mã được bảo vệ sẽ tự phá vỡ hoặc âm thầm đầu độc chính kết quả của nó. Điều đó ngăn được kẻ tấn công, nhưng theo mặc định bạn không bao giờ hay biết về nó. Bạn không thể biết bundle của mình bị dò xét thường xuyên đến mức nào, bộ phát hiện nào đã kích hoạt, hay liệu một cơ chế phòng thủ có đang phá vỡ trải nghiệm của một người dùng hợp lệ hay không.
Hai tùy chọn lấp khoảng trống đó. Cả hai đều không bật bất kỳ cơ chế phòng thủ nào - chúng chỉ quan sát và điều hướng những cơ chế phòng thủ bạn đã bật sẵn:
vmDefenseHook- một hàm callback toàn cục nhận một đối tượng tín hiệu mỗi khi một cơ chế phòng thủ phát hiện điều gì đó. Dùng nó để gửi telemetry về backend của bạn.vmDefenseReaction- một ánh xạ theo từng danh mục để chọn cách một cơ chế phòng thủ đang bật phản ứng: phá vỡ, decoy, hoặc không làm gì cục bộ.
Cả hai tùy chọn đều được giới thiệu ở v7.1.0, nhưng mọi ví dụ ở đây dùng dạng đối tượng vmDefenseHook: { name }, vốn yêu cầu
v7.4.0. Các phiên bản trước nhận một chuỗi trần (vmDefenseHook: '__vmDetection'); dạng đó bị từ chối kể từ v8.0.0, vì vậy
hãy dùng dạng đối tượng ở mọi nơi.
Công thức 1 - báo cáo các lần phát hiện về backend của bạn
Bước 1 - đăng ký một hàm hook toàn cục, trước khi bundle đã làm rối tải xong
Runtime của VM và các cơ chế phòng thủ của nó chạy trước chương trình được bảo vệ của bạn, nên nhiều lần phát hiện kích hoạt trong lúc khởi động. Hãy định nghĩa hook như một biến toàn cục thông thường trong trang host, đặt trước thẻ script đã làm rối:
Bước 2 - trỏ vmDefenseHook vào nó
Tùy chọn này là một đối tượng có name là hàm toàn cục cần gọi (aliases là tùy chọn - xem bên dưới):
Trong bảng điều khiển, trường VM Defense Hook xuất hiện trong mục Bảo vệ nâng cao một khi có ít nhất một cơ chế phòng thủ (vmSelfDefending, vmDebugProtection, hoặc vmDomainLock) được bật.
Bước 3 - nhận tín hiệu trên backend của bạn
Mỗi lần phát hiện sẽ gọi hook với một đối tượng signal duy nhất:
source- bộ phát hiện cụ thể:headless,node,agent,agentBrowser,domain,debugger,sandbox,nativeHook,timing, hoặcintegrity. Kể từ v7.4.0, các bộ phát hiệnenvvàinspectortrước đây báo cáo dướisource: 'debugger'.agentBrowserbáo cáo dướicategory: 'automation'và chạy trên các target dành cho trình duyệt khi bậtvmDebugProtection(v7.9.0+).category-automation,debugger,sandbox,domain,tamper, hoặcintegrity. Nguồnnodebáo cáo dướicategory: 'debugger'(v7.4.0+).score,threshold- điểm phát hiện và ngưỡng mà nó đã vượt qua
Một endpoint nhận tối giản (minh họa bằng Express; bất kỳ backend nào chấp nhận một POST đều được). Nó chuẩn hóa body thành một mảng để cũng xử lý được dạng gộp theo lô được gửi bởi mẫu buffer bên dưới:
Hook chỉ để báo cáo - giá trị trả về của nó bị bỏ qua, và một hook thiếu hoặc ném lỗi chỉ là một no-op âm thầm. Nó không bao giờ có thể tắt một cơ chế phòng thủ, nên việc kẻ tấn công xóa hoặc phá hook của bạn chẳng đem lại gì. Để thay đổi những gì một cơ chế phòng thủ làm, hãy dùng vmDefenseReaction (Công thức 2).
Định nghĩa hook trong trang host, không phải bên trong mã nguồn đã làm rối
Với telemetry bạn muốn nắm được mọi lần phát hiện, và nhiều lần trong số đó kích hoạt lúc khởi động - một hook được định nghĩa bên trong bundle đã làm rối được đăng ký quá muộn để bắt kịp những lần đó, và nếu nó bị biên dịch thành VM thì không thể truy cập được cho đến khi chương trình của bạn chạy. Dù thế nào nó vẫn an toàn (một hook thiếu sẽ no-op, và một hook tự nó kích hoạt một lần phát hiện sẽ không bị gọi lại theo kiểu đệ quy), nhưng để bao phủ đầy đủ hãy đăng ký nó ngay từ đầu trong trang host.
Ngoại lệ duy nhất là một hook chỉ phản ứng với một lần phát hiện lúc chạy - chẳng hạn như dọn dẹp khi một trình gỡ lỗi được mở trong lúc sử dụng. Hook đó có thể nằm bên trong bundle đã làm rối; xem Công thức 3.
Để vẫn bảo vệ logic báo cáo của bạn, hãy giữ hook đã đăng ký chỉ là một buffer một dòng và rút cạn nó từ mã đã làm rối của bạn:
Đổi tên các trường tín hiệu (aliases)
Các giá trị source / category mặc định là những cái tên mô tả, nên bất kỳ ai gắn mã vào callback (hoặc đọc đầu ra) đều có thể nhận ra cơ chế bảo vệ và bộ phát hiện nào đã kích hoạt. aliases đổi tên các trường tín hiệu thành những token mờ đục do bạn chọn, được áp dụng bên trong VM trước khi tín hiệu được phát ra, nên những cái tên đó không bao giờ xuất hiện trong đầu ra hay đến được callback. Ứng dụng của bạn biết ánh xạ của riêng nó và chuyển tiếp các token về backend của bạn.
Aliases áp dụng theo từng trường: mỗi trường nhận một key (tên thuộc tính mà callback nhận được); các trường tên dạng chuỗi source và category còn nhận thêm một ánh xạ values, trong khi score / threshold là số và chỉ nhận một key. Các mục không được đặt sẽ giữ tên mặc định của chúng.
Trong bảng điều khiển, phần Bí danh tín hiệu nằm dưới trường VM Defense Hook.
Đây là né tránh dấu vân tay, không phải bí mật - ánh xạ vẫn có thể bị suy ra bằng cách thử đi thử lại, nên lợi ích duy nhất của nó là không phơi bày những cái tên ổn định, tự giải thích.
Công thức 2 - điều chỉnh các phản ứng mặc định
vmDefenseReaction cấu hình cách mỗi danh mục phát hiện phản ứng. Nó không bật bất cứ thứ gì - bản thân các cơ chế phòng thủ được bật bởi vmSelfDefending, vmDebugProtection, và vmDomainLock; tùy chọn này chỉ chọn cách một cơ chế phòng thủ đang bật phản ứng. Danh mục là đơn vị kiểm soát: mọi bộ phát hiện trong một danh mục đều thực thi phản ứng của danh mục đó, và một phản ứng được đặt cho một danh mục mà tùy chọn của nó đang tắt thì đơn giản là không có tác dụng.
| Danh mục | Được bật bởi | Phản ứng khi |
|---|---|---|
automation | vmSelfDefending hoặc vmDebugProtection | Mã đang bị điều khiển bởi phần mềm thay vì một con người: một trình duyệt headless hoặc tự động hóa, một framework thu thập / kiểm thử, hoặc một AI coding-agent đang bước qua trang. |
debugger | vmDebugProtection hoặc vmSelfDefending | Ai đó đang mở một trình gỡ lỗi hoặc trình kiểm tra developer-tools của trình duyệt và đang bước qua mã đang chạy để hiểu nó. |
sandbox | vmDebugProtection | Mã hoàn toàn không chạy trong một trình duyệt thật - nó đã bị đưa vào một môi trường JavaScript được giả lập hoặc điều khiển bằng script để được thực thi và nghiên cứu ngoại tuyến. |
domain | vmDomainLock | Mã đang chạy trên một trang mà bạn không cho phép: một host không nằm trong danh sách cho phép vmDomainLock của bạn (ví dụ, bundle của bạn bị sao chép sang miền của người khác). |
tamper | vmSelfDefending | Môi trường JavaScript xung quanh VM đã bị sửa đổi để theo dõi hoặc chiếm quyền nó, chẳng hạn như các hàm dựng sẵn gốc của trình duyệt bị thay bằng các phiên bản đã được gắn mã theo dõi. |
integrity | vmSelfDefending | Chính mã của bundle được bảo vệ đã bị sửa đổi hoặc vá kể từ khi bạn tạo ra nó. |
Các khóa là sáu tên danh mục này, hoặc default (một phương án dự phòng cho các danh mục không được chỉ định). Các giá trị là:
break- phá vỡ ngay lập tứcdecoy- tiếp tục chạy trên trạng thái bị đầu độc, âm thầm tạo ra kết quả sai.decoycầnvmDebugProtectionhoặcvmDomainLocktrên một target dành cho trình duyệt; nếu không, nó hoạt động nhưbreak.none- không làm gì cục bộ (chỉ telemetry)
Một danh mục bạn không đặt sẽ quay về các giá trị mặc định dựng sẵn:
default áp đến mọi danh mục, bao gồm cả integrity và tamper, nên { default: 'none' } là một bản dựng thực sự không phá vỡ, chỉ telemetry:
Trong bảng điều khiển, các ô chọn VM Defense Reactions xuất hiện trong mục Bảo vệ nâng cao một khi có một cơ chế phòng thủ được bật; mỗi danh mục chỉ có thể chỉnh sửa khi một cơ chế phòng thủ phát ra các bộ phát hiện của nó đang bật.
Công thức 3 - chạy logic của riêng bạn trước khi một cơ chế phòng thủ phá vỡ
Hook không chỉ để báo cáo - nó còn là nơi đáng tin cậy duy nhất để chạy phản hồi của riêng bạn trước khi một cơ chế phòng thủ phản ứng. Khi một trình gỡ lỗi được mở trên một trang đang chạy, bạn có thể muốn xóa những gì đang hiển thị trên màn hình, hoặc thay giao diện bằng một trang 404, trước khi mã phá vỡ.
Vì sao là hook chứ không phải mã ở nơi khác trong ứng dụng của bạn: break dừng toàn bộ bytecode phía sau, nên một đoạn dọn dẹp chạy sau khi một cơ chế phòng thủ kích hoạt - đặc biệt khi bản thân nó cũng được làm rối VM - lại chính là thứ mà break ngăn không cho thực thi. Hook kích hoạt tại điểm phát hiện trước khi phản ứng được thực thi, một cách đồng bộ - nên một hàm đồng bộ mà nó gọi sẽ hoàn tất trước, rồi break dừng VM.
Định nghĩa phản hồi như vmDefenseHook của bạn. Vì lần phát hiện debugger kích hoạt vào lúc chạy - sau khi chương trình của bạn đã tải xong và định nghĩa hook - nên hook có thể là một phần của mã nguồn đã làm rối và được biên dịch thành bytecode cùng với phần còn lại của bundle. Rẽ nhánh theo signal.category để mỗi điều kiện nhận đúng phản hồi, giữ cho công việc luôn đồng bộ, rồi để phản ứng chạy:
Cách này áp dụng cho các lần phát hiện kích hoạt trong khi ứng dụng của bạn đang chạy - xem Khi việc biên dịch hook thành bytecode phát huy tác dụng bên dưới.
Hãy ghi nhớ những điểm sau:
- Chỉ công việc đồng bộ mới được đảm bảo hoàn tất trước. Phản ứng chạy ở câu lệnh ngay sau khi hook trả về. Các lệnh gọi kiểu bắn-rồi-quên bàn giao ngay lập tức thì không sao (
navigator.sendBeacon, các chỉnh sửa DOM và canvas đồng bộ); công việc bạn lên lịch cho sau này - mộtsetTimeout, một phần tiếp nối của promise, mộtawait- thì không, và bất cứ thứ gì cần thêm bytecode của VM đều sẽ không chạy, vì đó chính là thứ màbreakdừng lại. - Hook chạy trước phản ứng; nó không thay thế phản ứng. Giá trị trả về của nó bị bỏ qua, và nó không thể hủy, trì hoãn, hay thay đổi những gì phản ứng làm. Hãy dùng nó để hành động trước khi break, không phải để phủ quyết nó - để thay đổi chính phản ứng, hãy dùng
vmDefenseReaction(Công thức 2).
Khi việc biên dịch hook thành bytecode phát huy tác dụng
Việc đặt hook bên trong bundle đã làm rối theo cách này có tác dụng chỉ vì lần phát hiện debugger kích hoạt vào lúc chạy. VM kích hoạt vmDefenseHook khi nó vẫn còn sống, sau khi chương trình của bạn đã tải xong và định nghĩa hook, nên hook đã biên dịch thành bytecode được giải mã và chạy trước, rồi mới break. Đây chính là điều bảo vệ mã nguồn của chính hook.
Nó không có tác dụng với các lần phát hiện kích hoạt lúc khởi động - automation, sandbox, domain, hoặc một trình gỡ lỗi đã mở sẵn khi trang được tải - vì tại thời điểm đó hook đã biên dịch thành bytecode vẫn chưa được định nghĩa, nên cơ chế phòng thủ không tìm thấy hàm nào để gọi. Với những trường hợp đó, hãy đăng ký hook như một biến toàn cục thông thường trong trang host, như trong Công thức 1. Khi phân vân, một biến toàn cục thông thường của trang host bao phủ mọi lần phát hiện đến được hook; việc biên dịch thành bytecode chỉ thêm sự bảo vệ cho mã nguồn của chính hook, và chỉ với các lần phát hiện lúc chạy.
Từ telemetry đến thực thi
Khả năng quan sát và thực thi không nhất thiết phải đi cùng nhau. Hãy triển khai các cơ chế phòng thủ qua hai giai đoạn: đầu tiên là một bản dựng chỉ báo cáo, rồi - một khi telemetry trông đã sạch - một bản có phản ứng.
Bước 1 - phát hành một bản dựng chỉ quan sát
Bật mọi cơ chế phòng thủ bạn định dùng, trỏ vmDefenseHook vào endpoint của bạn, và tắt tất cả các phản ứng. Mọi bộ phát hiện vẫn chạy và báo cáo từng lần trúng về backend của bạn, nhưng không có gì phá vỡ:
Bước 2 - xem lại các tín hiệu đã thu thập
Sau khi bản dựng đã tiếp nhận lưu lượng thực, hãy tìm những lần phát hiện do việc sử dụng hợp lệ gây ra. Hai trường hợp phổ biến nhất:
- Các lần trúng
automationtừ chính các bài kiểm thử đầu-cuối hoặc giám sát thời gian hoạt động của bạn - hãy dựng các sản phẩm đó mà không có các cơ chế phòng thủ thay vì dung thứ danh mục này trong production. - Các lần trúng
domaintừ một host staging hoặc preview mà bạn quên đưa vào danh sách cho phépvmDomainLock- hãy thêm host đó.
Hãy ưu tiên sửa nguyên nhân hơn là làm dịu một phản ứng: mỗi danh mục để ở none là một bộ phát hiện mà kẻ tấn công có thể yên tâm bỏ qua.
Bước 3 - bật các phản ứng lên
Gỡ bỏ ghi đè default: 'none' để các phản ứng dựng sẵn theo từng danh mục có hiệu lực; chỉ riêng dòng đó là toàn bộ thay đổi. Nếu một danh mục cứ tiếp tục tạo ra dương tính giả mà bạn không thể loại bỏ, hãy để riêng danh mục đó ở none (ví dụ vmDefenseReaction: { automation: 'none' }) và thực thi phần còn lại.
Hãy giữ vmDefenseHook được đặt sau khi việc thực thi đã bật - hook kích hoạt bất kể phản ứng là gì, nên bạn vẫn giữ được khả năng quan sát ai đang dò xét bundle của mình trong khi các cơ chế phòng thủ hoạt động.
