免责声明
??????? 本文内容仅供技术学习与交流用,概不提供任何逆向工程产物与破解程序,? 列举的实例不针对任何公司,任何产品,? 如有侵权,请联系本人.
废话( 可略过 )
????????? 基于AVM本身特性,最终跑在AVM上的程序都会编译成一种叫ABC的中间码,而ABC的标准是公开的,所以由AS3编译而成的SWF,如果不加任何代码混淆,都能轻易的被反编译成可读性较高的AS3代码.这些反编译工具的佼佼者包括硕思,和某A软件(? 由于某A软件反盗版工作做的较好,网上破解版基本不能用,这里就不为它打广告了? ).
???????? 由于swf的反编译成本低廉,也推动了AS3技术的开源,一些webGame公司毫不吝啬于他们优秀的代码和美术资源公诸于众,故而不对自己的swf和资源做任何特殊的保护措施,任何一个AS3菜鸟下载一个硕思都能从这些项目中收益,促进了AS3技术的发展. 例如神仙道,大概一年前,我刚接触AS3时,从这个项目中收获甚丰.在此向这个在技术和产品一样优秀的产品致以崇高的敬意!
???????? 但也有一些公司对它们自己的产品进行了加密,这并不是他们对swf的加密抱有幻想,而是他们不愿意自己的美术资源和前端代码过于轻易地暴露在公众的视线中. 这其中原因各异,有些或许是代码写的太乱,不好意思拿出来晒.有些或许是美术资源太华丽了,不想被人随便糟践,等等...
???????? 神将X国是4X99旗下一款制作颇为精良的作品,采用了Alchemy来做解密逻辑,好奇心驱使下,花了大概5个小时的时间,成功将其主程序代码反编译. 由于太小白,走了不少弯路,所以花了不少时间. 不过也有所收获,挺值得的,在此将破解思路记录下来,破解方式并不一定是最高效的,但希望对一些人有点帮助.
第一波 传统反编译尝试
??????? 首先开了个httpWatch跑个一遍游戏,得到大致的加载列表,发现其加载了很多swf,png,jpg格式的资源,但大部分都是无法正常打开与查看的,这表明这些资源都被他们加密过,其中游戏刚开始加载的列表如下:
???????

?????? 这里发现了Main,GameLoader,? yesguo这三个可疑文件,按照惯例Main.swf应该就是游戏的主程序所在,可观察其大小仅有4k多,这显然不可能是,反编译后发现只是一个用于加载GameLoader的壳,于是反编译GameLoader,代码结构大致如下:
???????

????? 简要的浏览了下GameLoader的代码,发现其也是一个资源加载的壳,加载了诸如version.swf和firstloader.swf这些东西,但没有发现任何显式加载主文件的代码,其中有一段代码比较值得注意:
?????

?????????? 这里?var _loc_2:* = new CLibInit();是一个典型的alchemy初始化语句,初始化后又调了?var _loc_3:* = _loc_2.init();初始化一个对象_loc_3,在之后以二进制形式加载了某个资源,并将该资源的二进制数据传递给了_loc3对象的loadBytes.
???????? 可以推测,其加载的正是主文件,且将解密逻辑放在了alchemy层来做,而上面资源加载列表中那个紧随GameLoader后加载的1M多叫yesgo.jpg的文件很可能就是真正的主文件,同时gameLoader的源码中一个叫cmodule.encryption的包很可能就是解密逻辑的所在,或许可以直接copy这份代码,来进行解密,于是试图打开cmodule.encryption包,查看其代码是否具备可读性,包内代码结果如下:
?????????

