DEV Community

WangLiwen
WangLiwen

Posted on • Updated on

JavaScript Magic Tricks: Steganography

JavaScript Magic Tricks: Steganography

This article will use JavaScript to implement "Image Steganography".

What is steganography?

Steganography refers to the technique of writing text or other data into an image. In the example below, there may be hidden secret information, even though it appears to be just a regular image.

Image description

Effects and Applications

The effect of image steganography is the ability to write information into an image and read it back. It can be used for storing hidden content within images, adding invisible copyright markings to images, and so on.

Technical Principle

An image is composed of pixels, and each pixel is made up of RGB (Red, Green, Blue) color components. For example, in CSS, colors are defined using hexadecimal values like #FFFFFF, where FF represents the maximum intensity for each color component. In binary form, FF is represented as 8 bits: 11111111. Modifying the least significant bit (LSB) of this binary representation does not affect the color or visual appearance of the image. Therefore, steganography techniques utilize this fact by writing the hidden information into the LSB of these 8 bits.

Image description

For example, to steganographically embed the character "a", first convert "a" to binary using the following code: "a".charCodeAt(0).toString(2) This yields the value: "01100001". The steganographic embedding would then proceed as follows:

Image description

When implementing the program, the process is as follows:

  • 1. Read the original image and obtain the pixel-level bit information of the image.
  • 2.Convert the information to be steganographically embedded into binary.
  • 3.Write the binary steganographic information into the last bit of each pixel.
  • 4.Save and generate a new image. To retrieve the information, obtain the last bit of each pixel in the image, and convert the binary back into character information.

Source:

<html>
<body>
    选择文件:<input type='file' id='file' /><br>
    图片预览:<canvas id='canvas' style='width: 300px;'></canvas><br>
    隐写信息:<textarea id='message'></textarea><br>
    <button id='encode' class='submit'>隐写</button><br>
    隐写图片:<img id='output' style='width: 300px;'><br>
    <button id='decode'>从隐写图片读取信息</button><br>
    读出的隐写内容:<div id='message_decoded'></div><br>
    <script>
        //事件绑定
        window.onload = function() {
            var input = document.getElementById('file');
            input.addEventListener('change', import_image);
            var encode_button = document.getElementById('encode');
            encode_button.addEventListener('click', encode);
            var decode_button = document.getElementById('decode');
            decode_button.addEventListener('click', decode);
        };

        //选择文件后,将图片显示在canvas中
        var import_image = function(e) {
            var reader = new FileReader();
            reader.onload = function(event) {
                var img = new Image();
                img.onload = function() {
                    var ctx = document.getElementById('canvas').getContext('2d');
                    ctx.canvas.width = img.width;
                    ctx.canvas.height = img.height;
                    ctx.drawImage(img, 0, 0);
                };
                img.src = event.target.result;
            };
            reader.readAsDataURL(e.target.files[0]);
        };

        //隐写并保存图片
        var encode = function() {
            var message = document.getElementById('message').value;
            var output = document.getElementById('output');
            var canvas = document.getElementById('canvas');
            var ctx = canvas.getContext('2d');
            var pixel_count = ctx.canvas.width * ctx.canvas.height;
            if ((message.length + 1) * 16 > pixel_count * 4 * 0.75) {
                alert('内容太多了,超过了可写入的最大量');
                return;
            }
            //核心函数:隐写
            var img_data = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
            encode_message(img_data.data, message);
            ctx.putImageData(img_data, 0, 0);
            alert('隐写成功,信息已隐藏到图片中');
            output.src = canvas.toDataURL();
        };

        //读出隐写的信息
        var decode = function() {
            var ctx = document.getElementById('canvas').getContext('2d');
            var img_data = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
            //核心功能:从图片数据中读取隐写信息
            var message = decode_message(img_data.data);
            alert(message);
            document.getElementById('message_decoded').innerHTML = message;
        };

        //将二进制编码信息转为字符串
        var get_number_from_bits = function(bytes, history) {
            var number = 0;
            var pos = 0;
            while (pos < 16) {
                var loc = get_next_location(history, bytes.length);
                var bit = getBit(bytes[loc], 0);
                number = set_bit(number, pos, bit);
                pos++;
            }
            return number;
        };
        var get_next_location = function(history, total) {
            var pos = history.length;
            var loc = Math.abs(pos + 1) % total;
            while (true) {
                if (loc >= total) {
                    loc = 0;
                } else if (history.indexOf(loc) >= 0) {
                    loc++;
                } else if ((loc + 1) % 4 === 0) {
                    loc++;
                } else {
                    history.push(loc);
                    return loc;
                }
            }
        };
        var set_bit = function(number, location, bit) {
            return (number & ~(1 << location)) | (bit << location);
        };

        //将信息字符串转为二进制编码
        var get_message_bits = function(message) {
            var message_bits = [];
            for (var i = 0; i < message.length; i++) {
                var code = message.charCodeAt(i);
                message_bits = message_bits.concat(get_bits_from_number(code));
            }
            return message_bits;
        };
        var get_bits_from_number = function(number) {
            var bits = [];
            for (var i = 0; i < 16; i++) {
                bits.push(getBit(number, i));
            }
            return bits;
        };
        var getBit = function(number, location) {
            return ((number >> location) & 1);
        };

        //编码信息
        var encode_message = function(colors, message) {
            var message_bits = get_bits_from_number(message.length);
            message_bits = message_bits.concat(get_message_bits(message));
            var history = [];
            var pos = 0;
            while (pos < message_bits.length) {
                var loc = get_next_location(history, colors.length);
                colors[loc] = set_bit(colors[loc], 0, message_bits[pos]);
                while ((loc + 1) % 4 !== 0) {
                    loc++;
                }
                colors[loc] = 255;
                pos++;
            }
        };

        //解码信息
        var decode_message = function(colors) {
            var history = [];
            var message_size = get_number_from_bits(colors, history);
            if ((message_size + 1) * 16 > colors.length * 0.75) {
                return '';
            }
            var message = [];
            for(var i = 0; i < message_size; i++) {
                var code = get_number_from_bits(colors, history);
                message.push(String.fromCharCode(code));
            }
            return message.join('');
        };
    </script>
</body>
</html>

Enter fullscreen mode Exit fullscreen mode

Execution Result:

Image description

Source Code Analysis:

The function encode_message is the core operation for steganographic embedding. It converts the information into binary and saves it in the least significant bit of each pixel in the image. The corresponding function is decode_message, which retrieves the information from the steganographic image.

In practical applications, the steganography and information retrieval parts should be used independently and separated: steganography is performed on the backend, while information retrieval is done on the frontend.

To prevent others from analyzing and detecting the logic of the frontend information retrieval process, you can use JShaman to obfuscate and encrypt the JavaScript code used for frontend steganographic information retrieval.

Top comments (0)