> ## Documentation Index
> Fetch the complete documentation index at: https://docs.js-confuser.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom String Encodings

> Custom String Encodings allows you to define your own string encoding/decoding functions.

* Option name: `"customStringEncodings"`

* Option values: `true/false/0-1`

***

### Input / Output

This example showcases how `Custom String Encodings` transforms the code. Try it out by changing the input code and see changes apply in real-time.

```js title="Input.js" lines interactive-mode="obfuscate" theme={null}
// Input.js
// Base64 Encoding Demo

var str = 'Hello, World!';
console.log(str); // "Hello, World!"

// Output.js
function szYWALe(szYWALe) {
  return atob(szYWALe);
}
function H07Txxp(H07Txxp) {
  return szYWALe(LTQD2W[H07Txxp]);
}
var LTQD2W = [
  'QTNJUjdUUTQ2Z0s=',
  'VFBRYjJLUGluZkFmUXdRaEc4dw==',
  'V2ZHaUdkY3NQSHU2VFlCaVlp',
  'QXh1S3paUDBJOTllRkVuMDRNTTc4cm9ueW5wN25YcUNhSEtTVG5iRlN0V0FB',
  'R0NaWXVCMmFWTUt1bA==',
  'R2ZuUzFHMzNtV2hTVUNLdUFuRzkwdXR5OUs=',
  'b1BGcEhUS2N5QWltcVNHbGh0alJ5RlhqdEhw',
  'R2lzME4zTDZLVUl3ekRhcEhaNGRJemZGZkYzaDE0N3B4WlJKM1htalhoZg==',
  'aDVQd2k5d2Z4eHNs',
  'SmhzcE4yNFRXY2hXVk4xODhUWk05b0N4bg==',
  'NjlMWWpyNkZ0d0UzbFI=',
  'RU92TUtxaDdCNDJ6UmxqQW40ZFFHc0E1Y3pENldIM2h0cUs1WEpL',
  'UDh5ckd2Wk1RVUhucjBSTHlCaFppblFwN2x1',
  'U0swcUJITGxTelZhVTl6OFNx',
  'UnZFRzBHZXJyQ0xNVVVxdm1seTU3b0s=',
  'UmI4Q1kyU2dUTkpxU2RTbWhkeg==',
  'ZklEV3BQbmRtbk5oWXRQazRUQXhSQkJheTk=',
  'U05Vc21vZFE4Y2xSRGk=',
  'c09qaVk2RTlXUGFoR2hhRDNUOHU2emVvbXhHVmJCTUlzWXpCS0w2dQ==',
  'Tmo5RWUwTGUwbVR2UFVHYTFmWjlsY3lNcQ==',
  'd0hZQ1FwcUo2M2t2NnJraklTNEUxRTk3VlBwTHQ5SGRKeFhkdHNzT3dXS3pOb3E=',
  'TktFMmI=',
  'OXhjdjZZUUs2TlduVlNWTnZicEFqeEphenZs',
  'V3RCOUdlQmpSVmZjNGdxNkI3TGFWV0dWcWtodUNYRUdFNU02Vng=',
  'MHdCNkNtMllFWmFjdEl1a3RtTG1QOHlsc1luaFlvNDN6N29MM2NKa2xIdXV0QQ==',
  'c3VpZ3hRU0tNbmxkUGhiTHJoM3hIMXI3eHZFOHRRQmk5U1NmUWxsZ1ZnbzR0',
  'MlFTWnFETEJzdWx2aU8=',
  'WTF1VzI=',
  'aWV5azVzdWpwRjBEU21mZ1ByVWlUWDNYMXNuck9MbXlySg==',
  'SVdGVDE1RmE=',
  'UGQ1VTFXZkRQVDg4dmRoN3VwUlhVME45REVOYXltYTJIUmNEWQ==',
  'bG5DbXZlMmJSSXZ5UUlCcg==',
  'UHJaeGpJQUZta0Y4dkVuRVR2VDJhZU9qZVNnUE82cWd3MTV3czU1czlXTUlV',
  'dGNnT3QzMEl2Y3JKYWlEcQ==',
  'ZTlYR3MxUnhBQW5I',
  'bzNlNTFIMzRqV2RDaEM=',
  'bEcycUIyNlF4UHE1eHJKbk9HRmdHcllEVG5xenc=',
  'YzBJaWNxaUZUMjVQUjA=',
  'RnRaQ3I0MmFmY09TaEdZaHA=',
  'dzZsU2pITHZLVkE1VWhPWVFadnRpaTBQdVFwRnk1bmI0aWE2U3hOS0o3SkY=',
  'eUxnZEZ4Y2M4M1hVU0xzVkoyRFRFS2o0Tzlnd29iRW1pVzNXemRrSkRaTw==',
  'TzBkN0Y5bm1pSjZQMFR0NkpKU1pvR0dWMzU=',
  'MlI3QnZQOGdnM3hwQlE3Y2cwS044NlRqZ3FUdGdXUTcxeDNS',
  'ZTdvenA=',
  'SU5XVlJxaDdBd3c3WGlDMmtHUkUyVTRkTTRtQ01BdWJhQWNDUHNBbXRuQnNxQTJBSA==',
  'NlQ4V0FhUXBqMU9CdWxRMXpxbzZlSWNJQkZESDRvakRIazg=',
  'eDZQYlBzaE41clRLeHRnUU5sMGRua25meHk2WHJNTDF0ME1MWmQ1VUU2RVhKNEFO',
  'UTJHVEk0bERsaUR4R3YzYWpIYkJMclowWWtiN0ltcw==',
  'd3ZPaGo0eGNNM3NuaDJBc2NVQTJmZ0hsRmQ=',
  'd3FXUll3ZTVzRTlYUzEydlQ3',
  'dmR3eDhRaTl0ZVkyY3U5Tkha',
  'WnZoMDlMYUFsOGhNNDVxZDFFS3RzeQ==',
  'c21QeUNBaHJuWjFGQWFHckMwcTRXdDNpQzBLYzdzYThGOHQ=',
  'T2p1QWJjSld0UA==',
  'VGxEamVFa00=',
  'c2hwdDNkaWtYRVk2bFZKTUxGMm1kUXk0',
  'UEIybERpUEtmd1pEdWc=',
  'TzdRUjR2aEx0OVpwZUtSd3o=',
  'QlllUFc5RncwYzhLSTNqNmZzbkE1V1ZUMmVlMlpNU1RNVUZCMVNRRmw0',
  'NXZxS3pVS00zSVdUSXRxQVJOdVBOekV3YmdsdUdYeTRYdEV2RWZUNFFkZERrSDBicQ==',
  'VFlxaGRnRjc3WEM3Vk5jRA==',
  'a2lVckIwekdSU0RxeGQ4Vnkxc0dXcnZ0UGxPZEVGWA==',
  'UVVFbWg1QXhNOWtU',
  'WXpvYmE4cTNKMVJ6OElpZmY0U0g4SVgzbw==',
  'TmlISldKWXBzQzA1RldzWkR2RUxoelU0Y1BReTZUWVI2ZGU=',
  'dHVaRE91ZTNCVU1qQ0FPQnB2aHJVZ3JrWkZ3c2FQbWhRV1o5MmlEVDFjc2o=',
  'c0ZQd0RYak5WMGZNZTRYeVc2U251UDJBZ0VJNGdZT1hPVlpTQml4a3k=',
  'WGs3RDVpUTh5T1R1WHhrTzBx',
  'dUNUTmpaM0FCUkQ1UVk2YWJPMDlsRndKVm4=',
  'Q3k2UDNDblNFUDJkdDJIb1Y=',
  'bzE0eFo0RFdiYmdIRA==',
  'NVlJVDJwdUJNRXhaN3dvb0x5QXM=',
  'RnZLN2lwZ3dsWHlFZTVWRlVhR2dYdDh0ck5FOTI=',
  'OGE1ajNBSmZ3cVdCRjU3ODBuTklJZVBUY0h6Rnh0a2t1',
  'NnFCTlVjNllvN3pxckVzSFRDcGpsdlJHRWpGOA==',
  'M3pzYjQ=',
  'dzNndzFU',
  'MTJsZFZQVW5uUmVHTFlrWkhOS2NEYjVV',
  'ems3VjlNS3RRWk02VWxKb0c4VA==',
  'S3dLMjhiU3plZWhZ',
  'Yk92M1JtbTN4RFVKYmNjUWw3TjNxUGk5TWVlMHBQeklPdElpcnlFbzg3QWp3Q2k=',
  'VThnR2xKak5XelVOUERH',
  'YjduNnNJUWsxd2tiaTBOMzlhUEI5MHptV2lyOXRENnc2YkJTWg==',
  'VW1iemZnakJTUWNGT0QyVWh1cEp2Slo4b0w=',
  'aDRmYUh4QUJZVzNxeUpTNmNBajlmMjVkdw==',
  'UEVrdmtr',
  'SGVsbG8sIFdvcmxkIQ==',
  'bG9n',
];
function eYeBV9() {
  var szYWALe = [
      function () {
        return globalThis;
      },
      function () {
        return global;
      },
      function () {
        return window;
      },
      function () {
        return new Function('return this')();
      },
    ],
    H07Txxp,
    LTQD2W = [];
  try {
    H07Txxp = Object;
    LTQD2W.push(''.__proto__.constructor.name);
  } catch (e) {}
  XxWINV: for (var eYeBV9 = 0; eYeBV9 < szYWALe.length; eYeBV9++)
    try {
      H07Txxp = szYWALe[eYeBV9]();
      for (var ZCGOxn = 0; ZCGOxn < LTQD2W.length; ZCGOxn++)
        if (typeof H07Txxp[LTQD2W[ZCGOxn]] === 'undefined') continue XxWINV;
      return H07Txxp;
    } catch (e) {}
  return H07Txxp || this;
}
var ZCGOxn = eYeBV9() || {},
  _euU7X = ZCGOxn.TextDecoder,
  gdwMU4 = ZCGOxn.Uint8Array,
  xYmAxNB = ZCGOxn.Buffer,
  RIAq_0O = ZCGOxn.String || String,
  D_5Dj_ = ZCGOxn.Array || Array,
  iCW0Br2 = (function () {
    var szYWALe = new D_5Dj_(128),
      H07Txxp = RIAq_0O.fromCodePoint || RIAq_0O.fromCharCode,
      LTQD2W = [];
    return function (eYeBV9) {
      var ZCGOxn,
        _euU7X,
        gdwMU4 = eYeBV9.length;
      LTQD2W.length = 0;
      for (var xYmAxNB = 0; xYmAxNB < gdwMU4; ) {
        _euU7X = eYeBV9[xYmAxNB++];
        _euU7X <= 0x7f
          ? (ZCGOxn = _euU7X)
          : _euU7X <= 0xdf
            ? (ZCGOxn = ((_euU7X & 0x1f) << 6) | (eYeBV9[xYmAxNB++] & 0x3f))
            : _euU7X <= 0xef
              ? (ZCGOxn =
                  ((_euU7X & 0x0f) << 12) |
                  ((eYeBV9[xYmAxNB++] & 0x3f) << 6) |
                  (eYeBV9[xYmAxNB++] & 0x3f))
              : RIAq_0O.fromCodePoint
                ? (ZCGOxn =
                    ((_euU7X & 0x07) << 18) |
                    ((eYeBV9[xYmAxNB++] & 0x3f) << 12) |
                    ((eYeBV9[xYmAxNB++] & 0x3f) << 6) |
                    (eYeBV9[xYmAxNB++] & 0x3f))
                : ((ZCGOxn = 63), (xYmAxNB += 3));
        LTQD2W.push(szYWALe[ZCGOxn] || (szYWALe[ZCGOxn] = H07Txxp(ZCGOxn)));
      }
      return LTQD2W.join('');
    };
  })();
function DYCwdaw(szYWALe) {
  return typeof _euU7X !== 'undefined' && _euU7X
    ? new _euU7X().decode(new gdwMU4(szYWALe))
    : typeof xYmAxNB !== 'undefined' && xYmAxNB
      ? xYmAxNB.from(szYWALe).toString('utf-8')
      : iCW0Br2(szYWALe);
}
var f1Pdn4u = H07Txxp(86);
console[H07Txxp(87)](f1Pdn4u);
```

