Flex-SWFに自動try-catchを埋め込み( その3 )

概要

SWFからABC部をとりだしパッチしてSWFにもどす、の続きで、EvalSupporterクラスの説明とパッチしてみた結果です。

SWFからABC部をとりだしパッチしてSWFにもどす (2)

EvalSupporter
EvalSupportクラスを下にのせますが別に読まなくても結構です。

class EvalSupporter {
//------------------------------------------------------------------
var abc:RemakeABCFile;
var u8_main:uint;
var u8_e:uint;
var u8_Error:uint;
var u8_AbcTrace:uint;
var u8_logging:uint;
var u8_getLog:uint;
var u8_blank:uint;
//------------------------------------------------------------------
var ns_AbcTrace:uint;
var ns_blank:uint;
//------------------------------------------------------------------
var np_e:uint;
var np_Error:uint;
var np_logging:uint;
var np_getLog:uint;
//------------------------------------------------------------------
const CONSTANT_Namespace = 0x08;
const CONSTANT_QName = 0x07;
const CONSTANT_Multiname = 0x09;
//------------------------------------------------------------------
function EvalSupporter(abcByteStream){
abc = Remake::parseAbcFile(abcByteStream);
var bytes = abc.getBytes();
var bytesLength = bytes.length;
}
//------------------------------------------------------------------
function codeGen(code,cond=false){
//for(var i=0;i

このクラスを使い以下のような変換が可能です。

var a:Array =[];
var b:uint =0;
for(var i:uint=0;i<a.length;i++){
  a[i] = b;
}

try{
  var a:Array =[];
  var b:uint =0;
  for(var i:uint=0;i<a.length;i++){
    a[i] = b;
  }
}
catch(e:*){
  ABCAlert.show(e);
}

ABCAlert.show( e:Error ) は、Alert.show(e.message)的なことをするstaticなクラスです。


ABC的にいうと、以下のコードをmethodの下に追加することになります。


28 getlocal0
29 pushscope
30 newcatch 0
32 dup
33 pushscope
34 swap
35 setproperty e
38 findpropstrict {}::ABCAlert
41 getproperty {}::ABCAlert
44 findpropstrict e
47 getproperty e
50 callproplex show (1)
54 pop
55 popscope
56 returnvoid
必要なConstants-Indexは事前に追加しておきます。

以下のException Infoも追加します。

first_pc : 0 , <== 元のCodeの最初のライン
last_pc : 27 , <== 元のCodeの最後のライン
target_pc : 28 , <== Exception Handlerが開始するライン
exc_type : 0 , <== Errorのタイプ(*)
var_name : e <== Error変数Index



上のようにパッチすると、うまく例外をキャッチしてくれるはずと考えて作成したSWFを実行してみると、


VerifyError: Error #1068: Array and * cannot be reconciled.

(Arrayと*は共有できません。)



がおきてしまいました。


これは何でしょうか? ということで次回へ続く。

Flex-SWFに自動try-catchを埋め込み( その2 )

概要

SWFファイルのABC-Code上にあるmethod-bodyを解析して実行時の例外発生を検知する処理を埋め込んでみる、の続きです。

今回は、# ABC部の特定、# ABCヘのパッチの予定でしたが長くなるのでABC部の特定の途中までとします。

なお、パッチに関して結論をいうと、とある理由によりその目的を達成することができませんでしたので、そこら辺を書いてみます。

SWF内でのABC部の特定



参考資料:「 SWF File Format Specification Version 9 」



SWFファイルは以下の構造をもちますので、ABC Code の Tag(DoABC)をさがしていくことになります。

Header / FileAttributesタグ / タグ / タグ / ----- / タグ / タグ / Endタグ

なお、DoABCタグは複数存在することがありますので注意要です。



以下のロジックにて、DoABCタグの名前、start position、length を配列化しています。DoABCタグの名前とは、例えば、"frame1" "frame2"などです。



data = SWFファイル byteArray

var _type:int, h:int, length:int;
var offset:int;
var abcOffsetArray:Array = [];
var abcOffset = {};

loop:while (data.position < data.length) {
//----TagとLengthを取得する。
// Upper 10 bits: tag type
// if Lower 6 bits is 0x3F ?, RECORDHEADER(long)
_type = (h = data.readUnsignedShort()) >> 6;
abcOffset.doABCStart = data.position -2;
if ((length = h & 0x3F) == 0x3F){
length = data.readInt();
}
abcOffset.doABCLength = length;
switch (_type) {
case 0: // Endタグ
break loop;
case 82: // DoABCタグ
data.readInt();
abcOffset.abcName = readString();
length -= (data.position-pos1);
abcOffset.start = data.position;
abcOffset.length = length;
abcOffsetArray.push(abcOffset);
break;
default:
break;
}
data.position += length
}

private function readString():String {
var s:String = ""
var c:int
while (c=data.readUnsignedByte())
s += String.fromCharCode(c)
return s
}


DoABCタグ
------------------------------------------------------------
Field Type Comment
------ -------------- ----------------------------------------
Header RECORDHEADER Tag type = 82
------ -------------- ----------------------------------------
Flags UI32 A 32-bit flags value, which may contain the following bits
set: kDoAbcLazyInitializeFlag = 1: Indicates that the ABC block
should not be executed immediately, but only parsed.
A later finddef may cause its scripts to execute.

------ -------------- ----------------------------------------
Name STRING The name assigned to the bytecode.

------ -------------- ----------------------------------------
ABCData BYTE[] A block of .abc bytecode to be parsed by the ActionScript 3.0
virtual machine, up to the end of the tag.


RECORDHEADER (short)
------------------------------------------------------------
Field Type Comment
----------------- ------ ----------------------------------
TagCodeAndLength UI16 Upper 10 bits: tag type
Lower 6 bits: tag length


RECORDHEADER (long)
------------------------------------------------------------
Field Type Comment
----------------- ------ ----------------------------------
TagCodeAndLength UI16 Tag type and length of 0x3F
Packed together as in short header
----------------- ------ ----------------------------------
Length SI32 Length of tag


SWFからABC部をとりだしパッチしてSWFにもどす

パッチは、EvalSupporterクラスを使いおこないますが、当該クラスの説明は次回まわし。


data = SWFファイル byteArray

var newSwf = new ByteArray();
var startOffset = 0;
for(var i:uint=0; i

ABCByteStream : Tamarin/esc/src/bytes-tamarin.es にて定義

EvalSupporter : 次回へ。

今回はここまで。

Flex-SWFに自動try-catchを埋め込み( その1 )

概要

SWFファイルのABC-Code上にあるmethod-bodyを解析して、実行時の例外発生を検知する処理を埋め込んでみる。

こうすることで、FlashPlayer(non-debugging-version)のときでも例外情報をとることができます。

ABC-Codeをパッチすることになりますが、Tamarin-escのクラスを使用してパッチする先を特定することにします。

おおまかな手順

  • SWF CWS==>FWS化
  • ABC部の特定
  • ABCヘ自動try-catchを埋め込むパッチ
  • ABCをSWFに戻す
  • SWF FWS==>CWS化

今回は

SWFの CWS==>FWS化 / FWS==>CWS化を試してみます。

SWFは通常CWSとして圧縮され(ヘッダー以外)ていますので、中身をみようと思うときは解凍しなければなりません。

というわけで、rubyを使い、CWS<==>FWSの変換を行ってみました。

<SWF Converter (CWS to FWS)>

# SWF Converter (CWS to FWS) 
#!/usr/local/bin/ruby
require 'zlib'
inFileName = ARGV[0]
outFileName = ARGV[1]

if(outFileName == '') then
   p 'Output File Name is not specified.'
   exit! 1
end
infile = File.open(inFileName)
if(inFileName == outFileName) then
   p 'Output File Name is not currect.'
   exit! 1
end
outfile = File.open(outFileName,"wb")
header = infile.read(8)
if(header[0,3] != 'CWS') then
   p 'This is not CWS(compressed) File.'
   exit! 1
end
header = header.sub(/CWS/, 'FWS')    
outfile.write(header)
outfile.write(Zlib::Inflate.inflate(infile.read))

<SWF Converter (FWS to CWS)>

# SWF Converter (FWS to CWS)
#!/usr/local/bin/ruby
require 'zlib'
inFileName = ARGV[0]
outFileName = ARGV[1]
if(outFileName == '') then
   p 'Output File Name is not specified.'
   exit! 1
end
infile = File.open(inFileName)
if(inFileName == outFileName) then
   p 'Output File Name is not currect.'
   exit! 1
end
outfile = File.open(outFileName,"wb")
header = infile.read(8)
if(header[0,3] != 'FWS') then
   p 'This is not FWS(un-compressed) file.'
   exit! 1
end
header = header.sub(/FWS/, 'CWS')
outfile.write(header)
outfile.write(Zlib::Deflate.deflate(infile.read, Zlib::BEST_COMPRESSION))

圧縮するときはBEST_COMPRESSIONとすると、ちょうど良いようです。

実行例

cwsTofws.rbで解凍、fwsTocws.rbで圧縮してみました。


$ ls -l Make*.swf
-rwx------ 1 * * 96556 2008-08-18 12:51 MakeButton.swf
$
$ ruby cwsTofws.rb MakeButton.swf MakeButtonFWS.swf
$
$ ls -l Make*.swf
-rwx------ 1 * * 96556 2008-08-18 12:51 MakeButton.swf
-rw-r--r-- 1 * * 192534 2008-10-19 12:52 MakeButtonFWS.swf
$
$ ruby fwsTocws.rb MakeButtonFWS.swf MakeButtonCWS.swf
$
$ ls -l Make*.swf
-rwx------ 1 * * 96556 2008-08-18 12:51 MakeButton.swf
-rw-r--r-- 1 * * 96556 2008-10-19 12:54 MakeButtonCWS.swf
-rwx------ 1 * * 192534 2008-10-19 12:52 MakeButtonFWS.swf
$


次は、ABC-Codeのパッチまでやってみます。