光の天使スクリーンセーバーから動画部分のみを取り出したい 備忘録その1

その2があるか解らないけど、とりあえず備忘録その1。
Objective-C、SWFファイルフォーマット、ActionScript、Zlib辺りの知識が必要になりそうな。
 

概要

fla:verを使ってる

fla:verを使って、Flashファイルからスクリーンセーバーを生成していた。
仕組みは、WebViewでResources/data.pacに入ってるSWFファイルを再生しているっぽい。
 
光の天使とは関係ないけど、このfla:verをビルドしているのはユーザ名「maruyama」。
fla:verを作成している株式会社シリアルゲームズの社員紹介を見ると丸山国明さんという方がいらっしゃるようなので、おそらくこの方が製作者もしくはリリース者。
 
fla:ver -スクリーンセーバー開発作成ツール フレーバー
http://flaver.jp/
 
株式会社シリアルゲームズ|ソーシャルゲーム/スマートフォンアプリ/ゲームサーバ/WEBサービス開発
http://www.serialgames.co.jp/
 

メモリリークしてる

システム環境設定でスクリーンセーバ選択画面を表示し続けているとFlash Playerのメモリ使用量がどんどん増えていく。
fla:verが悪いのかSWFファイルが悪いのかは不明。
 

HTTP

User-Agent

