参考自:https://github.com/oakes/PixelJihad
千里码:隐写术-1
canvas文档:https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement
藏有信息的lenna
读取隐藏信息
前言
这里说一下我的心路历程。
最开始做千里码的时候就看到这个题了,觉得很有意思,但是一直不太理解最低有效位是个什么东西,也没有深究,觉得很神秘的样子。后来慢慢偶尔看到隐写术多看两眼,就了解到了。
这里我想说的是啊,针对我这种白痴的blog还是太少,对图片处理稍微不了解或者没听说过一些没解释的名词就看不懂了,很难受。
原理
图片基本单位?
每一个像素,就是屏幕上最小的一个点,就是图片最基本的一个单位了。
一个像素点在canvas中可以获取哪些信息?
类似于css中一种取色方法rbga,这四个字母分别代表红绿蓝以及透明度,这四个值在canvas中可以以数组形式读取。
获取图片的数据数组?
F12打开浏览器控制台执行下面的代码,就可以看到我lenna图片的数据,推荐使用chrome浏览器或chrome内核。
var canvas = document.createElement('canvas');
var img = new Image();
var ctx = canvas.getContext('2d');
img.onload = function(){
ctx.canvas.width = img.width;
ctx.canvas.height = img.height;
ctx.drawImage(img, 0, 0);
}
img.src = 'https://m00zik.com/usr/uploads/2017/12/3001927610.png';
console.log(ctx.getImageData(0, 0, 256, 256));
我本人是比较喜欢这种比较直接的展示形式,大白话说半天真不如试一下。
执行了上面的代码后,会看到返回了一个 ImageData
对象,里面的 data
就是图片的数据了。
里面每4个数字,就代表了一个像素点,分别是红、绿、蓝、透明度。
信息就隐藏在这些数字里。
最低有效位是什么?
把数字转换成二进制,最后那一位就是最低有效位英文 least significant bit
,简称就是 LSB
。
常见的数据隐藏方式?
在图片整体上对某个颜色的最低有效位进行批量更改,就可以获得基于图片大小的二进制存储空间,当然会损失图片质量,但是肉眼无法辨别,要眼神特别好而且显示器特别好才行。
千里码隐藏信息的方法不是把文字文本藏进二进制里,而是用最低有效位在原图上写了字,那就直接读取某个颜色的最低有效位,然后1代表有数据,0代表没数据,有数据的给赋值上颜色,没数据的给涂黑了,数据就能以图片的形式展示出来了。
解题过程
去千里码页面下载藏有信息的lenna照片,然后放到下面的小工具里就可以看到隐藏的信息了。
本文代码
window.onload = function () {
document.getElementById("file").addEventListener("change", importImage);
}
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);
}
var importImage = function (e) {
var reader = new FileReader();
reader.onload = function (event) {
img.src = event.target.result;
};
reader.readAsDataURL(e.target.files[0]);
};
var step1 = function (color) {
img.onload();
var ctx = document.getElementById("canvas").getContext('2d');
var imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
var delpos1, delpos2;
if (color === 'red') {
delpos1 = 1;
delpos2 = 2;
}
if (color === 'green') {
delpos1 = 0;
delpos2 = 2;
}
if (color === 'blue') {
delpos1 = 0;
delpos2 = 1;
}
for (var i = 0; i < imgData.data.length; i += 4) {
imgData.data[i + delpos1] = 0;
imgData.data[i + delpos2] = 0;
}
ctx.putImageData(imgData, 0, 0);
};
var step2 = function (color) {
img.onload();
var ctx = document.getElementById("canvas").getContext('2d');
var imgData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
var datapos;
if (color === 'red') {
datapos = 0;
}
if (color === 'green') {
datapos = 1;
}
if (color === 'blue') {
datapos = 2;
}
for (var i = 0; i < imgData.data.length; i += 4) {
if ((imgData.data[i + datapos]).toString(2).substr(-1) !== '1') {
imgData.data[i + 0] = 0;
imgData.data[i + 1] = 0;
imgData.data[i + 2] = 0;
} else {
imgData.data[i + 0] = 0;
imgData.data[i + 1] = 0;
imgData.data[i + 2] = 0;
imgData.data[i + datapos] = 255;
}
}
ctx.putImageData(imgData, 0, 0);
}
1 条评论
因为使用了cdn镜像存储,所以代码里面图片的地址被替换成cdn的地址了,自己把static去掉就行了。
刚发现这里也被替换了