2011年10月3日星期一

Obfuscate 兩個主從 SWF 後如何使用 Singleton

基本上,這篇只是上一篇的延伸,最後一樣是透過 metadata 去尋找指定 method name。只不過這篇的情況又更複雜一點,當 main.swf 與 external.swf 欲使用同一個 class 的 singleton 物件時,該如何達到。



其實,就算不做混淆,main.swf 載入 external.swf 時,若使用了全新的 ApplicationDomain 的話,Singleton 的應用就已經會出問題了~

/Main.as

package
{
 import flash.display.Loader;
 import flash.display.LoaderInfo;
 import flash.display.Sprite;
 import flash.events.Event;
 import flash.net.URLRequest;
 import flash.system.ApplicationDomain;
 import flash.system.LoaderContext;
 
 import idv.ben.util.MySingleton;
 
 [SWF(width="400", height="300")]
 public class Main extends Sprite
 {
  public function Main()
  {
   trace(this, "MySingleton's name is " + MySingleton.getInstance().getName());
   
   var ldr:Loader = new Loader();
   ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, on_ldr_complete);
   ldr.load(new URLRequest("External.swf"));
   
   var ldr2:Loader = new Loader();
   ldr2.contentLoaderInfo.addEventListener(Event.COMPLETE, on_ldr2_complete);
   var ctx2:LoaderContext = new LoaderContext();
   ctx2.applicationDomain = new ApplicationDomain();
   ldr2.load(new URLRequest("External.swf"), ctx2);
   
   trace("");
  }
  
  private function on_ldr_complete(e:Event):void{
   trace(this, "on_ldr_complete()");
   
   var info:LoaderInfo = e.currentTarget as LoaderInfo;
   info.removeEventListener(Event.COMPLETE, on_ldr_complete);
   
   var ldr:Loader = info.loader;
   this.addChild(ldr);
   
   trace("");
  }
  
  private function on_ldr2_complete(e:Event):void{
   trace(this, "on_ldr2_complete()");
   
   var info:LoaderInfo = e.currentTarget as LoaderInfo;
   info.removeEventListener(Event.COMPLETE, on_ldr2_complete);
   
   var ldr:Loader = info.loader;
   this.addChild(ldr);
   
   trace("");
  }
 }
}

/External.as

package
{
 import flash.display.Sprite;
 import flash.events.Event;
 
 import idv.ben.util.MySingleton;
 
 [SWF(width="400", height="300")]
 public class External extends Sprite
 {
  public function External()
  {
   addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
  }
  
  private function onAddedToStage(e:Event):void{
   removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
   
   trace(this, "MySingleton's name is " + MySingleton.getInstance().getName());
  }
 }
}

以上兩個輸出的 swf,都會用到以下類別,

/idv/ben/util/MySingleton.as

package idv.ben.util
{
 import flash.utils.getTimer;

 public class MySingleton
 {
  static private var _name:String = (Math.random() * 10000).toFixed();
  
  static private var _instance:MySingleton;
  static public function getInstance():MySingleton{
   if(!_instance)
    _instance = new MySingleton(new InnerClass);
   return _instance;
  }
  
  function MySingleton(ic:InnerClass){}
  
  public function getName():String{
   trace("MySingleton[" + _name + "].getName()");
   return _name;
  }
 }
}

internal class InnerClass{}

輸出結果:

MySingleton[8288].getName()
[object Main] MySingleton's name is 8288

[object Main] on_ldr2_complete()
MySingleton[5357].getName()
[object External] MySingleton's name is 5357

[object Main] on_ldr_complete()
MySingleton[8288].getName()
[object External] MySingleton's name is 8288

可看出 ldr2 使用 new ApplicationDomain 後,取得 singleton 物件就已經不是同一個物件了。

若再使用以下 command line 作混淆的話:

set SECURE_SWF_HOME=E:\Program Files (x86)\secureSWF_v3pro_win_build5762
set PROJECT=E:\works_test\TestMainExternalSingleton

java -jar "%SECURE_SWF_HOME%\secureSWF.jar" %PROJECT%/bin-debug/Main.swf %PROJECT%/bin-debug -p:most_aggressive -v:10
java -jar "%SECURE_SWF_HOME%\secureSWF.jar" %PROJECT%/bin-debug/External.swf %PROJECT%/bin-debug -p:most_aggressive -v:10