認証の為のHTTPリクエストは、User-Agentを見るにWebViewからリクエストされているっぽい?
ただ、fla:ver試用版を使った限り、HTTPリクエストをして認証する機能はついていない。
かと言ってActionScriptからのリクエストっぽくもない。

 @header=
  {"host"=>["www.hikarinotenshi12.jp"],
   "accept"=>["*/*"],
   "accept-language"=>["ja-jp"],
   "connection"=>["keep-alive"],
   "accept-encoding"=>["gzip, deflate"],
   "user-agent"=>
    ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.73.11 (KHTML, like Gecko)"]},

 

ソケットポリシーファイル

843/tcpに対してソケットポリシーファイルを取りに行っている様子はない。
そもそもソケットポリシーファイルはActionScriptでSocketを使う時に必要で、HTTPRequestするのには必要ないんだっけ?
忘れた。
 

Cocoa

Objective-Cの他にC++も使われているようだ。
いわゆるObjective-C++ってやつ。
 

使用されているフレームワークやライブラリ、ソースファイル等
/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa
/System/Library/Frameworks/ScreenSaver.framework/Versions/A/ScreenSaver
/System/Library/Frameworks/Carbon.framework/Versions/A/Carbon
/System/Library/Frameworks/Security.framework/Versions/A/Security
/System/Library/Frameworks/WebKit.framework/Versions/A/WebKit
/usr/lib/libz.1.dylib
/usr/lib/libstdc++.6.dylib
/usr/lib/libgcc_s.1.dylib
/usr/lib/libSystem.B.dylib
/usr/lib/libobjc.A.dylib
/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
/System/Library/Frameworks/ApplicationServices.framework/Versions/A/ApplicationServices
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation
/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit
/Users/maruyama/work/devel/Flaver/FlashScreenSaver/src/FlashSaverUtils.mm
/Users/maruyama/work/devel/Flaver/FlashScreenSaver/src/FlashScreenSaverView.mm
/Developer/SDKs/MacOSX10.6.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSGeometry.h
/Developer/SDKs/MacOSX10.6.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSRange.h
/Users/maruyama/work/devel/Flaver/FlashScreenSaver/src/FSSWindow.m
/Users/maruyama/work/devel/Flaver/FlashScreenSaver/src/SGClickableImageView.m
/Users/maruyama/work/devel/Flaver/FlashScreenSaver/src/SGClickableTextField.m
/Users/maruyama/work/devel/Flaver/FlashScreenSaver/src/SGMG2Codec.mm
/usr/lib/libmx.A.dylib
/Users/maruyama/work/devel/Flaver/FlashScreenSaverPPC/src/FlashSaverUtils.mm
/Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFByteOrder.h
/Users/maruyama/work/devel/Flaver/FlashScreenSaverPPC/../FlashScreenSaver/src/FlashScreenSaverView.mm
/Users/maruyama/work/devel/Flaver/FlashScreenSaverPPC/src/FSSWindow.m
/Users/maruyama/work/devel/Flaver/FlashScreenSaverPPC/src/SGClickableImageView.m
/Users/maruyama/work/devel/Flaver/FlashScreenSaverPPC/src/SGClickableTextField.m
/Users/maruyama/work/devel/Flaver/FlashScreenSaverPPC/src/SGMG2Codec.mm

 

宣言されているメソッド
+[FlashSaverUtilsPro14 GetURLReqByPath:]
+[FlashSaverUtilsPro14 GetResDirectory:]
+[FlashSaverUtilsPro14 CreateOutputDirectory:]
+[FlashSaverUtilsPro14 CreateFixedOutputDirectory:]
+[FlashSaverUtilsPro14 CreateFixedOutputDirectory:extentionFlag:]
+[FlashSaverUtilsPro14 RemoveFileFromArray:]
+[FlashSaverUtilsPro14 OpenSettingPList:]
+[FlashSaverUtilsPro14 GetSwfStageRect:]
+[FlashSaverUtilsPro14 GetSwfRect:]
-[FSV_915233D19F2543E045B67F0ABE91F22A(local) createFlashView:preview:]
-[FSV_915233D19F2543E045B67F0ABE91F22A(local) unloadFlashMovie]
-[FSV_915233D19F2543E045B67F0ABE91F22A(local) loadBlank]
-[FSV_915233D19F2543E045B67F0ABE91F22A(local) webView:identifierForInitialRequest:fromDataSource:]
-[FSV_915233D19F2543E045B67F0ABE91F22A(local) webView:resource:didFinishLoadingFromDataSource:]
-[FSV_915233D19F2543E045B67F0ABE91F22A(local) viewViewTree:indent:]
-[FSV_915233D19F2543E045B67F0ABE91F22A(local) webView:resource:didFailLoadingWithError:fromDataSource:]
-[FSV_915233D19F2543E045B67F0ABE91F22A(local) webView:plugInFailedWithError:dataSource:]
-[FSV_915233D19F2543E045B67F0ABE91F22A(local) webView:decidePolicyForNavigationAction:request:frame:decisionListener:]
-[FSV_915233D19F2543E045B67F0ABE91F22A(local) webView:decidePolicyForNewWindowAction:request:newFrameName:decisionListener:]
-[FSV_915233D19F2543E045B67F0ABE91F22A(local) webView:decidePolicyForMIMEType:request:frame:decisionListener:]
-[FSV_915233D19F2543E045B67F0ABE91F22A(local) webView:createWebViewWithRequest:]
-[FSV_915233D19F2543E045B67F0ABE91F22A(local) webView:contextMenuItemsForElement:defaultMenuItems:]
-[FSV_915233D19F2543E045B67F0ABE91F22A(local) getFlashVersion:]
-[FSV_915233D19F2543E045B67F0ABE91F22A initWithFrame:isPreview:]
-[FSV_915233D19F2543E045B67F0ABE91F22A dealloc]
-[FSV_915233D19F2543E045B67F0ABE91F22A setFrameSize:]
-[FSV_915233D19F2543E045B67F0ABE91F22A startAnimation]
-[FSV_915233D19F2543E045B67F0ABE91F22A stopAnimation]
-[FSV_915233D19F2543E045B67F0ABE91F22A drawRect:]
-[FSV_915233D19F2543E045B67F0ABE91F22A animateOneFrame]
-[FSV_915233D19F2543E045B67F0ABE91F22A isRightClickMenu]
-[FSV_915233D19F2543E045B67F0ABE91F22A isIgnoreKey]
-[FSV_915233D19F2543E045B67F0ABE91F22A setFinishSaver]
-[FSV_915233D19F2543E045B67F0ABE91F22A sendMouseMoved]
-[FSV_915233D19F2543E045B67F0ABE91F22A hasConfigureSheet]
-[FSV_915233D19F2543E045B67F0ABE91F22A uninstallSaver:]
-[FSV_915233D19F2543E045B67F0ABE91F22A sheetCancelDidEnd:returnCode:contextInfo:]
-[FSV_915233D19F2543E045B67F0ABE91F22A uninstallAllUserSaver:]
-[FSV_915233D19F2543E045B67F0ABE91F22A sheetCancelDidDismiss:returnCode:contextInfo:]
-[FSV_915233D19F2543E045B67F0ABE91F22A closeSheet:]
-[FSV_915233D19F2543E045B67F0ABE91F22A mouseMoved:]
-[FSV_915233D19F2543E045B67F0ABE91F22A mouseEntered:]
-[FSV_915233D19F2543E045B67F0ABE91F22A mouseExited:]
-[FSV_915233D19F2543E045B67F0ABE91F22A mouseDown:]
-[FSV_915233D19F2543E045B67F0ABE91F22A mouseDragged:]
-[FSV_915233D19F2543E045B67F0ABE91F22A mouseUp:]
-[FSV_915233D19F2543E045B67F0ABE91F22A scrollWheel:]
-[FSV_915233D19F2543E045B67F0ABE91F22A rightMouseDown:]
-[FSV_915233D19F2543E045B67F0ABE91F22A rightMouseUp:]
-[FSV_915233D19F2543E045B67F0ABE91F22A keyDown:]
-[FSV_915233D19F2543E045B67F0ABE91F22A keyUp:]
-[FSV_915233D19F2543E045B67F0ABE91F22A flagsChanged:]
-[FSV_915233D19F2543E045B67F0ABE91F22A configureSheet]
-[FSV_915233D19F2543E045B67F0ABE91F22A(local) loadFlashMovie]
-[FSSWindow becomeKeyWindow]
-[FSSWindow setConfigWindow:]
-[SGClickableImageView12a dealloc]
-[SGClickableImageView12a SetClickable]
-[SGClickableImageView12a resetCursorRects]
-[SGClickableImageView12a SetLinkedURL:cursorImage:]
-[SGClickableImageView12a SetUsingSafari:]
-[SGClickableImageView12a mouseDown:]
-[SGClickableTextField12a initWithCoder:]
-[SGClickableTextField12a initWithFrame:]
-[SGClickableTextField12a dealloc]
-[SGClickableTextField12a isOpaque]
-[SGClickableTextField12a drawRect:]
-[SGClickableTextField12a getStringViewSize]
-[SGClickableTextField12a mouseEntered:]
-[SGClickableTextField12a mouseExited:]
-[SGClickableTextField12a mouseDown:]
-[SGClickableTextField12a SetLinkString:url:cursorImage:]
-[SGClickableTextField12a setStringValue:]
-[SGClickableTextField12a resetCursorRects]
+[SGMG2Codec encode:]
+[SGMG2Codec decode:]
+[SGMFile(local) getCStringTempDirPath:]
+[SGMFile file]
-[SGMFile init]
-[SGMFile dealloc]
+[SGMFile getTempDirPath]
+[SGMFile remove:]
+[SGMFile getFileType:]
+[SGMFile isFileType:path:]
+[SGMFile createTempDirectory:]
+[SGMFile getPropertiesDirectory]
+[SGMFile checkDirectory:create:]
-[SGMFile openTempFile:]
-[SGMFile write:]
-[SGMFile close]
-[SGMIgnoreRightClick init]
-[SGMIgnoreRightClick initWithTarget:]
-[SGMIgnoreRightClick dealloc]
-[SGMIgnoreRightClick getEventTime]
-[SGMIgnoreRightClick setEventTime:]
-[SGMIgnoreRightClick setTargetView:]
-[SGMIgnoreRightClick getTargetView]
-[SGMIgnoreRightClick isIgnore]
-[SGMIgnoreRightClick start]
-[SGMIgnoreRightClick stop]

 

FSV_915233D19F2543E045B67F0ABE91F22Aクラス

メソッド名から察するに、おそらくNSViewもしくはWebViewを継承している。
「FSV」はFlashScreensaverViewの略だろうか。
「915233D19F2543E045B67F0ABE91F22A」部分はスクリーンセーバー毎に生成されるようだ。
16進数32桁という事はMD5だろうか。
 
クラスの宣言はこんな感じだろうか。

@interface FSV_915233D19F2543E045B67F0ABE91F22A : NSView <WebResourceLoadDelegate, WebPolicyDelegate, WebUIDelegate>

 

Contents/Resources/data.pac

fla:verの試用版を使い、自分でもスクリーンセーバーを作って比較しつつ確認した。
 
このファイル全体は、暗号化や圧縮はされていない可変長のアーカイブファイルという事が解った。
ただ、tarやzipのようなメジャーなものではなさそう。
 
暗号化も圧縮もされていないので、CWS形式のSWFファイルのヘッダ「CWS」のindexを探してそこからバイナリをsplitする事で元のSWFと完全一致するバイナリデータを抽出出来た。
 
元のSWFファイルがある場合はいいが、今回は元のSWFファイルがない為、ファイルの終端がどこにあるか解らない。
その為、SWFファイルをCWSからFWSへZlib解凍を試みて、正しく解凍出来た箇所をファイル終端とする事にした。
しかし、終端を1バイトずつ削っていって全パターン試したが、どこで区切っても正しく解凍出来ない。
SWFファイルに問題があるか、よしだの頭がてんてこまいなのかのいずれかだと思われる。
 

SWFファイル抽出

そもそも「incorrect header check」と出るので終端の位置の問題ではないのかも知れない。
でも自分で作ったスクリーンセーバーでは正しく動作するので、少なくとも基本的な部分は間違っていなそう。

#!/usr/bin/env ruby

require 'zlib'

bin = File.open("Resources/data.pac"){|f| f.read}
bin.force_encoding("ASCII-8BIT")

# data.pacからSWFファイルの開始場所を探す
start = bin.index("CWS")
puts "CWS index: #{start}"
puts "max swfsize: #{bin.length - start}"

# 最大長のSWFファイル(CWS形式)を書き出す
cwf = bin[start..-1]
File.open("data_cws.swf", "w"){|f| f.write(cwf)}

# SWFバージョン
puts "SWF Version: #{cwf[3].ord}"

# SWFの解凍後のファイルサイズ
puts "SWF FileLength: #{cwf[4..7].unpack("V")[0]}"

# SWFファイルをCWSからFWSへ解凍
zbin = cwf[8..-1]
fws = Zlib::Inflate.inflate(zbin) # incorrect header check (Zlib::DataError)
puts "FWS FileLength: #{fws.length + 8}"

# SWFファイル(FWS形式)を書き出す
File.open("data_fws.swf", "w"){|f| f.write("FWS" + cwf[3..7] + fws)}

 

Flash

SWFをFWSに解凍出来ないとなんとも。