mirror of
https://github.com/smallfawn/decode_action.git
synced 2025-12-19 16:25:10 +08:00
212 lines
5.1 KiB
JavaScript
212 lines
5.1 KiB
JavaScript
const t = require('@babel/types')
|
|
|
|
function mergeObject(path) {
|
|
const { id, init } = path.node
|
|
if (!t.isObjectExpression(init)) {
|
|
// 判断是否是定义对象
|
|
return
|
|
}
|
|
let name = id.name
|
|
let scope = path.scope
|
|
let binding = scope.getBinding(name)
|
|
const start = path.node.end
|
|
let end = -1
|
|
let violation = null
|
|
if (!binding.constant) {
|
|
// Find the first constantViolation after this declaration
|
|
for (let item of binding.constantViolations) {
|
|
if (item.node.start <= start) {
|
|
continue
|
|
}
|
|
if (item.isVariableDeclarator()) {
|
|
end = item.node.start
|
|
violation = item
|
|
break
|
|
}
|
|
if (item.isAssignmentExpression()) {
|
|
end = item.node.start
|
|
violation = item
|
|
break
|
|
}
|
|
return
|
|
}
|
|
}
|
|
// 添加已有的key
|
|
let keys = {}
|
|
let properties = init.properties
|
|
for (let prop of properties) {
|
|
let key = null
|
|
if (t.isStringLiteral(prop.key)) {
|
|
key = prop.key.value
|
|
}
|
|
if (t.isIdentifier(prop.key)) {
|
|
key = prop.key.name
|
|
}
|
|
if (key) {
|
|
keys[key] = true
|
|
}
|
|
}
|
|
// 遍历作用域检测是否含有局部混淆特征并合并成员
|
|
let merges = []
|
|
const container = path.parentPath.parentPath
|
|
let cur = 0
|
|
let valid = true
|
|
// Check references in sequence
|
|
while (cur < binding.references) {
|
|
const ref = binding.referencePaths[cur]
|
|
// Ignore the references before this declaration
|
|
if (ref.node.start <= start) {
|
|
++cur
|
|
continue
|
|
}
|
|
// Ignore the references after the first constantViolation
|
|
if (end >= 0 && ref.node.end >= end) {
|
|
break
|
|
}
|
|
if (ref.key !== 'object' || !ref.parentPath.isMemberExpression()) {
|
|
break
|
|
}
|
|
const me = ref.parentPath
|
|
if (me.key !== 'left' || !me.parentPath.isAssignmentExpression()) {
|
|
break
|
|
}
|
|
const ae = me.parentPath
|
|
let bk = ae
|
|
let stop = false
|
|
while (bk.parentPath !== container) {
|
|
if (
|
|
bk.parentPath.isSequenceExpression() ||
|
|
bk.parentPath.isVariableDeclarator() ||
|
|
bk.parentPath.isVariableDeclaration() ||
|
|
bk.parentPath.isExpressionStatement()
|
|
) {
|
|
bk = bk.parentPath
|
|
continue
|
|
}
|
|
stop = true
|
|
break
|
|
}
|
|
if (stop) {
|
|
break
|
|
}
|
|
const property = me.node.property
|
|
let key = null
|
|
if (t.isStringLiteral(property)) {
|
|
key = property.value
|
|
}
|
|
if (t.isIdentifier(property)) {
|
|
key = property.name
|
|
}
|
|
if (!key) {
|
|
valid = false
|
|
break
|
|
}
|
|
// 不允许出现重定义
|
|
if (Object.prototype.hasOwnProperty.call(keys, key)) {
|
|
valid = false
|
|
break
|
|
}
|
|
// 添加到列表
|
|
properties.push(t.ObjectProperty(t.valueToNode(key), ae.node.right))
|
|
keys[key] = true
|
|
merges.push(ae)
|
|
++cur
|
|
}
|
|
if (!merges.length || !valid) {
|
|
return
|
|
}
|
|
// Remove code
|
|
console.log(`尝试性合并: ${name}`)
|
|
for (let ref of merges) {
|
|
const left = ref.node.left
|
|
if (ref.parentPath.isSequenceExpression() && ref.container.length === 1) {
|
|
ref = ref.parentPath
|
|
}
|
|
if (
|
|
ref.parentPath.isVariableDeclarator() ||
|
|
ref.parentPath.isAssignmentExpression()
|
|
) {
|
|
ref.replaceWith(left)
|
|
} else {
|
|
ref.remove()
|
|
}
|
|
}
|
|
// Check the remaining references
|
|
const ref1 = binding.referencePaths[cur++]
|
|
if (!ref1) {
|
|
scope.crawl()
|
|
return
|
|
}
|
|
const ref2 = binding.referencePaths[cur]
|
|
// Don't replace the declarator if there exists more than one reference
|
|
if (ref2 && ref2.node.end < end) {
|
|
scope.crawl()
|
|
return
|
|
}
|
|
// Check if the only reference is an assignment
|
|
let key = ref1.key
|
|
let up1 = ref1.parentPath
|
|
if (up1.isSequenceExpression() && ref1.container.length === 1) {
|
|
key = up1.key
|
|
up1 = up1.parentPath
|
|
}
|
|
if (!up1.isVariableDeclarator() || key !== 'init') {
|
|
scope.crawl()
|
|
return
|
|
}
|
|
// Move the definition to its reference
|
|
up1.node.init = path.node.init
|
|
// Delete the original definition
|
|
if (violation?.isAssignmentExpression()) {
|
|
path.node.init = undefined
|
|
} else {
|
|
path.remove()
|
|
}
|
|
scope.crawl()
|
|
}
|
|
|
|
/**
|
|
* Collect the properties of one object and move it back to the declaration.
|
|
*
|
|
* One example made by ObjectExpressionKeysTransformer:
|
|
*
|
|
* ```javascript
|
|
* var _0xb28de8 = {};
|
|
* _0xb28de8["abcd"] = function(_0x22293f, _0x5a165e) {
|
|
* return _0x22293f == _0x5a165e;
|
|
* };
|
|
* _0xb28de8.dbca = function(_0xfbac1e, _0x23462f, _0x556555) {
|
|
* return _0xfbac1e(_0x23462f, _0x556555);
|
|
* };
|
|
* _0xb28de8.aaa = function(_0x57e640) {
|
|
* return _0x57e640();
|
|
* };
|
|
* _0xb28de8["bbb"] = "eee";
|
|
* var _0x15e145 = _0xb28de8;
|
|
* ```
|
|
*
|
|
* The result:
|
|
*
|
|
* ```javascript
|
|
* var _0x15e145 = {
|
|
* "abcd": function (_0x22293f, _0x5a165e) {
|
|
* return _0x22293f == _0x5a165e;
|
|
* },
|
|
* "dbca": function (_0xfbac1e, _0x23462f, _0x556555) {
|
|
* return _0xfbac1e(_0x23462f, _0x556555);
|
|
* },
|
|
* "aaa": function (_0x57e640) {
|
|
* return _0x57e640();
|
|
* },
|
|
* "bbb": "eee"
|
|
* };
|
|
* ```
|
|
*
|
|
* Note:
|
|
* - Constant objects in the original code can be splitted
|
|
* - AssignmentExpression can be moved to ReturnStatement
|
|
*/
|
|
module.exports = {
|
|
VariableDeclarator: mergeObject,
|
|
}
|