pause

重新執行,輸出結果如下,就連 ldr 載入的 swf 所取得的 singleton 物件也不會是同一個,因為在 main.swf 與 external.swf 中所各自存放的 class name 已經各自不同,所以已經被視同為兩個不同的類別,有著各自的 singleton 物件了:

MySingleton[868].getName()
[object Main] MySingleton's name is 868

[object Main] on_ldr2_complete()
MySingleton[8773].getName()
[object External] MySingleton's name is 8773

[object Main] on_ldr_complete()
MySingleton[8655].getName()
[object External] MySingleton's name is 8655

接下來,調整程式,使用 metadata 來找 method name 的作法,

/Main2.as

package
{
 import flash.display.Loader;
 import flash.display.LoaderInfo;
 import flash.display.Sprite;
 import flash.events.Event;
 import flash.net.URLRequest;
 import flash.system.ApplicationDomain;
 import flash.system.LoaderContext;
 import flash.utils.describeType;
 
 import idv.ben.util.MySingleton2;
 
 [SWF(width="400", height="300")]
 public class Main2 extends Sprite
 {
  public function Main2()
  {
   
   trace(this, "MySingleton2", MySingleton2.getInstance().getName());
   trace(this, "MySingleton2", MySingleton2.getInstance().getPropValue("abc"));

   var ldr3:Loader = new Loader();
   ldr3.contentLoaderInfo.addEventListener(Event.COMPLETE, on_ldr3_complete);
   var ctx3:LoaderContext = new LoaderContext();
   ctx3.applicationDomain = new ApplicationDomain();
   ldr3.load(new URLRequest("External2.swf"), ctx3);
   
   trace("");
  }

  private function on_ldr3_complete(e:Event):void{
   trace(this, "on_ldr3_complete()");
   
   var info:LoaderInfo = e.currentTarget as LoaderInfo;
   info.removeEventListener(Event.COMPLETE, on_ldr3_complete);
   
   var ldr:Loader = info.loader;
   methodCall(ldr.content, "setSingletonDelegate", [MySingleton2.getInstance()]);
   this.addChild(ldr);
   
   trace("");
  }
  
  private function methodCall(owner:Object, methodName:String, args:Array=null):*{
   trace(this, this.name, "methodCall()", "owner=" + owner, "methodName=" + methodName, "args=" + args);
   
   var xml:XML = describeType(owner);
   var find:XMLList = xml["method"]["metadata"].(@name=="PublicMethod")["arg"].(@key=="name");
   for(var i:int=0; i<find.length(); i++){
    if(find[i]["@value"] == methodName){
     var realMethodName:String = XML(XML(XML(find[i]).parent()).parent())["@name"];
     var returnValue:* = owner[realMethodName].apply(owner, args);
     trace(this, this.name, "methodCall()", "realMethodName=[" + realMethodName + "]", "return value=" + returnValue);
     return returnValue;
    }
   }
   return null;
  }
 }
}

/External2.as

package
{
 import flash.display.Sprite;
 import flash.events.Event;
 
 import idv.ben.util.MySingleton2;
 
 [SWF(width="400", height="300")]
 public class External2 extends Sprite
 {
  public function External2()
  {
   addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
  }
  
  private function onAddedToStage(e:Event):void{
   removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
   
   trace(this, "MySingleton2's name is ", MySingleton2.getInstance().getName());
   trace(this, "MySingleton2's prop is ", MySingleton2.getInstance().getPropValue("xyz"));
  }
  
  [PublicMethod(name="setSingletonDelegate")]
  public function setSingletonDelegate(delegate:Object):void{
   trace(this, "setSingletonDelegate()", delegate);
   
   MySingleton2.setDelegateInstance(delegate);
  }
 }
}

上面兩段程式碼,當 External2.swf 被 Main2.swf 載入完成後,會由 Main2.swf 將 MySingleton2 的 instance 塞進 External2.swf 中的 MySingleton2 中。

下面開始,MySingleton2 類別的架構有些不同之處,在於多了一個 setDelegateInstance() 方法,可以讓 MySingleton2 實際上不是使用自己建立出來的物件,而是使用別人提供的物件:

/idv/ben/util/MySingleton2.as

