开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

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

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


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

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

查看: 9777|回复: 22
收起左侧

[技术专题] 辰龙捕鱼3Dapp 分析登陆+TCP

[复制链接]

结帖率:71% (32/45)
发表于 2019-4-25 12:38:39 | 显示全部楼层 |阅读模式   广东省广州市
本帖最后由 宇智波·佐助 于 2019-4-26 09:41 编辑
调试环境:夜神模拟器、JADX、.NET Reflector、Fiddler、smsniff
apk文件:

1.分析任何app首要第一步:抓包

使用Fiddler抓包你会发现你抓不到任何有关登陆的包、这个时候可以进入logcat看看
发现log里面有很多关键信息:


2.拿到Url之后,开始分析java层,用到jadx,搜索一下关键词比如某个参数:user_name
你会发现java层根本没有这个关键词,再搜索一下log里面的tag(LOGINWORKER)也没有
CYYIZT_I3UV9ELIBGN.png

3.这个时候可以考虑一下so层或者是否为WebView,你会发现,这TM也没有啊!但是别放弃,进去apk里面assets文件夹看看(用winrar软件打开apk文件可以找到),你会发现,app里面为什么会有dll?dll不是在windows上面常用的动态链接库嘛?在Linux系统里着实稀奇,于是便去研究一下是个什么东西,首先由几个dll可以判断出这是一个C#生成的dll比如mscorlib.dll

EGG6H7BQQXQ8VM0WV.png
于是可以尝试一下去反编译这些dll,用到.Net Reflector


4.用.Net Reflector载入dll以后搜索关键词 LOGIN 你会找到在Logcat里看到的tag
TIM图片20190425113250.png

这个时候已经知道核心功能都在一个dll上:Assembly-CSharp.dll 所以我们主要研究这个dll
找到几个关键的类:
TIM图片20190425113853.png
都是Game开头的,其中,登陆Url的参数可以在GameCenterHttpHelper这个类中找到,那么登陆的分析就完成了
TIM图片20190425114018.png
登陆的URL为:https://3dlogin1.jhpangu.com:8388/login?user_name={user_name}&password={password}&mac={mac}&plat_type=1&nickname=&mid=B007&sid=01&deviceType=11&version=138&loginType=0&cid=b6db31ae7f46c8f26261bdd70612c71f&simpleType=004-23


你以为这就完成了吗?
不,这只是登陆的第一步,获取token和tcp服务器的ip和端口


下一步还要用smsniff抓包,根据刚才logcat的信息,比如: Socket Connect Try 找到是在GameCenterLoginWroker.OnInit里面输出的,看代码可以知道Socket的操作在GameCenter.Post里面
TIM图片20190425115242.png
进入GameCenter.Post里面分析:
  1. public void StartNet(ServerInfo server)
  2. {
  3.     string serverIP = !server.usingIp ? Dns.GetHostEntry(server.serverHost).AddressList[0].ToString() : server.ipAdress;
  4.     Debug.Log("[POSTOFFICE] Server IP : " + serverIP);
  5.     GameCenter.NowGameServerSocketHost = serverIP;
  6.     this.myMessageControl = new TRMessageControl();
  7.     this.mySocket = new TRSocket(this.myMessageControl, serverIP, server.port);
  8.     this.socketStart = true;
  9. }
复制代码

Socket被封装成了一个叫TRSOCKET的类里,进入分析初始化函数有如下方法和成员,我们研究一下发送和接收:receiveControl和sendControl函数
  1. public TRSocket(TRMessageControl control, string serverIP, int serverPort)
  2. {
  3.     this.timeOutSign = new ManualResetEvent(false);
  4.     this.MyServerIP = serverIP;
  5.     this.MyServerPort = serverPort;
  6.     this.MyMessageControl = control;
  7.     this.whenGotoBackStage = DateTime.Now;
  8.     this.CreateReceiveThread();
  9.     this.isClosedOnTimeOut = false;
  10. }
