{
	"id": "20e5b147-a0f3-4ccb-8428-57363b547379",
	"created_at": "2026-04-06T00:11:13.063107Z",
	"updated_at": "2026-04-10T13:12:43.662012Z",
	"deleted_at": null,
	"sha1_hash": "28b92810e52d2a150bcdfeda311423a2c2af094d",
	"title": "Deobfuscating JavaScript Malware Using Abstract Syntax Trees",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1054513,
	"plain_text": "Deobfuscating JavaScript Malware Using Abstract Syntax Trees\r\nArchived: 2026-04-05 13:38:26 UTC\r\nHappy Diwali!\r\nAt the time of publishing this blog, it is the Hindu festival of Diwali, which symbolizes the victory of light over\r\ndarkness. I wish you and your family a very happy and prosperous Diwali!\r\nImage credits: https://i.pinimg.com/474x/ab/3a/46/ab3a46973f797165f2b2f3776405b21c.jpg\r\nMintsLoader SHA256: 013296e33be6f884abaaeabab90a3d3089b7411c7f934f5d978917b3b789a635\r\nTable of Contents\r\nhttps://nikhilh-20.github.io/blog/deob_js_ast/\r\nPage 1 of 17\n\nIntroduction\r\nAbstract Syntax Tree\r\nInstalling Prequisites\r\nDeobfuscation using ASTs\r\nRemove Comment Lines\r\nRemove Unused Variables\r\nSimplifying Functions\r\nSignaturizing ASTs\r\nBringing It All Together to Deobfuscate MintsLoader\r\nSummary\r\nIntroduction\r\nMalware based on scripting languages such as VBScript and JavaScript (JS) can be heavily obfuscated. Analyzing\r\nthese malware statically may take considerable time and can be, generally, a frustrating experience for analysts.\r\nIn this blog, we’ll explore how abstract syntax trees (ASTs) can be leveraged to deobfuscate JS-based malware.\r\nWe’ll then apply these techniques to partially deobfuscate a recently reported JS-based malware called\r\nMintsLoader.\r\nDisclosure: The code was generated through a LLM. I provided the ideas and directed the model to create the\r\nrelevant code. Without this assistance, it would have taken me longer to codify the concept, given that I’m not\r\ndeeply versed in JS.\r\nAbstract Syntax Trees\r\nWhen we look at any language we understand—whether a natural language like English or a programming\r\nlanguage like JavaScript—we can identify the individual elements that make up a sentence or statement of that\r\nlanguage. For example, consider this English sentence:\r\nSome people can break down this sentence into parts of speech like nouns and verbs. Others may not know each\r\nelement’s label but can still recognize the individual components of the sentence.\r\nIn compiler theory, each of these individual elements is called a token , and the process of breaking source code\r\ninto tokens is known as tokenization , or lexical analysis . A token might be a mathematical operator like\r\n+ , an identifier like a variable name, etc. Naturally, each language has its own set of tokens.\r\nOnce source code is broken into tokens, we need a way to preserve its syntactical structure. For example, it would\r\nbe problematic if the structure of the sentence, I ate an apple , were lost during compilation.\r\nAn AST represents this syntactical structure in a tree format, with each node representing a syntactic construct in\r\nthe source code. Consider the following JS source code:\r\nlet x = 6;\r\nlet y = x + 2;\r\nhttps://nikhilh-20.github.io/blog/deob_js_ast/\r\nPage 2 of 17\n\nThe figure below shows two AST visualizations. The one on the left was generated using AST Explorer, and the\r\none on the right was generated using JointJs. Each node represents a syntactic construct. In this example:\r\nVariableDeclarator associates an identifier (variable name) with a value or expression.\r\nLiteral represents a fixed value like a number, string, or boolean.\r\nBinaryExpression represents an expression with two operands and an operator, like addition ( + ).\r\nAnd that is the extent of my knowledge about ASTs. With just this basic information, we are now capable of\r\nperforming JavaScript deobfuscation.\r\nInstalling Prequisites\r\nhttps://nikhilh-20.github.io/blog/deob_js_ast/\r\nPage 3 of 17\n\nIn this blog, our goal is to develop JS deobfuscation tooling, which will require certain JS libraries. I’m using a\r\nWindows 10 host and npm for installing these libraries.\r\nThere are 3 libraries which we need to install:\r\nesprima: a library for generating ASTs from JS code.\r\nestraverse: a library for traversing and analyzing a given AST.\r\nescodegen: a library for converting an AST back into source code.\r\n\u003e npm install esprima estraverse escodegen\r\nadded 5 packages, and audited 6 packages in 979ms\r\nfound 0 vulnerabilities\r\nDeobfuscation using ASTs\r\nConsider the following JS source code:\r\n// This is a comment\r\nvar x = 6;\r\nvar y = 2;\r\nOur goal is to transform the source code into an AST, print the variable names, and then transform the AST back\r\ninto source code. The JS code below achieves this.\r\nconst esprima = require(\"esprima\");\r\nconst estraverse = require(\"estraverse\");\r\nconst escodegen = require(\"escodegen\");\r\nconst fs = require(\"fs\");\r\nfunction print_variable_names(ast)\r\n{\r\n estraverse.traverse(ast, {\r\n enter(node, parent)\r\n {\r\n if (node.type === \"Identifier\")\r\n console.log(node.name);\r\n }\r\n });\r\n}\r\nfunction process_js_file(input_path)\r\n{\r\n // Read source code from input JS file\r\nhttps://nikhilh-20.github.io/blog/deob_js_ast/\r\nPage 4 of 17\n\nconst code = fs.readFileSync(input_path, \"utf-8\");\r\n console.log(\"Source code in file:\");\r\n console.log(code);\r\n \r\n // Generate AST\r\n const ast = esprima.parseScript(code);\r\n \r\n // Print variable names by traversing AST\r\n console.log(\"\\n\\nVariable names:\");\r\n print_variable_names(ast);\r\n \r\n // Generate JS source code from AST\r\n const cleaned_code = escodegen.generate(ast);\r\n console.log(\"\\n\\nSource code:\");\r\n console.log(cleaned_code);\r\n}\r\nprocess_js_file(\"test.js\");\r\nThe output of the above is shown below. An astute reader will notice that the source code generated from the AST\r\nno longer contains comments. Simply transforming JS source code into an AST and then back removes comment\r\nlines, which are often used in JS-based malware as an anti-analysis layer for obfuscation and to artificially\r\nincrease file size.\r\n\u003e node .\\testBuilder.js\r\nSource code in file:\r\n// This is a comment\r\nvar x = 6;\r\nvar y = 2;\r\nVariable names:\r\nx\r\ny\r\nSource code:\r\nvar x = 6;\r\nvar y = 2;\r\nRemove Unused Variables\r\nConsider the JS source code below. It is obvious that the variables y and z are initialized once and never\r\nreused. This kind of code falls under the umbrella of junk code. Like comments, these kinds of junk code serve as\r\nhttps://nikhilh-20.github.io/blog/deob_js_ast/\r\nPage 5 of 17\n\nan obfuscation layer, but they are more problematic because you must check the entire source code to ensure they\r\nhaven’t been referenced elsewhere.\r\nvar x = 6;\r\nvar y = 2;\r\nvar z = x + 2;\r\nx += 2;\r\nOur goal is to transform the source code into an AST, identify unused variables, remove them from the AST, and\r\nthen transform the AST back into source code. The JS code below achieves this.\r\nconst esprima = require(\"esprima\");\r\nconst estraverse = require(\"estraverse\");\r\nconst escodegen = require(\"escodegen\");\r\nconst fs = require(\"fs\");\r\nfunction find_declared_variables(ast) {\r\n const declared_vars = new Set();\r\n estraverse.traverse(ast, {\r\n enter(node)\r\n {\r\n // Find variable declarations and track their names\r\n if (node.type === \"VariableDeclarator\" \u0026\u0026 node.id.type === \"Identifier\")\r\n declared_vars.add(node.id.name);\r\n }\r\n });\r\n return declared_vars;\r\n}\r\nfunction find_used_variables(ast) {\r\n const used_vars = new Set();\r\n estraverse.traverse(ast, {\r\n enter(node, parent)\r\n {\r\n // Only add variables to used_vars if current node is not a declaration statement\r\n if (node.type === \"Identifier\" \u0026\u0026 !(parent \u0026\u0026 parent.type === \"VariableDeclarator\" \u0026\u0026 parent.id ===\r\n used_vars.add(node.name);\r\n }\r\n });\r\n \r\n return used_vars;\r\n}\r\nhttps://nikhilh-20.github.io/blog/deob_js_ast/\r\nPage 6 of 17\n\nfunction remove_unused_variables(ast, declared_vars, used_vars) {\r\n // unused_vars are those variables which were declared but not reused\r\n const unused_vars = new Set([...declared_vars].filter(var_name =\u003e !used_vars.has(var_name)));\r\n estraverse.replace(ast, {\r\n enter(node)\r\n {\r\n // Get the declaration statement\r\n if (node.type === \"VariableDeclaration\") {\r\n /* Remove declaration statements of unused variables and replace the current node's\r\n declarations with the result */\r\n node.declarations = node.declarations.filter(decl =\u003e !unused_vars.has(decl.id.name));\r\n // If the unused variable was the only declaration, then remove the entire statement\r\n if (node.declarations.length === 0)\r\n // Remove current node from parent array\r\n return this.remove();\r\n }\r\n }\r\n });\r\n}\r\nfunction process_js_file(input_path)\r\n{\r\n // Read source code from input JS file\r\n const code = fs.readFileSync(input_path, \"utf-8\");\r\n console.log(\"Source code in file:\");\r\n console.log(code);\r\n \r\n // Generate AST\r\n const ast = esprima.parseScript(code);\r\n \r\n // Find all declared and used variables\r\n // Then remove unused variables based on the difference\r\n const declared_vars = find_declared_variables(ast);\r\n const used_vars = find_used_variables(ast);\r\n remove_unused_variables(ast, declared_vars, used_vars);\r\n \r\n // Generate JS source code from AST\r\n const cleaned_code = escodegen.generate(ast);\r\n console.log(\"\\n\\nCleaned source code:\");\r\n console.log(cleaned_code);\r\n}\r\nprocess_js_file(\"test.js\");\r\nThe output of the above is shown below. As we can see, the unused variables y and z no longer appear in the\r\ncleaned code.\r\nhttps://nikhilh-20.github.io/blog/deob_js_ast/\r\nPage 7 of 17\n\n\u003e node .\\testBuilder.js\r\nSource code in file:\r\nvar x = 6;\r\nvar y = 2;\r\nvar z = x + 2;\r\nx += 2;\r\nCleaned source code:\r\nvar x = 6;\r\nx += 2;\r\nSimplifying Functions\r\nConsider the JS source code below. To someone knowledgeable in JS, it is quickly apparent that wrapper_func()\r\nsimply returns the array stored in variable x . However, this is still a roundabout way of implementing the\r\nfunction. Like the other techniques discussed previously, it serves as an obfuscation layer, consuming more of an\r\nanalyst’s time.\r\nfunction wrapper_func() {\r\n var x = [\"this\", \"is\", \"a\", \"wrapper\"];\r\n wrapper_func = function () { return x; };\r\n return wrapper_func();\r\n}\r\nvar x = wrapper_func();\r\nOur goal is to transform the source code into an AST, simplify functions like the one above, and then transform\r\nthe AST back into source code. The JS code below achieves this.\r\nconst esprima = require(\"esprima\");\r\nconst estraverse = require(\"estraverse\");\r\nconst escodegen = require(\"escodegen\");\r\nconst fs = require(\"fs\");\r\nfunction simplify_self_invoking_functions(ast) {\r\n estraverse.replace(ast, {\r\n enter(node)\r\n {\r\n // Check if a function has exactly three statements\r\n if (node.type === \"FunctionDeclaration\" \u0026\u0026 node.body.body.length === 3)\r\n {\r\n const [var_decl, self_assign, return_call] = node.body.body;\r\n // Check if the first statement is a variable declaration\r\nhttps://nikhilh-20.github.io/blog/deob_js_ast/\r\nPage 8 of 17\n\nif (var_decl.type === \"VariableDeclaration\" \u0026\u0026 var_decl.declarations.length === 1)\r\n {\r\n const var_declarator = var_decl.declarations[0];\r\n /* Check if the second statement assigns the current function to a different function\r\n that only returns the previously declared variable */\r\n if (self_assign.type === \"ExpressionStatement\" \u0026\u0026\r\n self_assign.expression.type === \"AssignmentExpression\" \u0026\u0026\r\n self_assign.expression.operator === \"=\" \u0026\u0026\r\n self_assign.expression.left.name === node.id.name \u0026\u0026\r\n self_assign.expression.right.type === \"FunctionExpression\" \u0026\u0026\r\n self_assign.expression.right.body.body.length === 1 \u0026\u0026\r\n self_assign.expression.right.body.body[0].type === \"ReturnStatement\" \u0026\u0026\r\n self_assign.expression.right.body.body[0].argument.name === var_declarator.id.name)\r\n {\r\n // Check if the third statement is a return statement that calls the modified function\r\n if (return_call.type === \"ReturnStatement\" \u0026\u0026\r\n return_call.argument.type === \"CallExpression\" \u0026\u0026\r\n return_call.argument.callee.name === node.id.name)\r\n {\r\n /* All conditions have been satisfied. Simplify the current function to a\r\n single return statement returning the variable's initialization value */\r\n return {\r\n type: \"FunctionDeclaration\",\r\n id: node.id,\r\n params: [],\r\n body: {\r\n type: \"BlockStatement\",\r\n body: [\r\n {\r\n type: \"ReturnStatement\",\r\n argument: var_declarator.init,\r\n },\r\n ],\r\n },\r\n generator: false,\r\n expression: false,\r\n async: false,\r\n };\r\n }\r\n }\r\n }\r\n }\r\n },\r\n });\r\n}\r\nhttps://nikhilh-20.github.io/blog/deob_js_ast/\r\nPage 9 of 17\n\nfunction process_js_file(input_path)\r\n{\r\n // Read source code from input JS file\r\n const code = fs.readFileSync(input_path, \"utf-8\");\r\n console.log(\"Source code in file:\");\r\n console.log(code);\r\n \r\n // Generate AST\r\n const ast = esprima.parseScript(code);\r\n \r\n // Simplify self-invoking functions\r\n simplify_self_invoking_functions(ast);\r\n \r\n // Generate JS source code from AST\r\n const cleaned_code = escodegen.generate(ast);\r\n console.log(\"\\n\\nCleaned source code:\");\r\n console.log(cleaned_code);\r\n}\r\nprocess_js_file(\"test.js\");\r\nThe output of the above is shown below. As we can see, the obfuscated function has been simplified.\r\n\u003e node .\\testBuilder.js\r\nSource code in file:\r\nfunction wrapper_func() {\r\n var x = [\"this\", \"is\", \"a\", \"wrapper\"];\r\n wrapper_func = function () { return x; };\r\n return wrapper_func();\r\n}\r\nvar x = wrapper_func();\r\nCleaned source code:\r\nfunction wrapper_func() {\r\n return [\r\n 'this',\r\n 'is',\r\n 'a',\r\n 'wrapper'\r\n ];\r\n}\r\nvar x = wrapper_func();\r\nSignaturizing ASTs\r\nhttps://nikhilh-20.github.io/blog/deob_js_ast/\r\nPage 10 of 17\n\nConsider the JS source code below. We would like to analyze the function func_name once and then\r\nprogrammatically recognize it the next time we encounter it. After analysis, I determined that func_name decodes\r\na hex string into a decimal array.\r\nfunction func_name(arg) {\r\n if (typeof arg !== \"string\") {\r\n throw new Error(\"Check input\");\r\n }\r\n if (arg.length % 2 !== 0) {\r\n throw new Error(\"Incorrect length\");\r\n }\r\n var decimal_array = [];\r\n for (var i = 0; i \u003c arg.length; i += 2) {\r\n var slice = arg.substr(i, 2);\r\n var decimal_val = 0;\r\n for (var j = 0; j \u003c slice.length; j++) {\r\n decimal_val \u003c\u003c= 4;\r\n var c = slice.charAt(j);\r\n if (c \u003e= \"0\" \u0026\u0026 c \u003c= \"9\") {\r\n decimal_val |= (c.charCodeAt(0) - \"0\".charCodeAt(0));\r\n } else if (c \u003e= \"A\" \u0026\u0026 c \u003c= \"F\") {\r\n decimal_val |= (c.charCodeAt(0) - \"A\".charCodeAt(0) + 10);\r\n } else if (c \u003e= \"a\" \u0026\u0026 c \u003c= \"f\") {\r\n decimal_val |= (c.charCodeAt(0) - \"a\".charCodeAt(0) + 10);\r\n } else {\r\n throw new Error(\"Throw something\");\r\n }\r\n }\r\nconsole.log(decimal_val)\r\n decimal_array.push(decimal_val);\r\n }\r\n return decimal_array;\r\n}\r\nvar x = func_name(\"4D616C776172652026204D7573696E6773\")\r\nOn execution, x will contain the below decimal array.\r\n[\r\n 77, 97, 108, 119, 97, 114,\r\n 101, 32, 38, 32, 77, 117,\r\nhttps://nikhilh-20.github.io/blog/deob_js_ast/\r\nPage 11 of 17\n\n115, 105, 110, 103, 115\r\n]\r\nOur goal is to transform the source code into an AST, identify the sequence of statements (in other words, a\r\nsignature) that represents hex decoding, rename the function and its references accordingly, and then transform the\r\nAST back into source code. The JS code below achieves this.\r\nconst esprima = require(\"esprima\");\r\nconst estraverse = require(\"estraverse\");\r\nconst escodegen = require(\"escodegen\");\r\nconst fs = require(\"fs\");\r\nfunction matches_hex_decode_signature(function_node) {\r\n let step = 0;\r\n let numeric = false, upper_alpha = false, lower_alpha = false\r\n estraverse.traverse(function_node.body, {\r\n enter(node)\r\n {\r\n switch (step)\r\n {\r\n case 0:\r\n // The first pattern to match is the update expression in the for loop\r\n if (node.type === \"ForStatement\" \u0026\u0026\r\n node.update \u0026\u0026 node.update.type === \"AssignmentExpression\" \u0026\u0026\r\n node.update.operator === \"+=\" \u0026\u0026\r\n node.update.right.type === \"Literal\" \u0026\u0026 node.update.right.value === 2)\r\n step++;\r\n break;\r\n case 1:\r\n // The second pattern to match is a substr() call\r\n if (node.type === \"CallExpression\" \u0026\u0026\r\n node.callee \u0026\u0026 node.callee.property \u0026\u0026 node.callee.property.name === \"substr\" \u0026\u0026\r\n node.arguments.length === 2 \u0026\u0026 node.arguments[1].type === \"Literal\" \u0026\u0026\r\n node.arguments[1].value === 2)\r\n step++;\r\n break;\r\n case 2:\r\n // The third pattern to match is the update expression in another for loop\r\n if (node.type === \"ForStatement\" \u0026\u0026\r\n node.update \u0026\u0026 node.update.type === \"UpdateExpression\" \u0026\u0026\r\n node.update.operator === \"++\")\r\n step++;\r\n break;\r\nhttps://nikhilh-20.github.io/blog/deob_js_ast/\r\nPage 12 of 17\n\ncase 3:\r\n // The fourth pattern to match is a charAt() call\r\n if (node.type === \"CallExpression\" \u0026\u0026\r\n node.callee \u0026\u0026 node.callee.property \u0026\u0026 node.callee.property.name === \"charAt\" \u0026\u0026\r\n node.arguments.length === 1 \u0026\u0026 node.arguments[0].type === \"Identifier\")\r\n step++;\r\n break;\r\n case 4:\r\n /* The fifth, sixth and seventh patterns can occur in any order and involve comparison with\r\n alphanumeric characters */\r\n if (node.type === \"BinaryExpression\") {\r\n if ((node.operator === \"\u003e=\" \u0026\u0026 node.right.type === \"Literal\" \u0026\u0026 node.right.value === \"0\"\r\n (node.operator === \"\u003c=\" \u0026\u0026 node.right.type === \"Literal\" \u0026\u0026 node.right.value === \"9\"\r\n numeric = true;\r\n if ((node.operator === \"\u003e=\" \u0026\u0026 node.right.type === \"Literal\" \u0026\u0026 node.right.value === \"A\"\r\n (node.operator === \"\u003c=\" \u0026\u0026 node.right.type === \"Literal\" \u0026\u0026 node.right.value === \"F\"\r\n upper_alpha = true;\r\n if ((node.operator === \"\u003e=\" \u0026\u0026 node.right.type === \"Literal\" \u0026\u0026 node.right.value === \"a\"\r\n (node.operator === \"\u003c=\" \u0026\u0026 node.right.type === \"Literal\" \u0026\u0026 node.right.value === \"f\"\r\n lower_alpha = true;\r\n }\r\n if (numeric \u0026\u0026 upper_alpha \u0026\u0026 lower_alpha)\r\n step++;\r\n break;\r\n }\r\n }\r\n });\r\n // Only return True if all steps in the sequence matched\r\n return step === 5;\r\n}\r\nfunction rename_function(ast) {\r\n let original_function_name = null;\r\n // First pass: rename the function itself if it matches the signature\r\n estraverse.traverse(ast, {\r\n enter(node)\r\n {\r\n if (node.type === \"FunctionDeclaration\") {\r\n if (matches_hex_decode_signature(node)) {\r\nhttps://nikhilh-20.github.io/blog/deob_js_ast/\r\nPage 13 of 17\n\noriginal_function_name = node.id.name;\r\n node.id.name = \"hex_decode\";\r\n }\r\n }\r\n },\r\n });\r\n // Second pass: rename any CallExpression references to the original function\r\n if (original_function_name) {\r\n estraverse.traverse(ast, {\r\n enter(node)\r\n {\r\n if (node.type === \"CallExpression\" \u0026\u0026\r\n node.callee.type === \"Identifier\" \u0026\u0026\r\n node.callee.name === original_function_name)\r\n node.callee.name = \"hex_decode\";\r\n },\r\n });\r\n }\r\n}\r\nfunction process_js_file(input_path)\r\n{\r\n // Read source code from input JS file\r\n const code = fs.readFileSync(input_path, \"utf-8\");\r\n console.log(\"Source code in file:\");\r\n console.log(code);\r\n \r\n // Generate AST\r\n const ast = esprima.parseScript(code);\r\n \r\n // Rename function based on signature\r\n rename_function(ast);\r\n \r\n // Generate JS source code from AST\r\n const cleaned_code = escodegen.generate(ast);\r\n console.log(\"\\n\\nCleaned source code:\");\r\n console.log(cleaned_code);\r\n}\r\nprocess_js_file(\"test.js\");\r\nThe output of the above is shown below. This can be considered function similarity detection via signatures. There\r\nare two important points to note:\r\nThe above “signature” is not the only viable one; another analyst might write a different signature, and\r\nthat’s okay.\r\nhttps://nikhilh-20.github.io/blog/deob_js_ast/\r\nPage 14 of 17\n\nDifferent implementations of the function may require different signatures, meaning this detection\r\nmechanism requires significant maintenance.\r\n\u003e node .\\testBuilder.js\r\nSource code in file:\r\nfunction func_name(arg) {\r\n if (typeof arg !== \"string\") {\r\n throw new Error(\"Check input\");\r\n }\r\n if (arg.length % 2 !== 0) {\r\n throw new Error(\"Incorrect length\");\r\n }\r\n var decimal_array = [];\r\n for (var i = 0; i \u003c arg.length; i += 2) {\r\n var slice = arg.substr(i, 2);\r\n var decimal_val = 0;\r\n for (var j = 0; j \u003c slice.length; j++) {\r\n decimal_val \u003c\u003c= 4;\r\n var c = slice.charAt(j);\r\n if (c \u003e= \"0\" \u0026\u0026 c \u003c= \"9\") {\r\n decimal_val |= (c.charCodeAt(0) - \"0\".charCodeAt(0));\r\n } else if (c \u003e= \"A\" \u0026\u0026 c \u003c= \"F\") {\r\n decimal_val |= (c.charCodeAt(0) - \"A\".charCodeAt(0) + 10);\r\n } else if (c \u003e= \"a\" \u0026\u0026 c \u003c= \"f\") {\r\n decimal_val |= (c.charCodeAt(0) - \"a\".charCodeAt(0) + 10);\r\n } else {\r\n throw new Error(\"Throw something\");\r\n }\r\n }\r\n console.log(decimal_val)\r\n decimal_array.push(decimal_val);\r\n }\r\n return decimal_array;\r\n}\r\nvar x = func_name(\"4D616C776172652026204D7573696E6773\")\r\nCleaned source code:\r\nfunction hex_decode(arg) {\r\n if (typeof arg !== 'string') {\r\n throw new Error('Check input');\r\nhttps://nikhilh-20.github.io/blog/deob_js_ast/\r\nPage 15 of 17\n\n}\r\n if (arg.length % 2 !== 0) {\r\n throw new Error('Incorrect length');\r\n }\r\n var decimal_array = [];\r\n for (var i = 0; i \u003c arg.length; i += 2) {\r\n var slice = arg.substr(i, 2);\r\n var decimal_val = 0;\r\n for (var j = 0; j \u003c slice.length; j++) {\r\n decimal_val \u003c\u003c= 4;\r\n var c = slice.charAt(j);\r\n if (c \u003e= '0' \u0026\u0026 c \u003c= '9') {\r\n decimal_val |= c.charCodeAt(0) - '0'.charCodeAt(0);\r\n } else if (c \u003e= 'A' \u0026\u0026 c \u003c= 'F') {\r\n decimal_val |= c.charCodeAt(0) - 'A'.charCodeAt(0) + 10;\r\n } else if (c \u003e= 'a' \u0026\u0026 c \u003c= 'f') {\r\n decimal_val |= c.charCodeAt(0) - 'a'.charCodeAt(0) + 10;\r\n } else {\r\n throw new Error('Throw something');\r\n }\r\n }\r\n console.log(decimal_val);\r\n decimal_array.push(decimal_val);\r\n }\r\n return decimal_array;\r\n}\r\nvar x = hex_decode('4D616C776172652026204D7573696E6773');\r\nBringing It All Together to Deobfuscate MintsLoader\r\nThe MintsLoader JS code contains all of the obfuscation measures we previously examined. By combining our\r\ndeobfuscation measures into a single script, the following figure shows the before-and-after comparsion.\r\nhttps://nikhilh-20.github.io/blog/deob_js_ast/\r\nPage 16 of 17\n\nSummary\r\nIn this blog, we explored the potential of ASTs for deobfuscating JS-based malware. Some of the techniques\r\ndescribed are more resilient to new implementations, while others, such as AST signatures, are less resilient.\r\nSource: https://nikhilh-20.github.io/blog/deob_js_ast/\r\nhttps://nikhilh-20.github.io/blog/deob_js_ast/\r\nPage 17 of 17",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://nikhilh-20.github.io/blog/deob_js_ast/"
	],
	"report_names": [
		"deob_js_ast"
	],
	"threat_actors": [],
	"ts_created_at": 1775434273,
	"ts_updated_at": 1775826763,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/28b92810e52d2a150bcdfeda311423a2c2af094d.pdf",
		"text": "https://archive.orkl.eu/28b92810e52d2a150bcdfeda311423a2c2af094d.txt",
		"img": "https://archive.orkl.eu/28b92810e52d2a150bcdfeda311423a2c2af094d.jpg"
	}
}