ben-sb / obfuscator-io-deobfuscator

A deobfuscator for scripts obfuscated by Obfuscator.io
https://obf-io.deobfuscate.io
Apache License 2.0
275 stars 61 forks source link

Reassignment removal breaks code with reused variables #22

Closed samfundev closed 2 months ago

samfundev commented 2 months ago

Here's some example code:

var a = {
    value: 1
}

var b = a;

var a = {
    value: 2
};

console.log(a.value, b.value);

The result is 2 1.

After deobfuscation:

var a = {
  value: 1
};
var a = {
  value: 2
};
console.log(a.value, a.value);

The result is now 2 2.

Disabling the reassignment removal fixes this.

ben-sb commented 2 months ago

There are still some edge cases to this that can't be fixed without more sophisticated program analysis (which I would consider to be out of scope for this repo currently), but that fix should handle your case

samfundev commented 2 months ago

My simplified example works but I might have simplified it too much from the original code since it uses a function and that breaks it again. If this would require too much effort to fix, I totally understand but I thought I'd at least ask.

var a = {
    value: 1
}

var b = a;

function c() {
    var a = {
        value: 2
    };

    console.log(a.value, b.value);
}

c();
ben-sb commented 2 months ago

That one is a slightly different problem with the inner variable shadowing the outer one. Detecting and handling edge cases like these is out of scope for this project (as it's targeted towards a specific obfuscator and can't be too general at the same time). Something like the Closure Compiler which does more program analysis, before performing any optimisation/simplification, might be more appropriate for your case.

samfundev commented 2 months ago

Something like the Closure Compiler

I'm not sure I understand how I could use that to help with deobfuscation. Could you explain?

as it's targeted towards a specific obfuscator

Apologizes, maybe I should have just started with the code I was trying to decompile. Here's the original code. If this isn't obfuscated with obfuscator.io, then you can just ignore this.

I've added comments to the code to explain what the problem is.

