File userscript.user.js chỉ có 15 dòng header. Mọi thứ được tải về và eval qua 4 tầng:
@require funcion.jsStage 4 được chạy trong sandbox Node với DOM/GM/fetch giả hoàn toàn (chặn process/require, không chạm trang thật). Nhật ký:
getQCaptchaToken, getTokenId, getCampainId, getVisitorInfo, getVisitorUUID, trafficSessionToken, getReferrer, uuidv4, AUTO_CHANGE_MISSION_ON_ERROR…#vnbypass-wrapper, #vnbypass-clean-style#qcaptcha-checkcode, #native-override-captcha-box, #native-override-button-boxeval code từ server tác giả qua 4 tầng, @connect * (được truy cập mọi domain), logic đổi bất kỳ lúc nào qua pastefy mà không cần update userscript. Cài = chạy code người lạ tùy thời điểm trên trang có tài khoản của bạn. Trang này chỉ nhằm mục đích minh bạch & nghiên cứu.Loader 4 tầng nguyên bản từ tác giả — nội dung stage có thể thay đổi bất kỳ lúc nào.
⬇ Cài bản gốcCấu trúc gồm 5 bộ phận ghép lại:
_0x55cc() trả về mảng ~350 chuỗi đã mã hóa Base64+RC4, ví dụ 'nSoOfmksjCoFW5/cGhq'. Mọi chuỗi trong code đều được thay bằng lời gọi hàm._0x3d1b(index, key): lấy phần tử mảng, giải Base64 (atob), rồi giải RC4 với key 4 ký tự (lấy từ tham số). Có memoization cache trên chính hàm decoder._0x25d53f(a,b,c,d,e){return _0x3d1b(c-0x2e1, b)}. Mục đích: mỗi call site có bộ số hex khác nhau, tìm-và-thay bằng tay là bất khả thi.while(true){ try{ parseInt(...)+... === 308604 ? break : arr.push(arr.shift()) } } — xoay mảng đến khi tổng checksum các parseInt khớp 308604. Sai vị trí = decode ra rác.selfDefending (check fn.bind().toString().indexOf('\n') + regex thảm họa (((.+)+)+)+$ → code bị beautify là treo máy), debugProtection (setInterval 4 s bắn debugger + vòng while(true){}), tắt console (ghi đè log/warn/info/error/exception/table).uy, Yw, pg, AI, wa…) — phá khả năng grep.uy(arr, 11) dùng String.fromCharCode — nhưng chính chữ fromCharCode cũng bị tách: "\x66rom"+"\x43\x68ar"+"C\x6f\x64\x65", bắt buộc phải unescape trước khi nhận diện.bG[Ex] = (bG[Ex] ^ ((up ^ Ez) + qK & 65535)) & 0xffff — decrypt lazy lúc runtime rồi cache. Vì vậy URL pastefy không thể thấy bằng cách đọc code.while (Mi !== 185297400) { if (Mi === 184570624) {…Mi = (509312512^358241464)>>>0} else if … }, hằng số state cũng là phép XOR.1103515245 + hash djb2 (Oh=(Oh<<5)+Oh+c) làm thêm nhiễu.(function vmsY(vmsQ){…})(this), cache module _$1KIqOR; toàn bộ logic được biên dịch thành bytecode, interpreter dùng DataView/getInt8/setInt8 để đọc lệnh.TypeError(Nx+' is not a function') — đây chính là "cửa" để patch & trace._0x55cc), closure ngược từ decoder lõi qua các wrapper (thân = 1 lệnh return gọi hàm khác trong closure) → thu được 125 hàm thuộc "máy".push/shift) vào Node vm.createContext. Logic chính không hề được thực thi._0x…(0x…, 'key', …) có tham số thuần hằng → cắt nguyên chuỗi nguồn của biểu thức, vm.runInNewContext('(' + callSrc + ')') trong chính sandbox đó → thay bằng StringLiteral. Kết quả: 505/505 lời gọi được giải.'coun'+'ter', tính gọn phép toán hex -0xff3+0x1396+0xbfd, chuyển obj['push'] → obj.push, !![] → true.2-funcion.RECONSTRUCTED.js).FunctionDeclaration 2 tham số có ^, sau khi unescape \xNN và xóa dấu nháy/dấu +/khoảng trắng rồi mới tìm chữ fromCharCode (bẫy: chuỗi bị tách 3 miếng).uy([...], 11) → string literal; fold 255 biểu thức bit hằng ((a^b)>>>0…); normalize escape hex; dot-notation 1200 chỗ.fetch/document/GM_* giả và ghi log: stage 2 gọi pastefy.app/5dKBHbgv/raw, stage 3 gọi pastefy.app/6ZzLL87y/raw.vm với DOM/GM/fetch/navigator giả; chặn process/require/fs… để không có lỗ hổng thực thi ra máy.throw new TypeError(Nx+' is not a function') bằng heal + log — để VM đi tiếp qua các API mà sandbox chưa giả lập.(((.+)+)+)+$ của self-defending để không tự khóa.querySelector/getElementById/fetch/GM_* → hồ sơ hành vi đầy đủ (mục 2).Toàn bộ script dùng cho phân tích nằm trong thư mục tools/ của repo: deob.js (tầng 1), deob2.js (tầng 2/3), run_sandbox.js (tầng 4), probe.js.