C++,python,热爱算法和机器学习
全部博文(1214)
分类: 网络与安全
2016-10-08 18:34:41
注意:由于这整个系列教程的目的并不是让大家去破解验证码做自动化,选择验证码破解这个话题只是因为它比较有趣,能让学习不那么枯燥,因此我不会将这个项目的源码放上GitHub。如果你想从破解中学到新技术,请按顺序完成整个教程的内容。
本来我是打算在这篇文章内完成请求模拟、图片下载和图片还原的。不过想了想,都写的话篇幅有点太长了,于是干脆分两篇发吧。这一篇里我们就只完成三个包的模拟和图片下载好了。如果你对三个包的内容、.NET下运行JavaScript的方法和C#下的网络请求还不熟悉的话,请先阅读这些文章:
如果你已经准备就绪,那就让我们开始吧。首先打开Visual Studio 2015,创建一个新的Windows Forms Application:
接着打开Package Manager Console:
打开后输入并回车:
Install-Package HtmlAgilityPack
接着在Form1上拉出一个Button和两个PictureBox:
双击button1,给button1_Click()函数加上async关键字,我们终于要开始写代码了!
首先是先创建一个NetworkHandler的实例:
NetworkHandler netHandler = new NetworkHandler();
首先模拟1号包,该包URL为:
参数是ts,内容是随机数。所以我们需要向这个地址发一个请求,这个过程使用NetworkHandler十分简单。至于随机数,我们用Random类来模拟即可:
string getResult = await netHandler.GETRequestAsync( "" + (new Random()).NextDouble().ToString()); MessageBox.Show(getResult);
NetworkHandler的使用使得Http包的模拟变得非常简单。我们看看运行的结果:
很好!我们已经成功模拟了一号包。返回的结果是JSON格式,我选择使用来处理。再次打开Package Manager Console,输入:
Install-Package Newtonsoft.Json
完成后,在Form1.cs顶部加入引用:
using Newtonsoft.Json;
是一个功能十分强大的工具,然而这里我们将只用到它的一个函数:
public static T DeserializeObject<T>(string value);
这个函数将JSON字符串还原为Object。因此我们首先要创建一个Class来承载返回的内容。在项目里新建一个类Config.cs:
public class Config { public int success { get; set; } public string gt { get; set; } public string challenge { get; set; } }
这个类就是用来储存返回值的。在完成第一个包的请求之后,将其结果转为一个Config实例即可:
Config config = JsonConvert.DeserializeObject<Config>(getResult);
接下来模拟2号包,其URL为:
两个参数分别是callback和gt。gt我们已经有了,而callback我们则通过在.NET中运行JavaScript的方法实现。之前我们已经发现callback参数的后缀是通过这个函数计算的:
var random = function () { return parseInt(Math.random() * 10000) + (new Date()).valueOf(); };
我们把它写成正常的格式,并加上类名:
class JS{ function random() { return parseInt(Math.random() * 10000) + (new Date()).valueOf(); } }
这里首先给项目添加Microsoft.JScript的Reference,并且在项目中加入一个新的类Scripts.cs。在这个类的Constructor里我们完成JavaScript脚本的编译,其后创建一个函数random供我们的C#代码使用:
public class Scripts { dynamic jsObject; public Scripts() { JScriptCodeProvider jsProvider = new JScriptCodeProvider(); CompilerParameters coP = new CompilerParameters() { GenerateExecutable = false, GenerateInMemory = true }; CompilerResults result = jsProvider.CompileAssemblyFromSource(coP, new string[] { JSCODE }); if (result.Errors.Count > 0) throw new Exception("Code errors"); System.Reflection.Assembly js = result.CompiledAssembly; jsObject = js.CreateInstance("JS"); } public string random() { return System.Convert.ToString(jsObject.random()); } const string JSCODE = @"class JS{ function random() { return parseInt(Math.random() * 10000) + (new Date()).valueOf(); } }"; }
接着回到我们的Form1.cs。模拟2号包也相当简单:
Scripts scripts = new Scripts(); await netHandler.GETRequestAsync( "" + scripts.random() + ">=" + config.gt);
正如我们上次看到的那样,2号包的内容对我们来说没有意义,因此我们也不会储存它的返回内容。(说不定这个包可以忽略,但是我没试过)
接下来是3号包,其URL为:
参数虽然有五个:callback、challenge、gt、offline和product,但是callback我们已经知道如何模拟了,而challenge和gt在1号包的返回值里,offline和product则固定不变,所以其实这个包也非常好模拟:
getResult = await netHandler.GETRequestAsync( "" + scripts.random() + "&challenge=" + config.challenge + ">=" + config.gt + "&offline=false&product=float");
用MessageBox.Show()把结果显示出来如下:
可以看到返回值并不是直接的JSON,而是把这段JSON当作一个参数传给了一个函数。而我们只对这段JSON,所以首先对这段字符串做一个处理,去掉头尾,然后才用JsonConvert。
getResult = getResult.Remove(0, getResult.IndexOf("(") + 1); getResult = getResult.Remove(getResult.Length - 1);
处理完毕后还有另一个问题,就是我们需要一个新的类来承载这个JSON对象。事实上我们不需要新建一个类,而只是把现有的Config类扩充就好了。我们只对JSON内的slice、fullbg、bg、https、staticservers和height感兴趣,修改后的Config类如下:
public class Config { public int success { get; set; } public string gt { get; set; } public string challenge { get; set; } public bool https { get; set; } public string[] staticservers { get; set; } public string bg { get; set; } public string fullbg { get; set; } public string slice { get; set; } public int height { get; set; } }
好了,接下来便可以把结果放进Config中了:
config = JsonConvert.DeserializeObject<Config>(getResult);
至此我们已经完成了三个包的模拟并成功地储存了返回值中的有效信息。接下来我们就要下载图片了。
首先要根据3号包的返回内容,构造出完整的图片URL地址:
string bgUrl = (config.https ? "https://" : "http://")
+ config.staticservers[0] + config.bg;
string fullbgUrl = (config.https ? "https://" : "http://")
+ config.staticservers[0] + config.fullbg;
接着我们要用NetworkHandler中的GETRequestAsImageAsync()函数下载图片。然而如果我们只给一个参数,服务器会返回错误。经过调试,发现服务器会验证Referer这个Header。Referer必须是""才能通过验证。而GETRequestAsImageAsync函数里可以通过参数直接设置该Header。因此写下这两行代码便能完成图片的下载:
Image bg = await netHandler.GETRequestAsImageAsync( bgUrl, false, null, false, ""); Image fullbg =await netHandler.GETRequestAsImageAsync( fullbgUrl, false, null, false, "");
最后一个参数便是设置了Referer的值。最后我们把两个pictureBox的Image属性设置为下载的图片便完成了:
pictureBox1.Image = bg; pictureBox2.Image = fullbg;
运行,效果如图:
效果很好,我们成功下载了两张图片。整篇文章写下来,我自己都觉得太过冗长了,有些地方好像过于详细了。无论如何,喜欢我的文章的话记得给个赞!下一篇文章我们会完成图片的还原,以及计算应该移动的距离。敬请期待!