开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 17314|回复: 76
收起左侧

[易语言纯源码] 赛尔号发包例子以及逆向过程

[复制链接]
发表于 2020-5-27 11:13:44 | 显示全部楼层 |阅读模式   广东省中山市
分享源码
界面截图:
是否带模块: 纯源码
备注说明: -
以前分享过摩尔庄园、奥比岛等发包例子,后来官方更改了加密手段,这次分享个赛尔号的发送数据例子

切勿用于非法用途,本贴只用于技术交流与学习

赛尔号通信数据的逆向分析与还原(思路篇)

0x01 前言

flash游戏即将淘汰,被称为最强的as加密方式Alchemy被应用于一些主流的页游,很多游戏都转战h5或者手游端。今天闲来没事,就上手一款童年游戏。"赛尔号",应该是全网首发吧

0x02 准备工具

FFDEC(强大的免费开源swf反编译工具) 、 Chrome浏览器 、 在线Hex编辑

0x03分析过程

使用 浏览器加载游戏并 通过 开发者工具抓取到数据包
可以发现一个dll.xml的配置文件
Dll信息
此游戏的习惯将核心数据存放于==TaomeeLibraryDLL.swf==文件当中。
此时直接将swf文件拖入FFDec会发现,读取失败。
读取失败
需要拖入Hex编辑器搞一下
CWS
==CWS== 是标识码 具体可以参考 Swf文件头信息,前面则是一些无关紧要的数据。
可以看到在Client.swf里对于swf文件的加载是这样处理的

private function onComplete(e:Event) : void
        {
            var info:DLLInfo = this._dllList[0];
            var byteArray:ByteArray = new ByteArray();
            if(this._isRelease)
            {
                this._stream.readBytes(new ByteArray(),0,7);//相当于设置了位置 前面可以看出到,标识码前面有7个字节是无用的数据 >>  this._stream.potion=7
            }
            this._stream.readBytes(byteArray);//
            if(this._isRelease)
            {
                byteArray.uncompress();//再进行Zlib解压处理
            }
            this._stream.close();//关闭字节流
            //然后就加载
            this._loader.loadBytes(byteArray,new LoaderContext(false,ApplicationDomain.currentDomain));
        }

思路清晰,可以干代码,这里使用了易语言
啦啦啦

导出文件
是

可以,再将其导入FFdec看看效果。
完美加载
解压后,开始找与通讯的类。一般搜索flash.net.Socke即可
==send(param1, param2) #4CAF50==发送数据包的函数

public function send(param1:uint, param2:Array) : uint
        {
            var loc3_:* = null;
            var loc4_:* = null;
            if(this.connected)
            {
                loc3_ = this.pack(this.userID,param1,param2);
                loc3_.position = 0;
                loc4_ = MessageEncrypt.encrypt(loc3_);//这里就开始加密了
                writeBytes(loc4_);//整个数据丢给
                flush();//这里就是发送数据出去了
                this.sendDataError(param1);
                DebugTrace.show(">>Socket[" + this.ip + ":" + this.port.toString() + "][cmdID:" + param1 + "]",getCmdLabel(param1),"[data length:" + this._sendBodyLen + "]");
                //这里可以清晰的看出 param1是数据包的命令码,param2为数据的整体
                return this._result;
            }
            return 0;
        }
    public function pack(param1:uint, param2:uint, param3:Array) : ByteArray
        {
            var loc4_:ByteArray = new ByteArray();
            this.serializeBinary(loc4_,param3);//这里是将数组序列化
            this._sendBodyLen = loc4_.length;
            var loc5_:ByteArray = this.packHead(param1,param2,loc4_);
            var loc6_:ByteArray = new ByteArray();
            loc6_.writeBytes(loc5_);
            loc6_.writeBytes(loc4_);
            return loc6_;
        }   