????????? 打开包后果然发现CLibInit类,且有很多Alchemy前缀的类,这显然是一个alchemy编译而成的模块,查看其代码,发现看不懂其代码逻辑,似乎不太可能拿这份代码重新编译后,进行解密了.
第二波 提取内存里的SWF
????????? 无论如何加密,最终还是要在运行的时候将主程序swf解密的,不然就没法玩了.? 只是这解密的swf是放在内存里的,于是进行了第二步尝试,在IE内存里找出所有有swf文件特征的内存,将其导出. 显然这种工具不需要我们自己写,而是直接google. google后发现有Swf Reader,XXX吸血鬼啥的,这些工具都是要钱的,看了一篇文章说swf Reader的2.3 demo版可以免费用,于是就懒得去找其他的了,直接去其网站下来用.
???????? 打开swf Reader -> file -> load from memory,然后选择要抓取的进程,选择了IE,然后点击find swfs,等了一会,抓取了一大堆swf,果然有用,太神了. 于是将swf导出,逐个用硕思查看,可以看到一些美术资源的swf 和许多损坏的swf. 但就是找不到主程序的文件. 异常沮丧.
??????? 难道是demo版的程序不给力,还是我用法有误,试了好几次,效果还是差不多,美术资源倒是可以看到不少,但是主程序的swf却提取不出来. 又去网上找找看有没其它更给力的内存swf抓取工具,发现很多都是要收费的,破解版也很难找. 不想找了,萌生了自己写一个的想法.
?????? 搜了网上一些文章,发现自己写一个是可行的,swf有分压缩和非压缩两种格式,文件开头和结尾都有固定的格式,所以写一个内存提取工具应该是可行的,可想想,又没多少动力去写了,万一写出来还是不行怎么办. 是否有其他更高效的方法呢?
第三波 以其人之道还治其人之身
????????? google了下"Alchemy解密"关键词,发现一篇文章中的方法不错,它直接用air把那个负责解密的swf加载进来,然后用applicationDomain.getDefinition把那个解密模块直接拿来用,? 好办法! 赞一个!
???????? 于是依样画葫芦,写了一段这样的代码.
????????
var loaderContentInfo:LoaderInfo = e.currentTarget as LoaderInfo;
var clib:Class = loaderContentInfo.applicationDomain.getDefinition( "cmodule.encryption.CLibInit" ) as Class;
var obj:Object = ( new clib() ).init();
obj.loadGame( _sprite );
var urlLoader:URLLoader = new URLLoader( new URLRequest( "asset/yesguo.jpg" ) );
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.addEventListener( Event.COMPLETE,function( e:Event ):void
{
obj.loadBytes( urlLoader.data );
}
);
??????? 运行了下,发现出现了[SWF] XXXXXXX bytes after decompression的字样,这表示obj.loadBytes真的就是一个解密函数,而且的确解密成功了. 现在这个主程序的swf已经在我程序的内存里了,Oh yeah!
??????? 可在内存里怎么把它拿出来呢?? 上面提到的那篇文章,同样是调用了一个解密函数,然后那个解密函数直接返回了一个byteArray,他将byteArray保存为swf,Over~~~ 可这里的loadBytes函数似乎啥也不返回,那那个解密后的swf到底在哪里呢? 用flash.utils.describeType函数和ObjectUtil.getClassInfo函数打印出这个obj对象的信息,看看是否还有其他可用的方法,可以拿出这个swf的数据,结果尝试无果.
?????? 回头又去看了看GameLoader的代码还是没有头绪~~ No~~ 又尝试了用Swf Reader 去抓取当前程序的内存,抓出来的几个swf还是很不中用. WTF~~
第四波 逆袭! 方法调用Log与ApplicationDomain 钩子
??????? 不知道obj.loadBytes都做了些什么,要拿出它解密后的数据无异于大海捞针. 这时我突然想起不久前看过的一篇文章,介绍flashlog几个隐藏的开关,其中一个是可以打印出运行过程中所有方法的调用. 这个或许会有用,于是在mm.cfg里加了这个开关 AS3Trace = 1.? 观察在obj.loadBytes后打印出来的方法调用log,终于让我发现了一行重要的log.
??????? org.easily.loader::LoaderUtils$/loadBytes ()
??????? 在obj.loadBytes调用时,它做了一系列内存操作,之后调用了LoaderUtils类的loadBytes方法,LoaderUtils类定义在GameLoader.swf里,这很可能就是把解密后的byteArray交给LoaderUtils去将它load成swf.? 太好了,我终于知道解密后数据的去处了.
????? 那么如果我可以伪装一个一摸一样的?? org.easily.loader::LoaderUtils类,让它调用这个类的loadBytes ()方法,我不就能全盘招收它解密后的byteArray咯~~
?????? 接下来,就是利用同一个ApplicationDomain,后面加载进来的类定义不能覆盖原先相同包路径的类定义的特点,只要我事先在air程序里定义一个相同包路径的? org.easily.loader.LoaderUtils类,且定义一个loadBytes方法,然后把加载GameLoader.swf的那个loader对象的loaderContext的ApplicationDomain设为ApplicationDomain.currentDomain,这样GameLoader里面那个真正的LoaderUtils是不能覆盖我现在已有的这个LoaderUtils的,所以调用obj.loadBytes后,它解密后的byteArray就会传入到我定义的方法里,之后哈哈~~~
??????? 到此,反编译过程结束,我终于顺利的看到了完整的主程序源码,当然这仅仅是为了学习与借鉴.