Here's what part of the code looks like when reassignment is disabled.
var e = J;
var c = N;
var s = {
  [c(713) + "GS"]: function (t, i) {
    return t < i;
  },
  [e(1046, "WJkG") + "UM"]: function (t, i) {
    return t + i;
  },
  [e(650, "g$lG") + "Dm"]: function (t, i) {
    return t >> i;
  },
  [e(314, "du#@") + "mt"]: function (t, i) {
    return t + i;
  },
  [c(1276) + "UF"]: function (t, i) {
    return t + i;
  }
};
var u = s; // s is reassigned as u
var r = {
  [c(1169) + "ue"]: true
};
pt[ti + hi](n, "t", r);
n.i = n.u = undefined;
var f = V[c(1334) + "qa"](V[e(773, "!g)g") + "Fw"](V[e(723, "WJkG") + "qa"](V[e(412, "SosE") + "jS"]("ABCDEF", "G") + "H" + "I" + "J" + "K" + "L" + "M" + "N" + "O", "P") + "Q", "R") + "S" + "T" + "U" + "V" + "W" + "X" + "Y", gs) + "+" + "/";
n.u = function (t) {
  var i = e;
  var n = c;
  if ((t = (t = (t = ""[o1](t))[Jr](/[\t\n\f\r]/g, ""))[_] % 4 == 0 ? t[Jr](/==?$/, "") : t)[_] % 4 == 1 || /[^+/0-9A-Za-z]/[g3](t)) {
    return null;
  }
  var s;
  var r = "";
  var o = 0;
  var h = 0;
  for (var u = 0; u < t[_]; u++) {
    s = t[u];
    o = (o <<= 6) | ((s = f[vn + "Of"](s)) < 0 ? undefined : s);
    if (24 === (h += 6)) {
      r = (r = (r += nu[b[n(1143) + "et"](Bt, xn) + Zi]((16711680 & o) >> 16)) + nu[Bt + xn + Zi]((65280 & o) >> 8)) + nu[b[n(1143) + "et"](Bt + xn, Zi)](255 & o);
      o = h = 0;
    }
  }
  if (12 === h) {
    o >>= 4;
    r += nu[Bt + xn + Zi](o);
  } else if (18 === h) {
    o >>= 2;
    r = (r += nu[b[i(1130, "*akk") + "fS"](Bt, xn) + Zi]((65280 & o) >> 8)) + nu[Bt + xn + Zi](255 & o);
  }
  return r;
};
n.i = function (t) {
  var i = e;
  var n = c;
  t = ""[o1](t);
  for (r = 0; r < t[_]; r++) {
    if (u[n(713) + "GS"](255, t[zs + Zi + "At"](r))) { // u is used here
      return null;
    }
  }
  var s = "";
  for (var r = 0; r < t[_]; r += 3) {
    var o = [undefined, undefined, undefined, undefined];
    o[0] = t[u[i(737, "771&") + "UM"](zs, Zi) + "At"](r) >> 2;
    o[1] = (3 & t[zs + Zi + "At"](r)) << 4;
    if (t[_] > r + 1) {
      o[1] |= u[i(650, "g$lG") + "Dm"](t[zs + Zi + "At"](r + 1), 4);
      o[2] = (15 & t[u[i(1155, "zfK&") + "UM"](zs + Zi, "At")](r + 1)) << 2;
    }
    if (t[_] > r + 2) {
      o[2] |= t[u[n(772) + "mt"](zs, Zi) + "At"](u[i(1115, "bs*w") + "UF"](r, 2)) >> 6;
      o[3] = 63 & t[zs + Zi + "At"](r + 2);
    }
    for (var h = 0; h < o[_]; h++) {
      s += ot == typeof o[h] ? "=" : function (t) {
        if (0 <= t && t < 64) {
          return f[t];
        }
      }(o[h]);
    }
  }
  return s;
};
Here's what part of the code looks like when reassignment is enabled:
// s has disappeared
var r = {
  [v(1169) + "ue"]: true
};
Object[ti + hi](n, "t", r);
n.i = n.u = undefined;
var f = V[v(1334) + "qa"](V[d(773, "!g)g") + "Fw"](V[d(723, "WJkG") + "qa"](V[d(412, "SosE") + "jS"]("ABCDEF", "G") + "H" + "I" + "J" + "K" + "L" + "M" + "N" + "O", "P") + "Q", "R") + "S" + "T" + "U" + "V" + "W" + "X" + "Y", gs) + "+" + "/";
n.u = function (t) {
  if ((t = (t = (t = ""[o1](t))[Jr](/[\t\n\f\r]/g, ""))[_] % 4 == 0 ? t[Jr](/==?$/, "") : t)[_] % 4 == 1 || /[^+/0-9A-Za-z]/[g3](t)) {
    return null;
  }
  var s;
  var r = "";
  var o = 0;
  var h = 0;
  for (var u = 0; u < t[_]; u++) {
    s = t[u];
    o = (o <<= 6) | ((s = f[vn + "Of"](s)) < 0 ? undefined : s);
    if (24 === (h += 6)) {
      r = (r = (r += String[b[v(1143) + "et"](Bt, xn) + Zi]((16711680 & o) >> 16)) + String[Bt + xn + Zi]((65280 & o) >> 8)) + String[b[v(1143) + "et"](Bt + xn, Zi)](255 & o);
      o = h = 0;
    }
  }
  if (12 === h) {
    o >>= 4;
    r += String[Bt + xn + Zi](o);
  } else if (18 === h) {
    o >>= 2;
    r = (r += String[b[d(1130, "*akk") + "fS"](Bt, xn) + Zi]((65280 & o) >> 8)) + String[Bt + xn + Zi](255 & o);
  }
  return r;
};
n.i = function (t) {
  t = ""[o1](t);
  for (r = 0; r < t[_]; r++) {
    if (s[v(713) + "GS"](255, t[zs + Zi + "At"](r))) { // s is used here, but it's not defined
      return null;
    }
  }
  var s = "";
  for (var r = 0; r < t[_]; r += 3) {
    var o = [undefined, undefined, undefined, undefined];
    o[0] = t[s[d(737, "771&") + "UM"](zs, Zi) + "At"](r) >> 2;
    o[1] = (3 & t[zs + Zi + "At"](r)) << 4;
    if (t[_] > r + 1) {
      o[1] |= s[d(650, "g$lG") + "Dm"](t[zs + Zi + "At"](r + 1), 4);
      o[2] = (15 & t[s[d(1155, "zfK&") + "UM"](zs + Zi, "At")](r + 1)) << 2;
    }
    if (t[_] > r + 2) {
      o[2] |= t[s[v(772) + "mt"](zs, Zi) + "At"](s[d(1115, "bs*w") + "UF"](r, 2)) >> 6;
      o[3] = 63 & t[zs + Zi + "At"](r + 2);
    }
    for (var h = 0; h < o[_]; h++) {
      s += ot == typeof o[h] ? "=" : function (t) {
        if (0 <= t && t < 64) {
          return f[t];
        }
      }(o[h]);
    }
  }
  return s;
};

Apologizes in advance if I have misunderstood the problem or the obfuscator that is being used.