private function packHead(param1:uint, param2:uint, param3:ByteArray) : ByteArray
        {
            var loc6_:* = 0;
            var loc7_:int = 0;
            var loc4_:ByteArray = new ByteArray();
            var loc5_:uint = param3.length + HEAD_LENGTH;
            loc4_.writeUnsignedInt(loc5_);//写入数据包的长度
            loc4_.writeUTFBytes(VERSION);//写入通信的协议号
            loc4_.writeUnsignedInt(param2);//写入CmdId
            loc4_.writeUnsignedInt(param1);//写入UserID
            if(param2 > 1000)
            {
                loc7_ = 0;
                while(loc7_ < param3.length)
                {
                    loc6_ = uint(loc6_ ^ param3[loc7_] & 255);
                    loc7_++;
                }
                this._result = MSerial(this._result,param3.length + HEAD_LENGTH,loc6_,param2);//计算序列号
                loc4_.writeInt(this._result);//写入序列号
            }
            else
            {
                loc4_.writeInt(0);
            }
            return loc4_;
        }
  • 这里有1个知识点,记一下
    public class SocketEncryptImpl extends Socket 这个类是继承了 Socket,所以在发送数据包的时候writeBytes(loc4_);和flush(); 其实是省略了 Socket.writeBytes和Socket.flush()

    private static var NO_ENCRYPT_LEN:int = 4;
        public function MessageEncrypt()
        {
            super();
        }
    
        public static function encrypt(param1:ByteArray) : ByteArray
        {
            var loc2_:int = param1.readUnsignedInt() - NO_ENCRYPT_LEN;
            var loc3_:ByteArray = new ByteArray();
            loc3_.writeUnsignedInt(0);
            MEncrypt(param1,loc2_,loc3_);
            loc3_.position = 0;
            loc3_.writeUnsignedInt(loc3_.length);
            trace("outData.length:" + loc3_.length);
            loc3_.position = 0;
            return loc3_;
        }
    
        public static function decrypt(param1:ByteArray) : ByteArray
        {
            var loc2_:int = param1.readUnsignedInt() - NO_ENCRYPT_LEN;
            var loc3_:ByteArray = new ByteArray();
            loc3_.writeUnsignedInt(0);
            MDecrypt(param1,loc2_,loc3_);
            loc3_.position = 0;
            loc3_.writeUnsignedInt(loc3_.length);
            loc3_.position = 0;
            return loc3_;
        }

    根据分析可得,MEncrypt(param1,loc2,loc3);
    传入的参数是 原始数据param1 、加密的长度、加密后的数据loc3_
    继续找 MEncrypt函数内容

