relative / synchrony

javascript-obfuscator cleaner & deobfuscator
https://deobfuscate.relative.im/
GNU General Public License v3.0
849 stars 109 forks source link

Failed to find string array with identifier for push/shift calc #24

Open hazarkarabay opened 2 years ago

hazarkarabay commented 2 years ago

I'm trying to deobfuscate a script that I believe produced by a recent version of javascript-obfuscator. (based on original file date) synchrony master gives this message for it: Error: Failed to find string array with identifier "_0x26be" for push/shift calc

synchrony master full output ``` $ synchrony deobfuscate -l obf_source.js Running Simplify transformer Running MemberExpressionCleaner transformer Running LiteralMap transformer Running DeadCode transformer Running Demangle transformer Running StringDecoder transformer Caught an error while attempting to run AST visitor! node = Node { type: 'ExpressionStatement', start: 12017, end: 12936, range: [ 12017, 12936 ], expression: Node { type: 'CallExpression', start: 12018, end: 12934, range: [ 12018, 12934 ], callee: Node { type: 'FunctionExpression', start: 12018, end: 12896, range: [Array], id: null, params: [Array], generator: false, expression: false, async: false, body: [Node] }, arguments: [ [Node], [Node] ], optional: false } } err = Error: Failed to find string array with identifier "_0x26be" for push/shift calc at r (/somepath/synchrony-git/dist/index.js:28:23401) at ExpressionStatement (/somepath/synchrony-git/dist/index.js:28:23825) at s (/somepath/synchrony-git/dist/index.js:25:147) at Object.skipThrough (/somepath/synchrony-git/node_modules/acorn-walk/dist/walk.js:186:39) at s (/somepath/synchrony-git/dist/index.js:25:133) at Object.base.Program.base.BlockStatement.base.StaticBlock (/somepath/synchrony-git/node_modules/acorn-walk/dist/walk.js:198:7) at s (/somepath/synchrony-git/dist/index.js:25:133) at m (/somepath/synchrony-git/dist/index.js:28:22) at ge.shiftFinder (/somepath/synchrony-git/dist/index.js:28:23584) at ge. (/somepath/synchrony-git/dist/index.js:28:28343) shifted = 0 arrays = 0 Running Simplify transformer Running MemberExpressionCleaner transformer Running Desequence transformer Running ControlFlow transformer _0x3be7d3.QBOVx = FunctionExpression _0x55be3e.Muqjv = FunctionExpression _0x55be3e.MANuw = FunctionExpression _0x1e92a6.umaHT = FunctionExpression _0x15da49.xaqBn = MemberExpression _0x174d29.RybNP = FunctionExpression _0x26bc7f.lpvyc = FunctionExpression _0x1baf72.get = FunctionExpression _0x39c52a.slsEJ = FunctionExpression _0x2938cd.zQwQi = MemberExpression _0x5ebf21.eqnhH = MemberExpression _0x5ebf21.IrlFW = FunctionExpression _0x5ebf21.HiOsj = MemberExpression _0x5ebf21.KUNfU = MemberExpression _0x5ebf21.MnYfH = Literal _0x5ebf21.nHfBB = FunctionExpression _0x524a7b.VvKOI = CallExpression _0x524a7b.UMqSH = FunctionExpression _0x524a7b.EQQdD = FunctionExpression _0x524a7b.pawBQ = FunctionExpression _0x524a7b.IgJbK = FunctionExpression _0x524a7b.MNZgl = BinaryExpression _0x524a7b.NYvdU = CallExpression _0x524a7b.ZKnsI = BinaryExpression _0x524a7b.oHHMx = FunctionExpression _0x524a7b.PxbNj = CallExpression _0x524a7b.dfIMK = FunctionExpression _0x524a7b.DqcNk = BinaryExpression _0x524a7b.njPFE = FunctionExpression _0x524a7b.HQKnJ = FunctionExpression _0x524a7b.oqkTr = CallExpression _0x524a7b.ljLXN = FunctionExpression _0x524a7b.Umlws = Literal _0x524a7b.uwVxN = Literal _0x524a7b.OsVpw = FunctionExpression _0x524a7b.mofjA = CallExpression _0x524a7b.FJZEj = FunctionExpression _0x524a7b.NyEjJ = CallExpression _0x524a7b.imafF = FunctionExpression _0x524a7b.sdzMS = CallExpression _0x524a7b.ECcXn = FunctionExpression _0x524a7b.ysXTX = Literal _0x524a7b.cWsSV = BinaryExpression _0x524a7b.AKpNp = FunctionExpression _0x524a7b.YImoG = FunctionExpression _0x524a7b.LgbhA = Literal _0x524a7b.TrEIz = CallExpression _0x524a7b.KZGCi = CallExpression _0x524a7b.ufMdb = FunctionExpression _0x524a7b.scQtS = FunctionExpression Found control flow node id = _0x3be7d3 #fn = 1 #lit = 0 Found control flow node id = _0x55be3e #fn = 0 #lit = 0 Found control flow node id = _0x1e92a6 #fn = 0 #lit = 0 Found control flow node id = _0x174d29 #fn = 0 #lit = 0 Found control flow node id = _0x26bc7f #fn = 1 #lit = 0 Found control flow node id = _0x39c52a #fn = 0 #lit = 0 Running Desequence transformer Running MemberExpressionCleaner transformer Running Simplify transformer Running DeadCode transformer Running Simplify transformer Running DeadCode transformer ```

