/**
* DEOBFUSCATED: JavaScript Obfuscator Service
* This code provides a client-side JavaScript obfuscation service
* that dynamically loads the javascript-obfuscator library and
* provides a window.JScrambler.protect() API
*/
// Anti-debugging interval - runs every 4 seconds
setInterval(function() {
antiDebugCheck();
}, 4000);
(function(window) {
"use strict";
// State variables
let libraryLoaded = false;
let libraryLoading = false;
const pendingCallbacks = [];
/**
* Extracts the domain from a URL string
* @param {string} url - The URL to extract domain from
* @returns {string} - The extracted domain
*/
function extractDomain(url) {
if (!url || typeof url !== "string") {
return "";
}
let domain = url.toLowerCase().trim();
domain = domain.replace(/^https?:\/\//, "");
domain = domain.replace(/^www\./, "");
domain = domain.split("/")[0];
domain = domain.split(":")[0];
domain = domain.split("?")[0];
domain = domain.split("#")[0];
return domain;
}
/**
* Loads the javascript-obfuscator library from CDN
* @returns {Promise} - Resolves when library is loaded
*/
function loadObfuscatorLibrary() {
return new Promise(function(resolve, reject) {
// Check if JavaScriptObfuscator is already available
if (typeof window.JavaScriptObfuscator !== "undefined") {
libraryLoaded = true;
return resolve();
}
// If already loading, queue the callback
if (libraryLoading) {
pendingCallbacks.push({
resolve: resolve,
reject: reject
});
return;
}
libraryLoading = true;
// Create script element to load the library
const script = document.createElement("script");
script.src = "https://cdn.jsdelivr.net/npm/javascript-obfuscator/dist/index.browser.js";
script.async = true;
script.onload = function() {
libraryLoaded = true;
libraryLoading = false;
setTimeout(function() {
if (typeof window.JavaScriptObfuscator !== "undefined") {
console.log("javascript-obfuscator library loaded successfully");
resolve();
pendingCallbacks.forEach(function(callback) {
callback.resolve();
});
pendingCallbacks.length = 0;
} else if (typeof JavaScriptObfuscator !== "undefined") {
console.log("javascript-obfuscator loaded (global)");
resolve();
pendingCallbacks.forEach(function(callback) {
callback.resolve();
});
pendingCallbacks.length = 0;
} else {
console.log("javascript-obfuscator failed to initialize");
const error = new Error("javascript-obfuscator library failed to initialize");
reject(error);
pendingCallbacks.forEach(function(callback) {
callback.reject(error);
});
pendingCallbacks.length = 0;
}
}, 200);
};
script.onerror = function() {
libraryLoading = false;
const error = new Error("Failed to load javascript-obfuscator library");
reject(error);
pendingCallbacks.forEach(function(callback) {
callback.reject(error);
});
pendingCallbacks.length = 0;
};
(document.head || document.documentElement).appendChild(script);
});
}
/**
* Base64 encode a string (with UTF-8 support)
* @param {string} str - String to encode
* @returns {string} - Base64 encoded string
*/
function base64Encode(str) {
try {
return btoa(unescape(encodeURIComponent(str)));
} catch (e) {
// Fallback for older browsers
const encoded = new TextEncoder().encode(str);
let binary = "";
for (let i = 0; i < encoded.length; i++) {
binary += String.fromCharCode(encoded[i]);
}
return btoa(binary);
}
}
/**
* Wraps code with domain verification
* @param {string} code - The code to wrap
* @param {object} options - Options containing allowedDomain
* @returns {string} - Code wrapped with domain checking
*/
function wrapWithDomainCheck(code, options) {
if (!options || !options.allowedDomain) {
return code;
}
// Get domains as array
const domains = Array.isArray(options.allowedDomain)
? options.allowedDomain
: [options.allowedDomain];
// Encode domains for comparison
const encodedDomains = domains.map(function(domain) {
return base64Encode(extractDomain(domain));
});
// Generate random variable names
const varNames = {
domains: "_" + Math.random().toString(36).substr(2, 8),
encoded: "_" + Math.random().toString(36).substr(2, 8),
current: "_" + Math.random().toString(36).substr(2, 8),
check: "_" + Math.random().toString(36).substr(2, 8)
};
// Build wrapper code
const wrapper = [];
wrapper.push("(function(){");
wrapper.push("var " + varNames.domains + "=" + JSON.stringify(encodedDomains) + ";");
wrapper.push("var " + varNames.encoded + "=function(s){try{return btoa(unescape(encodeURIComponent(s)))}catch(e){return ''}};");
wrapper.push("var " + varNames.current + "=" + varNames.encoded + "((window.location.hostname||'').toLowerCase().replace(/^www\\./,''));");
wrapper.push("var " + varNames.check + "=" + varNames.domains + ".indexOf(" + varNames.current + ")!==-1;");
wrapper.push("if(!" + varNames.check + "){");
wrapper.push("console.error('License validation failed: Invalid domain');");
wrapper.push("return;");
wrapper.push("};");
wrapper.push(code);
wrapper.push("})();");
return wrapper.join("");
}
/**
* Generates obfuscation options based on code size
* Higher size = lighter obfuscation for performance
* @param {number} codeLength - Length of code in characters
* @returns {object} - Obfuscation configuration object
*/
function generateObfuscationConfig(codeLength) {
const seed = Math.floor(Math.random() * 1000000);
const sizeKB = codeLength / 1024;
const isVerySmall = sizeKB <= 100;
const isSmall = sizeKB > 100 && sizeKB <= 400;
const isMedium = sizeKB > 400 && sizeKB <= 800;
const isLarge = sizeKB > 800 && sizeKB <= 1400;
const isVeryLarge = sizeKB > 1400;
// Very small code: Maximum obfuscation
if (isVerySmall) {
return {
compact: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 1,
deadCodeInjection: true,
deadCodeInjectionThreshold: 0.4,
debugProtection: true,
debugProtectionInterval: 4000,
disableConsoleOutput: true,
domainLock: [],
domainLockRedirectUrl: "",
forceTransformStrings: false,
log: false,
numbersToExpressions: true,
optionsPreset: "default",
renameGlobals: false,
renameProperties: false,
renamePropertiesMode: "safe",
reservedNames: [],
reservedStrings: [],
seed: seed,
selfDefending: true,
simplify: true,
splitStrings: true,
splitStringsChunkLength: 5,
stringArray: true,
stringArrayCallsTransform: true,
stringArrayCallsTransformThreshold: 0.9,
stringArrayEncoding: ["rc4", "base64"],
stringArrayIndexesType: ["hexadecimal-number"],
stringArrayIndexShift: true,
stringArrayRotate: true,
stringArrayShuffle: true,
stringArrayWrappersCount: 1,
stringArrayWrappersChainedCalls: true,
stringArrayWrappersParametersMaxCount: 2,
stringArrayWrappersType: "variable",
stringArrayThreshold: 1,
target: "browser",
transformObjectKeys: true,
unicodeEscapeSequence: true
};
}
// Small code: Heavy obfuscation
if (isSmall) {
return {
compact: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 0.85,
deadCodeInjection: true,
deadCodeInjectionThreshold: 0.3,
debugProtection: true,
debugProtectionInterval: false,
disableConsoleOutput: true,
domainLock: [],
domainLockRedirectUrl: "",
forceTransformStrings: false,
log: false,
numbersToExpressions: false,
optionsPreset: "default",
renameGlobals: false,
renameProperties: false,
renamePropertiesMode: "safe",
reservedNames: [],
reservedStrings: [],
seed: seed,
selfDefending: true,
simplify: true,
splitStrings: true,
splitStringsChunkLength: 7,
stringArray: true,
stringArrayCallsTransform: true,
stringArrayCallsTransformThreshold: 0.8,
stringArrayEncoding: ["rc4", "base64"],
stringArrayIndexesType: ["hexadecimal-number"],
stringArrayIndexShift: true,
stringArrayRotate: true,
stringArrayShuffle: true,
stringArrayWrappersCount: 1,
stringArrayWrappersChainedCalls: true,
stringArrayWrappersParametersMaxCount: 2,
stringArrayWrappersType: "variable",
stringArrayThreshold: 0.9,
target: "browser",
transformObjectKeys: true,
unicodeEscapeSequence: true
};
}
// Medium code: Moderate obfuscation
if (isMedium) {
return {
compact: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 0.7,
deadCodeInjection: true,
deadCodeInjectionThreshold: 0.25,
debugProtection: true,
debugProtectionInterval: false,
disableConsoleOutput: true,
domainLock: [],
domainLockRedirectUrl: "",
forceTransformStrings: false,
log: false,
numbersToExpressions: false,
optionsPreset: "default",
renameGlobals: false,
renameProperties: false,
renamePropertiesMode: "safe",
reservedNames: [],
reservedStrings: [],
seed: seed,
selfDefending: true,
simplify: true,
splitStrings: true,
splitStringsChunkLength: 10,
stringArray: true,
stringArrayCallsTransform: true,
stringArrayCallsTransformThreshold: 0.7,
stringArrayEncoding: ["rc4", "base64"],
stringArrayIndexesType: ["hexadecimal-number"],
stringArrayIndexShift: true,
stringArrayRotate: true,
stringArrayShuffle: true,
stringArrayWrappersCount: 1,
stringArrayWrappersChainedCalls: false,
stringArrayWrappersParametersMaxCount: 2,
stringArrayWrappersType: "variable",
stringArrayThreshold: 0.8,
target: "browser",
transformObjectKeys: true,
unicodeEscapeSequence: false
};
}
// Large code: Light obfuscation
if (isLarge) {
return {
compact: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 0.5,
deadCodeInjection: true,
deadCodeInjectionThreshold: 0.15,
debugProtection: false,
debugProtectionInterval: false,
disableConsoleOutput: true,
domainLock: [],
domainLockRedirectUrl: "",
forceTransformStrings: false,
log: false,
numbersToExpressions: false,
optionsPreset: "default",
renameGlobals: false,
renameProperties: false,
renamePropertiesMode: "safe",
reservedNames: [],
reservedStrings: [],
seed: seed,
selfDefending: false,
simplify: true,
splitStrings: true,
splitStringsChunkLength: 12,
stringArray: true,
stringArrayCallsTransform: false,
stringArrayCallsTransformThreshold: 0.6,
stringArrayEncoding: ["rc4", "base64"],
stringArrayIndexesType: ["hexadecimal-number"],
stringArrayIndexShift: true,
stringArrayRotate: true,
stringArrayShuffle: true,
stringArrayWrappersCount: 1,
stringArrayWrappersChainedCalls: false,
stringArrayWrappersParametersMaxCount: 2,
stringArrayWrappersType: "variable",
stringArrayThreshold: 0.7,
target: "browser",
transformObjectKeys: false,
unicodeEscapeSequence: false
};
}
// Very large code: Minimal obfuscation
if (isVeryLarge) {
return {
compact: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 0.35,
deadCodeInjection: false,
deadCodeInjectionThreshold: 0,
debugProtection: false,
debugProtectionInterval: false,
disableConsoleOutput: true,
domainLock: [],
domainLockRedirectUrl: "",
forceTransformStrings: false,
log: false,
numbersToExpressions: false,
optionsPreset: "default",
renameGlobals: false,
renameProperties: false,
renamePropertiesMode: "safe",
reservedNames: [],
reservedStrings: [],
seed: seed,
selfDefending: false,
simplify: true,
splitStrings: false,
splitStringsChunkLength: 18,
stringArray: true,
stringArrayCallsTransform: false,
stringArrayCallsTransformThreshold: 0.45,
stringArrayEncoding: ["rc4"],
stringArrayIndexesType: ["hexadecimal-number"],
stringArrayIndexShift: false,
stringArrayRotate: false,
stringArrayShuffle: true,
stringArrayWrappersCount: 1,
stringArrayWrappersChainedCalls: false,
stringArrayWrappersParametersMaxCount: 2,
stringArrayWrappersType: "variable",
stringArrayThreshold: 0.6,
target: "browser",
transformObjectKeys: false,
unicodeEscapeSequence: false
};
}
// Default fallback config
return {
compact: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 0.75,
disableConsoleOutput: true,
domainLock: [],
domainLockRedirectUrl: "",
optionsPreset: "default",
renameGlobals: false,
seed: seed,
selfDefending: true,
simplify: true,
stringArray: true,
stringArrayEncoding: ["rc4", "base64"],
stringArrayThreshold: 0.85,
target: "browser",
transformObjectKeys: true
};
}
/**
* Main API object exposed to window
*/
const JScrambler = {
/**
* Protects/obfuscates JavaScript code
* @param {string} code - The JavaScript code to obfuscate
* @param {object} options - Configuration options
* @returns {Promise<string>} - Promise resolving to obfuscated code
*/
protect: function(code, options) {
return new Promise(function(resolve, reject) {
// Validate input
if (!code || typeof code !== "string") {
return reject(new Error("Invalid input: code must be a non-empty string"));
}
// Check size limit (2.1 MB)
const maxSize = 2.1 * 1024 * 1024;
const codeSize = new Blob([code]).size;
if (codeSize > maxSize) {
const sizeMB = (codeSize / (1024 * 1024)).toFixed(2);
return reject(new Error("Code size (" + sizeMB + " MB) exceeds maximum allowed size of 2.1 MB"));
}
const sizeKB = (codeSize / 1024).toFixed(2);
console.log("JScrambler: Processing " + sizeKB + " KB of code");
// Validate domains if provided
if (options && options.allowedDomain) {
const domains = Array.isArray(options.allowedDomain)
? options.allowedDomain
: [options.allowedDomain];
for (let i = 0; i < domains.length; i++) {
if (typeof domains[i] !== "string" || domains[i].trim() === "") {
return reject(new Error("Invalid input: allowedDomain must be a non-empty string"));
}
const extracted = extractDomain(domains[i]);
if (!extracted) {
return reject(new Error("Invalid domain: " + domains[i]));
}
}
}
// Check if library is already loaded
if (typeof JavaScriptObfuscator !== "undefined") {
console.log("JScrambler: Using pre-loaded obfuscator");
try {
const wrappedCode = wrapWithDomainCheck(code, options);
const config = generateObfuscationConfig(wrappedCode.length);
const result = JavaScriptObfuscator.obfuscate(wrappedCode, config);
console.log("JScrambler: Obfuscation complete (" + sizeKB + " KB)");
resolve(result.getObfuscatedCode());
} catch (e) {
reject(e);
}
return;
}
// Load library and then obfuscate
console.log("JScrambler: Loading obfuscator library...");
loadObfuscatorLibrary()
.then(function() {
try {
const wrappedCode = wrapWithDomainCheck(code, options);
const config = generateObfuscationConfig(wrappedCode.length);
const result = JavaScriptObfuscator.obfuscate(wrappedCode, config);
console.log("JScrambler: Obfuscation complete (" + sizeKB + " KB)");
resolve(result.getObfuscatedCode());
} catch (e) {
reject(e);
}
})
.catch(function(error) {
reject(error);
});
});
}
};
// Expose API to window if JavaScriptObfuscator check passes
if (typeof window.JavaScriptObfuscator === "undefined") {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", function() {
loadObfuscatorLibrary().catch(function(error) {
console.log("JScrambler: Library load error", error.message);
});
});
} else {
setTimeout(function() {
loadObfuscatorLibrary().catch(function(error) {
console.log("JScrambler: Library load error", error.message);
});
}, 0);
}
}
// Expose the API globally
window.JScrambler = JScrambler;
})(window);
/**
* Anti-debugging/tampering detection function
* Called every 4 seconds by setInterval
* Uses various techniques to detect debugging
*/
function antiDebugCheck(counter) {
function innerCheck(value) {
if (typeof value === "string") {
return function(arg) {}
.constructor("while (true) {}")
.apply("counter");
} else if (("" + (value / value)).length !== 1 || value % 20 === 0) {
// Timing/integrity check
(function() {
return true;
})
.constructor("debu" + "gger")
.call("action");
} else {
// Alternative check
(function() {
return false;
})
.constructor("debu" + "gger")
.apply("stateObject");
}
innerCheck(++value);
}
try {
if (counter) {
return innerCheck;
} else {
innerCheck(0);
}
} catch (e) {
// Silently fail
}
}```