jslinux-deobfuscated-network

本文介绍如何在JSLinux环境中配置网络,并通过WebSocket实现与外部网络的数据交换。包括搭建网络桥接、配置IP转发、设置iptables规则及利用WebSocket进行通信的具体步骤。
基础知识
查看网络包

tcpdump -i eth0 -e -v -l
tcpdump -i eth0 -e -v -l -w a.pcap

a.pcap给wireshark用

这个是必要的
转发三步走:
1.配置Linux系统的ip转发功能

echo 1 >/proc/sys/net/ipv4/ip_forward

2.iptables转发:

iptables -t nat -A POSTROUTING -s "10.0.2.0/255.255.255.0" ! -d "10.0.2.0/255.255.255.0" -j MASQUERADE

3.开启混杂模式:
ip link set br1 promisc on


防止环路

#brctl stp br1 on
#brctl stp br1 off


##########################################
服务端(配置ip)
ip link set tap1 up
ifconfig tap1 inet 10.0.2.1

客户端
ip link set eth0 up
ifconfig eth0 inet 10.0.2.100
(配置默认路由)
route add default gw 10.0.2.1 dev eth0

测试
ping 192.168.128.1
echo 8.8.8.8 > /etc/resolv.conf
ping www.baidu.com


刚连websocket的时候
01:00:5e:00:00:01 dev eth0 self permanent
是组播地址

jslinux中的代码走向

FileReader.onload
net_handler
pc.serial2.send_chars(str);
pc.net0.receive_packet(data);

######################################
原理:
1.linux上建立br桥,桥用ipv4的转发和外网相通
2.py_websocket建立服务,每次连接时建立一个tap设备绑定到br上
3.web端用websocket连到py_websocket的服务上,
4.websocket的客户端用FileReader的onload特性(文件读取成功完成时触发)
接受消息pc.net0.receive_packet(data);
传入linux内核

定义net0的时候发现,linux向外发送消息是
ai定义了send_packet_func绑定自定义pi,
pi里面调用tuntap_sendData(withPrefix);
向websocket服务发送消息

最终实现
[color=red]
linux--->js(net0)--->filereader--->websocket--->服务(tap设备)---br网桥-->互联网
[/color]

########################
操作步骤


下载

git clone http://github.com/killinux/jslinux-deobfuscated-network

随便放在nginx等web服务器中跑起来

cd websocketstuntap/
按照launch.sh中的操作:
设置ip转发
echo 1 >/proc/sys/net/ipv4/ip_forward 

设置桥,和给桥设置ip

brctl addbr br1
brctl stp br1 on
ifconfig br1 10.0.2.1 netmask 255.0.0.0 up

设置桥的混杂模式
ip link set br1 promisc on


#给桥设置dhcp的server,连这个桥的自动获取ip,这步骤不是必须

#dnsmasq --strict-order --except-interface=lo --interface=br1 --listen-address=10.0.2.1 --bind-interfaces --dhcp-range=10.0.2.100,10.0.2.254 --conf-file="" --pid-file=/var/run/qemu-dhcp-br1.pid --dhcp-leasefile=/var/run/qemu-dhcp-br1.leases --dhcp-no-override


设置ip转发,让vm中的网络能通外网

iptables -t nat -A POSTROUTING -s "10.0.2.0/255.255.255.0" ! -d "10.0.2.0/255.255.255.0" -j MASQUERADE


设置虚拟python环境,防止污染本机python

#virtualenv mysite
#source mysite/bin/activate
#pip install mod_pywebsocket


启动websocket服务

python -m mod_pywebsocket.standalone -d . --log-level=info -p 3000


js/network-websockets.js
中的代码连到websocket的server
例子代码
[url]http://killinux.com/jslinux-deobfuscated-network/[/url]


jslinux中的网络已经和服务器在一个网络了
虚拟ip 10.0.2.0段
网关 10.0.2.1 是服务器的桥br1
可以尝试
ping www.baidu.com
ping www.google.com