***

#### Custom String Encoding API

The Custom String Encoding API allows you to define your own string encoding/decoding functions. These encodings will be randomly inserted throughout the code.

```js title="Options.js" lines theme={null}
module.exports = {
  target: "node",

  // Should be enabled
  stringConcealing: true,

  // Simple Base64 Encoding
  customStringEncodings: [
    {
      // This template decoder function will be inserted into the code
      code: `
            function {fnName}(str){
              return atob(str);
            }`,

      // Tells the obfuscator how to encode the string
      encode: (str) => btoa(str),
    },
  ],
};
```

***

The properties of the type `Custom String Encoding` are:

| Property    | Type       | Description                                         |
| ----------- | ---------- | --------------------------------------------------- |
| `code`      | `string`   | Template decoder code that must contain '{fnName}'. |
| `encode`    | `Function` | Encoding algorithm.                                 |
| `decode?`   | `Function` | Decoding algorithm. (Optional)                      |
| `identity?` | `string`   | Distinguishes multiple encodings. (Optional)        |

* The template `code` should contain the string `{fnName}`, which the obfuscator can interpolate with the function name.

* The functions `encode` and `decode` have the type: `(strValue: string) => string`.

* The function `decode` is optional. If provided, the obfuscator will validate each string to ensure it can be decoded. If the string cannot be decoded, the obfuscator will ignore the string.

