Layma.net Bypass CUTTAY PROMAX — Decobf

Phân tích & giải mã userscript v1.1.4 (tác giả gốc: DuckXkurumi) — xem toàn bộ mã nguồn đã deobfuscate, cài đặt userscript, và đọc chi tiết logic mã hóa + cách bẻ.
version 1.1.4 match https://layma.net/* 4 tầng loader deobf 100% tầng 1 · sandbox trace tầng 4 phân tích 2026-11-09

1Chuỗi nạp mã 4 tầng — file bạn cài KHÔNG chứa logic

File userscript.user.js chỉ có 15 dòng header. Mọi thứ được tải về và eval qua 4 tầng:

📦 [0] userscript.user.js — 655 B — chỉ header, @require funcion.js
▼ @require (Tampermonkey tải)
📜 [1] funcion.js — duckxkurumi.xyz — 64 KB obfuscator.io → deobf 100%
chỉ làm: fetch + eval ↓
▼ fetch + eval
📜 [2] layma.js — duckxkrmproject.x10.mx — 202 KB, chuỗi TEA-encrypt runtime → fetch + eval ↓
▼ fetch + eval
📜 [3] pastefy.app/5dKBHbgv/raw — 197 KB — loader kế → fetch + eval ↓
▼ fetch + eval
⚡ [4] pastefy.app/6ZzLL87y/raw — 211 KB — JSVMP thật (bytecode + interpreter): engine bypass nhiệm vụ / captcha
URL pastefy không nằm dưới dạng chữ trong file — các chuỗi được mã hóa TEA-16bit và chỉ decrypt lúc chạy. Chúng chỉ lộ khi chạy script trong sandbox (xem mục cách deobf).

2Tầng cuối chạy những gì? (trace trong sandbox)

Stage 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ý:

  • Đọc API nội bộ của chính layma.net: getQCaptchaToken, getTokenId, getCampainId, getVisitorInfo, getVisitorUUID, trafficSessionToken, getReferrer, uuidv4, AUTO_CHANGE_MISSION_ON_ERROR…
  • Inject DOM: #vnbypass-wrapper, #vnbypass-clean-style
  • Dò phần tử captcha: #qcaptcha-checkcode, #native-override-captcha-box, #native-override-button-box
  • Kết luận: tự động hóa "nhiệm vụ lấy mã" mở khóa chương — gọi lại API captcha của trang để tự có token, tự điền/nộp mã, đồng bộ visitor/session.
Rủi ro khi cài: script eval 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.

3Cài đặt / tải userscript

Bản gốc (duckxkurumi.xyz)

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ốc

Bản decobf trong repo

Engine độc lập đã giải mã, không nạp từ x10.mx / pastefy.

⬇ Cài bản decobf

4Xem toàn bộ source

chọn một file ở bên trái…
▲ Chọn file để xem nội dung

5Logic mã hóa của từng tầng

Tầng 1 — funcion.js: javascript-obfuscator (obfuscator.io)

Cấu trúc gồm 5 bộ phận ghép lại:

  1. String array — hàm _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.
  2. Decoder lõi — _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.
  3. 125 wrapper offset — mỗi hàm 5 tham số chỉ map lại tham số rồi gọi wrapper khác / decoder lõi: _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.
  4. IIFE xoay mảng — 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.
  5. Anti-tamper — 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).

Tầng 2–3 — x10.mx + pastefy: obfuscator kiểu Jscrambler/VM-lite

  1. Tên biến 2 ký tự (uy, Yw, pg, AI, wa…) — phá khả năng grep.
  2. Chuỗi XOR "miếng" — decoder 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.
  3. Chuỗi chính nằm trong mảng Uint16 mã hóa TEA-16bit: 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.
  4. Control-flow flatten — mỗi hàm biến thành state machine: while (Mi !== 185297400) { if (Mi === 184570624) {…Mi = (509312512^358241464)>>>0} else if … }, hằng số state cũng là phép XOR.
  5. Shuffle giả ngẫu nhiên LCG 1103515245 + hash djb2 (Oh=(Oh<<5)+Oh+c) làm thêm nhiễu.

Tầng 4 — JSVMP thật (bytecode + interpreter)

  • Entry (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.
  • 0 chuỗi plain-text trong 211 KB — mọi chuỗi/URL/selectors nằm trong bytecode mã hóa. Grep không ra gì.
  • Điểm nhận dạng: opcode CALL ném TypeError(Nx+' is not a function') — đây chính là "cửa" để patch & trace.
  • Khôi phục 100% về JS gốc đọc được bằng máy là không khả thi (cần viết tracer/trình decompile riêng cho VM này) — thay vào đó ta chạy trong sandbox và ghi lại toàn bộ hành vi.

6Cách decobf từng tầng (đúng quy trình đã làm)

Tầng 1 — 5 bước, deobf 100%

  1. Tách "máy giải mã" khỏi "logic": parse AST (Babel), tìm hàm chứa mảng lớn (_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".
  2. Sandbox: ném toàn bộ function declaration (vốn trơ — không chạy gì khi khai báo) + đúng 1 IIFE xoay mảng (nhận diện qua push/shift) vào Node vm.createContext. Logic chính không hề được thực thi.
  3. Evaluate từng call site: duyệt AST, gặp _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.
  4. Fold hằng số (đến khi hội tụ): gộp chuỗi nối liền 'coun'+'ter', tính gọn phép toán hex -0xff3+0x1396+0xbfd, chuyển obj['push'] → obj.push, !![] → true.
  5. Beautify + tách thủ công: js-beautify, rồi loại bỏ phần máy obfuscator sinh thêm (mảng chuỗi, decoder, wrapper, anti-debug) — phần còn lại chính là src gốc ~15 dòng (xem 2-funcion.RECONSTRUCTED.js).

Tầng 2–3 — fold tĩnh + bắt URL bằng sandbox

  1. Nhận diện decoder XOR: quét mọi 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).
  2. Decode 105 lời gọi uy([...], 11) → string literal; fold 255 biểu thức bit hằng ((a^b)>>>0…); normalize escape hex; dot-notation 1200 chỗ.
  3. URL thật không thể fold tĩnh (nằm trong TEA-Uint16) → chạy sandbox với fetch/document/GM_* giả và ghi log: stage 2 gọi pastefy.app/5dKBHbgv/raw, stage 3 gọi pastefy.app/6ZzLL87y/raw.

Tầng 4 — sandbox trace (không decompile)

  1. Sandbox Node 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.
  2. Vá đúng điểm ném lỗi của interpreter — thay 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.
  3. Neutralize regex (((.+)+)+)+$ của self-defending để không tự khóa.
  4. Ghi log mọi 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.

Tóm tắt một dòng: obfuscate = dịch code sang "máy khác người đọc"; decobf = cho chính cái máy đó chạy trong hộp kín rồi nghe nó thì thầm — thay vì dịch ngược từng ký tự bằng tay.
Đã copy!