复制代码


接收函数:
  1. private void receiveControl()
  2. {
  3.     int count = 0;
  4.     MemoryStream stream = new MemoryStream();
  5.     int @int = 0;
  6.     while (true)
  7.     {
  8.         byte[] buffer = new byte[0x400];
  9.         try
  10.         {
  11.             count = this.MySocket.Receive(buffer);
  12.         }
  13.         catch (Exception exception)
  14.         {
  15.             Debug.Log("[TRSOCKET] on ERROR when RECEIVE_CONTROL : " + exception.ToString());
  16.             if (this.MySocket != null)
  17.             {
  18.                 this.MySocket.Close();
  19.             }
  20.             this.MySocket = null;
  21.             this.isClosedOnTimeOut = true;
  22.             return;
  23.         }
  24.         if (count <= 0)
  25.         {
  26.             return;
  27.         }
  28.         stream.Position = stream.Length;
  29.         stream.Write(buffer, 0, count);
  30.         while (stream.Length > 4L)
  31.         {
  32.             stream.Position = 0L;
  33.             byte[] buffer2 = new byte[4];
  34.             stream.Read(buffer2, 0, 4);
  35.             @int = Base.GetInt(buffer2);
  36.             if (stream.Length < @int)
  37.             {
  38.                 break;
  39.             }
  40.             byte[] buffer3 = new byte[@int];
  41.             stream.Position = 0L;
  42.             stream.Read(buffer3, 0, @int);
  43.             int sourceIndex = 5;
  44.             byte[] destinationArray = new byte[@int - 5];
  45.             Array.Copy(buffer3, sourceIndex, destinationArray, 0, @int - 5);
  46.             int num4 = ((int) stream.Length) - @int;
  47.             byte[] buffer5 = new byte[num4];
  48.             stream.Position = @int;
  49.             stream.Read(buffer5, 0, num4);
  50.             new MemoryStream { Position = 0L }.Write(buffer5, 0, num4);
  51.             if (this.buildHashTable(destinationArray, out Hashtable hashtable))
  52.             {
  53.                 try
  54.                 {
  55.                     this.MyMessageControl.WriteMessage(hashtable);
  56.                 }
  57.                 catch (Exception exception2)
  58.                 {
  59.                     Debug.Log("[TRSOCKET] on ERROR when WRITE_MESSAGE_CONTROL : " + exception2.ToString());
  60.                 }
  61.             }
  62.         }
  63.     }
  64. }
复制代码

可以看出这是一个byteBuffer的操作,也就是操作字节的位置读写 先不具体分析他的逻辑,我们可以从发送函数去了解具体位置的字节是什么意思


发送函数:
  1. private bool sendControl(Hashtable msg)
  2. {
  3.     if (this.MySocket == null)
  4.     {
  5.         Debug.Log("[TRSOCKET] on ERROR when SEND_CONTROL : MySocket Null");
  6.         return false;
  7.     }
  8.     try
  9.     {
  10.         byte[] buffer = this.buildByteArray(msg);
  11.         return (this.MySocket.Send(buffer, buffer.Length, SocketFlags.None) == buffer.Length);
  12.     }
  13.     catch (Exception exception)
  14.     {
  15.         Debug.Log("[TRSOCKET] on ERROR when SEND_CONTROL : " + exception.ToString());
  16.         return false;
  17.     }
  18. }
复制代码

发现他是由一个键值表通过buildByteArray函数解析成一个字节集,进入这个函数分析:
  1. private byte[] buildByteArray(Hashtable msg)
  2. {
  3.     try
  4.     {
  5.         byte[] array = new byte[0x2000];
  6.         int index = 0;
  7.         IEnumerator enumerator = msg.Keys.GetEnumerator();
  8.         try
  9.         {
  10.             while (enumerator.MoveNext())
  11.             {
  12.                 string current = (string) enumerator.Current;
  13.                 byte[] bytes = Encoding.UTF8.GetBytes(current.ToCharArray());
  14.                 int length = bytes.Length;
  15.                 byte[] buffer5 = Base.ToByte(length);
  16.                 byte[] buffer6 = Encoding.UTF8.GetBytes((msg[current] as string).ToCharArray());
  17.                 int num3 = buffer6.Length;
  18.                 byte[] buffer7 = Base.ToByte(num3);
  19.                 buffer5.CopyTo(array, index);
  20.                 index += 4;
  21.                 bytes.CopyTo(array, index);
  22.                 index += length;
  23.                 buffer7.CopyTo(array, index);
  24.                 index += 4;
  25.                 buffer6.CopyTo(array, index);
  26.                 index += num3;
  27.             }
  28.         }
  29.         finally
  30.         {
  31.             if (enumerator is IDisposable disposable)
  32.             {
  33.                 IDisposable disposable;
  34.                 disposable.Dispose();
  35.             }
  36.         }
  37.         byte[] buffer8 = Base.ToByte(index + 5);
  38.         byte[] buffer2 = new byte[index + 5];
  39.         buffer8.CopyTo(buffer2, 0);
  40.         buffer2[4] = 1;
  41.         byte[] destinationArray = new byte[index];
  42.         Array.Copy(array, 0, destinationArray, 0, index);
  43.         destinationArray.CopyTo(buffer2, 5);
  44.         return buffer2;
  45.     }
  46.     catch (Exception exception)
  47.     {
  48.         Debug.LogWarning("[Network] ERROR :" + exception.ToString());
  49.         return null;
  50.     }
  51. }
复制代码


那么我们来具体分析了:
首先申请一个2000字节大小空间到变量array里面
然后创建一个index整型变量,来记录当前读写字节的位置
enumerator是保存键值表里面键的数组
然后进入循环,这个循环可以看作是计次循环,次数是键的数量,我们来研究其中一轮循环:
current是当前索引下的键名
bytes是键名的utf8编码格式下的字节集
length是bytes的长度
buffer5是length的字节集
buffer6是这个键对应的值的utf8编码格式下的字节集
num3是buffer6的长度
buffer7是num3的字节集
然后开始字节操作array,
当前位置(index)为0,写入buffer5,因为length是整型,所以buffer5长度是4位
当前位置(index)为4,写入bytes,bytes的长度是length
当前位置(index)为4+length,写入buffer7(num3是整型),buffer7的长度是4
当前位置(index)为4+length+4,写入buffer6,buffer6的长度是num3
可以分析出来其实array就是以下这样的格式
无标题.png

跳出循环后,
buffer8是整形(index+5)的字节集,长度为4位
申请了一个大小为index+5字节的变量buffer,这个是最终形成的字节集
将buffer8写入buffer2,现在buffer2的读写位置在4
将字节1写入buffer2,现在buffer2的读写位置在5
又申请了一个字节集变量destionArray,大小为index
将array全部写入到destionArray,即现在destionArray是和array一样的
将destionArray写入buffer2,至此buffer2已经全部写入完成


举个例子:键值表为{"test":"1234"}
buffer2为:
字节集:21{0,0,0,21,1,0,0,0,4,116,101,115,116,0,0,0,4,49,50,51,52}
{0,0,0,21}表示为buffer2的大小,整型表示21
第五位是写入的字节1
然后后面{0,0,0,4,116,101,115,116,0,0,0,4,49,50,51,52}是destionArray,也就是array的字节内容
{0,0,0,4}是键test的字节长度
{116,101,115,116}是test的字节集
{0,0,0,4}是值1234的字节长度
{49,50,51,52}是1234的字节集


那么通过分析,发送内容结构同理,也是这样
发送内容封装函数:
  