***

#### Advanced Randomized Encoding

The following example implements a custom Base64 encoding that uses a shuffled charset to encode and decode strings.

* This encoding algorithm is instantiated multiple times, each with a different shuffled charset. This makes it difficult to reverse-engineer the encoding algorithm.

```js title="Options.js" lines theme={null}
const { Template } = require("js-confuser");
const { stringLiteral } = require("@babel/types");

function shuffle(array) {
  // Fisher-Yates shuffle
  let currentIndex = array.length,
    randomIndex;
  while (currentIndex !== 0) {
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex--;

    [array[currentIndex], array[randomIndex]] = [
      array[randomIndex],
      array[currentIndex],
    ];
  }
  return array;
}

function createCustomStringEncoding() {
  function encode(input, charset) {
    const inputBuffer = new TextEncoder().encode(input);
    let output = "";

    for (let i = 0; i < inputBuffer.length; i += 3) {
      const chunk = [inputBuffer[i], inputBuffer[i + 1], inputBuffer[i + 2]];

      const binary = (chunk[0] << 16) | (chunk[1] << 8) | (chunk[2] || 0);

      output += charset[(binary >> 18) & 0x3f];
      output += charset[(binary >> 12) & 0x3f];
      output +=
        typeof chunk[1] !== "undefined" ? charset[(binary >> 6) & 0x3f] : "=";
      output += typeof chunk[2] !== "undefined" ? charset[binary & 0x3f] : "=";
    }

    return output;
  }

  const customCharset =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  const shuffledCharset = shuffle(customCharset.split("")).join("");

  return {
    code: new Template(`
      // Creates a reverse lookup table from the given charset
      function createReverseCharset(charset) {
        if (charset.length !== 64) {
          throw new Error("Charset must be exactly 64 characters long.");
        }
        const reverseCharset = {};
        for (let i = 0; i < charset.length; i++) {
          reverseCharset[charset[i]] = i;
        }
        return reverseCharset;
      }

      // Base64 decode using the shuffled charset
      function decode(input, charset) {
        const reverseCharset = createReverseCharset(charset);
        const cleanedInput = input.replace(/=+$/, '');  // Remove padding

        const byteArray = [];
        let buffer = 0;
        let bitsCollected = 0;

        for (let i = 0; i < cleanedInput.length; i++) {
          buffer = (buffer << 6) | reverseCharset[cleanedInput[i]];
          bitsCollected += 6;

          if (bitsCollected >= 8) {
            bitsCollected -= 8;
            byteArray.push((buffer >> bitsCollected) & 0xFF);
          }
        }

        // Convert to string, ensuring no extra characters
        return new TextDecoder().decode(Uint8Array.from(byteArray));
      }

      var {fnName} = (str) => decode(str, {shuffledCharset});
      `).setDefaultVariables({
      // This simply inserts 'shuffledCharset' (with proper escaping)
      shuffledCharset: stringLiteral(shuffledCharset),
    }),
    encode: (input) => {
      // Encode the string
      return encode(input, shuffledCharset);
    },

    // Identity key to help distinguish between different variants
    identity: shuffledCharset,
  };
}

module.exports = {
  target: "node",
  stringConcealing: true,
  customStringEncodings: [createCustomStringEncoding],
};
```

***

### Usage Example

The provided code example will obfuscate the file `input.js` and write the output to a file named `output.js`.

```js title="Usage Example" lines theme={null}
import JSConfuser from "js-confuser";
import {readFileSync, writeFileSync} from "fs";

// Read input code
const sourceCode = readFileSync("input.js", "utf8");
const options = {
  target: 'browser',
  customStringEncodings: [
    {
      code: `
            function {fnName}(str){
              return atob(str)
            }
            `,
      encode: function(str) {
              return btoa(str);
            },
      decode: function(str) {
              return atob(str);
            },
    },
  ],
  stringConcealing: true,
};

JSConfuser.obfuscate(sourceCode, options).then((result)=>{
  // Write output code
  writeFileSync("output.js", result.code);
}).catch(err=>{
  // Error occurred
  console.error(err);  
});
```

***

#### Enabled In

* [High Preset](/presets/high): No
* [Medium Preset](/presets/medium): No
* [Low Preset](/presets/low): No