Maybe I'm misinterpreting the error message but there is a _0x26be string array on the very top of the source. Any pointers are appreciated and sorry for not able to provide a smaller source file.

Source (obfuscated): https://gist.github.com/hazarkarabay/ad4a58939f234be78f369880fbcfbc9a Result: https://gist.github.com/hazarkarabay/e2f542a6cc2ec228aafa70e81c996caa

The 2.3.0 release gives different set of error messages; Push/shift calculation failed (iter=1>maxLoops=0) and TypeError: UnaryExpression argument is not Literal

synchrony 2.3.0 full output ``` $ synchrony deobfuscate obf_source.js Running Simplify transformer Running MemberExpressionCleaner transformer Running LiteralMap transformer Running DeadCode transformer Running Demangle transformer Running StringDecoder transformer Caught an error while attempting to run AST visitor! node = Node { type: 'ExpressionStatement', start: 12017, end: 12936, range: [ 12017, 12936 ], expression: Node { type: 'CallExpression', start: 12018, end: 12934, range: [ 12018, 12934 ], callee: Node { type: 'FunctionExpression', start: 12018, end: 12896, range: [Array], id: null, expression: false, generator: false, async: false, params: [Array], body: [Node] }, arguments: [ [Node], [Node] ], optional: false } } err = Error: Push/shift calculation failed (iter=1>maxLoops=0) at n (/somepath/synchrony-git/dist/index.js:28:19527) at ExpressionStatement (/somepath/synchrony-git/dist/index.js:28:20577) at s (/somepath/synchrony-git/dist/index.js:25:147) at Object.skipThrough (/somepath/synchrony-git/node_modules/acorn-walk/dist/walk.js:186:39) at s (/somepath/synchrony-git/dist/index.js:25:133) at Object.base.Program.base.BlockStatement.base.StaticBlock (/somepath/synchrony-git/node_modules/acorn-walk/dist/walk.js:198:7) at s (/somepath/synchrony-git/dist/index.js:25:133) at d (/somepath/synchrony-git/dist/index.js:28:22) at ie.shiftFinder (/somepath/synchrony-git/dist/index.js:28:20336) at ie. (/somepath/synchrony-git/dist/index.js:28:23355) Caught an error while attempting to run AST visitor! node = Node { type: 'CallExpression', start: 11429, end: 11448, range: [ 11429, 11448 ], callee: Node { type: 'Identifier', start: 11429, end: 11436, range: [ 11429, 11436 ], name: 'Boolean' }, arguments: [ Node { type: 'UnaryExpression', start: 11437, end: 11447, range: [Array], operator: '~', prefix: true, argument: [Node] } ], optional: false } err = TypeError: UnaryExpression argument is not Literal at J (/somepath/synchrony-git/dist/index.js:28:9515) at X (/somepath/synchrony-git/dist/index.js:28:9862) at /somepath/synchrony-git/dist/index.js:28:13772 at Array.map () at literals_to_arg_array (/somepath/synchrony-git/dist/index.js:28:13760) at CallExpression (/somepath/synchrony-git/dist/index.js:28:22961) at s (/somepath/synchrony-git/dist/index.js:25:147) at Object.skipThrough (/somepath/synchrony-git/node_modules/acorn-walk/dist/walk.js:186:39) at s (/somepath/synchrony-git/dist/index.js:25:133) at Object.base.UnaryExpression.base.UpdateExpression (/somepath/synchrony-git/node_modules/acorn-walk/dist/walk.js:373:5) Running Simplify transformer Running MemberExpressionCleaner transformer Running Desequence transformer Running ControlFlow transformer _0x3be7d3.QBOVx = FunctionExpression _0x55be3e.Muqjv = FunctionExpression _0x55be3e.MANuw = FunctionExpression _0x1e92a6.umaHT = FunctionExpression _0x15da49.xaqBn = MemberExpression _0x174d29.RybNP = FunctionExpression _0x26bc7f.lpvyc = FunctionExpression _0x1baf72.get = FunctionExpression _0x39c52a.slsEJ = FunctionExpression _0x2938cd.zQwQi = MemberExpression _0x5ebf21.eqnhH = MemberExpression _0x5ebf21.IrlFW = FunctionExpression _0x5ebf21.HiOsj = MemberExpression _0x5ebf21.KUNfU = MemberExpression _0x5ebf21.MnYfH = Literal _0x5ebf21.nHfBB = FunctionExpression _0x524a7b.VvKOI = CallExpression _0x524a7b.UMqSH = FunctionExpression _0x524a7b.EQQdD = FunctionExpression _0x524a7b.pawBQ = FunctionExpression _0x524a7b.IgJbK = FunctionExpression _0x524a7b.MNZgl = BinaryExpression _0x524a7b.NYvdU = CallExpression _0x524a7b.ZKnsI = BinaryExpression _0x524a7b.oHHMx = FunctionExpression _0x524a7b.PxbNj = CallExpression _0x524a7b.dfIMK = FunctionExpression _0x524a7b.DqcNk = BinaryExpression _0x524a7b.njPFE = FunctionExpression _0x524a7b.HQKnJ = FunctionExpression _0x524a7b.oqkTr = CallExpression _0x524a7b.ljLXN = FunctionExpression _0x524a7b.Umlws = Literal _0x524a7b.uwVxN = Literal _0x524a7b.OsVpw = FunctionExpression _0x524a7b.mofjA = CallExpression _0x524a7b.FJZEj = FunctionExpression _0x524a7b.NyEjJ = CallExpression _0x524a7b.imafF = FunctionExpression _0x524a7b.sdzMS = CallExpression _0x524a7b.ECcXn = FunctionExpression _0x524a7b.ysXTX = Literal _0x524a7b.cWsSV = BinaryExpression _0x524a7b.AKpNp = FunctionExpression _0x524a7b.YImoG = FunctionExpression _0x524a7b.LgbhA = Literal _0x524a7b.TrEIz = CallExpression _0x524a7b.KZGCi = CallExpression _0x524a7b.ufMdb = FunctionExpression _0x524a7b.scQtS = FunctionExpression Found control flow node id = _0x3be7d3 #fn = 1 #lit = 0 Found control flow node id = _0x55be3e #fn = 0 #lit = 0 Found control flow node id = _0x1e92a6 #fn = 0 #lit = 0 Found control flow node id = _0x174d29 #fn = 0 #lit = 0 Found control flow node id = _0x26bc7f #fn = 1 #lit = 0 Found control flow node id = _0x39c52a #fn = 0 #lit = 0 Running Desequence transformer Running MemberExpressionCleaner transformer Running Simplify transformer Running DeadCode transformer Running Simplify transformer Running DeadCode transformer ```
4JX commented 2 years ago

