聲明:本文只是對javascript文件進行了學習性的注釋,源文件是從網站上獲取的。
- 如果此文章侵犯了你的權益,請盡快聯系我刪除
- 為了防止引流,這里就不放原作者的url了。
- 可以依據此代碼寫一個對應的解密過程程序
- 為了簡化原文和方便閱讀,對一些函數進行了重命名。
- 由於簡化了輸入等控件,這個JavaScript不能運行,請知悉
在此感謝大佬 ybzjdsd(哆啦可尼夫)
let IMG1 = new Image();
let IMGINFO = [];
let img = new Image();
let MODE = 4;
let SRC1 = "";
let SRC2 = "";
function readFile() {
var oFReader = new FileReader();
var ofile = document.getElementById("ipt2").files[0];//指定讀入
oFReader.readAsDataURL(ofile);//將png轉換為data:開頭的base64編碼
oFReader.onloadend = function (oFRevent) {//在讀取結束時觸發下述事件
var base64_img = oFRevent.target.result;//將讀取器內的結果復制到變量中
img.src = base64_img;//確定圖片源便於下一步讀取
img.onload = function () {//在圖片讀取時操作,在頁面或圖像加載完成后立即發生
start()//開始進行解碼的預備
}
}
}
function start() {
try {
let f = sol();
//至此結束
//下面是處理HTML的過程
if (SRC2) {
URL.revokeObjectURL(SRC2)
}
SRC2 = URL.createObjectURL(f[0]);
document.getElementById("a2").href = SRC2;
document.getElementById("img2").src = SRC2;
document.getElementById("info2a").style.display = "block";
document.getElementById("a2").download = f[1];
document.getElementById("info2").innerHTML = f[1]
} catch (e) {
alert("圖片讀取失敗")
}
}
function utf8Decode(inputStr) {
var outputStr = "";
var code1,
code2,
code3,
code4;
for (var i = 0; i < inputStr.length; i++) {
code1 = inputStr.charCodeAt(i);
if (code1 < 128) {
outputStr += String.fromCharCode(code1);
} else if (code1 < 224) {
code2 = inputStr.charCodeAt(++i);
outputStr += String.fromCharCode(((code1 & 31) << 6) | (code2 & 63));
} else if (code1 < 240) {
code2 = inputStr.charCodeAt(++i);
code3 = inputStr.charCodeAt(++i);
outputStr += String.fromCharCode(((code1 & 15) << 12) | ((code2 & 63) << 6) | (code3 & 63));
} else {
code2 = inputStr.charCodeAt(++i);
code3 = inputStr.charCodeAt(++i);
code4 = inputStr.charCodeAt(++i);
outputStr += String.fromCharCode(((code1 & 7) << 18) | ((code2 & 63) << 12) | ((code3 & 63) << 6) | (code2 & 63));
}
}
return outputStr;
}
function sol() {
let cv = document.createElement("canvas");
let cvd = cv.getContext("2d");
cv.width = img.width;
cv.height = img.height;
cvd.drawImage(img, 0, 0);
let imgdata = cvd.getImageData(0, 0, img.width, img.height);//imgdata是圖像的RGBA數據,使用
/*imgdata數據結構
*
* R - 紅色 (0-255)
* G - 綠色 (0-255)
* B - 藍色 (0-255)
* A - alpha 通道 (0-255; 0 是透明的,255 是完全可見的)
* 其中第n個像素的RGBA值為
* (imgdata[(n-1)*4 + 0],
* imgdata[(n-1)*4 + 1] ,...)
**/
let klist = decodeFile(imgdata.data[2] % 8, imgdata);//圖像的解密模式在第一像素的藍色通道的8余數里
//這里返回的klist具有如下數據結構:
/**
* klist[0]文件詳細信息數組,,第一個參數([0])是字節數,第二個是文件名,第三個是文件類型
* klist[1]為Uint8Array類型
* */
let file = new File([klist[1].buffer], utf8Decode(klist[0][1]), {
//生成一個文件,文件的內容是buffer,名字為源文件名
//buffer:ArrayBuffer 對象用來表示通用的、固定長度的原始二進制數據緩沖區。它是一個字節數組,通常在其他語言中稱為“byte array”
type: klist[0][2]//類型也是源文件的類型
})
//返回一個數組,第一參數是生成的解密文件,第二參數是文件名
return [file, utf8Decode(klist[0][1])]
}
function decodeFile(mode, imgdata) {//這個mode似乎是表圖壓縮度
let aa = Math.ceil(3 * mode / 8);//ceil:向上取整
let n = imgdata.width * imgdata.height;//像素數
let j = 0;
let k = "";
let current_pixel_index = 1;
let mlist = [1, 2, 4, 8, 16, 32, 64, 128]; //冗余代碼
let word = "";
let blist
//=new Uint8Array();
let blength = 0;
//對每個像素點進行處理
while (current_pixel_index < n && (word.length == 0 || word.slice(-1).charCodeAt(0) > 0)) {
//如果i小於像素數並且
//word長度為零 或 word沒有結束(最后一個字符不為空
//關鍵在於此處運算,對RGB的值二進制序列化之后取后四位得到目標序列,即隱寫
k = k + (imgdata.data[4 * current_pixel_index] + 256).toString(2).slice(-mode);//將R值運算之后,以2進制表示並取后mode位,然后加和到k上,下同
k = k + (imgdata.data[4 * current_pixel_index + 1] + 256).toString(2).slice(-mode);
k = k + (imgdata.data[4 * current_pixel_index + 2] + 256).toString(2).slice(-mode);
current_pixel_index++//像素前移
for (let ii = 0; ii < aa; ii++) {
if (k.length >= 8 && (word.length == 0 || word.slice(-1).charCodeAt(0) > 0)) {
//如果k的長度大於等於8位 並且
//word長度為零 或 word沒有結束(最后一個字符不為空
word = word + String.fromCharCode(parseInt(k.slice(0, 8), 2));//將k的前8位轉換為字符,加到word里
k = k.slice(8);//k遞減
}
}
}
//像素處理結束
//從word里獲取源文件的信息,這里容易看到word的格式如下
/**
* 字節數(大小)\u0001 源文件名稱 \u0001 源文件類型(
*/
blength = parseInt(word.split(String.fromCharCode(1))[0]);//獲取源文件大小
if (!(blength > -1)) {
//如果源文件大小出錯則報錯
throw "error"
}
if (!(word.split(String.fromCharCode(1)).length > 2)) {
//如果不符合上面的數據結構,也就是找不到三個參數則報錯
throw "error"
}
blist = new Uint8Array(blength);//8位無符號整型數組,長度為源文件字節長度
//此處上述算法執行結束后k的值應該是8位2進制,
//將k的值按照2進制寫入blist[0]
if (k.length >= 8 && j < blength) {
blist[j] = parseInt(k.slice(0, 8), 2);
k = k.slice(8);
j++
}
//繼續讀取,直至讀完源文件字節數
while (current_pixel_index < n && j < blength) {
k = k + (imgdata.data[4 * current_pixel_index] + 256).toString(2).slice(-mode);
k = k + (imgdata.data[4 * current_pixel_index + 1] + 256).toString(2).slice(-mode);
k = k + (imgdata.data[4 * current_pixel_index + 2] + 256).toString(2).slice(-mode);
current_pixel_index++
for (let ii = 0; ii < aa; ii++) {
if (k.length >= 8 && j < blength) {
//同樣的,從此處獲取源文件的二進制值轉成RGB值存到目標組里
blist[j] = parseInt(k.slice(0, 8), 2);
k = k.slice(8);
j++
}
}
}
//返回一個組,第一參數是源文件信息,第二參數是文件的組
return [word.split(String.fromCharCode(0))[0].split(String.fromCharCode(1)), blist]
}
可以對應的寫出Python代碼:
import cv2
import numpy
import pylab
import matplotlib.pyplot as plt
import math
import struct
import array
import sys
import time
starttime = time.time()
argv = sys.argv
if(len(argv) != 2):
print("用法: dewytk [filename]")
print("將在執行路徑生成圖片")
endtime = time.time()
print('[!-1]耗時', str(round(endtime - starttime, 2)),'秒')
exit(-1)
else:
imgfile = argv[1]
try:
img = cv2.imread(imgfile,cv2.IMREAD_UNCHANGED)
imgdata = []
rawdata = dict()
#print("圖像的形狀,返回一個圖像的(行數,列數,通道數):",img.shape)
n = img.size
a = 1
mode = 4
for i in range(img.shape[0]):
for j in range(img.shape[1]):
p = img[i][j]
#BGRA
rawdata = dict()
rawdata['R'] = p[0]
rawdata['G'] = p[1]
rawdata['B'] = p[2]
imgdata.append(rawdata)
if(i == 0 and j == 0):
mode = p[0] % 8
if ( mode <= 0 or mode >4):
endtime = time.time()
print('[!-3]耗時', str(round(endtime - starttime, 2)),'秒')
exit(-3)
aa = math.ceil(3*mode/8)
n = img.size
j = 0
k = ""
current_pixel_index = 1
word = ""
blist = []
blength = 0
while(current_pixel_index < n and (len(word) == 0 or ord(word[-1])>0 )):
k = k+str(bin(imgdata[current_pixel_index]['B'] + 256))[-mode:]
k = k+str(bin(imgdata[current_pixel_index]['G'] + 256))[-mode:]
k = k+str(bin(imgdata[current_pixel_index]['R'] + 256))[-mode:]
current_pixel_index = current_pixel_index +1
for i in range(0,aa):
if(len(k) >= 8 and (len(word) == 0 or ord(word[-1]) > 0)):
word = word + chr(int(k[0:8],2))
k = k[8:]
#ref to lines#139
blength = int(word.split(chr(1))[0])
if(blength <= -1 or not (len(word.split(chr(1))) >2)):
endtime = time.time()
print('[!-2]無法處理未知的隱寫參數。耗時', str(round(endtime - starttime, 2)),'秒')
exit(-2)
blist = []
if(len(k) >= 8 and j < blength):
blist.append(int(k[0:8],2))
k = k[8:]
j = j + 1
while (current_pixel_index < n and j < blength):
k = k+str(bin(imgdata[current_pixel_index]['B'] + 256))[-mode:]
k = k+str(bin(imgdata[current_pixel_index]['G'] + 256))[-mode:]
k = k+str(bin(imgdata[current_pixel_index]['R'] + 256))[-mode:]
current_pixel_index = current_pixel_index +1
for i in range(0,aa):
if(len(k) >= 8 and j<blength):
blist.append(int(k[0:8],2))
k = k[8:]
j = j + 1
with open(str(word.split(chr(1))[1]), 'wb')as fp:
for x in blist:
a = struct.pack('B', x)
fp.write(a)
endtime = time.time()
print("已經向源文件 " + str(word.split(chr(1))[1]) + "\t寫入了 " + str(word.split(chr(1))[0]) + "\t字節,耗時 " + str(round(endtime - starttime, 2))+ "\t秒")
exit(0)
pass
except:
endtime = time.time()
print("[!-5]" + '耗時', str(round(endtime - starttime, 2)),'秒')
exit(-5)
pass
要解密整個文件夾下的所有加密文件,可以使用下述指令(cmd)
要提前將上述腳本保存為decode.py
@echo off
for %%i in (*) do ( python decode.py %%i)
