Skip to content

Commit

Permalink
split code
Browse files Browse the repository at this point in the history
  • Loading branch information
echo094 committed Dec 1, 2024
1 parent cc97a64 commit 48663b3
Show file tree
Hide file tree
Showing 13 changed files with 1,890 additions and 1,708 deletions.
1,733 changes: 25 additions & 1,708 deletions src/plugin/jsconfuser.js

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions src/utility/check-func.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
function checkPattern(code, pattern) {
let i = 0
let j = 0
while (i < code.length && j < pattern.length) {
if (code[i] == pattern[j]) {
++j
}
++i
}
return j == pattern.length
}

module.exports = {
checkPattern,
}
72 changes: 72 additions & 0 deletions src/utility/safe-func.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
const t = require('@babel/types')

function safeDeleteNode(name, path) {
let binding
if (path.isFunctionDeclaration()) {
binding = path.parentPath.scope.getBinding(name)
} else {
binding = path.scope.getBinding(name)
}
if (!binding) {
return false
}
binding.scope.crawl()
binding = binding.scope.getBinding(name)
if (binding.references) {
return false
}
for (const item of binding.constantViolations) {
item.remove()
}
const decl = binding.path
if (decl.removed) {
return true
}
if (!decl.isVariableDeclarator() && !decl.isFunctionDeclaration()) {
return true
}
binding.path.remove()
return true
}

function safeGetLiteral(path) {
if (path.isUnaryExpression()) {
if (path.node.operator === '-' && path.get('argument').isNumericLiteral()) {
return -1 * path.get('argument').node.value
}
return null
}
if (path.isLiteral()) {
return path.node.value
}
return null
}

function safeGetName(path) {
if (path.isIdentifier()) {
return path.node.name
}
if (path.isLiteral()) {
return path.node.value
}
return null
}

function safeReplace(path, value) {
if (typeof value === 'string') {
path.replaceWith(t.stringLiteral(value))
return
}
if (typeof value === 'number') {
path.replaceWith(t.numericLiteral(value))
return
}
path.replaceWithSourceString(value)
}

module.exports = {
safeDeleteNode,
safeGetLiteral,
safeGetName,
safeReplace,
}
53 changes: 53 additions & 0 deletions src/visitor/jsconfuser/anti-tooling.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const t = require('@babel/types')

function deAntiToolingCheckFunc(path) {
if (path.node.params.length) {
return false
}
const body = path.node.body
if (!t.isBlockStatement(body)) {
return false
}
if (body.body.length) {
return false
}
return true
}

function deAntiToolingExtract(path, func_name) {
let binding = path.scope.getBinding(func_name)
for (let ref of binding.referencePaths) {
if (!ref.parentPath.isCallExpression() || !ref.key === 'callee') {
continue
}
const call = ref.parentPath
if (!call.listKey === 'body') {
continue
}
for (let node of call.node.arguments) {
call.insertBefore(node)
}
call.remove()
}
binding.scope.crawl()
binding = path.scope.getBinding(func_name)
if (binding.references === 0) {
path.remove()
}
}

const deAntiTooling = {
FunctionDeclaration(path) {
const func_name = path.node.id?.name
if (!func_name) {
return
}
if (!deAntiToolingCheckFunc(path)) {
return
}
console.log(`AntiTooling Func Name: ${func_name}`)
deAntiToolingExtract(path, func_name)
},
}

module.exports = deAntiTooling
182 changes: 182 additions & 0 deletions src/visitor/jsconfuser/control-flow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
const safeFunc = require('../../utility/safe-func')
const safeGetLiteral = safeFunc.safeGetLiteral
const safeGetName = safeFunc.safeGetName
const safeReplace = safeFunc.safeReplace

function checkControlVar(path) {
const parent = path.parentPath
if (path.key !== 'right' || !parent.isAssignmentExpression()) {
return false
}
const var_path = parent.get('left')
const var_name = var_path.node?.name
if (!var_name) {
return false
}
let root_path = parent.parentPath
if (root_path.isExpressionStatement) {
root_path = root_path.parentPath
}
const binding = parent.scope.getBinding(var_name)
for (const ref of binding.referencePaths) {
if (ref === var_path) {
continue
}
let cur = ref
let valid = false
while (cur && cur !== root_path) {
if (cur.isSwitchCase() || cur === path) {
valid = true
break
}
cur = cur.parentPath
}
if (!valid) {
return false
}
if (ref.key === 'object') {
const prop = ref.parentPath.get('property')
if (!prop.isLiteral() && !prop.isIdentifier()) {
return false
}
continue
}
if (ref.key === 'right') {
const left = ref.parentPath.get('left')
if (!left.isMemberExpression()) {
return false
}
const obj = safeGetName(left.get('object'))
if (obj !== var_name) {
return false
}
continue
}
}
return true
}

/**
* Process the constant properties in the controlVar
*
* Template:
* ```javascript
* controlVar = {
* // strings
* key_string: 'StringLiteral',
* // numbers
* key_number: 'NumericLiteral',
* }
* ```
*
* Some kinds of deadCode may in inserted to the fake chunks:
*
* ```javascript
* controlVar = false
* controlVar = undefined
* controlVar[randomControlKey] = undefined
* delete controlVar[randomControlKey]
* ```
*/
const deControlFlowFlatteningStateless = {
ObjectExpression(path) {
if (!checkControlVar(path)) {
return
}
const parent = path.parentPath
const var_name = parent.get('left').node?.name
console.log(`[ControlFlowFlattening] parse stateless in obj: ${var_name}`)
const props = {}
const prop_num = path.node.properties.length
for (let i = 0; i < prop_num; ++i) {
const prop = path.get(`properties.${i}`)
const key = safeGetName(prop.get('key'))
const value = safeGetLiteral(prop.get('value'))
if (!key || !value) {
continue
}
props[key] = value
}
const binding = parent.scope.getBinding(var_name)
for (const ref of binding.referencePaths) {
if (ref.key !== 'object') {
continue
}
const prop = safeGetName(ref.parentPath.get('property'))
if (!prop) {
continue
}
if (!Object.prototype.hasOwnProperty.call(props, prop)) {
continue
}
const upper = ref.parentPath
if (upper.key === 'left' && upper.parentPath.isAssignmentExpression()) {
// this is in the fake chunk
ref.parentPath.parentPath.remove()
continue
}
safeReplace(ref.parentPath, props[prop])
}
binding.scope.crawl()
},
}

/**
*
* Template:
* ```javascript
* flaggedLabels = {
* currentLabel: { flagKey: 'xxx', flagValue : 'true or false' }
* }
* labelToStates[chunk[i].label] = stateValues: [] => caseStates[i]
* initStateValues = labelToStates[startLabel]
* endState
* chunks = [
* {
* body: [
* {
* type: "GotoStatement",
* label: "END_LABEL",
* }
* ],
* }
* {
* label: "END_LABEL",
* body: [],
* }
* ]
* while (stateVars) {
* switch (stateVars) {
* // fake assignment expression
* case fake_assignment: {
* stateVar = 'rand'
* // 'GotoStatement label'
* }
* // clone chunks
* case fake_clone: {
* // contain a real chunk
* }
* // fake jumps
* case real_1: {
* if (false) {
* // 'GotoStatement label'
* }
* // follow with real statements
* }
* }
* }
* The key may exist in its parent's map
* ```
*/
const deControlFlowFlatteningState = {
ObjectExpression(path) {
if (!checkControlVar(path)) {
return
}
},
}

module.exports = {
deControlFlowFlatteningStateless,
deControlFlowFlatteningState,
}
Loading

0 comments on commit 48663b3

Please sign in to comment.