-
Notifications
You must be signed in to change notification settings - Fork 343
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
1,890 additions
and
1,708 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
} |
Oops, something went wrong.