Flash之swf文件的加密与破解
Flash,现在叫animate,风靡于PC网络时代,整个时代弥漫着无数经典的作品,现在是移动互联时代H5逐渐代替了她的位置。逐渐地,flash变成了制作H5的一个工具。
深呼吸一下……顺便追忆前世今生 。
好了,闲言少叙,且看flash的swf文件。
swf文件是一种公开标准的文件格式,一般由flash软件发布。
文件格式(SWF)详细说明书
https://blog.csdn.net/yjfkpyu/article/details/4032202
http://www.360doc.com/content/15/0112/12/9200790_440101266.shtml
用其开发软件可以承载交互丰富的多媒体内容。
为了保护软件内容不被轻易的盗取,一般都要进行加密。
一般swf加密方法,都是按照一定的算法将文件的bytearray进行变换,得到一个player无法识别的文件。
player播放时需要按照算法逆运算,就可以得到swf文件然后进行装载播放了。
说白了加密解密就是玩一个字符串的游戏。
常用的操作如下
异或、替换或增加数据、改变字节顺序、base64转换、压缩等
本来嘛,内容既要隐藏又要展现就是矛盾的。
软件可以扫描内存从中提取展示的swf文件。
不管怎样内容还是要给人看的,所以再怎么变换也有让别人拿走的机会。
因此这些加密的手法也只能做到在一定程度上的防备。
对于一个经验多一点的闪客来说,想要的都能想办法拿到。
声明:本人遵纪、守法、讲道德,且一贯的尊重所有软件的知识产权。反编译代码仅为学习研究之目的,并无恶意。
下面举个栗子:
案例1
base64+增加数据实施对文件的加密。
反编译代码
package
{
import com.hurlant.util.*;
import flash.utils.*;
public class ByteCodeMix extends Object
{
public function ByteCodeMix(arg1:String)
{
super();
if (arg1)
{
_key = arg1 + innerKey;
}
else
{
_key = innerKey;
}
endkb = new flash.utils.ByteArray();
endkb.writeUTFBytes(endkey);
return;
}
public function enCodeMix(arg1:flash.utils.ByteArray, arg2:int):flash.utils.ByteArray
{
var loc3:*;
(loc3 = new flash.utils.ByteArray()).writeUTF(_key);
var loc4:*;
(loc4 = new flash.utils.ByteArray()).writeInt(loc3.length);
loc4.writeBytes(loc3);
loc4.writeBytes(arg1);
loc4.writeBytes(endkb);
var loc5:*;
(loc5 = new flash.utils.ByteArray()).writeBytes(loc4, 0, arg2);
var loc1:*=com.hurlant.util.Base64.encodeByteArray(loc5) + _key;
var loc2:*;
(loc2 = new flash.utils.ByteArray()).writeUTF(loc1);
loc2.writeBytes(loc4, arg2);
return loc2;
}
public function deCodeMix(arg1:flash.utils.ByteArray):flash.utils.ByteArray
{
arg1.position = 0;
var loc3:*=(loc3 = arg1.readUTF()).replace(_key, "");
var loc2:*=com.hurlant.util.Base64.decodeToByteArray(loc3);
var loc1:*=new flash.utils.ByteArray();
arg1.readBytes(loc1, 0, arg1.bytesAvailable);
var loc4:*;
(loc4 = new flash.utils.ByteArray()).writeBytes(loc2);
loc4.writeBytes(loc1);
var loc6:*=new flash.utils.ByteArray();
loc4.position = 0;
var loc5:*=loc4.readInt();
loc4.position = loc4.position + loc5;
loc4.readBytes(loc6, 0, loc4.length - loc5 - 4 - endkb.length);
return loc6;
}
private var _key:String;
private var innerKey:String="iZSBGbGV4IDQgQXBwbGljYXRpb248";
private var endkey:String="lt_xggh_fla.sprite185copy3_89";
private var endkb:flash.utils.ByteArray;
}
解密方法
loc1 = (loc3 = new ByteCodeMix("RpdGxlPjxkYzpkZXNjcmlwdG")).deCodeMix(flash.net.URLLoader(arg1.target).data as flash.utils.ByteArray);
分析:
能看出 有用的加密的文件结构是
--------------------------------------------------------------------------
| RpdGxlPjxkYzpkZXNjcmlwdGiZSBGbGV4IDQgQXBwbGljYXRpb248 |
--------------------------------------------------------------------------
| swf数据 |
--------------------------------------------------------------------------
| lt_xggh_fla.sprite185copy3_89 |
--------------------------------------------------------------------------
案例2
改变顺序反编译代码
package
{
import flash.display.*;
import flash.events.*;
import flash.net.*;
import flash.system.*;
import flash.utils.*;
public class DecryptLoader extends flash.events.EventDispatcher
{
public function DecryptLoader(arg1:String, arg2:String, arg3:Boolean=true)
{
super();
isEncode = arg3;
_strUrl = arg1;
_strKey = arg2;
return;
}
public function get getUrl():String
{
return _strUrl;
}
public function get getKey():String
{
return _strKey;
}
public function set setUrl(arg1:String):void
{
_strUrl = arg1;
return;
}
public function set setkey(arg1:String):void
{
_strKey = arg1;
return;
}
private function validKey(arg1:Array):Boolean
{
arg1.sort();
trace(arg1);
return arg1.every(sequential);
}
private function sequential(arg1:*, arg2:int, arg3:Array):Boolean
{
return arg1 == arg2;
}
public function startLoad(arg1:Function=null):void
{
var loc2:*=new flash.net.URLRequest(_strUrl);
var loc1:*=new flash.net.URLLoader();
loc1.dataFormat = "binary";
loc1.addEventListener("complete", handleUrlLoadComplete);
loc1.addEventListener("ioError", onErr);
if (arg1 != null)
{
_progress = arg1;
loc1.addEventListener("progress", arg1);
}
loc1.load(loc2);
return;
}
private function onErr(arg1:flash.events.IOErrorEvent):void
{
dispatchEvent(new flash.events.Event("LoadFail"));
return;
}
private function handleUrlLoadComplete(arg1:flash.events.Event):void
{
if (_progress != null)
{
arg1.target.removeEventListener("progress", _progress);
}
var loc1:*=arg1.target.data as flash.utils.ByteArray;
var loc4:*=new flash.utils.ByteArray();
if (isEncode)
{
if (!decrypt(loc1, loc4))
{
dispatchEvent(new flash.events.Event("LoadFail"));
return;
}
}
var loc2:*=new flash.display.Loader();
loc2.contentLoaderInfo.addEventListener("complete", handleByteLoadComplete);
var loc3:*;
(loc3 = new flash.system.LoaderContext()).allowCodeImport = true;
loc3.allowLoadBytesCodeExecution = true;
loc3.applicationDomain = flash.system.ApplicationDomain.currentDomain;
if (isEncode)
{
loc2.loadBytes(loc4, loc3);
bytes = loc4;
}
else
{
loc2.loadBytes(loc1, loc3);
bytes = loc1;
}
loader = loc2;
return;
}
private function decrypt(arg1:flash.utils.ByteArray, arg2:flash.utils.ByteArray):Boolean
{
var loc4:*=0;
var loc6:*=0;
var loc3:*=0;
if (arg1.length < 2000)
{
return false;
}
if (_strKey.length != 20 * 2)
{
return false;
}
var loc5:*=[];
var loc7:*=[];
loc4 = 0;
while (loc4 < _strKey.length)
{
loc5.push(_strKey.substr(loc4, 2));
loc7.push(_strKey.substr(loc4, 2));
loc4 = loc4 + 2;
}
if (!validKey(loc7))
{
return false;
}
var loc2:*=[];
var loc1:*=new flash.utils.ByteArray();
loc6 = 0;
while (loc6 < loc5.length)
{
loc2["byte" + loc6] = new flash.utils.ByteArray();
arg1.readBytes(loc2["byte" + loc6], 0, 2000 / 20);
++loc6;
}
arg1.readBytes(loc1);
loc3 = 0;
while (loc3 < loc5.length)
{
trace("byte" + loc5[loc3]);
arg2.writeBytes(loc2["byte" + int(loc5[loc3])]);
++loc3;
}
arg2.writeBytes(loc1);
return true;
}
private function handleByteLoadComplete(arg1:flash.events.Event):void
{
trace((arg1.target as LoaderInfo).loader);
dispatchEvent(arg1);
return;
}
public var loader:Loader;
public var bytes:ByteArray;
private const KEYLENGTH:uint=20;
private const BUFFERSIZE:uint=2000;
private var _strUrl:String;
private var _strKey:String;
private var _progress:Function;
private var isEncode:Boolean;
}
}
}
解密代码
var loc1:*=new DecryptLoader(arg1, "0010170918061315121405160704110803021901");
loc1.addEventListener("LoadFail", loadFail);
loc1.startLoad();
分析:
很有意思的参数"0010170918061315121405160704110803021901"是0-19的组合
也是打乱顺序的规则。
100个字节为1组,打乱前20组。