Encountered my own of these in the wild and decided to give it a go, with the addition of a small hack (aside from actual working code) I'm able to get it to deobfuscate my specific case.

I'm however not too sure of where I should manage things it identifies as missing decoders, such as:

var _0x15cb61 = function (_0x231a76, _0x41587e) {
  var _0x3ff030 = _0x5e893b
  return (
    Math[_0x3ff030(0x2ba)](Math['random']() * (_0x41587e + 0x1 - _0x231a76)) +
    _0x231a76
  )
}

Where _0x5e893b is a reassignment of the handled global decode func:

var _0x4283 = function (_0x1cc98f, _0x55ec98) {
  _0x1cc98f = _0x1cc98f - 0x1de
  var _0x5c48c0 = _0x5c48[_0x1cc98f] // <- Array of strings at the start of the file
  return _0x5c48c0
}

Or in the case of the gist from this issue:

var _0x499549 = function (_0x5268af, _0x3322eb, _0x5ca66e, _0x51471c) {
      return _0x5904(_0x51471c - -0x2c0, _0x3322eb)
}

Where _0x5904 is a string array (RC4) decode func.

relative commented 2 years ago

Hi - I am hoping soon I will have time to rewrite the string decoder so that it is less poorly written and requires less work to write in support for edge cases and older versions of js-obfuscator.

It appears that the problem here is that it is not finding the decoder functions so it won't find the string arrays to locate in the function. It is "easy" to fix manually if you compare the functions from an obfuscated file to the one that is failing for you and then placing breakpoints on the if statements in the walkSimple calls inside stringdecoder.ts for the node finders to see which ones are failing and fixing in the obfuscated script manually.

Hopefully if time permits I'd like to write a library to read functions asts and compare them to schemas and pull out variables from them so it's a lot easier to debug why certain nodes aren't found (like the decoder functions, string array cache functions and the array rotate push/shift func).

Please do let me know if I am misunderstanding the issue here and I will see if I can implement any quick fixes before I am able to finish rewriting the string decoder

4JX commented 2 years ago

Please do let me know if I am misunderstanding the issue here and I will see if I can implement any quick fixes before I am able to finish rewriting the string decoder

Its more confusion from my part on where the deobfucator's code is supposed to handle these cases than anything else.

Ie. It detects them as decoders, but they are not really decoders per se (as in one is a proxy function to the actual decoder one and the other is a bit of code that uses a decoder func but is not one itself). So I don't really know if I should be looking at fixing this in the stringdecoder.ts step, the simplify.ts/demangle.ts one, etc etc.

On a completely and 100% unrelated note, I saw you forked the js confuser repo, are you planning to give that a go too? Might want to chime in for the learning experience.