JavaScript Magic Tricks: Invisible Characters
Published: 2023-09-28 03:00:46

In this article, we will share a peculiar JavaScript programming technique that enables strings to be "invisible" and undetectable!

The effect demonstration is shown in the following image. After undergoing a specific operation, a string has a length of 621 bytes but its content is invisible and undetectable! 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cg6m4jul4wns1p3phc3t.png)

To run this in the browser:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/707br64327glcug2jwa9.png)

## What is the purpose of the function?
This technique can be applied to many areas and is highly practical. Some examples include: code obfuscation, data encryption, text steganography, content confidentiality, invisible watermarking, and more.

## Technical principles
To achieve invisible strings, the technical principle is "zero-width characters".
What are "zero-width characters"? In Unicode encoding, there is a strange type of character format that is invisible and unprintable, mainly used to adjust the display format of characters. Common types of zero-width characters include:
Zero-width space: formatted as U+200B, used for line breaks in longer characters;
Zero-width non-breaking space: formatted as U+FEFF, used to prevent line breaks at specific positions;
Zero-width joiner: formatted as U+200D, used in Arabic and Indian languages to create ligatures between characters that wouldn't normally join;
Zero-width non-joiner: formatted as U+200C, used in Arabic, German, and Indian languages to prevent ligatures between characters that would normally join;
Left-to-right mark: formatted as U+200E, used to specify the direction of text layout as left-to-right in mixed direction text;
Right-to-left mark: formatted as U+200F, used to specify the direction of text layout as right-to-left in mixed direction text.
To implement invisible character functionality in programming, first convert the string to binary, then convert 1 in binary to \u200b, 0 to \u200c, and spaces to \u200d, finally using \ufeff zero-width non-breaking space as a separator. These Unicode characters are all invisible, so when they are combined after conversion, they form a completely invisible "invisible" string.

## Source

```
function text_2_binary(text){
    return text.split('').map(function(char){ return char.charCodeAt(0).toString(2)}).join(' ');
}
function binary_2_hidden_text(binary){
    return binary.split('').map(function (binary_num){ 
        var num = parseInt(binary_num, 10);
        if (num === 1) {
            return '\u200b';
        } else if(num===0) {
            return '\u200c';
        }
        return '\u200d';
    }).join('\ufeff')
}
var text = "jshaman是专业且强大的JS代码混淆加密工具";
var binary_text = text_2_binary(text);
var hidden_text = binary_2_hidden_text(binary_text);
console.log("原始字符串：",text);
console.log("二进制：",binary_text);
console.log("隐藏字符：",hidden_text,"隐藏字符长度：",hidden_text.length);
```

Invisible Restoration Next, we will introduce how to restore the content after it has been made "invisible". After understanding the principle of character invisibility in the previous text, and combined with the source code, we know that restoring the invisible content requires performing the inverse operation: converting the hidden Unicode encoding into binary, and then converting the binary back into the original characters. The complete source code for invisibility and restoration:

```
//隐藏部分
function text_2_binary(text){
    return text.split('').map(function(char){ return char.charCodeAt(0).toString(2)}).join(' ');
}
function binary_2_hidden_text(binary){
    return binary.split('').map(function (binary_num){ 
        var num = parseInt(binary_num, 10);
        if (num === 1) {
            return '\u200b';
        } else if(num===0) {
            return '\u200c';
        }
        return '\u200d';
    }).join('\ufeff')
}
var text = "jshaman是专业且强大的JS代码混淆加密工具";
var binary_text = text_2_binary(text);
var hidden_text = binary_2_hidden_text(binary_text);
console.log("原始字符串：",text);
console.log("二进制：",binary_text);
console.log("隐藏字符：",hidden_text,"隐藏字符长度：",hidden_text.length);

//还原部分
function hidden_text_2_binary(string){
    return string.split('\ufeff').map(function(char){
    if (char === '\u200b') {
        return '1';
    } else if(char === '\u200c') {
        return '0';
    }
    return ' ';
    }).join('')
}
function binary_2_Text(binaryStr){
    var text = ""
    binaryStr.split(' ').map(function(num){
    text += String.fromCharCode(parseInt(num, 2));
    }).join('');
    return text.toString();
}
console.log("隐形字符转二进制：",hidden_text_2_binary(hidden_text));
console.log("二进制转原始字符：",binary_2_Text(hidden_text_2_binary(hidden_text)));
```

## Result

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gf8b5hnyg92bhy68mc0w.png)

If the "invisible" character content is provided directly in the code, such as during AJAX communication, and the encrypted content is decrypted using the aforementioned method, the transmitted content can be very confidential.

Note: "Invisible character" technology can be used in both the front-end and back-end JS execution environments. It can be executed in Node.js and also in browsers. If it is used for the front-end, to improve code security, the JS code can first be obfuscated and encrypted using JShaman.