public function MEncrypt(_arg_1:ByteArray, _arg_2:int, _arg_3:ByteArray):void
    {
        var _local_16:*;
        var _local_15:int;
        var _local_7:int;
        var _local_10:int;
        var _local_11:int;
        var _local_13:int;
        var _local_14:int;
        var _local_6:int;
        var _local_9:int;
        var _local_4:int = ESP;
        _local_15 = _local_4;
        _local_4 = (_local_4 - 16);
        ESP = (_local_4 & 0xFFFFFFF0);
//获取尺寸,可忽略
var _local_5:int = getDefinitionByName("org.taomee.net.SocketEncryptImpl").size;

        if (_local_5 == 51706)
        {
            ESP = (_local_4 & 0xFFFFFFF0);
            _local_6 = _arg_2;
            _local_4 = (_local_4 - 16);
            si32(_local_6, _local_4);
            ESP = _local_4;
            F_malloc();
            _local_4 = (_local_4 + 16);
            _local_7 = eax;
            ESP = (_local_4 & 0xFFFFFFF0);
            CModule.writeBytes(_local_7, _local_6, _arg_1);
            _local_9 = 0;
            si32(_local_9, (_local_15 - 4));
            ESP = (_local_4 & 0xFFFFFFF0);
            ESP = (_local_4 & 0xFFFFFFF0);
            //_local_10此时是取解密的密钥Key
            _local_10 = CModule.mallocString(getDefinitionByName("com.robot.core.net.SocketConnection").key);
            _local_11 = (_local_10 & 0xFFFFFFFC);
            var _local_12:* = li32(_local_11);
            var _temp_1:* = (_local_12 + -16843009);
            _local_12 = (_local_12 & 0x80808080);
            _local_12 = (_local_12 ^ 0x80808080);
            _local_5 = (_local_12 & _temp_1);

            outer_block:
            {

                if (_local_5 != 0)
                {
                    _local_13 = (_local_11 + 4);
                    while ((_local_14 = (_local_10 + _local_9)) < _local_13)
                    {
                        _local_5 = li8(_local_14);
                        if (_local_5 == 0) break outer_block;
                        _local_9 = (_local_9 + 1);
                    };
                };
                _local_13 = (_local_11 + 4);
                do 
                {
                    _local_14 = li32(_local_13);
                    _local_5 = (_local_14 + -16843009);
                    _local_12 = (_local_14 & 0x80808080);
                    _local_12 = (_local_12 ^ 0x80808080);
                    _local_5 = (_local_12 & _local_5);
                    if (_local_5 != 0)
                    {
                        _local_5 = (_local_14 & 0xFF);
                        if (_local_5 == 0)
                        {
                            _local_9 = (_local_13 - _local_10);
                            break;
                        };
                        _local_5 = li8(_local_13 + 1);
                        if (_local_5 == 0)
                        {
                            _local_5 = (1 - _local_10);
                            _local_9 = (_local_5 + _local_13);
                            break;
                        };
                        _local_5 = li8(_local_13 + 2);
                        if (_local_5 == 0)
                        {
                            _local_5 = (2 - _local_10);
                            _local_9 = (_local_5 + _local_13);
                            break;
                        };
                        _local_5 = li8(_local_13 + 3);
                        if (_local_5 == 0)
                        {
                            _local_5 = (3 - _local_10);
                            _local_9 = (_local_5 + _local_13);
                            break;
                        };
                    };
                    _local_13 = (_local_13 + 4);
                } while (true);

            }//outer_block
            _local_4 = (_local_4 - 32);
            _local_5 = (_local_15 - 4);
            si32(_local_5, (_local_4 + 16));
            si32(_local_9, (_local_4 + 12));
            si32(_local_10, (_local_4 + 8));
            si32(_local_6, (_local_4 + 4));
            si32(_local_7, _local_4);
            ESP = _local_4;

            F__Z15MEncrypt_x86_32PKhiS0_iPi();
            _local_4 = (_local_4 + 32);
            _local_6 = eax;
            if (_local_7 != 0)
            {
                _local_4 = (_local_4 - 16);
                si32(_local_7, _local_4);
                ESP = _local_4;
                F_idalloc();
                _local_4 = (_local_4 + 16);
            };
            _local_5 = li32(_local_15 - 4);
            ESP = (_local_4 & 0xFFFFFFF0);
            CModule.readBytes(_local_6, _local_5, _arg_3);
            if (_local_6 != 0)
            {
                _local_4 = (_local_4 - 16);
                si32(_local_6, _local_4);
                ESP = _local_4;
                F_idalloc();
                _local_4 = (_local_4 + 16);
            };
            if (_local_10 != 0)
            {
                _local_4 = (_local_4 - 16);
                si32(_local_10, _local_4);
                ESP = _local_4;
                F_idalloc();
                _local_4 = (_local_4 + 16);
            };
        };
        _local_4 = _local_15;
        ESP = _local_4;
        return (_local_16);
    }

     public function F__Z15MEncrypt_x86_32PKhiS0_iPi():void
    {
        var _local_17:int;
        var _local_2:int;
        var _local_9:int;
        var _local_7:int;
        var _local_5:int;
        var _local_3:int;
        var _local_12:int;
        var _local_6:int;
        var _local_14:int;
        var _local_11:int;
        var _local_15:int;
        var _local_16:int;
        var _local_4:int;
        var _local_10:int;
        var _local_1:int = ESP;
        _local_17 = _local_1;
        _local_2 = li32(_local_17 + 4);
        _local_3 = (_local_2 + 1);
        _local_5 = li32(_local_17 + 16);
        si32(_local_3, _local_5);
        _local_1 = (_local_1 - 16);
        si32(_local_3, _local_1);
        ESP = _local_1;
        F_malloc();
        _local_7 = li32(_local_17 + 12);
        _local_9 = li32(_local_17 + 8);
        _local_11 = li32(_local_17);
        _local_1 = (_local_1 + 16);
        _local_12 = eax;
        _local_14 = _local_12;
        _local_15 = _local_2;
        _local_16 = 0;
        if (_local_2 >= 1)
        {
            do 
            {
                _local_6 = li8(_local_11);
                _local_4 = 0;
                _local_10 = _local_9;
                if (_local_16 != _local_7)
                {
                    _local_10 = (_local_9 + _local_16);
                    _local_4 = (_local_16 + 1);
                };
                var _local_8:* = li8(_local_10);
                _local_8 = (_local_8 ^ _local_6);
                si8(_local_8, _local_14);
                _local_14 = (_local_14 + 1);
                _local_11 = (_local_11 + 1);
                _local_15 = (_local_15 + -1);
                _local_16 = _local_4;
            } while (_local_15 != 0);
        };
        _local_14 = (_local_12 + _local_2);
        si8(0, _local_14);
        _local_8 = (_local_2 + -1);
        if (_local_8 >= 0)
        {
            _local_11 = (0 - _local_2);
            if (_local_11 <= -1)
            {
                _local_11 = -1;
            };
            _local_8 = (_local_2 + _local_11);
            _local_11 = (_local_8 + 1);
            do 
            {
                _local_8 = li8(_local_14);
                var _local_13:* = li8(_local_14 - 1);
                _local_13 = (_local_13 >>> 3);
                _local_8 = (_local_13 | _local_8);
                si8(_local_8, _local_14);
                _local_8 = li8(_local_14 - 1);
                _local_8 = (_local_8 << 5);
                si8(_local_8, (_local_14 - 1));
                _local_14 = (_local_14 + -1);
                _local_11 = (_local_11 + -1);
            } while (_local_11 != 0);
        };
        _local_8 = li8(_local_12);
        _local_8 = (_local_8 | 0x03);
        si8(_local_8, _local_12);
        _local_8 = (_local_2 % _local_7);
        _local_8 = (_local_9 + _local_8);
        _local_8 = li8(_local_8);
        _local_8 = (_local_8 * 13);
        _local_9 = (_local_8 % _local_3);
        if (_local_9 != 0)
        {
            _local_1 = (_local_1 - 16);
            si32(__ZZ15MEncrypt_x86_32PKhiS0_iPiE10temp_bytes, _local_1);
            _local_8 = (_local_12 + _local_9);
            si32(_local_8, (_local_1 + 4));
            _local_8 = (_local_3 - _local_9);
            si32(_local_8, (_local_1 + 8));
            ESP = _local_1;
            Fmemcpy();
            _local_1 = (_local_1 + 16);
            _local_1 = (_local_1 - 16);
            si32(_local_9, (_local_1 + 8));
            si32(_local_12, (_local_1 + 4));
            _local_8 = (__ZZ15MEncrypt_x86_32PKhiS0_iPiE10temp_bytes + _local_8);
            si32(_local_8, _local_1);
            ESP = _local_1;
            Fmemcpy();
            _local_1 = (_local_1 + 16);
            var _temp_1:* = li32(_local_5);
            _local_1 = (_local_1 - 16);
            si32(_temp_1, (_local_1 + 8));
            si32(__ZZ15MEncrypt_x86_32PKhiS0_iPiE10temp_bytes, (_local_1 + 4));
            si32(_local_12, _local_1);
            ESP = _local_1;
            Fmemcpy();
            _local_1 = (_local_1 + 16);
        };
        eax = _local_12;
        _local_1 = _local_17;
        ESP = _local_1;
    }

