Skip to content

Commit acd4b35

Browse files
committed
StringConcealing and OpaquePredicates
1 parent 516c7d2 commit acd4b35

File tree

1 file changed

+188
-4
lines changed

1 file changed

+188
-4
lines changed

src/plugin/jsconfuser.js

+188-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const traverse = require('@babel/traverse').default
44
const t = require('@babel/types')
55
const ivm = require('isolated-vm')
66
const calculateConstantExp = require('../visitor/calculate-constant-exp')
7+
const pruneIfBranch = require('../visitor/prune-if-branch')
78

89
const isolate = new ivm.Isolate()
910

@@ -844,6 +845,9 @@ function insertDepItemVar(deps, name, path) {
844845
*/
845846
function findGlobalFn(path) {
846847
const glo_fn_name = path.node.id?.name
848+
if (path.parentPath.getFunctionParent()) {
849+
return null
850+
}
847851
if (!glo_fn_name) {
848852
return null
849853
}
@@ -963,6 +967,9 @@ function findBufferToString(obj) {
963967
insertDepItemVar(obj.deps, obj.a2s_name, obj.a2s_path)
964968
break
965969
}
970+
if (!obj.a2s_name) {
971+
return false
972+
}
966973
binding = obj.a2s_path.scope.getBinding(obj.a2s_name)
967974
const b2s_path = binding.referencePaths[0].getFunctionParent()
968975
obj.b2s_name = safeGetName(b2s_path.get('id'))
@@ -986,14 +993,15 @@ function findBufferToString(obj) {
986993
},
987994
})
988995
if (!valid) {
989-
return
996+
return false
990997
}
991998
child.push({
992999
name: decode_fn.node.id.name,
9931000
decoder: decode_fn,
9941001
})
9951002
}
9961003
obj.child = child
1004+
return true
9971005
}
9981006

