本篇介绍如何破解汽车之家配置页面的伪元素和混淆的JS。
** 温馨提示:如需转载本文,请注明内容出处。**
本文链接:https://www.cnblogs.com/grom/p/9242156.html
(本文分多次编辑,可从原文章查看最新更新)
笔者爬取得网站中,印象最为深刻的就是汽车之家的网站了,也是麻烦最多的网站之一了,特点是页面大面积使用伪元素代替关键字,解析伪元素的JS进行了动态混淆,每次刷新后的JS都是不同的,页面被禁用右键菜单,无法选中或复制。
(因为破解了一周,怕分享出来后汽车之家就改了所以到现在运行了半年后才分享出来= =)
网站地址:http://car.autohome.com.cn/config/spec/25898.html
基本就是这样,如果单纯的抓取页面元素后会是这样:
开始分析:
1.整个页面通过及配置数据都是直接Write出来的,因为配置项的详情在页面JS里同页面一起生成,并非通过接口。
2.配置项数据在页面上
(PS小妙招:将网页保存本地后发现文字依旧显示,然后大面积删除JS后刷新页面,如果文字依旧显示,继续删,直到找到加载数据的JS为止)
事后发现第一个变量keyLink是左边配置名称的超链接
第二个变量config是我们要的配置上半页(到车轮制动那),
第三个变量option是主/被动安全装备及以下,
第三四个变量color和innerColor是外观内饰颜色
其他的没什么用,第五个可能是什么运动套装之类的,豪车才有,没仔细看。
3.解密JS在这里
这个JS是被混淆过的,不可以根据变量名去获取。
4.破解流程,拿到这个配置JSON串,然后找到解析JS,计算JS的变量得到字典集(一大串文字)和下标集(一大串数字集合)根据下标取字典里对应的文字,得到真正的数据字典,然后替换指定的伪元素。
5.解析被混淆的JS,格式化后可以得到这样的一串JS
提供一个完整的JS,有兴趣的小伙伴可以去研究研究
6.全文所有JS代码因为被混淆,可能会有差异,但结构一样,可仔细寻找。
开始解析:
如上图所示,里面有好多函数和变量,里面会返回一段文字或者符号,这些零零散散的文字将被组成一个完成数据字典库,
大致分为这几种:
直接变量赋值的,如
var mH_ = '例'
通过函数为变量赋值的,值等于return后面的字符串,如
var lI_ = function() { 'return lI_'; return '3;7'; };
函数,调用的时候获得值,值等于return后面的字符串,如
function hw_() { function _h() { return 'hw_'; }; if (_h() == 'hw__') { return _h(); } else { return 're'; } }
(其实笔者想过使用.net直接运行JS,后来发现他们这个JS是有错误的,并且(……)(document)这种形式使用MSScriptControl.ScriptControl和JScript都无法识别,只能硬着头皮分析了。。。如果有能识别这种JS,求留言推荐,十分感谢。)
众所周知,函数是需要被调用才能运行的,那么入口呢,就很巧妙的隐藏在了这里 ↓
var HH_ = $FillDicData$('iU_');
接着会跳到这个函数
这个就是调用上面的那些大部分的变量组成字典集
紧接着下面的这个方法就是获取下标集合
这个方法实现根据坐标集取得字典 ,注意这个方法是不混淆的!可以直接搜索方法名找到。
"77,7"就是"环保" 通过这种方式替换页面的伪元素
分析到这里了,后面也就不难了,不再详细说明,如有不明白的,可以留言给我。
获取数据字典,模拟了刚才分析的JS
1 #region 获取汽车之家车辆信息 2 /// <summary> 3 /// 获取汽车之家车辆信息 4 /// </summary> 5 /// <param name="Parameter">参数(汽车之家ID或者Url)</param> 6 /// <param name="Url">是否为Url</param> 7 /// <param name="JsonKeyLink"></param> 8 /// <param name="JsonConfig"></param> 9 /// <param name="JsonOption"></param> 10 /// <param name="JsonColor"></param> 11 /// <param name="JsonInnerColor"></param> 12 /// <param name="JsonBag"></param> 13 /// <param name="ErrorMessage"></param> 14 /// <returns></returns> 15 public bool GetAutoHomeCarInfo(string Parameter, bool Url, ref string JsonKeyLink, ref string JsonConfig, ref string JsonOption, ref string JsonColor, ref string JsonInnerColor, ref string JsonBag, ref string ErrorMessage) 16 { 17 if (Url) return false; 18 #region 19 try 20 { 21 //这里的变量是车型ID 22 string strUrl = Url ? Parameter : "http://car.autohome.com.cn/config/spec/" + Parameter + ".html"; 23 HttpWebRequest webrequest = (HttpWebRequest)WebRequest.Create(strUrl); 24 webrequest.AllowAutoRedirect = true; 25 webrequest.Timeout = 30000; 26 CookieContainer c = new CookieContainer(); 27 webrequest.CookieContainer = c; 28 HttpWebResponse response = (HttpWebResponse)webrequest.GetResponse(); 29 StreamReader read = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding("utf-8")); 30 string strAllHTML = read.ReadToEnd(); 31 32 #region 获取数据字典 33 string[] KeyLink = null; 34 string[] Configpl = null; 35 string[] Optionpl = null; 36 GetAutoHomeDictionary(strAllHTML, ref KeyLink, ref Configpl, ref Optionpl); 37 #endregion 38 39 MatchCollection carInfoMatches = Regex.Matches(strAllHTML, "<script type=\"text/javascript\">((?:.|\\n)*?)</script>"); 40 string strCarInfo = string.Empty; 41 for (int i = 0; i < carInfoMatches.Count; i++) 42 { 43 if (carInfoMatches[i].Result("$1").Trim().IndexOf("var option =") > 0) strCarInfo = carInfoMatches[i].Result("$1").Trim(); 44 } 45 if (strCarInfo != string.Empty) 46 { 47 Hashtable htCarInfo = new Hashtable(); 48 if (strCarInfo.IndexOf("var keyLink =") > -1) htCarInfo.Add(strCarInfo.IndexOf("var keyLink ="), "JsonKeyLink"); 49 if (strCarInfo.IndexOf("var config =") > -1) htCarInfo.Add(strCarInfo.IndexOf("var config ="), "JsonConfig"); 50 if (strCarInfo.IndexOf("var option =") > -1) htCarInfo.Add(strCarInfo.IndexOf("var option ="), "JsonOption"); 51 if (strCarInfo.IndexOf("var color =") > -1) htCarInfo.Add(strCarInfo.IndexOf("var color ="), "JsonColor"); 52 if (strCarInfo.IndexOf("var innerColor =") > -1) htCarInfo.Add(strCarInfo.IndexOf("var innerColor ="), "JsonInnerColor"); 53 if (strCarInfo.IndexOf("var bag =") > -1) htCarInfo.Add(strCarInfo.IndexOf("var bag ="), "JsonBag"); 54 ArrayList arrayList = new ArrayList(htCarInfo.Keys); 55 arrayList.Sort(); 56 for (int i = 0; i < arrayList.Count; i++) 57 { 58 //有些没有的字典和解析JS要筛掉 59 string JsonTemp = string.Empty; 60 if (i == arrayList.Count - 1) 61 { 62 continue; 63 JsonTemp = strCarInfo.Substring(int.Parse(arrayList[i].ToString()), strCarInfo.Length - int.Parse(arrayList[i].ToString())); 64 JsonTemp = JsonTemp.Substring(0, JsonTemp.IndexOf("]}};")) + "]}};"; 65 } 66 else 67 { 68 JsonTemp = strCarInfo.Substring(int.Parse(arrayList[i].ToString()), int.Parse(arrayList[i + 1].ToString()) - int.Parse(arrayList[i].ToString())); 69 } 70 //if (JsonTemp.IndexOf("_baikeVJ") > 0) 71 if (Regex.IsMatch(JsonTemp, @"<span class='hs_kw.*?_baike\w{0,2}'></span>")) 72 { 73 string tmp = JsonTemp.Substring(JsonTemp.IndexOf("_baike") , 8); 74 for (int j = 0; j < KeyLink.Length; j++) 75 { 76 JsonTemp = JsonTemp.Replace("<span class='hs_kw" + j + tmp + "'></span>", KeyLink[j]); 77 } 78 } 79 if (Regex.IsMatch(JsonTemp, @"<span class='hs_kw.*?_config\w{0,2}'></span>")) 80 { 81 string tmp = JsonTemp.Substring(JsonTemp.IndexOf("_config"), 9); 82 for (int j = 0; j < Configpl.Length; j++) 83 { 84 JsonTemp = JsonTemp.Replace("<span class='hs_kw" + j + tmp + "'></span>", Configpl[j]); 85 } 86 } 87 if (Regex.IsMatch(JsonTemp, @"<span class='hs_kw.*?_option\w{0,2}'></span>")) 88 { 89 string tmp = JsonTemp.Substring(JsonTemp.IndexOf("_option"), 9); 90 for (int j = 0; j < Optionpl.Length; j++) 91 { 92 JsonTemp = JsonTemp.Replace("<span class='hs_kw" + j + tmp + "'></span>", Optionpl[j]); 93 } 94 } 95 switch (htCarInfo[arrayList[i]].ToString()) 96 { 97 //这里只解析了左边配置栏和上下配置,其他可自行修改 98 case "JsonKeyLink": 99 JsonTemp = JsonTemp.Replace("var keyLink =", string.Empty).Replace(";", string.Empty).Trim(); 100 JsonKeyLink = JsonTemp; 101 break; 102 case "JsonConfig": 103 JsonTemp = JsonTemp.Replace("var config =", string.Empty).Replace(";", string.Empty).Trim(); 104 JsonConfig = JsonTemp; 105 break; 106 case "JsonOption": 107 JsonTemp = JsonTemp.Replace("var option =", string.Empty).Replace(";", string.Empty).Trim(); 108 JsonOption = JsonTemp; 109 break; 110 } 111 } 112 } 113 return true; 114 } 115 catch (Exception Ex) 116 { 117 ErrorMessage = Ex.Message; 118 return false; 119 } 120 #endregion 121 } 122 #endregion
破解数据字典,其实就是模拟我们上面分析的JS解析过程,其中用到大量的正则分别处理不同格式的数据集
1 /// <summary> 2 /// 获取数据字典 3 /// </summary> 4 /// <param name="strAllHTML"></param> 5 /// <param name="keyLink"></param> 6 /// <param name="configpl"></param> 7 /// <param name="optionpl"></param> 8 public void GetAutoHomeDictionary(string strAllHTML, ref string[] keyLink, ref string[] configpl, ref string[] optionpl) 9 { 10 MatchCollection carInfoMatches = Regex.Matches(strAllHTML, "<script>((?:.|\\n)*?)</script>"); 11 List<string> matcheslist = new List<string>(); 12 foreach (var item in carInfoMatches) 13 { 14 if (item.ToString().IndexOf("try{document.") < 0 && item.ToString().Length > 500) 15 { 16 matcheslist.Add(item.ToString()); 17 } 18 } 19 for (int i = 0; i < matcheslist.Count; i++) 20 { 21 #region 生成文字集1 22 Dictionary<string, string> dc = new Dictionary<string, string>(); 23 MatchCollection matchlist = Regex.Matches(matcheslist[i].Replace("})(document);</script>", " function"), @"function\s(\S){0,2}_\(\)\s*\{.*?\}.*?(?=function)");//取出function 24 for ( int j = 0; j < matchlist.Count; j++) 25 { 26 string str1 = string.Empty, str2 = string.Empty; 27 getStr(matchlist[j].Value, ref str1, ref str2); 28 dc.Add(str1, str2); 29 } 30 try 31 { 32 MatchCollection matchlist2 = Regex.Matches(matcheslist[i], @"var\s?\S\S_=\s?'\S*'");//取出赋值变量 33 for (int j = 0; j < matchlist2.Count; j++) 34 { 35 string str1 = string.Empty, str2 = string.Empty; 36 getStr2(matchlist2[j].Value, ref str1, ref str2); 37 dc.Add(str1, str2); 38 } 39 40 MatchCollection matchlist3 = Regex.Matches(matcheslist[i], @"var\s?\S\S_=\s?function\s?\(\)\s?\{.*?return.*?return.*?\}");//取出赋值函数 41 for (int j = 0; j < matchlist3.Count; j++) 42 { 43 string str1 = string.Empty, str2 = string.Empty; 44 getStr3(matchlist3[j].Value, ref str1, ref str2); 45 dc.Add(str1, str2); 46 } 47 } 48 catch (Exception ex) 49 { 50 throw ex; 51 } 52 StringBuilder sb = new StringBuilder(); 53 string str = Regex.Match(matcheslist[i], @"function\s*\$FillDicData\$\s*\(\)\s*?{.*?\$RenderToHTML").Value; 54 string tmp2 = str.Substring(str.IndexOf("$GetWindow$()"), str.IndexOf("$rulePosList$") - str.IndexOf("$GetWindow$()")); 55 string tmp3 = tmp2.Substring(tmp2.IndexOf(']') + 1); 56 string[] tmp4 = tmp3.Split('+'); 57 try 58 { 59 for (int j = 1; j < tmp4.Length - 1; j++) 60 { 61 //if (Regex.IsMatch(tmp4[j], @"[\u4e00-\u9fbb]{1,5}")) 62 //{ 63 // sb.Append(Regex.Match(tmp4[j], @"[\u4e00-\u9fbb]{1,5}").ToString()); 64 //} 65 if (Regex.IsMatch(tmp4[j], @"\(function\s{0,3}\(\)\{.*?return.*?return.*?\}\)")) 66 { 67 var strtmp = Regex.Match(tmp4[j], @"\(function\s{0,3}\(\)\{.*?return.*?return.*?\}\)").Value; 68 var strtmp2 = Regex.Match(strtmp, "return.*?(.*?).*?return.*(.*?)").Value.Split(new string[] { "return" }, StringSplitOptions.RemoveEmptyEntries); 69 foreach (var item in strtmp2) 70 { 71 if (item.Split('\'').Length == 3) sb.Append(item.Split('\'')[1].Replace("'", "").Trim()); 72 } 73 } 74 else if (Regex.IsMatch(tmp4[j], @"\('([A-Z]|[a-z]|[0-9]|[,]|[']|[;]|[\u4e00-\u9fbb]){1,10}'\)")) 75 { 76 sb.Append(Regex.Match(tmp4[j], @"\('([A-Z]|[a-z]|[0-9]|[,]|[']|[;]|[\u4e00-\u9fbb]){1,10}(?='\))").ToString().Substring(2)); 77 } 78 else if (Regex.IsMatch(tmp4[j], @"\(\)")) 79 { 80 sb.Append(dc[tmp4[j].Replace("()", "")]); 81 } 82 else if (Regex.IsMatch(tmp4[j], @"'([A-Z]|[a-z]|[0-9]|[,]|[']|[;]|[\u4e00-\u9fbb]){1,10}'(?!\))")) 83 { 84 sb.Append(Regex.Match(tmp4[j], @"'([A-Z]|[a-z]|[0-9]|[,]|[']|[;]|[\u4e00-\u9fbb]){1,10}'").ToString().Replace("'","")); 85 } 86 else if (Regex.IsMatch(tmp4[j], @"\S{3}")) 87 { 88 sb.Append(dc[tmp4[j]]); 89 } 90 else 91 { 92 sb.Append("X"); 93 } 94 } 95 } 96 catch (Exception ex) 97 { 98 throw; 99 } 100 #endregion 101 102 #region 取下标 103 string tmp11 = str.Substring(str.IndexOf("$rulePosList$")); 104 string tmp12 = tmp11.Substring(0, tmp11.IndexOf("$SystemFunction2$")); 105 StringBuilder sb2 = new StringBuilder(); 106 string[] tmp13 = tmp12.Split('+'); 107 try 108 { 109 tmp13[tmp13.Length - 1] = tmp13[tmp13.Length - 1].Replace("),", ""); 110 for (int j = 1; j < tmp13.Length; j++) 111 { 112 if (Regex.IsMatch(tmp13[j], @"\('([A-Z]|[a-z]|[0-9]|[,]|[']|[;]|[\u4e00-\u9fbb]){1,10}'\)")) 113 { 114 sb2.Append(Regex.Match(tmp13[j], @"\('([A-Z]|[a-z]|[0-9]|[,]|[']|[;]|[\u4e00-\u9fbb]){1,10}(?='\))").ToString().Substring(2)); 115 } 116 else if (Regex.IsMatch(tmp13[j], @"return\s{0,2}'([0-9]|[,]|[;]){1,10}'