子程序名返回值类型公开备 注
buildByteArray字节集 
参数名类 型参考可空数组备 注
msg存取键值表
变量名类 型静态数组备 注
keys文本型0
index整数型 
i整数型 
current文本型 
keyBytes字节集 
length整数型 
valueBytes字节集 
length2整数型 
arrayByteBuffer 
buffer2ByteBuffer 
destinationArray字节集 
tmp字节集 
keys = msg.取键数组 ()
' array = 取空白字节集 (2000)
array.allocate (2000)
计次循环首 (取数组成员数 (keys), i)
current = keys [i]
keyBytes = 到字节集 (current)
length = 取字节集长度 (keyBytes)
valueBytes = 到字节集 (msg.取文本 (keys [i]))
length2 = 取字节集长度 (valueBytes)
array.putInt (length)
array.put2 (keyBytes, array.getPosition (), length)
array.putInt (length2)
array.put2 (valueBytes, array.getPosition (), length2)
计次循环尾 ()
buffer2.allocate (array.getLength () + 5)
buffer2.putInt (array.getLength () + 5)
buffer2.SetPosition (4)
buffer2.putByte (1)
buffer2.SetPosition (5)
tmp = array.ToByteArray ()
buffer2.put2 (tmp, 0, 取字节集长度 (tmp))
destinationArray = buffer2.ToByteArray ()
buffer2.Dispose ()
array.Dispose ()
返回 (destinationArray)


i支持库列表   支持库注释   
e2eeE2EE互联网服务器套件


附上源码:(键值表用了E2EE支持库,请e2ee,jimstone,com,cn/downloads/下载)
chenlong.rar (1.85 MB, 下载次数: 62)

评分

参与人数 4好评 +3 精币 +9 收起 理由
letgo + 1 奉上小小红包希望笑纳
ノ朽默℡ + 1 + 4 支持开源~!感谢分享
筱洛洛 + 1 + 2 感谢分享,很给力!~
乌云科技团队 + 1 + 2 我居然看懂了。。。

查看全部评分


结帖率:50% (1/2)
发表于 2020-3-17 12:49:46 | 显示全部楼层   广东省惠州市
分析的很到位,技术大牛!
回复 支持 反对

使用道具 举报

结帖率:100% (11/11)

签到天数: 2 天

发表于 2020-3-3 12:19:03 | 显示全部楼层   浙江省温州市
不是一般的强 是超级大佬
回复 支持 反对

使用道具 举报

头像被屏蔽
发表于 2019-10-16 12:36:11 | 显示全部楼层   湖北省武汉市
感谢发布原创作品,精易因你更精彩
回复 支持 反对

使用道具 举报

结帖率:96% (55/57)
发表于 2019-7-15 14:26:17 | 显示全部楼层   河南省洛阳市
请问有logcat 工具吗?
回复 支持 反对

使用道具 举报

结帖率:50% (7/14)
发表于 2019-7-15 01:09:14 | 显示全部楼层   湖北省宜昌市
老哥  手游协yi  接单吗,价格好谈,
回复 支持 反对

使用道具 举报

发表于 2019-6-3 05:46:17 | 显示全部楼层   四川省成都市
大佬做成视频不是更好
回复 支持 反对

使用道具 举报

发表于 2019-5-15 09:30:13 | 显示全部楼层   江苏省无锡市
mark下大神的帖子
回复 支持 反对

使用道具 举报

发表于 2019-5-8 02:36:28 高大上手机用户 | 显示全部楼层   安徽省滁州市
作者有联系方式吗
回复 支持 反对

使用道具 举报

签到天数: 19 天

发表于 2019-5-7 23:01:07 | 显示全部楼层   河北省保定市
高级  支持开源~!感谢分享
回复 支持 反对

使用道具 举报

发表于 2019-5-7 07:02:32 | 显示全部楼层   四川省成都市
完全不知道是什么  看起来666666666
回复 支持 反对

使用道具 举报

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

本版积分规则 致发广告者

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

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

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