SWFバイナリ編集のススメ第二回

こんにちは。メディア開発部のよやです。

バイナリ編集のタイトルにもかかわらず前回はバイナリの読み方で終わってしまいました。
すみません。今回は編集まで話しを進めます。

バイナリ編集の手始め

まず、バイナリデータを切り出し、プログラムで適切に決めた内部形式に保存して、
それを何も加工せず出力し、元と一致するバイナリデータが生成されるのを確認する所から始めます。

前回、BitReader クラスを使ってバイナリのデータを切り出しました。今回は、
バイナリデータを組み立てる BitWriter 機能が加わった IO_Bit クラスを利用します。

 pear channel-discover openpear.org
 pear install openpear/IO_Bit

swfcopy

前回の swfdump プログラムを改造する流れで説明します。

swfdump で行っていた

  • バイナリから切り出したデータをそのまま echo で表示

の処理をクラスに包み、swfdump のコードを入力ロジック(a)と表示ロジック(b)に分けて、かつ(a)を元に出力ロジック(c)を作成します。

  • (a) バイナリから切り出したデータをクラス変数に保存 (parse)
  • (b) クラス変数を echo で表示 (dump)
  • (c) クラス変数を元にバイナリとして組み立て (build)

UI_SWF

具体的なコードとしては、swfdump では、

$reader = new BitReader();
$reader->input($swfdata);
echo 'Signature: '.$reader->getData(3).PHP_EOL;
echo 'Version: '.$reader->getUI8().PHP_EOL;

としていたコードを以下のように書き換えます。

require_once 'IO/Bit.php';
class IO_SWF {
    var $_headers = array();
    function parse($swfdata) {
        $reader = new IO_Bit();
        $reader->input($swfdata);
        $this->_headers['Signature'} = $reader->getData(3);
        $this->_headers['Version'] = $reader->getUI8();
    }
    function dump() {
        echo 'Signature: '.$this->_headers['Signature'].PHP_EOL;
        echo 'Version: '.$this->_headers['Version'].PHP_EOL;
    }
    function build() {]
        $writer = new IO_Bit();
        $writer->putData($this->_headers['Signature']);
        $writer->putUI8($this->_headers['Version']);
    }

あとは単純作業で、残り全てのデータについて上記の対応を行うと以下のクラスが出来上がります。

 pear channel-discover openpear.org
 pear install openpear/IO_SWF

さて、IO_SWF を用いて SWFのバイナリを取り込み、加工せずにそれを組み立て直してみます。

require 'IO/SWF.php';
$swfdata = file_get_contents($argv[1]);
$swf = new IO_SWF();
$swf->parse($swfdata);
echo $swf->build();

実行

% php swfcopy.php orz.swf  > t.swf
% md5sum  orz.swf t.swf
337a4edee9590382133f8f66ff67259b  orz.swf
337a4edee9590382133f8f66ff67259b  t.swf

完璧に一致しました。

後は、クラス変数を書き換えてからバイナリを組み立てる事で、好きなようにバイナリをいじる事が出来ます。

背景色変更

試しに、IO_SWF を利用して背景色を変更するだけのプログラムを作成してみます。

背景色を表すタグは SetBackgroundColor (Tag Code=9) です。
8bit depth RGB で 3Byte のデータを保持するので、そこを書き換えます。

SetBackgroundColor入れ替え

タグ番号を指定して保持するデータを入れ替えるメソッドは以下のように実装できます。
IO_SWF の parse でタグのリストがクラス変数の $this->_tags に入るのを利用します。

class IO_SWF_Editor extends IO_SWF {
    // var $_tags; // protected
    function replaceTagContent($tagCode, $content) {
        foreach ($this->_tags as &$tag) {
            if ($tag['Code'] == $tagCode) {
                $tag['Length'] = strlen($content);
                $tag['Content'] = $content;
                 break;
            }
        }
    }
}

上のメソッドを利用して、引数で指定した色の値で入れ替えます。

$swfdata = file_get_contents($argv[1]);
$swf = new IO_SWF_Editor();
$swf->parse($swfdata);
$color = pack('CCC', $argv[2], $argv[3], $argv[4]);
$swf->replaceTagContent(9, $color);
echo $swf->build();
  • 実行
% php swfsetbgcolor.php  orz.swf  0 0 255 > blueorz.swf
% php swfsetbgcolor.php  orz.swf  0 255 0 > greenorz.swf
  • 実行前 SWF (orz.swf)

  • 実行後 SWF (blueorz.swf)

  • 実行後 SWF (greenorz.swf)

背景色が変わりました。

書き換える際の注意ですが、サイズが変わる書き換えを行う場合(例えば画像や音声のデータを入れ替える等)は、
tag の length と header の FileLength のフィールドもあわせて書き換える必要があります。
サイズフィールドの書き換えについては、次回のブログで取り上げます。

次回予告

次回は内部ID入れ替えと、画像の入れ替え処理についてお話します。
それでは失礼します。

Author: よや