已解决:Go Error: cannot use ‘funcName‘ (type func()) as type func(string) in argument to ‘otherFuncName

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁

在这里插入图片描述


🐅🐾 猫头虎建议程序员必备技术栈一览表📖

云原生技术 Cloud Native:

  • 🔥 Golang
  • 🐳 Docker
  • ☸️ Kubernetes
  • ⛵ Helm
  • 🔥 Serverless
  • 🌩️ AWS Lambda
  • ☁️ Google Cloud Functions
  • 📦 Microservices
  • 🚀 Envoy
  • 🌐 Istio
  • 📊 Prometheus

🦄 博客首页:


🪁🍁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬请批评指正!🐅🐾🍁🐥

《已解决:Go Error: cannot use ‘funcName’ (type func()) as type func(string) in argument to ‘otherFuncName’ 问题》 🐾👩‍💻

摘要

喵喵!在云原生的世界里,Go语言是构建强大服务的利爪。但就算是最敏捷的猫头虎博主,也有可能在类型系统的树上遇到障碍。今天,我们遇到了一个类型不匹配的Bug:一个函数的类型与另一个函数期望的类型不一致。别担心,本博主将带领各位在Go的丛林中巧妙跳跃,解决这个cannot use 'funcName' as type func(string)的问题,并分享如何避免未来的类型陷阱。让我们开始这趟Go语言的冒险之旅吧!

引言

在Go语言中,函数类型是一等公民,我们经常会将它们作为参数传递给其他函数。但是,当函数签名不匹配时,Go编译器会抛出错误,提示我们类型不符合预期。这个问题有点像试图让猫头虎穿上不合脚的靴子——不仅不舒服,而且无法继续前行。让我们探索这个Bug背后的原因,并找出解决它的方法。

正文

错误详解

在Go中,每个函数都有一个特定的签名,包括它的参数和返回值。当我们尝试将一个函数作为参数传递给另一个函数,但它们的签名不匹配时,就会出现错误。

错误信息:

cannot use 'funcName' (type func()) as type func(string) in argument to 'otherFuncName'

意味着我们试图将一个不接受参数的函数funcName,传递给一个期望接受一个string类型参数的函数otherFuncName

错误原因

函数签名不匹配

函数funcName的定义可能是这样的:

func funcName() {
    // function body
}

otherFuncName期望的是这样一个函数:

func someFunc(s string) {
    // function body
}

解决方案

调整函数签名

我们需要修改funcName的签名,使其接受一个string类型的参数。

func funcName(s string) {
    // updated function body
}
使用适配器函数

如果不能直接修改funcName函数,我们可以创建一个适配器函数来适配预期的签名。

func adapterFunc(s string) {
    funcName()
}

然后将adapterFunc传递给otherFuncName

如何避免

严格类型检查

在编写函数和调用它们之前,仔细检查预期的类型签名。

代码审查

通过代码审查来确保所有函数调用都符合预期的类型签名。

使用接口

在某些情况下,使用接口可以定义一个函数应满足的行为,这可以增加代码的灵活性。

代码示例

下面是一个简化的示例,展示如何修改函数签名以解决问题:

package main

import "fmt"

// 原始的不符合预期的函数
func funcName() {
    fmt.Println("funcName called without arguments")
}

// 期望的函数
func otherFuncName(f func(string)) {
    f("hello")
}

// 适配器函数
func adapterFunc(s string) {
    funcName()
}

func main() {
    // 使用适配器函数作为参数
    otherFuncName(adapterFunc)
}

实践建议表格

问题建议解决策略代码示例
类型不匹配调整函数签名以匹配预期func funcName(s string) {...}
无法修改函数使用适配器函数func adapterFunc(s string) { funcName() }

总结

面对Go中函数类型不匹配的问题,正确的做法是调整我们的函数,使其符合预期的签名,或者是使用适配器模式来桥接差异。掌握这些技能将使我们能够更好地在Go的类型系统中航行,避免未来可能遇到的类型错误。就像猫头虎在树林中优雅地穿梭,理解并利用Go的类型系统,可以让我们在代码的世界中自由跳跃。

参考资料


希望这篇博客能帮助你解决Go中的函数类型不匹配问题,愿你的代码之路顺畅,喵!🐱💻📚

在这里插入图片描述

原创声明

======= ·

  • 原创作者: 猫头虎
  • 编辑 : GoCloudNative

作者wx: [ libin9iOak ]
公众号:猫头虎技术团队

学习复习

本文为原创文章,版权归作者所有。未经许可,禁止转载、复制或引用。

作者保证信息真实可靠,但不对准确性和完整性承担责任

未经许可,禁止商业用途。

如有疑问或建议,请联系作者。

感谢您的支持与尊重。

点击下方名片,加入IT技术核心学习团队。一起探索科技的未来,共同成长。

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
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值