0x1 前言
本文对摩尔庄园页游进行逆向与分析,解密并复现其核心加解密算法。本思路也适用于其他形式的加解密算法分析过程,尤其是对flascc技术(一种能够在flash中运行C++编译生成的字节码的技术)进行逆向分析。
0x2 所用工具
WireShark(数据包抓包)
ffdec(Flash反编译)
0x3 逆向分析过程
首先,我们进入游戏,使用抓包工具抓得swf文件,尝试反编译,发现其加密。
我们使用ffdec提供的“搜索内存中的SWF”功能,尝试直接从内存中dump出swf文件。
这里有个小技巧。当下的浏览器为了追求稳定和渲染速度都采用一种技术,将多个页面分为多个进程,很难确定游戏页面到底在哪一个进程里面。我这里自己写了个单进程的小程序。
这样就能轻松dump出我们想要的swf文件了。对文件大小排个序,剔除掉尺寸特别大的,然后逐个反编译。最终我们找到了核心加解密算法所在的文件。
看看反编译的结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
| package com.fcc { import avm2.intrinsics.memory.li8; import avm2.intrinsics.memory.si32; import avm2.intrinsics.memory.si8; import flash.utils.ByteArray; import flash.utils.getDefinitionByName; import sample.MEncrypt.CModule; import sample.MEncrypt.ESP; import sample.MEncrypt.F_malloc; import sample.MEncrypt.eax; import sample.MEncrypt__3A__5C_Development_5C_Crossbridge_5C_cygwin_5C_tmp_5C_cc4krDaY_2E_lto_2E_bc_3A_d3346e37_2D_9080_2D_43e6_2D_a632_2D_6710752e3a2f.F_idalloc; import sample.MEncrypt__3A__5C_Development_5C_Crossbridge_5C_cygwin_5C_tmp_5C_cc4krDaY_2E_lto_2E_bc_3A_d3346e37_2D_9080_2D_43e6_2D_a632_2D_6710752e3a2f.L__2E_str5; public function MDecrypt(param1:ByteArray, param2:int, param3:ByteArray) : void { var _loc6_:* = 0; var _loc17_:* = 0; var _loc16_:int = 0; var _loc10_:* = 0; var _loc13_:int = 0; var _loc14_:int = 0; var _loc12_:int = 0; var _loc11_:* = 0; var _loc9_:* = 0; var _loc7_:* = 0; var _loc5_:* = int(ESP); _loc6_ = _loc5_; ESP = _loc5_ & -16; var _loc4_:* = int(getDefinitionByName("com.taomee.mole.net.ConnectionServerAgent").size); if(_loc4_ == 5477) { ESP = _loc5_ & -16; _loc17_ = param2; _loc5_ = int(_loc5_ - 16); si32(_loc17_,_loc5_); ESP = _loc5_; F_malloc(); _loc5_ = int(_loc5_ + 16); _loc16_ = eax; ESP = _loc5_ & -16; CModule.writeBytes(_loc16_,_loc17_,param1); _loc5_ = int(_loc5_ - 16); _loc14_ = _loc17_ + -1; si32(_loc14_,_loc5_); ESP = _loc5_; F_malloc(); _loc5_ = int(_loc5_ + 16); _loc13_ = eax; if(_loc14_ >= 1) { _loc12_ = _loc16_ + 1; _loc11_ = int(_loc17_ + -1); _loc10_ = li8(_loc16_); _loc9_ = _loc13_; do { _loc4_ = _loc10_ & 224; var _loc8_:int = _loc4_ >>> 5; _loc10_ = li8(_loc12_); _loc4_ = _loc10_ << 3; _loc4_ = _loc4_ | _loc8_; si8(_loc4_,_loc9_); _loc12_ = _loc12_ + 1; _loc11_ = int(_loc11_ + -1); _loc9_ = int(_loc9_ + 1); } while(_loc11_ != 0); if(_loc14_ >= 1) { _loc12_ = _loc17_ + -1; _loc11_ = _loc13_; _loc7_ = 0; do { _loc10_ = li8(_loc11_); _loc9_ = 0; _loc17_ = int(L__2E_str5); if(_loc7_ != 21) { _loc17_ = int(L__2E_str5 + _loc7_); _loc9_ = int(_loc7_ + 1); } _loc4_ = li8(_loc17_); _loc4_ = _loc4_ ^ _loc10_; si8(_loc4_,_loc11_); _loc11_ = int(_loc11_ + 1); _loc12_ = _loc12_ + -1; _loc7_ = _loc9_; } while(_loc12_ != 0); } } if(_loc16_ != 0) { _loc5_ = int(_loc5_ - 16); si32(_loc16_,_loc5_); ESP = _loc5_; F_idalloc(); _loc5_ = int(_loc5_ + 16); } ESP = _loc5_ & -16; CModule.readBytes(_loc13_,_loc14_,param3); if(_loc13_ != 0) { _loc5_ = int(_loc5_ - 16); si32(_loc13_,_loc5_); ESP = _loc5_; F_idalloc(); _loc5_ = int(_loc5_ + 16); } } _loc5_ = _loc6_; ESP = _loc5_; return undefined; } }
|
相信各位看了后也感觉这代码的可读性的确不佳,在此我对它进行简单的解读。
首先介绍一下代码中所用到的一些函数:
函数签名 |
函数解释 |
CModule.readBytes(sourceAddress,count,target) |
将源地址的数据写到as代码的变量中 |
CModule.writeBytes(targetAddress,count,source) |
将as代码的变量的值写到目标地址中 |
si32/si16/si8(value,address) |
将定长(8/16/32位)的值写到地址中 |
li32/li16/li8(address) |
从地址中读取定长的数据(8/16/32位) |
因为Flash技术的过时,因此我之前并没有接触过ActionScript和Flascc技术,但是相信大家看到这里,应该敏感的发现,这类函数似乎是在模拟底层的内存读写。并且,我们还发现反编译代码中有“ESP”,“eax”这样的关键词,可以大胆的猜测,flascc技术应该是模拟了一个虚拟的运行环境,包含虚拟文件系统,标准的C函数等,这样的代码更接近于汇编代码。
我们通过分析汇编代码的思路,直接分析这个反编译代码,发现其不过是先对每一个byte的8bit进行拆分的预处理,然后异或一个常量值(L__2E_str5),这里的关键就是找到这个常量值在哪。
我们找到这个变量定义的地方:
继续查找定义:
发现其数据拷贝自DS2这个类,我们查看这个swf文件的二进制数据块:
发现了我们想要的:
查看其内容(计算数据偏移:208=0xD0):
嗯,应该是这个没错了。我们直接写一个python脚本,抓取数据包测试我们的加解密函数。
这里我们选择抓取喊话数据包,因为如果我们的解密算法正确,那么我们解密后的数据包中就能看到我们所发送的字符。
抓到包了,丢进python脚本试试。
成功了,看到了我发送的文本。
0x4 后记
话说淘米这算法是真坑啊,当初以为是循环异或,下标应该是0,1,2…n,0,1,2…这样循环的,没想到nt开发者竟然是这样设计的:0,1,2…n,0,0,1,2…给我整了两个0,我都懵了。
其实有了这算法,加上swf反编译后的那些代码,可以做一个真正的云玩家了,懂我意思吧。
0x5 附录
- 加解密脚本下载