Python处理JS加密和混淆算法获取__jsl_clearance(二)
通过上一个教的详细分析,我们理出来思路,用Python来实现
第一步拿到源码部分Js,python请求代码如下
1 import requests 2 from urllib3 import disable_warnings 3 from urllib3.exceptions import InsecureRequestWarning 4 import execjs 5 import re 6 7 8 9 disable_warnings(InsecureRequestWarning) 10 11 12 header = { 13 'User-Agent':"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36", 14 'Accept-Encoding': 'br, gzip, deflate', 15 'Accept':'*/*' 16 } 17 18 19 url = 'https://www.xxx.com' 20 21 response = requests.get(url,headers=header) 22 23 html = response.text
本教程是为了验证正常性,不用重新请求了,源码直接从浏览器抓包那复制过来了,JS源代码如下:
1 <script>var x="@@join@g@36@__jsl_clearance@false@parseInt@location@@@rOm9XFMtA3QKV7nYsPGT4lifyWwkq5vcjH2IdxUoCbhERLaz81DNB6@firstChild@@2F9k@hantom@@0xEDB88320@56@RegExp@e@2FMLE@@m0@else@8@fromCharCode@for@match@GMT@href@challenge@__p@19@d@@@@@@catch@29@while@@captcha@JYf@as@JgSe0upZ@s@charAt@onreadystatechange@Expires@@try@@@@0@charCodeAt@2@replace@18@@@06@length@eval@toLowerCase@@1500@cookie@f@function@@reverse@toString@substr@@new@@@Path@@a@@@chars@Sun@1534655216@return@@@Aug@if@DOMContentLoaded@@https@@var@@@search@D@String@@@window@Array@@2BAz@addEventListener@@@0xFF@@document@createElement@div@@@@attachEvent@setTimeout@split@g9@innerHTML@1@pathname".replace(/@*$/,"").split("@"),y="25 2l=1q(){2t('9.v=9.2y+9.28.1e(/[\\?|&]J-w/,\\'\\')',1n);2m.1o='6=1G.G|1b|'+(1q(){25 2=[1q(2l){1H 2l},1q(2){1H 2},(1q(){25 2l=2m.2n('2o');2l.2w='<1B v=\\'/\\'>2r</1B>';2l=2l.d.v;25 2=2l.t(/23?:\\/\\//)[1b];2l=2l.1u(2.1j).1l();1H 1q(2){s(25 2r=1b;2r<2.1j;2r++){2[2r]=2l.13(2[2r])};1H 2.3('')}})()],2r=[[(+!2d['x'+'g'+'10'])]+[(+!2d['x'+'g'+'10'])],([-~{}-~{}]*((-~{}-~{}^-~[]))+[]+[[]][1b]),[(+!2d['x'+'g'+'10'])]+(-~{}-~{}+[]),(-~~~{}-~[(-~![]+[~~[]])/[((+!2d['x'+'g'+'10'])<<(+!2d['x'+'g'+'10']))]]+[[]][1b]),(-~((1d)*[(((+!2d['x'+'g'+'10'])<<(+!2d['x'+'g'+'10'])))*[((+!2d['x'+'g'+'10'])<<(+!2d['x'+'g'+'10']))]])+[]+[[]][1b]),((-~{}-~{}^-~[])+[]+[]),[(+!2d['x'+'g'+'10'])]+[~~{}],(-~{}-~{}+[]),[~~{}],[([((+!2d['x'+'g'+'10'])<<(+!2d['x'+'g'+'10']))]+~~''>>((+!2d['x'+'g'+'10'])<<(+!2d['x'+'g'+'10'])))],[(-~~~{}+[((+!2d['x'+'g'+'10'])<<(+!2d['x'+'g'+'10']))]>>((+!2d['x'+'g'+'10'])<<(+!2d['x'+'g'+'10'])))+([((+!2d['x'+'g'+'10'])<<(+!2d['x'+'g'+'10']))]+~~''>>((+!2d['x'+'g'+'10'])<<(+!2d['x'+'g'+'10'])))],[-~-~[]-~-~[]],[(+!2d['x'+'g'+'10'])]];s(25 2l=1b;2l<2r.1j;2l++){2r[2l]=2[[1b,2x,1d,2x,1b,2x,1b,2x,1d,1b,2x,1b,2x][2l]]([[[(+!2d['x'+'g'+'10'])]+(-~{}-~{}+[])],'29','o','K%2g%m',((-~{}-~{}^-~[])+[]+[]),[[]-{}+[]][1b].13(1d),'2v','l','12%f%',[{}+[]+[[]][1b]][1b].13(-~{}+[~~{}]-(-~{})),(!~~''+[]).13((+!2d['x'+'g'+'10']))+[!''+[[]][1b]][1b].13(~~{})+[!{}+[]+[]][1b].13((-~-~[]<<(+!2d['x'+'g'+'10']))),({}+[]+[[]][1b]).13((1d)*[(((+!2d['x'+'g'+'10'])<<(+!2d['x'+'g'+'10'])))*[((+!2d['x'+'g'+'10'])<<(+!2d['x'+'g'+'10']))]]),[[-~-~[]-~-~[]]]][2r[2l]])};1H 2r.3('')})()+';15=1F, y-1K-1f 1i:1i:j u;1z=/;'};20((1q(){17{1H !!2d.2h;}F(l){1H 7;}})()){2m.2h('21',2l,7)}p{2m.2s('14',2l)}",f=function(x,y){var a=0,b=0,c=0;x=x.split("");y=y||99;while((a=x.shift())&&(b=a.charCodeAt(0)-77.5))c=(Math.abs(b)<13?(b+48.5):parseInt(a,36))+y*c;return c},z=f(y.match(/\w/g).sort(function(x,y){return f(x)-f(y)}).pop());while(z++)try{eval(y.replace(/\b\w+\b/g, function(y){return x[f(y,z)-1]||("_"+y)}));break}catch(_){}</script>
过滤掉Scrpt标签:
1 #匹配js代码,只取以<script>开关 以</script>结束 2 ret = re.findall('\\s*?<script>(.*?)</script>',html) 3 4 #获得到js代码 5 js = ret[0]
获取到,第一次console.log打印的代码,自定义一个函数名getResult调用返回值
#由于Js代码加密了,需要解密,即第一次console.log打印的代码,返回给Python做处理 js = js.replace(r"eval(", r"return ("); jsstr = "function getResult(){" + js + "};"; ctx = execjs.compile(jsstr) #调用解密,得到返回的代码 js = ctx.call('getResult')
效果如下图:

