DoSWF 5.5.0(包含商业授权功能)破解
2017/8/5
一、背景了解
DoSWF是曾经的一款被喻为最强大的SWF加密软件。
本文使用的DoSWF版本是DoSWF Pro 5.5.0,是目前的最新版。由于它已经好久没更新了,再加上Flash已死的消息,我猜测作者已经基本放弃这款软件,所以给出完美破解思路。(其实在几个月前我已经完成了完整的破解)
下图证明可以使用它的商业授权功能。
二、破解方法
鉴于SWF文件的特殊性,它的破解方法可谓极其容易。按照DoSWF目前的加密与混淆强度,内存提取完全可以得到基本上已经解密的SWF文件,并且得到逻辑上正确的代码。
三、破解准备
1. JPEXS Free Flash Decompiler (只需要文件内存提取功能)
2. AS3 Sorcerer (或者ffdec也是可以的,只不过我认为这个查看起来方便一点,主要是反编译速度较快。没有搜索功能,可以导出AS3文件后使用编辑器的文件夹搜索,比如sublime text中就有这个功能。)
四、本地算法还原
(省略SWF内存提取过程)
1. 短暂浏览即可找到这个KeyScope:
2. 查看关键字符串:
3. 找到关键函数:
4. 还原被混淆函数:(就是把定义的字符串替换掉关键函数里的)
还原前:
还原后:
[Actionscript3] 纯文本查看 复制代码 private var checkUserFromServer__MN18862:* = "soft";
private var key2_MN15285:* = "laan";
private var key2_MN15283:* = "15";
private var key4_MN18861:* = "_";
private var key4_MN18865:* = "64";
private var key4_MN11447:* = "16";
private function validateKey__():void
{
var _local_2:String;
if (((((!(this.appKey_MN18836)) || (!(this.appKey_MN18836.appId))) || (!(this._key))) || (!(this._userName))))
{
return;
};
if (this._userName.indexOf(String.fromCharCode("64")) < 0) // char = "@" : There must be "@" in the userName. (should be email address, from the snap from the official website.)
{
while ("laan".length)
{
"laan" = ("laan" + "laan");
};
};
var _local_1:String = this._key.substr(-2); // The last 2 characters of the key.
if (_local_1 == "16")
{
_local_2 = (MD5.hash(((((((((this.appKey_MN15235 + "_") + "_") + "laan") + MD5.hash((this._userName + "_"))) + "15") + "soft") + "_") + this.appKey_MN18836.appId)) + _local_1);
this._isValidated = (_local_2 == this._key);
}
else
{
if (_local_1 == "15")
{
_local_2 = (MD5.hash((((((((this.appKey_MN15235 + MD5.hash(this._userName)) + "_") + "laan") + "_") + "soft") + "_") + this.appKey_MN18836.appId)) + _local_1);
this._isValidated = (_local_2 == this._key);
}
else
{
dispatchEvent(new Ⱥ(Ⱥ.Ϭ, "The key system had updated, please use the new key."));
}; // the last two character must be "15" or "16".
};
ɂ.instance.a = _local_1;
}
5. 找回缺失字符串
… 查找”KeyScope”:
… 查找” ȿ”:
… 查找”isInternalUsed_MN10638”:
6. 返回继续还原:
// appKey_MN18836.appId = 776
// appKey_MN15235 = "newSprite()"
替换后:
进一步简化后:
7. 注册机编写
(略,把上面的最简代码直接拷贝进Flash即可。)
只需要注意,要自己导入库的。必须自己下载下面的库:
import com.adobe.crypto.MD5;
或者用其他语言编写。
我是直接用Flash写的。
下面给出一组可用的注册码:
用户名:190720@evaluation.only
注册码:fd88ea01ca576b022395ef861018a10015
8. 试试看
好高兴对吧,终于算出一组注册码。试一试,呃,注册成功后,又弹出“Your licence has been removed.”。好吧,有网络验证。抓包,得到网址,或者从下面得到网址全部屏蔽掉,注册得以保留。但是实际试用,会发现“删除数据类型声明”和“方法同化”是不能使用的。仔细分析(见下一节),会发现有网络验证。
五、模拟网络验证
1. 找关键函数
关键函数还是在KeyScope里面:
发现好像还挺高级,使用到二进制“与”运算。
2. 适当理解与简化:
[Actionscript3] 纯文本查看 复制代码 private function checkUserFromServer__MN10009(_arg_1:Event):void
{
var _local_5:Array;
var _local_6:String;
var _local_2:URLLoader = (_arg_1.target as URLLoader);
_local_2.removeEventListener(Event.COMPLETE, this.checkUserFromServer__MN10009);
_local_2.removeEventListener(IOErrorEvent.IO_ERROR, this.checkUserFromServer__MN10141);
_local_2.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, this.checkUserFromServer__MN10141);
ɂ.instance.d = _arg_1.type;
// the code above are the process of getting returned value from the server
var _local_3:String = (_local_2.data as String); // get returned data
var _local_4:String = MD5.hash(this.checkRandom + this._key + MD5.hash(this._userName + "laan_soft"));
if (((_local_3) && (_local_3.indexOf(_local_4) > 0))) // there must be _local_4 in a part of _local_3
{
this._keyCode = uint(_local_3.substring(1, _local_3.indexOf("]"))); // _keyCode = _local_3[1 to "]"]
ǹ.setData("kc", this._keyCode); // Make it 'public'.
_local_3 = _local_3.replace(/(\[\d+\]|)/g, "").replace(_local_4, ""); // get rid of unwanted characters.
_local_5 = _local_3.split(",");
_local_6 = _local_5.shift();
if (((_local_6) && (this._keyCode & 0x01)))
{
dispatchEvent(new Ⱥ(Ⱥ.Ϭ, _local_6)); // display the message from the server.
};
if ((this._keyCode & 0x02))
{
this.setUserNameUsable_MN18838(true); // Success.
ǹ.setData("cft", 0);
ɂ.instance.e = (ɂ.instance.e + this._userName);
if ((this._keyCode & 0x10))
{
this.ҏ_MN18841 = _local_5.shift(); // Expire Time.
};
dispatchEvent(new Ⱥ(Ⱥ.VERIFIED));
}
else
{
this.setUserNameUsable_MN18838(false);
this.removeKey_();
};
}
else
{
this.checkUserFromServer__MN10141(_arg_1);
};
dispatchEvent(new Ⱥ(Ⱥ.REMOTE_CHECK_COMPLETE));
}
3. 校验码的生成
显然,_local_4是校验的关键,于是抓包分析发送的数据:
发送数据中,真正在验证算法中有用的只有r、k、u三项。与DoSWF程序中变量对应关系如下:r = checkRandom
u = _userName
k = _key
所以,以php举例,返回数据的代码应是:
[PHP] 纯文本查看 复制代码 echo md5($r.$k.md5($u."laan_soft"));
4. 其他数据的返回
现在好像有点没头绪了,因为此处没有对于商业版与否的校验。
不过,好在“与”运算特征明显,只要搜索“& 0x”,找到两处关键:
(I)
“c” = 商业授权,“p” = 个人授权。所以keyCode& 0x04 必须不为0。
(II)
一看,就知道这个是命令行功能,是商业版才能用的。所以keyCode & 0x08 必须也不为0。
5. 梳理思路
首先,返回数据必须有校验码,而且根据代码的情况来看,这个校验码最好放在返回字符串的最后。
返回字符串最开始,可以是[d]的形式。中间还可以有服务器返回的提示消息,还可以有授权到期日期。
这个d就是程序内获取到的keyCode,且 d & 0x02 && d & 0x04 && d & 0x08 ==true.
这个的话,自己写个程序过滤50以内的数,就可以获得符合要求的。
如果要让程序与服务器通信后显示消息,d & 0x01也要是true.
如果要让程序有授权过期时间,d & 0x10也要是true.
所以,这些条件一限制,50以内符合要求的数就不多了,最多两个。
估计这个程序对于d最大也有限制,我没有研究这个。最小的数经过测试都是可以的。
下面给出一个既有过期时间,又有服务器返回提示的php示例代码:
[PHP] 纯文本查看 复制代码 echo '[31]'.'Commercial Cracked by 190720,20191231';
echo md5($r.$k.md5($u."laan_soft"));
6. 测试效果:
六、总结
其实,这个DoSWF 5.5.0的破解过程就是需要人的仔细与耐心,看到一个个不认识的字符不要慌,按照逻辑走下去就是。从中可以学习到的是,作者把软件中经常出现的字符串作为生成Key的关键字,所以使用一般的查找就容易找不到。还有,在进行网络验证的同时返回校验码,如果校验码错误就移除license,这也是值得学习的地方。
但是,可见,DoSWF的加密程度已经无法抵御成熟的反编译软件。在之后,将继续写出最新版的DoSWF加密的程序的破解方法。
|