9991007
function generatorStringConcealingDepCode(obj) {
@@ -1192,7 +1200,9 @@ const deStringConcealing = {
11921200
return
11931201
}
11941202
findGlobalFnRef(obj)
1195-
findBufferToString(obj)
1203+
if (!findBufferToString(obj)) {
1204+
return
1205+
}
11961206
generatorStringConcealingDepCode(obj)
11971207
for (const item of obj.child) {
11981208
processSingleGetter(obj, item.name, item.decoder)
@@ -1204,10 +1214,147 @@ const deStringConcealing = {
12041214
},
12051215
}
12061216

1217+
function tryStringConcealingPlace(path) {
1218+
const parent = path.parentPath
1219+
if (!parent.isAssignmentExpression()) {
1220+
return
1221+
}
1222+
const name = safeGetName(parent.get('left'))
1223+
let binding = parent.scope.getBinding(name)
1224+
if (binding?.constantViolations?.length !== 1) {
1225+
return
1226+
}
1227+
const code = generator(parent.node).code
1228+
const vm = isolate.createContextSync()
1229+
vm.evalSync('var ' + code)
1230+
for (const ref of binding.referencePaths) {
1231+
if (ref.key !== 'object') {
1232+
continue
1233+
}
1234+
const test = generator(ref.parent).code
1235+
const res = vm.evalSync(test)
1236+
safeReplace(ref.parentPath, res)
1237+
}
1238+
safeDeleteNode(name, parent)
1239+
}
1240+
1241+
const deStringConcealingPlace = {
1242+
ArrayExpression(path) {
1243+
let valid = true
1244+
if (path.node.elements.length === 0) {
1245+
return
1246+
}
1247+
for (const ele of path.node.elements) {
1248+
if (!t.isStringLiteral(ele)) {
1249+
valid = false
1250+
break
1251+
}
1252+
}
1253+
if (!valid) {
1254+
return
1255+
}
1256+
tryStringConcealingPlace(path)
1257+
},
1258+
ObjectExpression(path) {
1259+
let valid = true
1260+
if (path.node.properties.length === 0) {
1261+
return
1262+
}
1263+
for (const ele of path.node.properties) {
1264+
if (!t.isStringLiteral(ele.value)) {
1265+
valid = false
1266+
break
1267+
}
1268+
}
1269+
if (!valid) {
1270+
return
1271+
}
1272+
tryStringConcealingPlace(path)
1273+
},
1274+
}
1275+
1276+
function checkOpaqueObject(path) {
1277+
const parent = path.parentPath
1278+
if (!parent.isAssignmentExpression()) {
1279+
return null
1280+
}
1281+
const tmp_name = safeGetName(parent.get('left'))
1282+
const func_path = parent.getFunctionParent()
1283+
if (
1284+
!func_path ||
1285+
func_path.key !== 'callee' ||
1286+
!func_path.parentPath.isCallExpression()
1287+
) {
1288+
return null
1289+
}
1290+
const func_body = func_path.node.body?.body
1291+
if (!func_body || func_body.length < 2) {
1292+
return null
1293+
}
1294+
const last_node = func_body[func_body.length - 1]
1295+
if (
1296+
!t.isReturnStatement(last_node) ||
1297+
last_node.argument?.name !== tmp_name
1298+
) {
1299+
return null
1300+
}
1301+
const root_path = func_path.parentPath.parentPath
1302+
if (!root_path.isAssignmentExpression()) {
1303+
return null
1304+
}
1305+
const pred_name = safeGetName(root_path.get('left'))
1306+
const obj = {
1307+
pred_name: pred_name,
1308+
pred_path: root_path,
1309+
props: {},
1310+
}
1311+
for (const prop of path.node.properties) {
1312+
const key = prop.key.name
1313+
const value = prop.value
1314+
if (t.isNumericLiteral(value)) {
1315+
obj.props[key] = {
1316+
type: 'number',
1317+
}
1318+
continue
1319+
}
1320+
if (t.isStringLiteral(value)) {
1321+
obj.props[key] = {
1322+
type: 'string',
1323+
}
1324+
continue
1325+
}
1326+
if (t.isArrayExpression(value)) {
1327+
if (value.elements.length === 0) {
1328+
obj.props[key] = {
1329+
type: 'array_dep',
1330+
}
1331+
}
1332+
continue
1333+
}
1334+
if (t.isArrowFunctionExpression(value) || t.isFunctionExpression(value)) {
1335+
const param = value.params?.[0]?.left?.name
1336+
if (!param) {
1337+
continue
1338+
}
1339+
const code = generator(value).code
1340+
const template =
1341+
`(${param}=){if(${pred_name}[0])${pred_name}push()` +
1342+
`return${pred_name}${param}}`
1343+
if (checkPattern(code, template)) {
1344+
obj.props[key] = {
1345+
type: 'array',
1346+
}
1347+
}
1348+
continue
1349+
}
1350+
}
1351+
return obj
1352+
}
1353+
12071354
/**
12081355
* Template:
12091356
* ```javascript
1210-
* // This is defined in the glocal space
1357+
* // This is defined in the global space
12111358
* var predicateName = (function () {
12121359
* var tempName = {
12131360
* prop_array_1: [],
@@ -1230,7 +1377,41 @@ const deStringConcealing = {
12301377
* ```
12311378
*/
12321379
const deOpaquePredicates = {
1233-
MemberExpression(path) {},
1380+
ObjectExpression(path) {
1381+
const obj = checkOpaqueObject(path)
1382+
if (!obj) {
1383+
return
1384+
}
1385+
console.log(`[OpaquePredicates] predicateName : ${obj.pred_name}`)
1386+
const vm = isolate.createContextSync()
1387+
const code = generator(obj.pred_path.node).code
1388+
vm.evalSync('var ' + code)
1389+
obj.pred_path.get('right').replaceWith(t.numericLiteral(0))
1390+
let binding = obj.pred_path.scope.getBinding(obj.pred_name)
1391+
binding.scope.crawl()
1392+
binding = binding.scope.getBinding(obj.pred_name)
1393+
for (const ref of binding.referencePaths) {
1394+
if (ref.key !== 'object') {
1395+
continue
1396+
}
1397+
const member = ref.parentPath
1398+
const prop = member.get('property')
1399+
if (!prop || !Object.prototype.hasOwnProperty.call(obj.props, prop)) {
1400+
continue
1401+
}
1402+
let expr = member
1403+
while (
1404+
expr.parentPath.isCallExpression() ||
1405+
expr.parentPath.isMemberExpression()
1406+
) {
1407+
expr = expr.parentPath
1408+
}
1409+
const test = generator(expr.node).code
1410+
const res = vm.evalSync(test)
1411+
safeReplace(expr, res)
1412+
}
1413+
safeDeleteNode(obj.pred_name, obj.pred_path)
1414+
},
12341415
}
12351416

12361417
module.exports = function (code) {
@@ -1254,10 +1435,13 @@ module.exports = function (code) {
12541435
traverse(ast, deStringCompression)
12551436
// StringConcealing
12561437
traverse(ast, deStringConcealing)
1438+
traverse(ast, deStringConcealingPlace)
12571439
// StringSplitting
12581440
traverse(ast, calculateConstantExp)
12591441
// OpaquePredicates
12601442
traverse(ast, deOpaquePredicates)
1443+
traverse(ast, calculateConstantExp)
1444+
traverse(ast, pruneIfBranch)
12611445
code = generator(ast, {
12621446
comments: false,
12631447
jsescOption: { minimal: true },

0 commit comments

Comments
 (0)