SWF研究の第2弾として、内部構造が大体理解できたので、比較的簡単そうな「jpeg」データの取得を行なってみたいと思います。
SWFで使用できる画像は主にjpeg、GIF,PNGの3つ(※BMPも使えたっけ?)、まあ、WEBで使う画像としてもこの3つなので、
これをコントロールできる事が、実践の第一歩であると考えてます。
前回の
SWFファイルの情報取得のソースを流用して、内部に格納されているJPEGデータを吸い出してJPEGファイルを作成してみたいと思います。
ちなみに、yoyaさんをリスペクトしてPHPコマンドで行なってみたいと思います。
※他言語へのローカライズは別の機会に行いたいと思います。
ソース
getImage.php
ソースの冒頭に簡単な仕様記述をしておきました。
<?php
date_default_timezone_set('Asia/Tokyo');
/**
* Images of swf-file to pick-up.
* write : yugeta
* date : 2016/4/29
*
* [sample]
* getImage.php f=../swf/hoge.swf d=true
*
* [specification]
* param @ f : target-file (*must)
* param @ d : view dump [true / false(*default)]
**/
//CLIの場合argvをREQUESTに変換する。
if(isset($argv)){
for($i=1,$c=count($argv);$i<$c;$i++){
if(!$argv[$i]){continue;}
//各クエリの分解
$q = explode("=",$argv[$i]);
if(count($q)<2){continue;}
if($q[0]!=''){
//requestに格納
$key = $q[0];
$val = join("=",array_slice($q,1));
$_REQUEST[$key]=$val;
}
}
}
// File-check
if(!$_REQUEST["f"] || !is_file($_REQUEST["f"])){
echo "Error ! : not-file".PHP_EOL;
exit(0);
}
// Init
require_once "BitReader.php";
$swfdata = file_get_contents($_REQUEST["f"]);
$reader = new BitReader;
$reader->input($swfdata);
$outputPath = "output/".date("YmdHis").".".explode(" ",microtime())[1];
mkdir($outputPath,0777,true);
file_put_contents($outputPath."/".basename($_REQUEST["f"]) , $swfdata);
// Get header
$signature = $reader->getData(3);
$version = $reader->getUI8();
$fileLength = $reader->getUI32LE();
$NBits = $reader->getUIBits(5);
$xmin = ($reader->getUIBits($NBits) / 20);
$xmax = ($reader->getUIBits($NBits) / 20);
$ymin = ($reader->getUIBits($NBits) / 20);
$ymax = ($reader->getUIBits($NBits) / 20);
$reader->byteAlign();
$frameRate = ($reader->getUI16LE() / 0x100);
$frameCount = $reader->getUI16LE();
// SWF Tags
$imgCnt=0;
$tagsView = array();
array_push($tagsView, 'Tag:'.PHP_EOL);
while (true) {
$startOffset = $reader->_byte_offset;
$tagAndLength = $reader->getUI16LE();
$type = $tagAndLength >> 6;
$length = $tagAndLength & 0x3f;
if ($length == 0x3f) {
$length = $reader->getUI32LE();
}
$contents = $reader->getData($length);
array_push($tagsView, "\t"."type: $type length: $length tagAndLength: $tagAndLength");
array_push($tagsView, "\t(".$startOffset." / ".$reader->_byte_offset." / ".($reader->_byte_offset-$startOffset).")");
array_push($tagsView, PHP_EOL);
// END Tag
if ($type == 0) {break;}
// JPEG -----
// getImage (jpeg)
else if($type == 21){
$img = LIB::data2jpeg($contents);
file_put_contents($outputPath."/img_".$imgCnt."_type_".$type.".jpg", $img);
$imgCnt++;
}
}
if($_REQUEST["d"]=="true"){
echo 'Signature: '.$signature.PHP_EOL;
echo 'Version: '.$version.PHP_EOL;
echo 'FileLength: '.$fileLength.PHP_EOL;
echo 'FrameSize: '.PHP_EOL;
echo "\tXmin: ".$xmin.PHP_EOL;
echo "\tXmax: ".$xmax.PHP_EOL;
echo "\tYmin: ".$ymin.PHP_EOL;
echo "\tYmax: ".$ymax.PHP_EOL;
echo 'FrameRate: '.$frameRate.PHP_EOL;
echo 'FrameCount: '.$frameCount.PHP_EOL;
echo join("",$tagsView);
}
// finish
echo "Finished !!".PHP_EOL;
exit(0);
// class-library
class LIB{
// type:--
function data2img($contents){
return substr($contents, 4, strlen($contents)-4);
}
// type:20 gif
function data2gif_rgb($contents){
$image_id = ord(substr($contents, 0, 2));
$compossed = ord(substr($contents, 2, 1));
$width = substr($contents, 3, 2);
$height = substr($contents, 5, 2);
//$uncomp = gzuncompress(substr($contents, 8, strlen($contents)-8));
//color-table
$colorTable = "";
$offset = "";
//data Bitmap-Format(compossed)==3 only
if($compossed==3){
$colortable_num = ord(substr($contents, 7, 1));
$cnt = (($colortable_num+1)*3);
for($i=0;$i<=255;$i++){
if($i<=$colortable_num){
$colorTable.= substr($contents, 8+($i*3), 3);
}
else{
$colorTable.= pack("h",0).pack("h",0).pack("h",0);
}
}
//$colorTable = substr($contents, 8, $cnt);
$cnt+=9;
$pixeldata = substr($contents, $cnt, strlen($contents) - $cnt);
}
else{
$colortable_num = 0;
$cnt = 7;
//$pixeldata = gzuncompress(substr($contents, $cnt, strlen($contents) - $cnt));
$pixeldata = substr($contents, $cnt, strlen($contents) - $cnt);
}
// Header --
$data = "GIF"."87a";
$data.= pack("v",ord($width)).pack("v",ord($height));
$data.= pack("h","1");//Global Color Table Flag(1 Bit)
$data.= pack("h","7").pack("h","7").pack("h","7");//Color Resolution(3 Bits)
$data.= pack("h","0");//Sort Flag(1 Bit)
$data.= pack("h","7").pack("h","7").pack("h","7");//Size of Global Color Table(3 Bits)
$data.= pack("h",0);//Background Color Index(1 Byte)
$data.= pack("h",0);//Pixel Aspect Ratio(1 Byte)
$data.= $colorTable;
// Image --
$data.= pack("n",0x2C);//Image Separator(1B)
$data.= pack("v",0).pack("v",0);//Image Left,Top Position(2B,2B)
$data.= pack("v",ord($width)).pack("v",ord($height));//Image Width,Height(2B,2B)
$data.= pack("h",0);//Local Color Table Flag(1b)
$data.= pack("h",0);//Interlace Flag((1b)
$data.= pack("h",0);//Sort Flag(1b)
$data.= pack("v",0);//Reserved(2b)
//$data.= pack("h",0).pack("h",0).pack("h",0);//Size of Local Color Table(3b)--
$data.= pack("h",0);//LZW Minimum Code Side(1 Byte)
//$data.= pack("h",strlen($pixeldata));//Block Size(1 Byte)
$data.= pack("h",0);//Block Size(1 Byte)
$data.= $pixeldata;
//$data.= pack("n",0x00);//image-end
//Tailer
$data.= pack("n",0x3B);//end
return $data;
}
// type:20 gif
function data2gif_rgba($contents){
return substr($contents, 6, strlen($contents)-6);
}
// type:21 jpeg2
function data2jpeg($contents){
$offset = 4;
return substr($contents, $offset, strlen($contents)-$offset);
}
//type 20 -> png
function data2png($contents){
$image_id = ord(substr($contents, 0, 2));
$compossed = ord(substr($contents, 2, 1));
$width = substr($contents, 3, 2);
$height = substr($contents, 5, 2);
$colortable_num = ord(substr($contents, 7, 1));
$cnt = (($colortable_num+1)*3);
$colorTable = substr($contents, 8, $cnt);
$cnt+=9;
$pixeldata = substr($contents, $cnt, strlen($contents) - $cnt);
//--
$data = pack("n",0x89).pack("n",0x50).pack("n",0x4E).pack("n",0x47);
$data.= pack("n",0x0D).pack("n",0x0A).pack("n",0x1A).pack("n",0x0A);
$dataPart = substr($contents, 8, strlen($contents)-8);
$data.= pack("h",strlen($dataPart));
$data.= pack("h","aaaa");
$data.= $dataPart;
$data.= "";
return $data;
}
}
BitReader.php
今回もyoyaさんのソースを流用してバイナリ読み込みを行なってます。
<?php
class BitReader {
var $_data; // input_data
var $_byte_offset;
var $_bit_offset;
function input($data) {
$this->_data = $data;
$this->_byte_offset = 0;
$this->_bit_offset = 0;
}
function byteAlign() {
if ($this->_bit_offset > 0) {
$this->_byte_offset ++;
$this->_bit_offset = 0;
}
}
function getData($length=1) {
$this->byteAlign();
$data = substr($this->_data, $this->_byte_offset, $length);
$data_len = strlen($data);
$this->_byte_offset += $data_len;
return $data;
}
function getUI8() {
$this->byteAlign();
$value = @ord($this->_data{$this->_byte_offset});
$this->_byte_offset += 1;
return $value;
}
function getUI16LE() {
$this->byteAlign();
$ret = @unpack('v', substr($this->_data, $this->_byte_offset, 2));
$this->_byte_offset += 2;
return $ret[1];
}
function getUI32LE() {
$this->byteAlign();
$ret = @unpack('V', substr($this->_data, $this->_byte_offset, 4));
$this->_byte_offset += 4;
return $ret[1];
}
function getUIBit() {
$value = @ord($this->_data{$this->_byte_offset});
$value = 1 & ($value >> (7 - $this->_bit_offset));
$this->_bit_offset ++;
if (8 <= $this->_bit_offset) {
$this->_byte_offset++;
$this->_bit_offset = 0;
}
return $value;
}
function getUIBits($width) {
$value = 0;
for ($i = 0 ; $i < $width ; $i++) {
$value <<= 1;
$value |= $this->getUIBit();
}
return $value;
}
function getImage() {
$this->byteAlign();
$ret = @unpack('v', substr($this->_data, $this->_byte_offset, 2));
//$this->_byte_offset += 2;
//return $this->_data;
//return join(",", $ret);
return $this->_byte_offset;
//return $ret[1];
}
}
実行
jpegデータの格納されているSWFファイルを利用してください。
※GIF、PNGは無視されます。
$ php getImage.php sample.swf
上記を実行すると「output」というフォルダを自動で作成して、実行IDフォルダ、対象SWFファイルのコピー
そして内部に格納されているJPEGデータが存在する分だけ作成されます。
output/
20160505000000/
sample.swf
img_1.jpg
img_2.jpg
とりあえず、Jpegは簡単だということはわかりましたが、GIF、とPNGはかなりの難題であることも同時に理解できました。
しばらくは画像フォーマットのSDKとにらめっこしなければいけないようです・・・orz
参考リンク
SWFバイナリ編集のススメ第三回 (JPEG)
0 件のコメント:
コメントを投稿