[img]http://dl2.iteye.com/upload/attachment/0123/0483/cb62b94f-a12c-3f57-b706-4e354de177c3.png[/img]
const fs = require('fs'); const path = require('path'); const parser = require('@babel/parser'); const traverse = require('@babel/traverse').default; const t = require('@babel/types'); const generator = require('@babel/generator').default; const vm = require('vm'); // Helper to check if a node is a literal function isLiteral(node) { return t.isStringLiteral(node) || t.isNumericLiteral(node) || t.isBooleanLiteral(node) || t.isNullLiteral(node); } async function main() { const inputFile = path.join(__dirname, 'example-1.js'); if (!fs.existsSync(inputFile)) { console.error(`File ${inputFile} not found.`); return; } const code = fs.readFileSync(inputFile, 'utf8'); console.log(`Processing ${inputFile} (${code.length} bytes)...`); const ast = parser.parse(code, { sourceType: 'script', plugins: ['bigInt'] }); // --------------------------------------------------------- // Step 1: Prepare Execution Environment (Prelude & Full Execution) // --------------------------------------------------------- let mainIIFEPath = null; let mainBlock = null; let iifeScope = null; traverse(ast, { CallExpression(path) { if (t.isFunctionExpression(path.node.callee) && !mainIIFEPath) { const body = path.node.callee.body.body; if (body.length > 50) { mainIIFEPath = path; mainBlock = body; iifeScope = path.get('callee').scope; path.stop(); } } } }); if (!mainIIFEPath) { console.error("Could not find main IIFE."); return; } console.log("Found main IIFE."); // Identify IIFE variables and functions const iifeVars = new Set(); mainBlock.forEach(node => { if (t.isVariableDeclaration(node)) { node.declarations.forEach(dec => { if (t.isIdentifier(dec.id)) { iifeVars.add(dec.id.name); } }); } else if (t.isFunctionDeclaration(node)) { if (node.id && t.isIdentifier(node.id)) { iifeVars.add(node.id.name); } } }); // Add potentially missing critical variables ['tC', 'Rl', 'Wg', 'MS', 'Um', 'kg', 'hW', 'JQ', 'qp', 'KC', 'lQ', 'l2', 'b6', 'hO', 'An', 'H8', 'Cc', 'bL', 'Y8', 'Un', 'ZO', 'DH', 'd7', 'cY', 'XO', 'A7', 'QS', 'Mp', 'TO', 'Vg', 'k6', 'Vx', 'OC', 'kH', 'N8', 'pH', 'vm', 'Qc', 'IO', 'Qm', 'nm', 'CX', 'wc', 'XB', 'sS', 'Up', 'Bf', 'Fb', 'zw', 'lC', 'IX', 'Zc', 'Xl', 'jl', 'cH', 'T4', 'pv', 'tY', 'A8', 'rL', 'lF', 'B8', 'YO', 'Mq', 'K6', 'XKN', 'AQ', 'SL', 'zH', 'hd', 'n7', 'QQ', 'Hr', 'Ih', 'fm', 'pB', 'md', 'm4', 'ptN', 'cQ', 'ZhN', 'cw', 'krN', 'Vp', 'Dc', 'kt', 'ZS', 'pp', 'X8', 'El'].forEach(v => iifeVars.add(v)); // Setup VM Sandbox with enhanced mocks const sandbox = { window: { screen: { colorDepth: 24, pixelDepth: 24, availWidth: 1920, availHeight: 1080 }, navigator: { userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', plugins: [], maxTouchPoints: 0, languages: ['en-US', 'en'], hardwareConcurrency: 4, deviceMemory: 8, webdriver: false, onLine: true, cookieEnabled: true, javaEnabled: () => false, doNotTrack: null, platform: 'Win32', appName: 'Netscape', appCodeName: 'Mozilla', product: 'Gecko', vendor: 'Google Inc.' }, document: { createElement: () => ({ style: {}, appendChild: () => {}, remove: () => {}, getAttribute: () => null, setAttribute: () => {} }), location: { protocol: 'https:', hostname: 'example.com', href: 'https://example.com/' }, addEventListener: () => {}, attachEvent: () => {}, documentElement: { style: {} }, head: { appendChild: () => {} }, body: { appendChild: () => {} }, cookie: '', referrer: '', getElementById: () => null, getElementsByTagName: () => [] }, chrome: { runtime: {} }, performance: { memory: { jsHeapSizeLimit: 2147483648, totalJSHeapSize: 10000000, usedJSHeapSize: 5000000 }, now: () => Date.now() }, localStorage: { getItem: () => null, setItem: () => {}, removeItem: () => {} }, sessionStorage: { getItem: () => null, setItem: () => {}, removeItem: () => {} }, indexedDB: {}, openDatabase: () => {}, DeviceOrientationEvent: {}, DeviceMotionEvent: {}, TouchEvent: {}, history: {}, location: { protocol: 'https:', hostname: 'example.com', href: 'https://example.com/' }, getComputedStyle: () => ({}), frames: { ServiceWorkerRegistration: {} }, ServiceWorker: {}, ServiceWorkerContainer: {}, innerWidth: 1920, innerHeight: 1080, outerWidth: 1920, outerHeight: 1080, screenX: 0, screenY: 0, pageXOffset: 0, pageYOffset: 0, top: {}, parent: {}, self: {} }, document: { createElement: () => ({ style: {}, appendChild: () => {}, remove: () => {}, getAttribute: () => null, setAttribute: () => {} }), location: { protocol: 'https:', hostname: 'example.com' }, documentElement: { style: {} }, head: { appendChild: () => {} }, body: { appendChild: () => {} }, referrer: '' }, navigator: { userAgent: 'Mozilla/5.0' }, console: console, Array: Array, String: String, Object: Object, Math: Math, parseInt: parseInt, parseFloat: parseFloat, RegExp: RegExp, Date: Date, Function: Function, JSON: JSON, Error: Error, TypeError: TypeError, setTimeout: (fn) => fn(), setInterval: () => {}, clearTimeout: () => {}, clearInterval: () => {}, encodeURIComponent: encodeURIComponent, decodeURIComponent: decodeURIComponent, unescape: unescape, escape: escape }; // Circular references and polyfills sandbox.window.window = sandbox.window; sandbox.window.self = sandbox.window; sandbox.window.global = sandbox.window; sandbox.window.top = sandbox.window; sandbox.window.parent = sandbox.window; // Copy standard globals to window ['Array', 'String', 'Object', 'Math', 'parseInt', 'parseFloat', 'RegExp', 'Date', 'Function', 'JSON', 'Error', 'TypeError', 'encodeURIComponent', 'decodeURIComponent', 'unescape', 'escape', 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval'].forEach(prop => { sandbox.window[prop] = sandbox[prop]; }); sandbox.self = sandbox.window; sandbox.global = sandbox.window; const vmCtx = vm.createContext(sandbox); // Instrument IIFE to export variables at the end using Try-Catch-Finally // Split into functions (export early) and variables (export late) const funcNames = new Set(); const varNames = new Set(); mainBlock.forEach(node => { if (t.isFunctionDeclaration(node) && node.id && t.isIdentifier(node.id)) { funcNames.add(node.id.name); } else if (t.isVariableDeclaration(node)) { node.declarations.forEach(dec => { if (t.isIdentifier(dec.id)) { varNames.add(dec.id.name); } }); } }); // Manual list classification (best effort) const manualFuncs = ['ZhN', 'cw', 'krN', 'Vp', 'Dc', 'kt', 'ZS', 'pp', 'X8', 'El', 'fw', 'wf', 'j8', 'v3N', 'k3N', 'LM', 'FPN', 'P6', 'Zc', 'l2', 'G0N']; const manualVars = ['tC', 'Rl', 'Wg', 'MS', 'Um', 'kg', 'hW', 'JQ', 'qp', 'KC', 'lQ', 'b6', 'hO', 'An', 'H8', 'Cc', 'bL', 'Y8', 'Un', 'ZO', 'DH', 'd7', 'cY', 'XO', 'A7', 'QS', 'Mp', 'TO', 'Vg', 'k6', 'Vx', 'OC', 'kH', 'N8', 'pH', 'vm', 'Qc', 'IO', 'Qm', 'nm', 'CX', 'wc', 'XB', 'sS', 'Up', 'Bf', 'Fb', 'zw', 'lC', 'IX', 'Xl', 'jl', 'cH', 'T4', 'pv', 'tY', 'A8', 'rL', 'lF', 'B8', 'YO', 'Mq', 'K6', 'XKN', 'AQ', 'SL', 'zH', 'hd', 'n7', 'QQ', 'Hr', 'Ih', 'fm', 'pB', 'md', 'm4', 'ptN', 'cQ']; manualFuncs.forEach(f => funcNames.add(f)); manualVars.forEach(v => varNames.add(v)); const funcExportCode = Array.from(funcNames).map(v => `try { window['${v}'] = ${v}; } catch(e) {}`).join('\n'); const varExportCode = Array.from(varNames).map(v => `try { window['${v}'] = ${v}; } catch(e) {}`).join('\n'); const originalBody = [...mainBlock]; mainBlock.length = 0; // Clear // Prepend function exports to try block const funcExportNodes = parser.parse(funcExportCode).program.body; const tryBodyNodes = [...funcExportNodes, ...originalBody]; const tryBlock = t.blockStatement(tryBodyNodes); const catchBlock = t.catchClause(t.identifier('e'), t.blockStatement([ t.expressionStatement(t.callExpression( t.memberExpression(t.identifier('console'), t.identifier('log')), [t.stringLiteral("Error in IIFE execution:"), t.memberExpression(t.identifier('e'), t.identifier('message'))] )) ])); const finalizerBlock = t.blockStatement(parser.parse(varExportCode).program.body); const tryStmt = t.tryStatement(tryBlock, catchBlock, finalizerBlock); mainBlock.push(tryStmt); // Re-generate the code from AST const instrumentedCode = generator(ast).code; console.log("Executing instrumented IIFE in VM..."); try { vm.runInContext(instrumentedCode, vmCtx, { timeout: 10000 }); console.log("IIFE execution completed (possibly with caught errors)."); } catch (e) { console.error("Error executing IIFE wrapper:", e.message); } function evaluateInVM(codeSnippet) { try { return vm.runInContext(codeSnippet, vmCtx); } catch (e) { return undefined; } } // --------------------------------------------------------- // Step 2: Constant Propagation (Aggressive & Assignment Aware) // --------------------------------------------------------- console.log("Starting constant propagation..."); let constantCount = 0; // 1. Track assignments to all top-level variables const assignmentCounts = new Map(); const invalidCandidates = new Set(); traverse(ast, { AssignmentExpression(path) { if (t.isIdentifier(path.node.left)) { const name = path.node.left.name; assignmentCounts.set(name, (assignmentCounts.get(name) || 0) + 1); } }, UpdateExpression(path) { if (t.isIdentifier(path.node.argument)) { const name = path.node.argument.name; invalidCandidates.add(name); } }, VariableDeclarator(path) { if (t.isIdentifier(path.node.id) && path.node.init) { const name = path.node.id.name; assignmentCounts.set(name, (assignmentCounts.get(name) || 0) + 1); } } }); // 2. Identify constants from VM state const constantBindings = new Map(); // binding -> value for (const key of iifeVars) { if (invalidCandidates.has(key)) continue; const count = assignmentCounts.get(key) || 0; // Allow 1 assignment (init) or 0 (if implicit/VM magic, though unlikely) if (count <= 1) { try { const val = sandbox.window[key]; if (val !== undefined && (typeof val === 'number' || typeof val === 'boolean' || typeof val === 'string')) { const binding = iifeScope.getBinding(key); if (binding) { constantBindings.set(binding, val); } } } catch (e) {} } } console.log(`Identified ${constantBindings.size} global constants (single-assignment).`); // 3. Propagate traverse(ast, { Identifier(path) { // Don't replace declarations or assignments if (path.key === 'id' || path.key === 'params' || path.key === 'property' && !path.parent.computed) return; if (t.isAssignmentExpression(path.parent) && path.parent.left === path.node) return; if (t.isUpdateExpression(path.parent)) return; const binding = path.scope.getBinding(path.node.name); if (binding && constantBindings.has(binding)) { path.replaceWith(t.valueToNode(constantBindings.get(binding))); constantCount++; } } }); console.log(`Propagated ${constantCount} constants.`); // --------------------------------------------------------- // Step 3: Proxy Replacement & String Decoding // --------------------------------------------------------- console.log("Starting proxy replacement and string decoding..."); let replacedCount = 0; // Helper to statically extract array from function function extractArrayFromFunction(funcName, funcNode) { // Case 1: function f() { return ['a', 'b']; } if (funcNode.body.body.length === 1 && t.isReturnStatement(funcNode.body.body[0])) { const arg = funcNode.body.body[0].argument; if (t.isArrayExpression(arg)) { return arg.elements.map(e => t.isLiteral(e) ? e.value : null); } } // Case 2: function f() { var x = ['a']; f = function() { return x; }; return x; } // Akamai style: // function ZhN() { // var wXN = ['XA', ...]; // ZhN = function() { return wXN; }; // return wXN; // } const body = funcNode.body.body; for (const stmt of body) { if (t.isVariableDeclaration(stmt)) { for (const dec of stmt.declarations) { if (t.isArrayExpression(dec.init)) { return dec.init.elements.map(e => t.isLiteral(e) ? e.value : null); } } } } return null; } // Detect array-based string decoders const stringArrays = new Map(); // Debug: Check if ZhN is available try { console.log("Type of ZhN in VM:", evaluateInVM("typeof ZhN")); console.log("Type of cw in VM:", evaluateInVM("typeof cw")); } catch(e) { console.log("Debug check failed:", e.message); } mainBlock.forEach(node => { if (t.isFunctionDeclaration(node) && node.id) { const funcName = node.id.name; let result = null; // Try static extraction first (safer/faster) const staticArray = extractArrayFromFunction(funcName, node); if (staticArray && staticArray.length > 10 && typeof staticArray[0] === 'string') { console.log(`Statically detected string array function: ${funcName} (${staticArray.length} strings)`); result = staticArray; } // Try VM if static failed if (!result) { try { const vmResult = evaluateInVM(`${funcName}()`); if (Array.isArray(vmResult) && vmResult.length > 10 && typeof vmResult[0] === 'string') { console.log(`VM detected string array function: ${funcName} (${vmResult.length} strings)`); result = vmResult; } } catch (e) { // console.log(`Failed to evaluate ${funcName}: ${e.message}`); } } if (result) { stringArrays.set(funcName, result); } } }); const stringHelpers = new Map(); mainBlock.forEach(node => { if (t.isFunctionDeclaration(node) && node.id) { const body = node.body.body; if (body.length === 1 && t.isReturnStatement(body[0])) { const arg = body[0].argument; if (t.isMemberExpression(arg) && t.isCallExpression(arg.object) && t.isIdentifier(arg.object.callee)) { const arrayFuncName = arg.object.callee.name; if (stringArrays.has(arrayFuncName)) { console.log(`Detected string helper: ${node.id.name} -> ${arrayFuncName}`); stringHelpers.set(node.id.name, { arrayFunc: arrayFuncName, array: stringArrays.get(arrayFuncName) }); } } } } }); traverse(ast, { CallExpression(path) { if (isLiteral(path.node)) return; if (t.isIdentifier(path.node.callee) && stringHelpers.has(path.node.callee.name)) { const helper = stringHelpers.get(path.node.callee.name); if (path.node.arguments.length === 1 && isLiteral(path.node.arguments[0])) { let index = -1; if (t.isNumericLiteral(path.node.arguments[0])) index = path.node.arguments[0].value; if (index >= 0 && index < helper.array.length) { path.replaceWith(t.valueToNode(helper.array[index])); replacedCount++; return; } } } // General VM evaluation for other proxies (now that constants are propagated) try { const codeSnippet = generator(path.node).code; if (codeSnippet.length > 100) return; const allSimple = path.node.arguments.every(a => isLiteral(a) || t.isIdentifier(a)); if (!allSimple) return; const ids = codeSnippet.match(/[a-zA-Z_$][a-zA-Z0-9_$]*/g) || []; const allGlobal = ids.every(id => sandbox.window.hasOwnProperty(id) || sandbox.hasOwnProperty(id) || global[id] !== undefined); if (allGlobal) { const result = evaluateInVM(codeSnippet); if (result !== undefined && (typeof result === 'string' || typeof result === 'number' || typeof result === 'boolean')) { path.replaceWith(t.valueToNode(result)); replacedCount++; } } } catch (e) {} }, MemberExpression(path) { if (t.isCallExpression(path.node.object) && t.isIdentifier(path.node.object.callee)) { const funcName = path.node.object.callee.name; if (stringArrays.has(funcName)) { const arr = stringArrays.get(funcName); let index = -1; if (t.isNumericLiteral(path.node.property)) { index = path.node.property.value; } else if (path.node.computed) { // Now that constants are propagated, this might resolve try { const propCode = generator(path.node.property).code; const val = evaluateInVM(propCode); if (typeof val === 'number') index = val; } catch(e) {} } if (index >= 0 && index < arr.length) { path.replaceWith(t.valueToNode(arr[index])); replacedCount++; } } } } }); console.log(`Replaced ${replacedCount} proxy calls/strings.`); // --------------------------------------------------------- // Step 4: Control Flow Unflattening // --------------------------------------------------------- let unflattenedCount = 0; const flattenedFunctions = new Map(); traverse(ast, { Function(path) { if (path.isFunctionDeclaration() || path.isFunctionExpression()) { const info = analyzeFlattenedFunction(path); if (info) { if (path.node.id) flattenedFunctions.set(path.node.id.name, info); if (path.parentPath.isVariableDeclarator()) { flattenedFunctions.set(path.parentPath.node.id.name, info); } } } } }); function analyzeFlattenedFunction(path) { const body = path.get('body'); if (!body.isBlockStatement()) return null; const statements = body.get('body'); let loopStmt = statements.find(p => p.isWhileStatement() || p.isForStatement()); if (!loopStmt) return null; let loopBody = loopStmt.get('body'); if (!loopBody.isBlockStatement()) return null; let switchStmt = loopBody.get('body').find(p => p.isSwitchStatement()); if (!switchStmt) return null; const discriminant = switchStmt.node.discriminant; if (!t.isIdentifier(discriminant)) return null; const stateVarName = discriminant.name; const cases = new Map(); switchStmt.get('cases').forEach(casePath => { const test = casePath.node.test; if (test) { if (t.isLiteral(test)) { cases.set(test.value, casePath); } else { try { const val = evaluateInVM(generator(test).code); if (val !== undefined) cases.set(val, casePath); } catch(e) {} } } }); const params = path.node.params; let entryArgIndex = -1; params.forEach((param, index) => { if (t.isIdentifier(param) && param.name === stateVarName) entryArgIndex = index; }); return { path, stateVarName, cases, entryArgIndex }; } traverse(ast, { CallExpression(path) { const callee = path.node.callee; let funcName = null; if (t.isIdentifier(callee)) funcName = callee.name; if (funcName && flattenedFunctions.has(funcName)) { const info = flattenedFunctions.get(funcName); if (info.entryArgIndex === -1) return; const startStateArg = path.node.arguments[info.entryArgIndex]; if (!startStateArg) return; let startStateVal; if (isLiteral(startStateArg)) startStateVal = startStateArg.value; else { try { startStateVal = evaluateInVM(generator(startStateArg).code); } catch(e) {} } if (startStateVal !== undefined) { console.log(`Unflattening call to ${funcName} with state ${startStateVal}`); const newBody = reconstructPath(info, startStateVal, path.node.arguments); if (newBody && newBody.length > 0) { const iife = t.callExpression( t.functionExpression(null, [], t.blockStatement(newBody)), [] ); t.inheritsComments(iife, path.node); path.replaceWith(iife); unflattenedCount++; } } } } }); function reconstructPath(info, startState, callArgs) { const { stateVarName, cases } = info; let currentState = startState; const nodes = []; const visited = new Set(); let iterations = 0; const MAX_ITERATIONS = 5000; while (iterations < MAX_ITERATIONS) { if (!cases.has(currentState)) break; if (visited.has(currentState)) break; visited.add(currentState); const casePath = cases.get(currentState); const caseBody = casePath.node.consequent; let nextState = null; let hasReturn = false; for (const stmt of caseBody) { if (t.isBreakStatement(stmt)) continue; if (t.isExpressionStatement(stmt) && t.isAssignmentExpression(stmt.expression)) { const left = stmt.expression.left; if (t.isIdentifier(left) && left.name === stateVarName) { try { const operator = stmt.expression.operator; const rightCode = generator(stmt.expression.right).code; const rightVal = evaluateInVM(rightCode); if (rightVal !== undefined) { if (operator === '=') nextState = rightVal; else if (operator === '+=') nextState = currentState + rightVal; else if (operator === '-=') nextState = currentState - rightVal; } else { nextState = null; } } catch (e) {} continue; } } if (t.isReturnStatement(stmt)) hasReturn = true; nodes.push(t.cloneNode(stmt)); } if (hasReturn) break; if (nextState === null) break; currentState = nextState; iterations++; } return nodes; } console.log(`Unflattened ${unflattenedCount} calls.`); // --------------------------------------------------------- // Step 5: Function Inlining // --------------------------------------------------------- console.log("Starting function inlining..."); let inlinedCount = 0; const inlineableFunctions = new Map(); traverse(ast, { VariableDeclarator(path) { if (t.isIdentifier(path.node.id) && t.isFunctionExpression(path.node.init)) { if (isInlineable(path.node.init)) inlineableFunctions.set(path.node.id.name, path.node.init); } }, FunctionDeclaration(path) { if (t.isIdentifier(path.node.id)) { if (isInlineable(path.node)) inlineableFunctions.set(path.node.id.name, path.node); } } }); function isInlineable(node) { const body = node.body.body; if (body.length !== 1) return false; return t.isReturnStatement(body[0]); } traverse(ast, { CallExpression(path) { const callee = path.node.callee; if (t.isIdentifier(callee) && inlineableFunctions.has(callee.name)) { const funcNode = inlineableFunctions.get(callee.name); const returnArg = funcNode.body.body[0].argument; const params = funcNode.params; const args = path.node.arguments; if (args.length >= params.length) { const substitutions = new Map(); params.forEach((param, index) => { if (t.isIdentifier(param)) substitutions.set(param.name, args[index]); }); const safeReplace = (node) => { if (!node) return node; if (t.isIdentifier(node) && substitutions.has(node.name)) return t.cloneNode(substitutions.get(node.name)); if (t.isBinaryExpression(node) || t.isLogicalExpression(node)) { node.left = safeReplace(node.left); node.right = safeReplace(node.right); return node; } if (t.isUnaryExpression(node)) { node.argument = safeReplace(node.argument); return node; } if (t.isCallExpression(node)) { node.callee = safeReplace(node.callee); node.arguments = node.arguments.map(safeReplace); return node; } if (t.isMemberExpression(node)) { node.object = safeReplace(node.object); if (node.computed) node.property = safeReplace(node.property); return node; } return node; }; path.replaceWith(safeReplace(t.cloneNode(returnArg))); inlinedCount++; } } } }); console.log(`Inlined ${inlinedCount} function calls.`); // --------------------------------------------------------- // Step 6: Constant Folding // --------------------------------------------------------- console.log("Starting constant folding..."); let foldedCount = 0; traverse(ast, { BinaryExpression(path) { if (isLiteral(path.node.left) && isLiteral(path.node.right)) { try { const val = evaluateInVM(generator(path.node).code); if (val !== undefined && (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean')) { path.replaceWith(t.valueToNode(val)); foldedCount++; } } catch(e) {} } }, ConditionalExpression(path) { if (isLiteral(path.node.test)) { if (path.node.test.value) path.replaceWith(path.node.consequent); else path.replaceWith(path.node.alternate); foldedCount++; } } }); console.log(`Folded ${foldedCount} expressions.`); // --------------------------------------------------------- // Step 7: Dead Code Elimination // --------------------------------------------------------- console.log("Starting dead code elimination..."); let deadCount = 0; traverse(ast, { Program(path) { path.scope.crawl(); } }); traverse(ast, { VariableDeclarator(path) { const binding = path.scope.getBinding(path.node.id.name); if (binding && !binding.referenced && binding.constant) { if (isPure(path.node.init)) { path.remove(); deadCount++; } } } }); function isPure(node) { if (!node) return true; if (isLiteral(node) || t.isIdentifier(node) || t.isFunction(node)) return true; return false; } console.log(`Removed ${deadCount} unused variables.`); // Output const outputOpts = { compact: false, comments: true }; const output = generator(ast, outputOpts).code; fs.writeFileSync(path.join(__dirname, 'example-1-deobfuscated.js'), output); console.log("Saved to example-1-deobfuscated.js"); } main().catch(err => console.error(err)); what next ?
最新发布
11-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值