关于flash封装JS代码的一些TIPS

作者:mikawawa

今天看rss reader上又有几个好友发了一些关于flash封装js代码的相关文章。看完后,仔细琢磨了一下,感觉应该把一些TIPS发到blog上做个备份吧。

一、首先,咱们说说flash代码的保护。
关于这个问题,我觉得对swf进行加密混淆很关键,因为浏览器在浏览到swf文件后都会缓存到本地,所以被拿到样本也就不足为奇了。但是如果能对其进行必要的加密混淆,我相信对于大多数人来说想要看到完整的源代码似乎就变得非常困难了。网上的混淆加密工具很多,我这里给一个大家可以测试下:
http://doswf.googlecode.com/files/DoSWF4.0.2.exe
当然好的加密混淆工具也是非常难得,看大家怎么去找了。

如果混淆和加密的算法都很强壮的话,相信很多人都会拿这个swf文件没有办法了。当然有朋友说,我可以不管你这个swf怎么加密,只要被加载到内存,最终代码还是会在内存里出现的等等,这些说法我同意,你都要用调试器进行动态调试了,我能有什么办法呢~~~起码给你静态调试增加点难度吧。

二、关于js代码的保护
JS代码可以由FLASH进行任意加密,调用之前当然会需要解密,看上去没什么区别。但是在使用静态分析的朋友的眼里那可就变得更复杂了一些。 因为经过加密后的js代码会被放入flash里一起编译进swf文件里,再对swf文件进行混淆,那个反编译出来的DD还有几个人能看得懂撒?

说到这里,又有朋友说了,我不管你怎么加密,你的js最终还是要通过外部接口交由浏览器执行的,既然你执行,那么肯定会调用eval这个js函数的,那好, 我hook这个函数不就行了吗?~~~我只能说,你很聪明,但还不够聪明。谁说的我执行js代码就非要用eval函数的撒?网上那么流传的而已,我使用 setTimeout 函数行不行?你说啥,你要把这个函数也hook了?OK,我使用匿名函数,我看你hook啥去?不相信?look下面的代码:

package{
import flash.external.ExternalInterface;

public class Movie extends Sprite{

public function Movie()  {
var myjs:String="alert('see me?');";//换成你的js
ExternalInterface.call("(function(){"+myjs+"})()");

}
}
}

这样的代码,我看看你再hook啥去?啊?哈哈!除了这个难道就没别的了吗?多啊,匿名对象?toString重载?toValue重载?等等!
2009.8.26更新:


上面讨论的这个问题,有幸得到inking同学的关注,他在自己的blog上先后发了两篇相关的主题,link如下:
动态解密flash网马(二)
动态解密flash网马(三)

其中也有我的回复,最终讨论的结果大概是这样的:
使用匿名函数依然可以通过hookFunction构造函数来获得执行内容,代码如下:

<script language="javascript">

// hook
fnobj = Function;
Function = function(v) {
alert("Hooker said: \r\n\t\r\n\tYou are...\r\n\t\r\n\t" + v);
alert("Begin to execute your codes...");
fn = new fnobj("(" + v + ")()");
fn();
}

// ExternalInterface.call("...")
(function() {
alert("you can not see me");
})();

</script>

而使用匿名类即:

new function(){
//js代码
}

是hook不到的,至于inking说的使用调试工具来获取执行内容那是我们脚本力所不能及的了,从纯脚本上来说最好的办法就是使用上面这个匿名类的方式,另外ExternalInterface.call的使用不是那么一成不变的,就像我在inking文章回复中提到的那样,你还可以这样玩:

ExternalInterface.call("a='see me',alert(a),alert(a+'?')");

关于这方面的讨论基本就这些了,有见解的请留言回复!


2009.8.26更新:结束!
这种保护就够了吗?NO!发挥一下想象力,配合服务端脚本尽可能的保护代码。这里写几个TIP吧,我自己用了好久了,这里都发出来大家交流下,有更好方法的希望也能拿出来学习一下。

首先看看,服务端脚本能做些什么?不管你是挂马还是XSS,不管你是用flash封装还是直接引入js代码,服务端脚本都可以起到一定程度保护和迷惑作用。首先,我首选的放置脚本的地方是些免费提供空间存储同时又支持php的*nix主机,为什么要选这个呢?第一,php脚本我比较属性,而且强大,写点东西也非常简单。二,为什么要是*nix主机呢,因为我们要用到一个apache非常强大的特性–.htaccess文件。这个文件有啥用呢?先看下下面的代码:

<script src="http://www.myserver.com/ma.js"></script>

当你在一个网站上看到这个链接地址的时候你第一反应会是啥?被挂马了呗!第二呢?你会把这个地址复制下来,然后直接放到浏览器访问吗?我想大多数人会这么做的(包括我)。因为什么呢?因为那个文件的后缀是个.js文件,大多数人的潜意识当中就会认为这个就是个普通的文本文件,所以直接访问就可以看到。如果是下面的代码:

<script src="http://www.myserver.com/ma.php"></script>

大多数人就会想到,有可能这个ma.php做了referer的判断,因为加上个referer头就可以轻松绕过限制了。那么我们怎么让web服务器 将.js的后缀给解释成php代码呢?这里就用到了.htaccess文件,这个文件在虚拟主机那里都会启用的,方便用户自己定制一些特性。那么我们可以写一个.htaccess然后放到我们的web目录里,内容如下:

<Files ma.js>
ForceType application/x-httpd-php
SetHandler application/x-httpd-php
</Files>
Options -Indexes

