Open MichaelXF opened 1 month ago
It is not quite helping a lot against monkey patching in general, it is helping against very specific rare case when hacker try to proxify global object.
By the way it can be written differently:
let root = {};
eval("root=this"); // note that variable name can be changed by "rename variables" property
// if eval wasn't redefined root will be a global object
If you want to learn a bit more about monkey patching I can give you access to my "old" repository with my client side anit-cheat solution with detailed description of some of the protection methods and attack methods. Also I can give you a link to the huge discussion about monkey patching and proxy testing.
Also this eval "trick" can pretty much help with safely creating any code with Function (RGF).
Here eval that pretty much acts as Function with no prototype (cannot use "new" and other stuff that prototype does)
// Function
// CANNOT be used for getting prototype
// does NOT protect your source from reading if eval was redefined, but source cannot be rewritten
function Function(...args) {
let s = "function anonymous(";
for (let i=0; i<args.length - 2; i++) {
s += args[i] + ',';
}
s += args[args.length - 2] + "\n) {\n" + args[args.length - 1] + "\n}";
eval(s);
return anonymous; // can cause errors because editors cannot actually see where it is defined
}
// Function V2
// CANNOT be used for getting prototype
// Protects source code from reading and rewriting, but if eval was redefined will return null;
function Function(...args) {
let c = false;
eval((_ => {for (const key in {c}) return key})() + '=!0'); // prevent variable name change by obfuscator
if (!c) {
return null;
}
let s = "function anonymous(";
for (let i=0; i<args.length - 2; i++) {
s += args[i] + ',';
}
s += args[args.length - 2] + "\n) {\n" + args[args.length - 1] + "\n}";
eval(s);
return anonymous;
}
If you want to learn a bit more about monkey patching I can give you access to my "old" repository with my client side anit-cheat solution with detailed description of some of the protection methods and attack methods. Also I can give you a link to the huge discussion about monkey patching and proxy testing.
That would be great!
Also this eval "trick" can pretty much help with safely creating any code with Function (RGF).
You are right. Using this method we can pass the runtime generated into the secure eval. This prevents any chance for a reverse engineer to inspect the code. I'm thinking of creating a lock.tamperProtection
option that will implement these features.
Here is the prototype: https://github.com/MichaelXF/js-confuser/blob/dev/docs/TamperProtection.md
This monkey-patch can be detected by inspecting the
fetch.toString()
value
can be bypassed:
const { toString } = Function.prototype;
const hiddenFunctions = new Map();
Function.prototype.toString = function () {
return toString.call(hiddenFunctions.get(this) || this);
};
hiddenFunctions.set(Function.prototype.toString, toString);
const _fetch = fetch;
fetch = (url, ...args) => {
console.log("Fetch request intercepted!", url, ...args);
return _fetch(url, ...args);
};
hiddenFunctions.set(fetch, _fetch);
console.log(Function.prototype.toString.toString()); // function toString() { [native code] }
console.log(fetch.toString()); // function fetch() { [native code] }
@j4k0xb Great find, at least there is some protection. The error stack changes so maybe we have to looks for patterns in it.
// Untampered
Function.prototype.toString.call(undefined)
//index.js:161 Uncaught TypeError: Function.prototype.toString requires that 'this' be a Function
// at toString (<anonymous>)
// at index.js:161:71
// at <anonymous>:1:29
// Tampered
Function.prototype.toString.call(undefined)
//index.js:161 Uncaught TypeError: Function.prototype.toString requires that 'this' be a Function
// at toString (<anonymous>)
// at index.js:161:71
// at Function.toString (<anonymous>:4:19) // <- Unusual trace here
// at <anonymous>:1:29
Indeed. But there's work arounds. Stack is not consistent thing and changes from browser to browser since it is experimental feature. So we unlikely can be 100% sure what is going on, if not mapping all possible ways of stack check.
Call can be tempered.
Stack can be tempered on FireFox.
We don't know how signature should really look like. For example FireFox has different signatures for native functions.
Implement @doctor8296's trick to detect tampering to the global object. Securing the global object is a great protection for fighting against monkey patching.
Note: This only works when Eval is enabled and not in Strict mode.
New configuration settings will need to be added like javascript-obfuscator's
target
option, indicating if Eval is allowed and Strict Mode. Additionally, thelock.countermeasures
function should be called upon tamper detection.