我们来看这个Key是如何获取的 ==com.robot.core.net.SocketConnectio==  找到此类
在 ==RobotCoreDLL.swf==此dll上面可以找出

public static function setEncryptKeyStringArr(param1:Array) : void
        {
            _encryptKeyStringArr = param1;
        }
public static function get key() : String
        {
            var loc2_:int = 0;
            var loc3_:* = null;
            var loc1_:String = "";
            if(_encryptKeyStringArr == null)
            {
                loc1_ = "!crAckmE4nOthIng:-)";
            }
            else
            {
                loc2_ = 0;
                while(loc2_ < _encryptKeyStringArr.length)
                {
                    loc3_ = StringUtil.replace(_encryptKeyStringArr[loc2_],"*","");
                    loc1_ = loc1_ + loc3_;
                    loc2_++;
                }
            }
            return loc1_;
        }

这位程序员也是挺可爱的,默认的密钥为 ==crack me Nothing :-)==
然后找出设置 ==setEncryptKeyStringArr== 的地方,一般设置密钥如果不是固定的,则可能是服务器返回的数据。如登录的时候,返回密钥。所以我们可以找登录的函数

private static function onLogin(param1:SocketEvent) : void
        {
            if(MainManager.isNewUser)
            {
                StatManager.sendStat2014("_newtrans_","fOnlineSucc","");
            }
            SocketConnection.removeCmdListener(CommandID.LOGIN_IN,onLogin);
            EventManager.addEventListener(RobotEvent.CREATED_ACTOR,onCreatedActor);
            MainManager.setup(param1.data);
            var loc2_:ByteArray = param1.data as ByteArray;
            var loc3_:int = loc2_.readUnsignedInt();
            initKey(loc3_);
            MapConfig.setup();
            MapSeatPointConfig.setup();
            sendSystemInfo();
            SocketConnection.send(1022,86066824);
        }