这个时候再访问ma.js,该文件就会被服务器端当成php脚本进行解析了,这样你在ma.js里尽情书写你的php代码,加个referer判断,如果通过判断则返回你的真正js代码,如果没通过判断则返回一个假的js代码,这样当大多数人看到”<script src=”http://www.myserver.com/ma.js”></script>”这样的代码时,他会把地址复制下来, 然后在浏览器里直接打开,结果返回的是一个假的js,而这个人他并不知道是假的,说不定他心里还在骂:这个SB挂马的人居然还用这么老的网马?(啊哈哈, 也不知道谁SB了~~~)。

那么将这个特性用于flash封装里,也是有很多地方可以应用到的。首先就是flash引用代码的引入,那么就可以上面说的这个小技巧来伪装一下。其次,我们可以把要封装的js代码分成至少两个部分,其中一部分放在swf里封装好,另一部分放到一个服务端脚本里。当swf文件加载执行后,它会去服务端脚本取回缺少的部分,然后组合起来,然后触发。那么这里又有很多的小技巧可以使用,我这里就不多赘述了,我这里只简单提一下,判断来源不仅仅只可以通过referer来判断的,开动一下脑筋,你会发现还有更好的判断方法等着你挖掘哦~~~

三、flash的其它应用
flash 的应用还是可以更大的发挥的。比如,xss里常碰到的跨域回传数据的问题,我们就可以通过flash来完美解决。举个例子,你可以在flash里写一个回传数据到另一个域的函数,然后将这个函数使用ExternalInterface.addCallback注册为可被外部调用。那么当使用js代码获取到信息后就可以使用js调用哪个注册好的函数,将获得的信息发送到另一个域去。因为flash的特性,对于跨域传递数据只要另一个域下有策略文件允许其它域的flash访问即可完成跨域信息的传递,而另一个用来接收回传数据的那个域又是可控的,所以这个限制就不算什么限制了。这样隐蔽又快捷,何乐而不为?

我这里在扩展的说一下上面部分讲到的将js代码分段保存,然后再组合应用的细节吧。其实分段保存无非是为了尽可能达到让分析人员头疼的目的。也就是说怎么麻烦怎么来,转的对方头晕脑胀目的也就达到了。比如本地共享对象,除了可以打水印跟踪客户端外,还可以保存一些数据。比如flash第一次加载时,flash利用flash.system.Capabilities里的一些客户端属性计算出一个唯一标识(这种方法还有待测试,因为我不知道是否可以达到绝对唯一的程度),然后使用URLLoader类向指定服务端脚本发送请求(其中会携带计算出的唯一标识以及一个来源标识),服务端脚本接收到请求后,判断来源标识,如果不是由特定flash发送过来的数据则放弃后续处理。判断通过后接着会去数据库中查询此唯一标识是否已被注册(即访问过),如果已注册则返回false,否则返回已加密的js分段,并注册该唯一标识。flash接收到返回的加密后的js后,解密还原然后与自身的另一分段组合后触发。这样就能保证这段js代码在客户机上只执行一次,而当客户机的用户即使发现了问题,获取到了这个样本flash,但是由于这个客户机的唯一标识已在服务端注册过,所以再也不会获取到js代码的另一部分了,那么即使swf文件被反编译成功,因为缺失的另一部分导致也无所获取整个样本。

说到这里有可能有人会说,那我再找另一个从来没访问过的机器去访问,然后用嗅探器抓包,不就抓到了吗?的确,这样是可以的。这就是我为什么用了“尽可能”这三个字的缘由了。

四、关于flash封装网马
今天LCX老大哥说,它用flash封装一个溢出型网马的时候老是不成功。这个问题我至今就碰到一次,那就是封装前些日子闹得沸沸扬扬的flash10的网马,这个呢属于特例因为漏洞是flash的,所以再用flash封装的时候会有冲突,导致封装不成功。不过用flash封装其它的网马我测试得都是没问题的,那么为什么LCX大哥在封装溢出行网马的时候会有问题呢?其实这跟是不是溢出行没啥关系,主要是网马代码的问题。有很多人都是直接将网马代码复制过来,然后接着就放到ExternalInterface.call里去执行了。这样做的后果就是,如果原始网马代码中包含有<script> 标签的话将导致不成功。先说这个是为啥吧:当flash是用网页加载的时候,它的外部容器就是这个html,因此falsh使用外部接口执行的js函数都是在HTML这个容器中的<script>里执行的,所以如果原始网马代码中也有script标签肯定是要去掉滴,不然肯定是不成功滴!
另外,如果原始代码中有document.write,那么也要修改成别的写法,否则一样不成功。为啥?道理很简单,网页元素的加载顺序是先脚本,后图片啊flash啊啥的。当加载到flash的时候,整个页面的DOM树已经成型,而且document的输出流也早已关闭,如果这个时候flash又调用外部接口执行js代码,而js代码里又恰巧包含有document.write的话,那么执行到这句就会重新打开输出流,结果就是新输出的内容会把先前的页面整个覆盖掉,导致不成功。

类似这样的问题大家可以以此类推,只要把类似这样的代码都解决了,封装触发就没问题了。

以后想到什么,再补充吧。本文不定时更新撒!

flash的应用不止于此,我这次讲得东西估计很多人也早就用得倍儿熟了,以后会再陆续公布其它的应用技巧,希望能有更多人参与进来相互交流,相互学习,保留一份纯真的技术交流氛围吧!~~

相关日志

发表评论