由于Python调用Js库的特性问题,只能return返回值且最外层只能用一个函数供外面调用且不支持像window,document这类元素,JS代码中
若包含都统统要去掉,通过分析如下代码没有无用

首先获取正则获到_21的函数体部分代码如下:
1 code = [] 2 start = False; 3 deep = 0; 4 for c in js: 5 if not start: 6 if c == '{': 7 start = True 8 deep += 1 9 else: 10 continue 11 else: 12 13 if c == '{': 14 deep += 1 15 code.append(c) 16 elif c == '}': 17 deep -= 1 18 if deep == 0: 19 break 20 else: 21 code.append(c) 22 else: 23 code.append(c) 24 #获取函数主体 25 func_body = ''.join(code)
接下来正则去掉__jsl_clearance=和之前的部分,代码如下:
1 #去掉前面设置Cookie的部分 2 pattern = re.compile(r'(.*?__jsl_clearance=)') 3 4 5 #设置为返回值,去掉前的值 6 func_body = re.sub(pattern, '', func_body)
经过多次测试__jsl_clearance=有可能是一个变量替换的所以用时间戳来判断,并处理
如果不是时间戳开头,就再次处理下,从头到时间戳,也可以直接到时间戳的位置
1 if str(func_body[0:10]).isnumeric(): 2 func_body = 'return \'' + func_body; 3 else: 4 ret = re.findall(r'(.*?)([0-9]{10})\.',func_body) 5 if len(ret) > 0 and len(ret[0])> 1 and (ret[0][1]).isnumeric(): 6 pattern = re.compile(r'(.*?)[0-9]{10}\.') 7 func_body = re.sub(pattern,'return \'' + ret[0][1]+'.', func_body) 8 else: #没有找到时间戳 9 print(func_body)
接下来处理document.createElement的位置按图上的位置到href值是url
1 pattern = re.compile(r"document\..*?\.href") 2 func_body = re.sub(pattern,"\""+ url+ "\"", func_body)
接下来处理windows元素的干扰问题,windows这个干扰随机变化的只能用则处理,还有可能是多个种
1 #去掉window干扰 2 #func_body = func_body.replace("window['__p'+'hantom'+'as']","\"\"") 3 4 #func_body = func_body.replace("while(window._phantom||window.__phantomas){};", ""); 5 6 7 pattern = re.compile("(window\[[^\]]+\])") 8 func_body = re.sub(pattern,"undefined", func_body)
经统计有多种类型的windows元素干扰
#(window.headless + [[]][0]) ret= re.findall(r'window[^\+\]]*',func_body) if len(ret) > 0: func_body = func_body.replace(ret[0],"\"\"")
关于干扰问题还有多个变量的干扰比如retrun,function被他替换成了
一个别的变量名,这种比较变态了,只有少数的情况,基本上面的处理
可以达到90%以上的能返回了
最后直接合成完成的JS函数给Python调用,代码如下:
jsstr = "function getResult(){" + func_body + "};"; ctx = execjs.compile(jsstr) #调用获取cookie cookie_value = ctx.call('getResult') print(cookie_value)
得到如下结果:

1534655216.29|0|Og99ebJYf%2BAz%2FMLErtem0mNs%2F9k%3D;Expires=Sun, 19-Aug-18 06:06:56 GMT;Path=/;
上面就是Python实现的全过程了,如果觉得对您有帮忙,请给一个赞!!!

浙公网安备 33010602011771号