private static function initKey(param1:int) : void
        {
            var loc2_:String = "c&o&m.--rob-ot.c--o-r-e.&n-et.S-oc-ke-t&C-on-n-e-c-t-i-on";
            var loc3_:* = "s*e*tE&&&n*c";
            loc3_ = loc3_ + "r*yp*t&&&&Ke*yS*tr*i&n&&g*Arr";
            loc2_ = StringUtil.replace(loc2_,"-","");
            loc2_ = StringUtil.replace(loc2_,"&","");
            loc3_ = StringUtil.replace(loc3_,"*","");
            loc3_ = StringUtil.replace(loc3_,"&","");
            param1 = param1 ^ MainManager.actorInfo.userID;
            var loc4_:String = MD5.hash(param1 + "");
            var loc5_:* = MainManager.actorInfo.userID + "";
            var loc6_:* = [];
            var loc7_:int = 0;
            while(loc7_ < 10)
            {
                loc6_[loc7_] = "*" + loc4_.charAt(loc7_) + "*";
                loc7_++;
            }
            getDefinitionByName(loc2_)[loc3_](loc6_);
        }

在 ==RobotAppDLL.swf==此dll中,可以找到登陆包返回处理的函数。里面包含初始化密钥的处理方式,而且是只取前10位
当然直接搜索 ==setEncryptKeyStringArr== 是无效的,因为是==getDefinitionByName==
综合以上就基本上完成数据的解密工作了。

我使用的是易语言来进行操作
seer

MDe

Key

_De

Key获取
Key处理Key获取

效果图

0x04 写在最后

tm的加解密方式是通用的,只是密钥不同。
如赛尔号的是从返回包截取的
摩尔庄园 “^FStx,wl6NquAVRF@f%6”
赛尔号2  “taomee_seer2k~#”
小花仙 “v#93ta0mee+fl0WErd0t2eatMee&#$+@n0”

从09年到现在,tm的游戏从序列号自加1到封包的加密,也是历经了一段历史了吧,如今各种技术层出不穷。写这篇文章也纯属爱好和对技术的热爱。以前期盼着每周五的更新,到现在也是对过去的一种致敬吧。



赛尔号发包例子.zip (1.43 MB, 下载次数: 286)

评分

参与人数 10好评 +9 精币 +222 收起 理由
易语言资源网 + 1 + 5 支持开源~!感谢分享
Ossian + 1 + 3 牛逼
mgfz + 1 + 2 新技能已get√
熬夜 + 1 奉上小小红包希望笑纳
精易客服 + 1 + 5 精彩文章希望继续努力
LvZHuang + 1 + 2 新技能已get√
小白子 + 1 支持开源~!感谢分享
冰点 + 1 + 200 感谢发布原创作品,精易因你更精彩!
道易有道 + 1 + 2 hcj牛逼
方片A + 1 + 2 新技能已get√

查看全部评分


发表于 2024-8-8 19:14:37 | 显示全部楼层   福建省龙岩市
666666666666666666666666666666666666666666
回复 支持 反对

使用道具 举报

结帖率:0% (0/1)
发表于 2024-6-20 23:26:24 | 显示全部楼层   浙江省杭州市
正好学习学习
回复 支持 反对

使用道具 举报

结帖率:0% (0/1)
发表于 2024-6-20 23:26:07 | 显示全部楼层   浙江省杭州市
感谢大佬的开源分享
回复 支持 反对

使用道具 举报

发表于 2024-6-19 21:23:09 | 显示全部楼层   江西省赣州市
可以讲讲造梦西游4(有空的话)
回复 支持 反对

使用道具 举报

发表于 2024-6-19 21:22:45 | 显示全部楼层   江西省赣州市
感谢分享支持开源
回复 支持 反对

使用道具 举报

签到天数: 4 天

发表于 2024-5-11 11:16:03 | 显示全部楼层   安徽省合肥市
好用我赞你
回复 支持 反对

使用道具 举报

发表于 2023-12-26 01:00:53 高大上手机用户 | 显示全部楼层   河南省郑州市
6666666666
回复 支持 反对

使用道具 举报

签到天数: 20 天

发表于 2023-12-3 15:51:08 高大上手机用户 | 显示全部楼层   河北省*
感谢分享
回复 支持 反对

使用道具 举报

发表于 2023-7-17 13:58:47 | 显示全部楼层   广东省珠海市
很好用我赞你
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 致发广告者

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报电话0663-3422125,QQ: 793400750,邮箱:wp@125.la
网站简介:精易论坛成立于2009年,是一个程序设计学习交流技术论坛,隶属于揭阳市揭东区精易科技有限公司所有。
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备12094385号-1) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

快速回复 返回顶部 返回列表