Skip to content

Fully Open Source now! P.S you should give credit to javascript-obfuscator #1

Description

@SamHoque

/**
 * 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
  }
}```

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions