SWF Binary Hacks ~ eval のいばら道 ~ Shibuya.JS Technical Talk#3 – Shibuya.es 竹迫 良範
2 はじめに
3 謝罪 今日 Flex2 SDK を インストールしたばかり です 嘘言ってたらごめんなさ い
4 eval の発音について eval イーヴァルの検索結果 約 4 件 eval イヴァルの検索結果 約 11 件 eval エヴァル の検索結果 約 12 件 eval イーバルの検索結果 約 13 件 eval イバルの検索結果 約 13 件 eval エバルの検索結果 約 28 件 ※某 G 調査機関による調べによる
JavaScript でフォント名一覧を 取得する 3 つの方法 (1) IE 限定 (2) LiveConnect (3) Flash 利用 Yet another CybozuLabs
6 (1) Dialog Helper Object でフォント名一覧取 得 <OBJECT id="dlgHelper " CLASSID="clsid:3050f819-98b5-11cf-bb82-00aa00bdce0b" width="0px" height="0px"> function getAllFontsByDialogHelper() { var fontslist = ''; try { for (var i = 1; i < dlgHelper.fonts.count; i++) { fontslist += dlgHelper.fonts(i) + '\n'; } catch(e) { fontslist = 'sorry, could not get fonts list.'; } return fontslist; }
7
8 ※動作するブラウザが激しく IE に限定される
9 (2) LiveConnect で Java アプレットを操作 function getAllFontsByLiveConnect() { var fontslist = ''; try { var fonts = java.awt.GraphicsEnvironment. getLocalGraphicsEnvironment().getAllFonts(); for (var i = 0; i < fonts.length; i++) { fontslist += fonts[i].getFontName() + '\n'; } catch(e) { fontslist = 'sorry, could not get fonts list.'; } return fontslist; } LiveConnect - Netscape3 以降の仕様 ( JavaScript1.1 ) - Firefox, Opera で動作 Java のオブジェクトを JavaScript から操 作 java.awt.GraphicsEnvironment#getAllFonts()
10 LiveConnect とは? できること Java のオブジェクトを JavaScript から操作できる その逆も 動作環境 Netscape3 以降( JavaScript1.1 )の仕様 Firefox, Opera で動作 使用できる条件 Java アプレットの実行が許可されているとき
11 (3) Flash の TextField.getFontList() を利用 var user_fonts = TextField.getFontList(); user_fonts.sort(); getURL('javascript:fontList("‘ + escape(user_fonts) + ‘“)’, '_self'); こんな感じで.as ファイルを作って getFontList.swf を作る
でも、いちいち swf ファイルを 作るの面倒だよなぁ … Flash ( ActionScript )で eval 使えたっけ?
13 SWF Binary Hacks ~ eval のいばら道 ~ 斜め下から始める 初めての Flash 斜め下から始める 初めての Flash
14 FlashProxy.swf で Flash 関数を動的実行 Collection & Copy - JavaScript 用 Flash プロキ シ、 Javascript Sound Kit ( id:brazil さん作) Sound.methods = "loadSound start stop getId3 getPan setPan getTransform setTransform getVolume setVolume getDuration setDuration getPosition setPosition getBytesLoaded getBytesTotal".split(" "); function Sound(){ return new FlashProxy("Sound", Sound.methods); } var sound = new Sound(); : みたいな感じで JavaScript から Flash の関数を動的に呼び出すことができる
15 FlashProxy.as のソースコード( id:brazil さ ん) import flash.external.ExternalInterface; class FlashProxy{ static var proxy; var target; function FlashProxy() { this.target = new (eval(_root.className)); ExternalInterface.addCallback("dispatch", this, dispatch); ExternalInterface.addCallback("addListener", this, addListener); } function dispatch(prop, args) { var obj = this.target[prop]; if (obj instanceof Function) { return obj.apply(this.target, args); } return obj; } function addListener(event) { this.target[event] = function(){ ExternalInterface.call("FlashProxy.onTrigger", _root.id, event, arguments); } static function main(mc) { proxy = new FlashProxy(); }
16 お、 Flash で も eval できる?
17
18 なんか Flash にはいろいろ制限があるみたい import flash.external.ExternalInterface; class FlashProxy{ static var proxy; var target; function FlashProxy() { this.target = new (eval(_root.className)); ExternalInterface.addCallback("dispatch", this, dispatch); ExternalInterface.addCallback("addListener", this, addListener); } function dispatch(prop, args) { var obj = this.target[prop]; if (obj instanceof Function) { return obj.apply(this.target, args); } return obj; } function addListener(event) { this.target[event] = function(){ ExternalInterface.call("FlashProxy.onTrigger", _root.id, event, arguments); } static function main(mc) { proxy = new FlashProxy(); } この方法だと TextField.getAllFonts() や System.setClipboard みたいな スタティック関数の呼び出しができな い (インスタンスを直接生成できない abstract クラスのメソッド)
19 次のバージョンでは eval 自体廃止されちゃうし
20 JavaScript から Flash の スタティック関数を eval っぽく実行したい!
21 例えば Ruby の場合 … 3つの eval 関数があるのに! 1. ふつうの eval 2. Object#instance_eval 3. Module#module_eval/class_eval → 空前のメタプログラミング期ブーム
22 それ Flashy.swf でできるよ
23 それ Flashy.swf 使えばできるよ Javascript から Flash の任意の静的関数を 呼び出すことができる var flash = document.getElementById('flashy'); flash.setStatic('flash.system.IME.enabled', true); // IME オン Labs: JavaScript から Flash の便利な機能を使う方法
24 Flashy.as のソースコード(奥一穂さん) package { import flash.display.*; import flash.external.ExternalInterface; import flash.utils.getDefinitionByName; public class Flashy extends Sprite { public function Flashy() { ExternalInterface.addCallback("getClass", getClass); ExternalInterface.addCallback("getStatic", getStatic); ExternalInterface.addCallback("setStatic", setStatic); ExternalInterface.addCallback("callStatic", callStatic); } public function getClass(expr:String):Object { return getDefinitionByName(expr); } public function getStatic(expr:String):Object { var m:Object = expr.match(/^(.*)\.(.*?)$/); return this.getClass(m[1])[m[2]]; } public function setStatic(expr:String, value:Object):void { var m:Object = expr.match(/^(.*)\.(.*?)$/); this.getClass(m[1])[m[2]] = value; } public function callStatic(expr:String, args:Array):Object { var m:Object = expr.match(/^(.*)\.(.*?)$/); return this.getClass(m[1])[m[2]].apply(m[1], args); } スタティック関数の呼び出しを動的に (インスタンスを直接生成できない abstract クラスのメソッドも呼べ る)
Flash の関数を呼ぶだけじゃなく AS のコードを直接実行してみたい eval 使えないけど、どうする???
26 JavaScript で SWF を動的生成すればできる よ! <embed width="0px" height="0px" type="application/x-shockwave-flash" flashvars= " param=val" src="data:application/x-shockwave-flash;base64, Q1dTCbwDAAB42l1SQW/TMBS23a5uu5VtGqqGEFKlTaqE1CbtDmhVFzG1KxoHi uCCkCbqOm5j5iaR4y7dATEu/AROO8PP4IzEoTvwBzhx4R9wwE7G2mFF8nufv+ /zy3uegexPAO58BmAbgu7mPQDA+61vEIC2dEetF91eZTYRftTS2UHVUypsWVY cx/V4rx7IsdXY39+37KbVbNY0oxad+4rMan60U3USgy6LqOSh4oFfMTkZBlN1 UK1eu7r0xjScSpFYutRigk2YryKrUW9oI5e2RoGcEOWQMBScEmNnzWqRF9DTm Jyx2kiQyGtbC6LRKK4Ecw7dYMgqPcFmlWblcKFP2CnFkN1Foc7SbxKjrtNgYo UycKdU1zTSVol4WWIswulQ8Mhj0pn6p34Qp1csUMOhkhEV3Gb8w8y5IP54Ssb MOXqWnN3kSY1EMadp248se89q7qVFGKxt/dfra0SPzwHdjV+ZNuigj5dfXhf1 YJOFwKc3KIl+X95/t6nh78Uel5HqcEkFA1/vftCYYY8kmbAG2AB1gC4u/mCgw 9Ulainpfd3lUSjIee5lKLli+bEkocdpVBiyMfd7XIiiK0mcSjDzXQPliet2PC 7cXH/4llG1llqxMzP79SOzdbUtUVS3r9RNb0ipm8e+YpJQxc9YipRvnXcC/RC 5z+RKGZYz26i8CkoYIoyyGK5gmMMQY5jHsIBhEWfXMCphdAej9QxYXhCiQibp AkQQZiHQCcwXMnP7iYayqFBszO3KBfoxPwYn6CkCOrwaZWqwn4V6333+cLfdX 8nosJ8DJ/iqj6EWIu2V35nbDAzy9qBgD4r2YNUerNkDpL9X4EGyPEPd2DKXLw /msc7/AuHTEs4=">
27 URI data: スキームで GIF 画像を動的に生成 GIF 画像を base64 でエンコード( RFC2397 ) var data = ‘data:image/gif;base64,’+ ‘R0lGODlhAAEwAMQAAJ2M5Me98GRK1DoYyYBr3PHv ・・・(中 略)・・・ Pe99XO81Y50auc6PBkZEgpzbmt7HJa2I57CffgnMNqmWHAWNBwwGsKp KsrmJqltOOV69nuYxSkqpoTata18rWtrr1rTIIAQA7'; var icon_elem = document.getElementById("icon_here"); icon_elem.src = data; 残念ながら IE では動作しませんが・・・ Firefox, Opera で動作
28 GIF SWF
29 SWF ファイルフォーマットの仕様書 Adobe Player Licensing から入手可能 メールアドレス( Adobe ID )を登録 ライセンスに同意する必要がある Flash6 以降の情報のみ Alexis' SWF Reference Flash6 以前の情報についても書いてある 2001 年からの蓄積
30 Alexis' SWF Reference ::::
31 SWF File Header, SWF Tags … struct swf_header { unsigned charf_magic[3];'FWS' or 'CWS' unsigned charf_version; unsigned longf_file_length; } struct swf_header_movie { swf_rectf_frame_size; unsigned short fixedf_frame_rate; unsigned shortf_frame_count; }; struct swf_csmtextsettings { swf_tagf_tag;/* 74 */ unsigned shortf_text_id_ref; unsignedf_use_flag_type : 2; unsignedf_grid_fit : 3; unsignedf_reserved : 3; long floatf_thickness; long floatf_sharpness; unsigned charf_reserved; };
32 SWF ファイルを DIS アセンブル しながら勉強したい …
33 swfdump コマンドで DIS アセンブル SWFTOOLS の swfdump コマンドを使うと SWF ファイルの内容をダンプすることができ る > swfdump --full FlashProxy.swf [HEADER] File version: 8 [HEADER] File is zlib compressed. Ratio: 63% [HEADER] File size: 796 (Depacked) [HEADER] Frame rate: [HEADER] Frame count: 1 [HEADER] Movie width: [HEADER] Movie height: [009] 3 SETBACKGROUNDCOLOR (ff/ff/ff) [027] 4 DEFINESPRITE defines id [000] 0 END [038] 21 EXPORTASSETS exports as "__Packages.MTASC" [03b] 643 DOINITACTION adds information to id ( 206 bytes) action: Constantpool(22 entries) String:"FlashProxy" String:"_global" String:"target" String:"_root" String:"className" String:"dispatch" String:"flash" String:"external" String:"ExternalInterface" String:"addCallback" String:"addListener" String:"prototype" String:"Function" String:"apply" String:"event" String:"arguments" String:"id" String:"FlashProxy.onTrigger" String:"call" String:"main" String:"proxy" String:"ASSetPropFlags" ( 2 bytes) action: Push Lookup:0 ("FlashProxy") ( 0 bytes) action: GetVariable ( 0 bytes) action: Not ( 2 bytes) action: If 418
34 swfdump はアクションレコードの抽出が苦手 : ( 8 bytes) action: unknown[8e] (remainder of 8 bytes:"\0\0\0\2)\0p\0") : アクションレコードの抽出に失敗 … orz
35 SWF::Parser (CPAN) なら・・・ dumpswf.plx dumpswf.plx Flash.swf > Flash.pl Flash.swf そのものを生成する Perl スクリプト「 Flash.pl 」を生成
36 Sothink SWF Decompiler (試用期間 30 日)
37 DIS アセンブル結果( Sothink SWF Decompiler )
38 しかし、 URI データスキームに 埋め込みの SWF は Flash Player 9 では動かない …orz Flash Player 8 では動いたのに(><)
Flash 上で動く ECMAScript の処理系 があればいいじゃね? Flash Lite 職人である鴨志田さんに聞いてみると そういう処理系があるらしい!
40
41 ScriptEngine.swf の特徴 バイトコードインタプリタ 一旦スクリプトをバイトコード的なものに変換し てから独自 VM 上で実行 ECMA-262 3rd Edition にほぼ準拠 +いくつかの拡張(コルーチンなど) ActionScript1 的な感じで記述が可能 詳しくは Web で
42 Xelf (ゼルフ)の開発者 – 新藤さん(高校 3 年 生) 名前の由来: Flex の逆で xleF ゲームを製作しやすくし ようプロジェクトの一環 として、 ECMAScript + α の処理系を Flash(AS) 上に 実装 期待 age
43 まとめ
44 Flash で eval っぽいことをするには? JavaScript でできないことをプラグインで Java アプレット + LiveConnect ( IE では使えな い) Flash 使えばいろいろできる(クロスブラウザ) JavaScript から Flash のコードを実行したい ActionScript で任意の関数を呼ぶ方法 FlashProxy.swf ( id:brazil さん) Flashy.swf (奥一穂さん) SWF ファイルの動的生成( URI データスキーム) Flash Player 9 以降ではもう使えない技 … orz Flash 上で動く ECMAScript + α の処理系あるよ これ最強! 新藤さん GJ
45 ご清聴ありがとうございました Special Thanks to: CybozuLabs 奥さん、鴨志田さん