Skip to content

Commit

Permalink
Merge pull request from GHSA-jg82-xh3w-rhxx
Browse files Browse the repository at this point in the history
Advisory fix 1
  • Loading branch information
relative authored Oct 17, 2023
2 parents faddb67 + 9e499f0 commit b583126
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 112 deletions.
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
"@types/estree": "0.0.51",
"@types/mersenne-twister": "1.1.2",
"@types/node": "17.0.17",
"@types/prettier": "2.4.4",
"@types/yargs": "17.0.8",
"esbuild": "0.14.21",
"escodegen": "2.0.0",
Expand All @@ -47,7 +46,6 @@
"acorn-walk": "8.2.0",
"eslint-scope": "7.1.1",
"mersenne-twister": "1.1.0",
"prettier": "2.5.1",
"yargs": "17.3.1"
}
}
20 changes: 4 additions & 16 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 3 additions & 5 deletions src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ interface ControlFlowLiteral {
identifier: string
value: string | number
}
interface ControlFlowStorage {
export interface ControlFlowStorage {
identifier: string
aliases: string[]
functions: ControlFlowFunction[]
Expand All @@ -96,9 +96,7 @@ export default class Context {
stringDecoders: DecoderFunction[] = []
stringDecoderReferences: DecoderReference[] = []

controlFlowStorageNodes: {
[x: BlockId]: ControlFlowStorage
} = {}
controlFlowStorageNodes = new Map<BlockId, ControlFlowStorage>()

removeGarbage: boolean = true
transformers: InstanceType<typeof Transformer>[]
Expand All @@ -112,7 +110,7 @@ export default class Context {
ast: Program,
transformers: [string, Partial<TransformerOptions>][],
isModule: boolean,
source?: string,
source?: string
) {
this.ast = ast
this.transformers = this.buildTransformerList(transformers)
Expand Down
38 changes: 2 additions & 36 deletions src/deobfuscator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import * as acornLoose from 'acorn-loose'
import { Transformer, TransformerOptions } from './transformers/transformer'
import { Node, Program, sp } from './util/types'
import Context from './context'
import prettier from 'prettier'
import { walk } from './util/walk'

const FILE_REGEX = /(?<!\.d)\.[mc]?[jt]s$/i // cjs, mjs, js, ts, but no .d.ts
Expand Down Expand Up @@ -44,6 +43,8 @@ export interface DeobfuscateOptions {
* for work with Prettier
* https://github.com/prettier/prettier/pull/12172
* (default = true)
*
* @deprecated Prettier is no longer used in the deobfuscator
*/
transformChainExpressions: boolean

Expand Down Expand Up @@ -205,41 +206,6 @@ export class Deobfuscator {
source = escodegen.generate(ast, {
sourceMapWithCode: true,
}).code
try {
source = prettier.format(source, {
semi: false,
singleQuote: true,

// https://github.com/prettier/prettier/pull/12172
parser: (text, _opts) => {
let ast = this.parse(text, acornOptions, options)
if (options.transformChainExpressions) {
walk(ast as Node, {
ChainExpression(cx) {
if (cx.expression.type === 'CallExpression') {
sp<any>(cx, {
...cx.expression,
type: 'OptionalCallExpression',
expression: undefined,
})
} else if (cx.expression.type === 'MemberExpression') {
sp<any>(cx, {
...cx.expression,
type: 'OptionalMemberExpression',
expression: undefined,
})
}
},
})
}
return ast
},
})
} catch (err) {
// I don't think we should log here, but throwing the error is not very
// important since it is non fatal
console.log(err)
}

return source
}
Expand Down
46 changes: 27 additions & 19 deletions src/transformers/controlflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ import {
Identifier,
ObjectExpression,
Statement,
BlockStatement,
} from '../util/types'
import { Transformer, TransformerOptions } from './transformer'
import { walk } from '../util/walk'
import * as Guard from '../util/guard'
import Context from '../context'
import Context, { ControlFlowStorage } from '../context'
import {
immutate,
literalOrIdentifierToString,
Expand All @@ -38,24 +39,33 @@ export default class ControlFlow extends Transformer<ControlFlowOptions> {
if (!fx.body.body[0].argument)
throw new TypeError('Function in CFSN was invalid (void return)')

let params = fx.params as Identifier[],
paramMap: { [ident: string]: Node } = {}
const params = fx.params as Identifier[],
paramMap = new Map<string, Node>()
let i = 0
for (const p of params) {
paramMap[p.name] = cx.arguments[i]
paramMap.set(p.name, cx.arguments[i])
++i
}
let immRtn = immutate(fx.body.body[0].argument)
walk(immRtn, {
Identifier(id) {
if (!paramMap[id.name]) return
sp<Node>(id, paramMap[id.name])
const node = paramMap.get(id.name)
if (!node) return
sp<Node>(id, node)
},
})

return immRtn as Node
}

private getStorageNode(
context: Context,
node: BlockStatement
): ControlFlowStorage | undefined {
const bid = getBlockId(node)
return context.controlFlowStorageNodes.get(bid)
}

// fixes empty object inits where there are setters in the same block
populateEmptyObjects(context: Context) {
walk(context.ast, {
Expand Down Expand Up @@ -125,7 +135,8 @@ export default class ControlFlow extends Transformer<ControlFlowOptions> {
// /shrug
let bid = getBlockId(node)

if (context.controlFlowStorageNodes[bid]) return
let cfsn = context.controlFlowStorageNodes.get(bid)
if (cfsn) return
if (node.body.length === 0) return

walk(node, {
Expand All @@ -145,13 +156,14 @@ export default class ControlFlow extends Transformer<ControlFlowOptions> {
)
)
continue
context.controlFlowStorageNodes[bid] = {

cfsn = {
identifier: decl.id.name,
aliases: [decl.id.name],
functions: [],
literals: [],
}
const cfsn = context.controlFlowStorageNodes[bid]
context.controlFlowStorageNodes.set(bid, cfsn)
for (const prop of decl.init.properties as PropertyLiteral[]) {
let kn: Identifier | Literal = prop.key
let key = (
Expand Down Expand Up @@ -228,12 +240,10 @@ export default class ControlFlow extends Transformer<ControlFlowOptions> {

findStorageNodeAliases = (context: Context, ast: Node) => {
walk(ast, {
BlockStatement(node) {
let bid = getBlockId(node)

if (!context.controlFlowStorageNodes[bid]) return
BlockStatement: (node) => {
if (node.body.length === 0) return
const cfsn = context.controlFlowStorageNodes[bid]
const cfsn = this.getStorageNode(context, node)
if (!cfsn) return

walk(node, {
VariableDeclaration(vd) {
Expand Down Expand Up @@ -268,11 +278,9 @@ export default class ControlFlow extends Transformer<ControlFlowOptions> {
replacer = (context: Context, ast: Node) => {
const { translateCallExp } = this
walk(ast, {
BlockStatement(node) {
const bid = getBlockId(node)
if (!context.controlFlowStorageNodes[bid]) return
const cfsn = context.controlFlowStorageNodes[bid]

BlockStatement: (node) => {
const cfsn = this.getStorageNode(context, node)
if (!cfsn) return
walk(node, {
MemberExpression(mx) {
if (!Guard.isIdentifier(mx.object)) return
Expand Down
57 changes: 30 additions & 27 deletions src/transformers/jsconfuser/controlflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,7 @@ function inverseOperator(operator: BinaryOperator) {
throw new Error("Invalid operator to inverse '" + operator + "'")
}
}
interface VarStack {
[x: string]: number
}
type VarStack = Map<string, number>
function generateCode(ast: Node): string {
return escodegen.generate(ast as any, {
sourceMapWithCode: true,
Expand All @@ -69,41 +67,46 @@ function evaluateAssignmentExpr(
operator: AssignmentOperator,
value: number
) {
if (operator === '=') return stack.set(vk, value)

const stackVal = stack.get(vk)
if (typeof stackVal !== 'number')
throw new Error(
'Unexpected non-numeric value in jsconfuser controlflow stack'
)

switch (operator) {
case '=':
return (stack[vk] = value)
case '+=':
return (stack[vk] += value)
return stack.set(vk, stackVal + value)
case '-=':
return (stack[vk] -= value)
return stack.set(vk, stackVal - value)
case '*=':
return (stack[vk] *= value)
return stack.set(vk, stackVal * value)
case '/=':
return (stack[vk] /= value)
return stack.set(vk, stackVal / value)
case '%=':
return (stack[vk] %= value)
return stack.set(vk, stackVal % value)
case '<<=':
return (stack[vk] <<= value)
return stack.set(vk, stackVal << value)
case '>>=':
return (stack[vk] >>= value)
return stack.set(vk, stackVal >> value)
case '>>>=':
return (stack[vk] >>>= value)
return stack.set(vk, stackVal >>> value)
case '&=':
return (stack[vk] &= value)
return stack.set(vk, stackVal & value)
case '^=':
return (stack[vk] ^= value)
return stack.set(vk, stackVal ^ value)
case '|=':
return (stack[vk] |= value)
return stack.set(vk, stackVal | value)
default:
throw new Error(
'Invalid assignment expression operator "' + operator + '"'
)
}
}
function updateIdentifiers(stack: VarStack, obj: any) {
for (const vk in stack) {
let value = stack[vk],
node = createLiteral(value)
for (const [vk, value] of stack) {
const node = createLiteral(value)

walk(obj, {
Identifier(id) {
Expand Down Expand Up @@ -149,7 +152,7 @@ function evaluateSequenceAssignments(
continue
}
if (!Guard.isIdentifier(expr.left)) continue
if (!(expr.left.name in stack)) continue
if (!stack.has(expr.left.name)) continue
const vk = expr.left.name,
operator = expr.operator

Expand Down Expand Up @@ -179,7 +182,7 @@ function evaluateSequenceAssignments(

let effect = literalOrUnaryExpressionToNumber(ie)
evaluateAssignmentExpr(stack, vk, operator, effect)
log(`stack[${vk}] = ${stack[vk]}`)
log(`stack[${vk}] = ${stack.get(vk)}`)
log('='.repeat(32))
;(expr as any).type = 'EmptyStatement'
}
Expand Down Expand Up @@ -208,24 +211,24 @@ export default class JSCControlFlow extends Transformer<JSCControlFlowOptions> {
)
continue

const stack: VarStack = {}
const stack: VarStack = new Map()

let bx = w.test,
additive = false
while (Guard.isBinaryExpression(bx)) {
additive = bx.operator === '+'
if (Guard.isIdentifier(bx.left)) {
stack[bx.left.name] = bx.left.start
stack.set(bx.left.name, bx.left.start)
}
if (Guard.isIdentifier(bx.right)) {
stack[bx.right.name] = bx.right.start
stack.set(bx.right.name, bx.right.start)
}
bx = bx.left as BinaryExpression
}
if (!additive) continue
for (const vk in stack) {
for (const [vk, value] of stack) {
let vref = scope.references.find(
(i) => i.identifier.range![0] === stack[vk]
(i) => i.identifier.range![0] === value
)
if (!vref) continue
if (
Expand All @@ -246,7 +249,7 @@ export default class JSCControlFlow extends Transformer<JSCControlFlowOptions> {
i.range![0] !== def.node.range![0] &&
i.range![1] !== def.node.range![1]
)
stack[vk] = literalOrUnaryExpressionToNumber(def.node.init)
stack.set(vk, literalOrUnaryExpressionToNumber(def.node.init))
}
const endState = literalOrUnaryExpressionToNumber(w.test.right)
context.log(stack, endState)
Expand Down
Loading

0 comments on commit b583126

Please sign in to comment.