package idv.ben.util
{
 import flash.utils.getTimer;

 public class MySingleton2
 {
  static private var _name:String = (Math.random() * 10000).toFixed();
  static private var _prop:String = (Math.random() * 10000).toFixed();
  
  static private var _instance:MySingleton2;
  static public function getInstance():MySingleton2{
   if(!_instance)
    _instance = new MySingleton2();
   return _instance;
  }
  
  static public function setDelegateInstance(delegate:Object):void{
   _instance = new MySinbleton2Wrapper(delegate);
  }
  
  [PublicMethod(name="getName")]
  public function getName():String{
   trace("MySingleton2[" + _name + "].getName()");
   return _name;
  }
  
  [PublicMethod(name="getPropValue")]
  public function getPropValue(propName:String):String{
   trace("MySingleton2[" + _name + "].getPropValue(" + propName + ")");
   return _name + "'s [" + propName + "] is " + _prop;
  }
 }
}

/ben/idv/util/MySingleton2Wrapper.as

package idv.ben.util
{
 import flash.utils.describeType;

 public class MySinbleton2Wrapper extends MySingleton2
 {
  private var _delegate:Object;
  
  public function MySinbleton2Wrapper(delegate:Object){
   _delegate = delegate;
  }
  
  private function methodCall(methodName:String, args:Array=null):*{
   var xml:XML = describeType(_delegate);
   var find:XMLList = xml["method"]["metadata"].(@name=="PublicMethod")["arg"].(@key=="name");
   for(var i:int=0; i<find.length(); i++){
    if(find[i]["@value"] == methodName){
     var realMethodName:String = XML(XML(XML(find[i]).parent()).parent())["@name"];
     var returnValue:* = _delegate[realMethodName].apply(_delegate, args);
     return returnValue;
    }
   }
   return null;
  }
  
  override public function getName():String{
   return methodCall("getName");
  }
  
  override public function getPropValue(prop1:String):String{
   return methodCall("getPropValue", [prop1]);
  }
 }
}

輸出結果:

MySingleton2[7110].getName()
[object Main2] MySingleton2 7110
MySingleton2[7110].getPropValue(abc)
[object Main2] MySingleton2 7110's [abc] is 6545

[object Main2] on_ldr3_complete()
[object Main2] root1 methodCall() owner=[object External2] methodName=setSingletonDelegate args=[object MySingleton2]
[object External2] setSingletonDelegate() [object MySingleton2]
[object Main2] root1 methodCall() realMethodName=[setSingletonDelegate] return value=undefined
MySingleton2[7110].getName()
[object External2] MySingleton2's name is 7110
MySingleton2[7110].getPropValue(xyz)
[object External2] MySingleton2's prop is 7110's [xyz] is 6545

可以看出,即使使用 new ApplicationDomain,也可以使用到同一份由 Main2.swf 所提供的 singleton 物件。

經以下 command line 作混淆:

set SECURE_SWF_HOME=E:\Program Files (x86)\secureSWF_v3pro_win_build5762
set PROJECT=E:\works_test\TestMainExternalSingleton

java -jar "%SECURE_SWF_HOME%\secureSWF.jar" %PROJECT%/bin-debug/Main2.swf %PROJECT%/bin-debug -p:most_aggressive -v:10
java -jar "%SECURE_SWF_HOME%\secureSWF.jar" %PROJECT%/bin-debug/External2.swf %PROJECT%/bin-debug -p:most_aggressive -v:10

pause

再次執行,輸出結果:

MySingleton2[5757].getName()
[object Main2] MySingleton2 5757
MySingleton2[5757].getPropValue(abc)
[object Main2] MySingleton2 5757's [abc] is 9602

[object Main2] on_ldr3_complete()
[object Main2] root1 methodCall() owner=[object External2] methodName=setSingletonDelegate args=[object MySingleton2]
[object External2] setSingletonDelegate() [object MySingleton2]
[object Main2] root1 methodCall() realMethodName=[setSingletonDelegate] return value=undefined
MySingleton2[5757].getName()
[object External2] MySingleton2's name is 5757
MySingleton2[5757].getPropValue(xyz)
[object External2] MySingleton2's prop is 5757's [xyz] is 9602

仍可抓到同一個 singleton 物件。

0 意見: