Steganography encoded javascript encoder / decoder project. Create an image containing your javascript code. Generate a scriptloader and embed it onto your site.
An experiment to
- encode javascript to image data
- run image data as javascript
| var VERSION = 2; | |
| function Drawer(canvas) { | |
| this.canvas = canvas; | |
| this.width = canvas.width; | |
| this.height = canvas.height; | |
| this.ctx = canvas.getContext("2d"); | |
| this.imageData = this.ctx.createImageData(1, 1); | |
| } | |
| Drawer.prototype.putPixel = function (x, y, r, g, b, a) { | |
| this.imageData.data[0] = r; | |
| this.imageData.data[1] = g; | |
| this.imageData.data[2] = b; | |
| this.imageData.data[3] = a; | |
| this.ctx.putImageData(this.imageData, x, y); | |
| } | |
| Drawer.prototype.pushPixel = function (a, r, g, b) { | |
| this.determineNextPixel(); | |
| // Defaults | |
| r = r === undefined ? rnd() : r; | |
| g = g === undefined ? rnd() : g; | |
| b = b === undefined ? rnd() : b; | |
| a = a === undefined ? 255 : a; | |
| this.putPixel(this.x, this.y, r, g, b, a); | |
| } | |
| Drawer.prototype.encode = function (array, resize) { | |
| var self = this, len = array.length; | |
| resize && this.resize(len); | |
| console.log('Encoding length:', len, this.canvas.width, this.canvas.height); | |
| if (len >= this.width * this.height) throw 'Too large input!'; | |
| this.pushPixel(VERSION); | |
| array.forEach(function (d) { | |
| self.pushPixel(d); | |
| }); | |
| this.pushPixel(0); | |
| } | |
| Drawer.prototype.determineNextPixel = function () { | |
| if (this.x === undefined) { | |
| this.x = this.y = 0; | |
| } else { | |
| this.x++; | |
| if (this.x >= this.width) { | |
| this.x = 0; | |
| this.y++; | |
| } | |
| } | |
| } | |
| Drawer.prototype.resize = function (newSize) { | |
| var dim = Math.ceil(Math.sqrt(newSize)); | |
| this.width = this.height = this.canvas.width = this.canvas.height = dim; | |
| this.ctx = this.canvas.getContext("2d"); | |
| this.imageData = this.ctx.createImageData(1, 1); | |
| } | |
| function rnd() { | |
| return Math.floor(Math.random() * 80 + 80); | |
| } | |
| Drawer.prototype.toString = function () { | |
| var str = Drawer.prototype.constructor.toString() + '\n'; | |
| for (var i in Drawer.prototype) { | |
| str += 'Drawer.prototype.' + i + ' = ' + Drawer.prototype[i].toString() + '\n'; | |
| } | |
| return str; | |
| } |
| var ImageMangler = function (canvas) { | |
| this.canvas = canvas; | |
| this.reset(); | |
| } | |
| ImageMangler.prototype.isEmpty = function () { | |
| return this.data.length === 0; | |
| } | |
| ImageMangler.prototype.reset = function () { | |
| this.drawer = new Drawer(this.canvas); | |
| this.data = []; | |
| } | |
| ImageMangler.prototype.append = function (fn) { | |
| this.data = this.data.concat(convertFnToImageData(fn)); | |
| return this; | |
| } | |
| ImageMangler.prototype.draw = function () { | |
| this.drawer.encode(this.data, false); | |
| this.reset(); | |
| } | |
| function convertFnToImageData(func) { | |
| var returnData = [], | |
| input = func.toString().split(''); | |
| returnData = input.map(function (c) { | |
| return c.charCodeAt(0); | |
| }); | |
| return returnData; | |
| } |
| function ImageUploader(onImageLoadedCb) { | |
| this.onImageLoadedCb = onImageLoadedCb; | |
| // Create <input type="file" id="file" accept="image/*"> | |
| this.input = document.createElement('INPUT'); | |
| this.input.type = 'file'; | |
| this.input.id = 'file'; | |
| this.input.accept = 'image/*'; | |
| document.body.appendChild(this.input); | |
| this.input.addEventListener('change', this.onFileInputChange.bind(this)); | |
| } | |
| ImageUploader.prototype.onFileInputChange = function () { | |
| var file = this.input.files[0]; | |
| var reader = new FileReader(); | |
| reader.addEventListener('load', this.onFileLoad.bind(this)); | |
| reader.readAsDataURL(file); | |
| } | |
| ImageUploader.prototype.onFileLoad = function (e) { | |
| this.img = getImage(); | |
| this.img.addEventListener('load', this.onImageLoad.bind(this)); | |
| this.img.src = e.target.result; | |
| } | |
| ImageUploader.prototype.onImageLoad = function () { | |
| this.onImageLoadedCb(this.img); | |
| } | |
| function getImage() { | |
| var img = document.getElementById('image'); | |
| if (!img) { | |
| img = document.createElement('IMG'); | |
| img.id = 'image'; | |
| img.style = 'display:none'; | |
| document.body.appendChild(img); | |
| } | |
| return img; | |
| } |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <link rel="stylesheet" href="style.css"> | |
| <script src="Drawer.js"></script> | |
| <script src="Runner.js"></script> | |
| <script src="ImageUploader.js"></script> | |
| <script src="ImageMangler.js"></script> | |
| <script src="script.js"></script> | |
| </head> | |
| <body> | |
| <h1>polyvinyl</h1> | |
| <h2>Javascript to image encoder</h2> | |
| Canvas: | |
| <canvas id="myCanvas" style="border:1px solid #d3d3d3;" width="500" height="500"></canvas> | |
| <hr/> | |
| <p>Check console...</p> | |
| </body> | |
| </html> |
| var VERSION = 2; | |
| function Runner(canvas) { | |
| this.canvas = canvas; | |
| this.ctx = canvas.getContext("2d"); | |
| this.reset(); | |
| } | |
| Runner.prototype.reset = function () { | |
| this.x = this.y = undefined; | |
| this.width = this.canvas.width; | |
| this.height = this.canvas.height; | |
| } | |
| Runner.prototype.decode = function () { | |
| var dataLength, | |
| stringData = ''; | |
| this.reset(); | |
| if (this.popPixel() === VERSION) { | |
| this.popPixel(); | |
| var chr; | |
| while ((chr = this.popPixel()) > 0) stringData += String.fromCharCode(chr); | |
| } | |
| return stringData; | |
| } | |
| Runner.prototype.popPixel = function () { | |
| this.determineNextPixel(); | |
| this.data = this.ctx.getImageData(this.x, this.y, 1, 1).data; | |
| return this.data[3]; | |
| } | |
| Runner.prototype.determineNextPixel = function () { | |
| if (this.x === undefined) { | |
| this.x = this.y = 0; | |
| } else { | |
| this.x++; | |
| if (this.x >= this.width) { | |
| this.x = 0; | |
| this.y++; | |
| } | |
| } | |
| } | |
| Runner.prototype.toString = function () { | |
| var str = Runner.prototype.constructor.toString() + '\n'; | |
| for (var i in Runner.prototype) { | |
| str += 'Runner.prototype.' + i + ' = ' + Runner.prototype[i].toString() + '\n'; | |
| } | |
| return str; | |
| } |
| // Code goes here | |
| window.addEventListener('load', onLoad); | |
| function onLoad(e) { | |
| var canvas = document.getElementById('myCanvas'), | |
| tamperer = new ImageMangler(canvas), | |
| runner = new Runner(canvas), | |
| uploader = new ImageUploader(uploaded); | |
| // example payload: | |
| tamperer.append(tamperer).append(runner).append(uploader); | |
| console.log('init done'); | |
| function uploaded(image) { | |
| //console.log('New image selected!', image); | |
| canvas.width = image.width; | |
| canvas.height = image.height; | |
| canvas.getContext("2d").drawImage(image, 0, 0); | |
| console.log('Decoded message:', runner.decode()); | |
| if (!tamperer.isEmpty()) { | |
| setTimeout(tamperer.draw.bind(tamperer), 5000); | |
| } | |
| } | |
| } |
| /* Styles go here */ | |