-前言:
之前对中国裁判网文书网关于JS加密过程进行了详细的讲解,但是还留下了一些问题,关于文书ID和RunEval的加密还未解开,这里分开上下来讲,也是因为最后这边关于文书ID的解密会比之前参数加密要复杂和麻烦一些,上下并不关联,所以即使单看这一章也没有问题。
一、介绍
我们上一章爬虫网络请求之JS解密三(中国裁判文书网上)讲到获取到网页List中文书ID后,或者打开裁判文书网抓到的数据报文找到获取list数据的接口返回的数据,可以看到如图1-1所示:
我们能得到的就是一个RunEval的参数和加密了的文书ID,而我们需要的文书ID应该是如图1-2所示:
真实DocID是有数字加字母和‘-’组成,所以我们需要想办法加密获取到的文书ID
二、页面分析
我们先在网页上找到链接获取位置,发现如图2-1所示:
我们发现在一个id=dataItem1的div标签中属性key为我们获取到的DocID密文,在下面有一个点击触发的JS函数,函数名Navi。所以我们需要获取Navi的JS代码才能清楚,如何解密DocID。
三、JS解密
我们同样通过浏览器的抓包发现在Lawyee.CPWSW.List.js下发现Navi函数,代码如下:
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函数定义中我们可以看到里面定义了一个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参数。所以我们只需要得到com.str.Decrypt和unzip函数就可以解开Navi加密的DocID。
我们接下来找unzip函数定义位置,在数据报文/Assets/Js/pako.min.js中找到我们要找的函数,代码如下:
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};
这里的函数很重要,因为很多东西只能猜,我们能看懂的是这里有一个unzip函数定义,下面有一个com={},定义了一个字典类型(dict)参数com,它有两个个默认值_KEY、_IV。其中_Key为我们需要解密DocID用的秘钥,另一个_IV应该是解开KeyWord参数的秘钥,但是我们这里只需要DocID就可以了,所以只需要这个_KEY,这里有个坑,一定要留意,大家如果用这里的_KEY作秘钥,即使最后获得了解密方法也无法解出来DocID ,因为这里给的秘钥是一个默认密钥,只能解最开始默认的那一页。
同样我们需要改写这里的函数,我们知道com.str下面定义了Encrypt,Decrypt,DecryptInner函数,并且传入了参数this._KEY,我们在这些函数头增加新的参数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的关键。
我们回忆之前获取到图1-1中数据报,除了文书ID外还有一个参数RunEval,这个就是我们需要的用来解开_KEY的关键。
在Lawyee.CPWSW.List.js文件下也就是找到Navi函数定义那,检索RunEval会发现如图3-2所示:
之前也是在想这个参数有什么作用,每次请求一次Listcontent都会有一个新的RunEval值,所以怀疑这里会是KEY,这也就是需要靠点直觉,毕竟这么多js文件 也不能全看明白。如果你能找到这里,会看到它会用到unzip(RunEval),我们继续用谷歌控制后台(console)运行这一步。发现如图3-3所示:
这里发现$hidescript=string,fromCharCode()函数,string,fromCharCode()作用是接受一个指定的 Unicode 值,然后返回一个字符串。里面全是一个加密的东西。这个就是使用了JSFuck的加密手段,只用六个字符 [ ] ( ) ! +来表示一段代码,据说是黑客为了防止js注入代码被过滤所开发的。附上源码地址jsfuck源码git地址 感兴趣可以去看看。这里被加密了。
这里其实有jsfuck之外,还进行了一些额外的操作,如果单独运行这段代码,会发现只有一小部分代码,如图3-4 所示
这里其实就是返回了$hidescript的值,这里jsfuck后半段代码,如图3-5所示:
在jsfuck中_="constructor"; _ [ _ ] [ _ ]()就是执行一个匿名函数,后面有很多$hidescript混在jsfuck代码中,我们其实只需要翻译出执行的匿名函数,提取这段代码,分为两部分,第一部分得出$hidescript的值,第二部分代码中用第一部分得出$hidescript的值替换所有$hidescript,同时去掉最末尾括号。最后运行并返回被隐藏的js代码运行结果,如图3-6所示:
这样我们就发现了com.str._KEY,然后我们改写代码Navi函数,加入我们解密的_key。
function Navi(id,str_key){
var unzipid=unzip(id);
var realid=com.str.Decrypt(unzipid,str_key);
return realid
}
将我们解开的_Key带入,加入我们得到的一段DocID密文进去,运行结果如3-7所示:
这里我们关于DocID的解密工作就完成了( ^ - ^)~!
结语:
关于裁判文书网的js内容就全部总结完了,我之前写的裁判爬虫网络请求之JS解密裁判文书网上,一直被简书网管给锁定发不出来,不知道是不是写了代码原因,所以这里也就不贴我的测试代码了。写关于js解密的初衷也只是总结自己对JS加密研究和解密手段并分享一些心得,感兴趣的朋友一起探讨和学习。并没有想让大家都去爬这个网站。所以一直也没有写一个完整的爬虫代码出来。还是那句话,仅供学习参考,切勿用于商业用途。