有你在真好 的个人博客
[反爬虫技术]破解加速乐最新爬虫防护机制(C#实现)
阅读:2452 添加日期:2021/3/27 23:28:56 原文链接:https://www.toutiao.com/item/6305992186605339137/

这么多年做爬虫,遇到了各类防爬虫的情况!这不,一个天天要抓数据的网站,突然的全部521错误,经分析发现,网站是启用了知道创宇的加速乐超强防护,导致无法通过直接请求抓取内容。浏览器打开,效果如下图所示:

[反爬虫技术]破解加速乐最新爬虫防护机制(C#实现)防护机制分析

依靠多年经验,分析代码发现加入了JS校验机制,因为现有的HTTPREQUEST类不执行JS,也不方便执行,导致获取不到合法的COOKIES值!

第一次不带COOKIES提交抓取代码如下(JS代码):

<script>var x="challenge@1500@parseInt@location@5@toString@a@Fu@firstChild@replace@3D@x@href@43@rhh@Path@charAt@2@match@document@Expires@0@return@innerHTML@13@1@Mon@createElement@GMT@3@1465217983@join@16@substr@div@r@Jun@while@var@captcha@h@l@36@2A@dc@__phantomas@https@length@cookie@_phantom@989@function@i@06@cd@6MZF@f@dC6@setTimeout@for@__jsl_clearance@window@59@toLowerCase@if@try@addEventListener@catch@e@false@DOMContentLoaded@else@attachEvent@onreadystatechange".replace(/@*$/,"").split("@"),y="16 19=1j(){15(1t.1h||1t.1d){};16 1m,1c='1s=v.1i|m|';16 1o=[1j(c){n c},1j(c){n c;},(1j(){16 18=k.s('12');18.o='<7 d=\\'/\\'>c</7>';18=18.9.d;16 13=18.j(/1e?:\\/\\//)[m];18=18.11(13.1f).1v();n 1j(c){1r(16 1k=m;1k<c.1f;1k++){c[1k]=18.h(c[1k])};n c.w('')}})(),1j(c){1r(16 1k=m;1k<c.1f;1k++){c[1k]=3(c[1k]).6(1a)};n c.w('')}];1m=['1p',[~~[]],[(-~((-~{}-~{}^(+!+[])))+[[], ~~{}][~~[]]),[~~[]]],'c',[-~{}-~{}+5],'1n',[~~[]],[(-~{}+[[], ~~{}][~~[]])+[-~{}-~{}+5],[(-~![]<<-~![])]+(-~((-~[]<<(-~![]<<-~![])))+[[]+[], []][-~[]])],'f',[[(-~![]<<-~![])]+(-~{}+[[], ~~{}][~~[]])],'1b%',[(-~![]<<-~![])],'8',[[(-~[]+[(-~![]<<-~![])]>>(-~![]<<-~![]))]+[(-~[]+[(-~![]<<-~![])]>>(-~![]<<-~![]))]],[(-~{}+[[], ~~{}][~~[]])+(-~((-~{}-~{}^(+!+[])))+[[], ~~{}][~~[]])],[~~[]]+[-~{}/~~{}+[]+[[]][~~!{}]][m].h(~~[]),'%b'];1r(16 1k=m;1k<1m.1f;1k++){1m[1k]=1o[[q,m,i,q,m,q,m,u,q,u,q,m,q,u,i,m,q][1k]](1m[1k])};1m=1m.w('');1c+=1m;1q('4.d=4.d.a(/[\\?|&]17-1/,\\'\\')',2);k.1g=(1c+';l=r, 1l-14-10 p:1u:e t;g=/;');};1w((1j(){20{n !!1t.21;}22(23){n 24;}})()){k.21('25',19,24);}26{k.27('28',19);}",z=0,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},g=y.match(/\b\w+\b/g).sort(function(x,y){return f(x)-f(y)}).pop();while(f(g,++z)-x.length){};eval(y.replace(/\b\w+\b/g, function(y){return x[f(y,z)-1]}));</script>

HTTP HEAD信息:

HTTP/1.1 521

Date: Mon, 06 Jun 2016 13:22:32 GMT

Content-Type: text/html

Transfer-Encoding: chunked

Connection: close

Set-Cookie: __jsluid=700bc1eef2051fae74718e053f836421; path=/

X-Cache: error

HEAD返回状态为521,并只传递了一个 __jsluid的COOKIES值,这个值要和JS中设置的一个校验值对应后才认为是合法的访问身份。

反防护破解

下面我们就来分析这个JS代码,代码看起来是加密过的,首先我们先解密。

懂点JS代码是必须的,这段代码中关键部分如下:

eval(y.replace(/\b\w+\b/g, function(y){return x[f(y,z)-1]}))

eval就是执行解密后的JS代码,首先我们想办法把解密后的代码提取出来,并且赋值给我们自己程序里的变量,C#里我们自己得有个执行JS代码并返回执行结果的函数,代码如下:

  1. ///需要添加引用COM:Microsoft Script Control 1.0

  2. /// <summary>

  3. /// 执行JS代码并返回结果

  4. /// </summary>

  5. /// <param name="sScript">JS代码</param>

  6. /// <param name="sName">JS函数名</param>

  7. /// <returns>结果</returns>

  8. private static string ExecJS(string sScript, string sName)

  9. {

  10. //string sInitUrl = "http://ipangu.baidu.com/ipangu-hint/hint/hintCustAdd_init.action";

  11. ScriptControlClass js = new ScriptControlClass();//使用ScriptControlClass

  12. object result = null;

  13. try

  14. {

  15. js.Language = "javascript";

  16. js.Reset();

  17. js.Eval(sScript);//指向js脚本

  18. object[] obj = new object[] { };

  19. result = js.Run(sName, ref obj);//传入参数执行

  20. }

  21. catch (Exception ex)

  22. {

  23. result = ex.Message;

  24. }

  25. return result.ToString();

  26. }

然后我们把抓取到的代码,提交给这段函数执行,直接执行恐怕不行,应为程序不知道返回哪个变量值,这里我们就需要对这段JS做下修改,假设我们先把这段JS代码存在了string sHtmlJs这个字符串变量里,我们需要把eval这里执行的结果提取出来,把eval替换成 return,然后把整个代码放到一个JS函数里,方式如下:

  1. sHtmlJs.Replace("eval", "return ");

  2. sHtmlJs= "function getResult(){" + HtmlJs+ "};";

  3. string sResult= ExecJS(sHtmlJs, "getResult");

执行后,我们得到了解密的代码,格式化后如下:

  1. var l = function() {

  2. while (window._phantom || window.__phantomas) {};

  3. var cd, dc = '__jsl_clearance=1465220574.595|0|';

  4. var f = [function(x) {

  5. return x

  6. },

  7. function(x) {

  8. return x;

  9. },

  10. (function() {

  11. var h = document.createElement('div');

  12. h.innerHTML = '<a href=\'/\'>x</a>';

  13. h = h.firstChild.href;

  14. var r = h.match(/https?:\/\//)[0];

  15. h = h.substr(r.length).toLowerCase();

  16. return function(x) {

  17. for (var i = 0; i < x.length; i++) {

  18. x[i] = h.charAt(x[i])

  19. };

  20. return x.join('')

  21. }

  22. })(),

  23. function(x) {

  24. for (var i = 0; i < x.length; i++) {

  25. x[i] = parseInt(x[i]).toString(36)

  26. };

  27. return x.join('')

  28. }];

  29. cd = ['n', [[ - ~ {}] + (6 + [[], !-[]][~~ ! {}])], 'S', (5 + [] + [[]][~~ {}]), 'WHYQCpV', [[ - ~ ( + !~~ [])] + ( - ~ [ - ~ [] + ( - ~~~ {} << -~~~ {}) + ( - ~ ( + !~~ [])) * [ - ~ ( + !~~ [])]] + [] + [[]][~~ {}])], [(2 << -~ ! [])], [[ - ~ {}] + (5 + [] + [[]][~~ {}]), [ - ~ {}] + [ - ~ {}]], 'j%2BfUU', [[ - ~ ( + !~~ [])] + (5 + [] + [[]][~~ {}])], [ - ~ {} + [( + [])] - ( - ~ {})], 'd', ( - ~ [] / ~~ [] + []).charAt(~~ {}), 'PFA%3D'];

  30. for (var i = 0; i < cd.length; i++) {

  31. cd[i] = f[[1, 2, 1, 0, 1, 3, 0, 2, 1, 3, 0, 1, 0, 1][i]](cd[i])

  32. };

  33. cd = cd.join('');

  34. dc += cd;

  35. setTimeout('location.href=location.href.replace(/[\?|&]captcha-challenge/,\'\')', 1500);

  36. document.cookie = (dc + ';Expires=Mon, 06-Jun-16 14:42:54 GMT;Path=/;');

  37. };

  38. if ((function() {

  39. try {

  40. return !! window.addEventListener;

  41. } catch(e) {

  42. return false;

  43. }

  44. })()) {

  45. document.addEventListener('DOMContentLoaded', l, false);

  46. } else {

  47. document.attachEvent('onreadystatechange', l);

  48. }

哈哈,真牛,依然加密!这里我们稍微分析下代码,我们主要事项获取它设置JS的那段代码,其他的一些跳转代码直接可以不需要执行的。所以什么windows对象,document对象的一些操作就可以直接屏蔽掉了。C#里执行里涉及到这些对象的操作也会直接报错。

注意看代码中的:

  1. document.cookie = (dc + ';Expires=Mon, 06-Jun-16 14:42:54 GMT;Path=/;');

这里可以知道DC就是我们需要的COOKIES值了!和前面一样,执行JS,返回DC就OK了!

去掉不需要的代码,去掉的函数我就不写了,很简单,得到如下关键代码:

  1. var l = function() {

  2. var cd, dc = '__jsl_clearance=1465220574.7|0|';

  3. var f = [function(x) {

  4. return x

  5. },

  6. function(x) {

  7. return x;

  8. },

  9. (function() {

  10. var h = 'x';

  11. return function(x) {

  12. for (var i = 0; i < x.length; i++) {

  13. x[i] = h.charAt(x[i])

  14. };

  15. return x.join('')

  16. }

  17. })(),

  18. function(x) {

  19. for (var i = 0; i < x.length; i++) {

  20. x[i] = parseInt(x[i]).toString(36)

  21. };

  22. return x.join('')

  23. }];

  24. cd = ['5K8', [[ - ~ {}] + (5 + [] + [[]][~~ {}])], [(5 + [] + [[]][~~ {}]), (5 + [] + [[]][~~ {}])], 'G', [[ - ~ {}] + [ - ~ ( + !~~ [])]], 'dA', [[ - ~ ( + !~~ [])] + [ - ~ {}]], [[ - ~ {}] + ( - ~ [ - ~ [] + ( - ~~~ {} << -~~~ {}) + ( - ~ ( + !~~ [])) * [ - ~ ( + !~~ [])]] + [] + [[]][~~ {}])], [(2 << -~ ! [])], 'F', [[ - ~ ( + !~~ [])] + [ - ~ {}]], 'txnK%', [ - ~ ( + !~~ [])], 'B', [[ - ~ {}] + ( - ~ [] + 2 + ( - ~ ( + !~~ [])) * [ - ~ ( + !~~ [])] + [[]][~~ {}])], 'TuL', [( - ~ [] + 2 + ( - ~ ( + !~~ [])) * [ - ~ ( + !~~ [])] + [[]][~~ {}])], 'L', [[ - ~ {}] + [(2 << -~ ! [])]], '%3D'];

  25. for (var i = 0; i < cd.length; i++) {

  26. cd[i] = f[[1, 3, 2, 1, 2, 1, 3, 2, 0, 1, 3, 1, 0, 1, 3, 1, 2, 1, 2, 1][i]](cd[i])

  27. };

  28. cd = cd.join('');

  29. dc += cd;

  30. return dc;

  31. };

然后把这个代码提交给我们的ExecJS函数执行:

sResult= ExecJS(sHtmlJS, "l"); //这个函数名“l”是原有JS自己的,我们可以直接调用

看返回结果:

__jsl_clearance=1465220574.595|0|nS5WHYQCpVs4j%2BfUUp9dIPFA%3D

这就是我们想要的!哈哈哈...

和前面获取到的COOKIES值进行拼接后:

__jsluid=700bc1eef2051fae74718e053f836421; __jsl_clearance=1465220574.595|0|nS5WHYQCpVs4j%2BfUUp9dIPFA%3D

带着这个COOKIES再请求服务器,服务器返回200啦!

至此,完美解决!

ICP备案号:苏ICP备14035786号-1 苏公网安备 32050502001014号