-前言:
之前對中國裁判網(wǎng)文書網(wǎng)關(guān)于JS加密過程進行了詳細(xì)的講解,但是還留下了一些問題,關(guān)于文書ID和RunEval的加密還未解開,這里分開上下來講,也是因為最后這邊關(guān)于文書ID的解密會比之前參數(shù)加密要復(fù)雜和麻煩一些,上下并不關(guān)聯(lián),所以即使單看這一章也沒有問題。
一、介紹
我們上一章爬蟲網(wǎng)絡(luò)請求之JS解密三(中國裁判文書網(wǎng)上)講到獲取到網(wǎng)頁List中文書ID后,或者打開裁判文書網(wǎng)抓到的數(shù)據(jù)報文找到獲取list數(shù)據(jù)的接口返回的數(shù)據(jù),可以看到如圖1-1所示:

我們能得到的就是一個RunEval的參數(shù)和加密了的文書ID,而我們需要的文書ID應(yīng)該是如圖1-2所示:

真實DocID是有數(shù)字加字母和‘-’組成,所以我們需要想辦法加密獲取到的文書ID
二、頁面分析
我們先在網(wǎng)頁上找到鏈接獲取位置,發(fā)現(xiàn)如圖2-1所示:

我們發(fā)現(xiàn)在一個id=dataItem1的div標(biāo)簽中屬性key為我們獲取到的DocID密文,在下面有一個點擊觸發(fā)的JS函數(shù),函數(shù)名Navi。所以我們需要獲取Navi的JS代碼才能清楚,如何解密DocID。
三、JS解密
我們同樣通過瀏覽器的抓包發(fā)現(xiàn)在Lawyee.CPWSW.List.js下發(fā)現(xiàn)Navi函數(shù),代碼如下:
function Navi(id,keyword){
var unzipid=unzip(id);
try{
var realid=com.str.Decrypt(unzipid);
if(realid==""){
setTimeout("Navi('"+id+"','"+keyword+"')",1000)}
else{
var url="/content/content?DocID="+realid+"&KeyWord="+encodeURI(keyword);
openWin(url)
}
}
catch(ex){
setTimeout("Navi('"+id+"','"+keyword+"')",1000)}
}
Navi函數(shù)定義中我們可以看到里面定義了一個url,url="/content/contentDocID="+realid+"&KeyWord="+encodeURI(keyword)
其中DocID=realid,我們讀這個代碼知道,realid=com.str.Decrypt(unzip(id)),id獲取位置在這個js文件中找到如圖3-1所示:

id就等于dataitem屬性中key的值,這就如我們想的一樣,我們獲取到加密的文書ID就是這里的id參數(shù)。所以我們只需要得到com.str.Decrypt和unzip函數(shù)就可以解開Navi加密的DocID。
我們接下來找unzip函數(shù)定義位置,在數(shù)據(jù)報文/Assets/Js/pako.min.js中找到我們要找的函數(shù),代碼如下:
function unzip(b64Data){
var strData;if(!window.atob){}else{}
var charData;
if(!Array.prototype.map){}else{}
strData=Base64_Zip.btou(RawDeflate.inflate(Base64_Zip.fromBase64(b64Data)));
return strData
}
var com={};com.str={_KEY:"12345678900000001234567890000000",_IV:"abcd134556abcedf",
Encrypt:function(str){var key=CryptoJS.enc.Utf8.parse(this._KEY);
var iv=CryptoJS.enc.Utf8.parse(this._IV);var encrypted="";
var srcs=CryptoJS.enc.Utf8.parse(str);encrypted=CryptoJS.AES.encrypt(srcs,key,{iv:iv,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7});
return encrypted.ciphertext.toString()},
Decrypt:function(str){var result=com.str.DecryptInner(str);
try{var newstr=com.str.DecryptInner(result);if(newstr!=""){result=newstr}}catch(ex){var msg=ex}return result},
DecryptInner:function(str){var key=CryptoJS.enc.Utf8.parse(this._KEY);var iv=CryptoJS.enc.Utf8.parse(this._IV);var encryptedHexStr=CryptoJS.enc.Hex.parse(str);var srcs=CryptoJS.enc.Base64.stringify(encryptedHexStr);var decrypt=CryptoJS.AES.decrypt(srcs,key,{iv:iv,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7});
var decryptedStr=decrypt.toString(CryptoJS.enc.Utf8);
var result=decryptedStr.toString();try{result=Decrypt(result)}catch(ex){var msg=ex}return result
}};
function iemap(myarray,callback,thisArg){
var T,A,k;if(myarray==null){throw new TypeError(" this is null or not defined")}var O=Object(myarray);var len=O.length>>>0;
if(typeof callback!=="function"){throw new TypeError(callback+" is not a function")}if(thisArg){T=thisArg}A=new Array(len);k=0;while(k<len){var kValue,mappedValue;if(k in O){kValue=O[k];mappedValue=callback.call(T,kValue,k,O);A[k]=mappedValue}k++}return A};
這里的函數(shù)很重要,因為很多東西只能猜,我們能看懂的是這里有一個unzip函數(shù)定義,下面有一個com={},定義了一個字典類型(dict)參數(shù)com,它有兩個個默認(rèn)值_KEY、_IV。其中_Key為我們需要解密DocID用的秘鑰,另一個_IV應(yīng)該是解開KeyWord參數(shù)的秘鑰,但是我們這里只需要DocID就可以了,所以只需要這個_KEY,這里有個坑,一定要留意,大家如果用這里的_KEY作秘鑰,即使最后獲得了解密方法也無法解出來DocID ,因為這里給的秘鑰是一個默認(rèn)密鑰,只能解最開始默認(rèn)的那一頁。
同樣我們需要改寫這里的函數(shù),我們知道com.str下面定義了Encrypt,Decrypt,DecryptInner函數(shù),并且傳入了參數(shù)this._KEY,我們在這些函數(shù)頭增加新的參數(shù)str_key,這樣來傳入我們獲取的秘鑰。改寫如下所示:
function unzip(b64Data){
var strData;if(!window.atob){}else{}
var charData;
if(!Array.prototype.map){}else{}
strData=Base64_Zip.btou(RawDeflate.inflate(Base64_Zip.fromBase64(b64Data)));
return strData
}
var com={};com.str={_KEY:"12345678900000001234567890000000",_IV:"abcd134556abcedf",
Encrypt:function(str,str_key){var key=CryptoJS.enc.Utf8.parse(str_key);
var iv=CryptoJS.enc.Utf8.parse(this._IV);var encrypted="";
var srcs=CryptoJS.enc.Utf8.parse(str);encrypted=CryptoJS.AES.encrypt(srcs,key,{iv:iv,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7});
return encrypted.ciphertext.toString()},
Decrypt:function(str,str_key){var result=com.str.DecryptInner(str,str_key);
try{var newstr=com.str.DecryptInner(result,str_key);if(newstr!=""){result=newstr}}catch(ex){var msg=ex}return result},
DecryptInner:function(str,str_key){var key=CryptoJS.enc.Utf8.parse(str_key);var iv=CryptoJS.enc.Utf8.parse(this._IV);var encryptedHexStr=CryptoJS.enc.Hex.parse(str);var srcs=CryptoJS.enc.Base64.stringify(encryptedHexStr);var decrypt=CryptoJS.AES.decrypt(srcs,key,{iv:iv,mode:CryptoJS.mode.CBC,padding:CryptoJS.pad.Pkcs7});
var decryptedStr=decrypt.toString(CryptoJS.enc.Utf8);
var result=decryptedStr.toString();try{result=Decrypt(result)}catch(ex){var msg=ex}return result
}};
function iemap(myarray,callback,thisArg){
var T,A,k;if(myarray==null){throw new TypeError(" this is null or not defined")}var O=Object(myarray);var len=O.length>>>0;
if(typeof callback!=="function"){throw new TypeError(callback+" is not a function")}if(thisArg){T=thisArg}A=new Array(len);k=0;while(k<len){var kValue,mappedValue;if(k in O){kValue=O[k];mappedValue=callback.call(T,kValue,k,O);A[k]=mappedValue}k++}return A};
改寫完后,我們需要做的就是找到之前講的秘鑰_KEY,這個也是解開DocID的關(guān)鍵。
我們回憶之前獲取到圖1-1中數(shù)據(jù)報,除了文書ID外還有一個參數(shù)RunEval,這個就是我們需要的用來解開_KEY的關(guān)鍵。
在Lawyee.CPWSW.List.js文件下也就是找到Navi函數(shù)定義那,檢索RunEval會發(fā)現(xiàn)如圖3-2所示:

之前也是在想這個參數(shù)有什么作用,每次請求一次Listcontent都會有一個新的RunEval值,所以懷疑這里會是KEY,這也就是需要靠點直覺,畢竟這么多js文件 也不能全看明白。如果你能找到這里,會看到它會用到unzip(RunEval),我們繼續(xù)用谷歌控制后臺(console)運行這一步。發(fā)現(xiàn)如圖3-3所示:

這里發(fā)現(xiàn)$hidescript=string,fromCharCode()函數(shù),string,fromCharCode()作用是接受一個指定的 Unicode 值,然后返回一個字符串。里面全是一個加密的東西。這個就是使用了JSFuck的加密手段,只用六個字符 [ ] ( ) ! +來表示一段代碼,據(jù)說是黑客為了防止js注入代碼被過濾所開發(fā)的。附上源碼地址jsfuck源碼git地址 感興趣可以去看看。這里被加密了。
這里其實有jsfuck之外,還進行了一些額外的操作,如果單獨運行這段代碼,會發(fā)現(xiàn)只有一小部分代碼,如圖3-4 所示

這里其實就是返回了$hidescript的值,這里jsfuck后半段代碼,如圖3-5所示:

在jsfuck中_="constructor"; _ [ _ ] [ _ ]()就是執(zhí)行一個匿名函數(shù),后面有很多$hidescript混在jsfuck代碼中,我們其實只需要翻譯出執(zhí)行的匿名函數(shù),提取這段代碼,分為兩部分,第一部分得出$hidescript的值,第二部分代碼中用第一部分得出$hidescript的值替換所有$hidescript,同時去掉最末尾括號。最后運行并返回被隱藏的js代碼運行結(jié)果,如圖3-6所示:

這樣我們就發(fā)現(xiàn)了com.str._KEY,然后我們改寫代碼Navi函數(shù),加入我們解密的_key。
function Navi(id,str_key){
var unzipid=unzip(id);
var realid=com.str.Decrypt(unzipid,str_key);
return realid
}
將我們解開的_Key帶入,加入我們得到的一段DocID密文進去,運行結(jié)果如3-7所示:

這里我們關(guān)于DocID的解密工作就完成了( ^ - ^)~!
結(jié)語:
關(guān)于裁判文書網(wǎng)的js內(nèi)容就全部總結(jié)完了,我之前寫的裁判爬蟲網(wǎng)絡(luò)請求之JS解密裁判文書網(wǎng)上,一直被簡書網(wǎng)管給鎖定發(fā)不出來,不知道是不是寫了代碼原因,所以這里也就不貼我的測試代碼了。寫關(guān)于js解密的初衷也只是總結(jié)自己對JS加密研究和解密手段并分享一些心得,感興趣的朋友一起探討和學(xué)習(xí)。并沒有想讓大家都去爬這個網(wǎng)站。所以一直也沒有寫一個完整的爬蟲代碼出來。還是那句話,僅供學(xué)習(xí)參考,切勿用于商業(yè)用途。