uzullaの日記

本家:about等:Twitter
|

2008-05-01

ionCube PHP EncoderをつかってPHPアプリを暗号化した

| 02:04

仕事でionCube PHP Encoder(以下ipe)をつかってPHPアプリを暗号化したりした。

http://www.asial.co.jp/ioncube/

どんなもんかというと

<?php

phpinfo();
print "aaaa";
?>

こんなコードが

<?php //0035e
if(!extension_loaded('ionCube Loader')){$__oc=strtolower(substr(php_uname(),0,3));$__ln='/ioncube/ioncube_loader_'.$__oc.'_'.substr(phpversion(),0,3).(($__oc=='win')?'.dll':'.so');$__oid=$__id=realpath(ini_get('extension_dir'));$__here=dirname(__FILE__);if(strlen($__id)>1&&$__id[1]==':'){$__id=str_replace('\\','/',substr($__id,2));$__here=str_replace('\\','/',substr($__here,2));}$__rd=str_repeat('/..',substr_count($__id,'/')).$__here.'/';$__i=strlen($__rd);while($__i--){if($__rd[$__i]=='/'){$__lp=substr($__rd,0,$__i).$__ln;if(file_exists($__oid.$__lp)){$__ln=$__lp;break;}}}@dl($__ln);}else{die('The file '.__FILE__." is corrupted.\n");}if(function_exists('_il_exec')){return _il_exec();}echo('Site error: the file <b>'.__FILE__.'</b> requires the ionCube PHP Loader '.basename($__ln).' to be installed by the site administrator.');exit(199);
?>
4+oV55IZRKMmTbPhMHluPllc445Sxm8ZeFVUTPsiTmbh+dr/4gbJPHv6kgYB5bjOoCc544M4w8m0
G3821XHaRHeC5130ZUTq0DgWh7Q16oTatx18dsqJwuy0YTiLDSRvO8JQbwC96Sc8bYrbWZ9YE0eI
wQ/0+zSsKH2TfvCquFB+myYTukloO4UZNUGmzJPauFO6pBFoCKV+1m9l08MnqAK1oioqVyVQ4Ul9
r8Ts0K451guhjxt8+p76WwyVX8TFOvp7bbB2zw6WTzbXVKHdrnsdfMrjfl0SONyVAuj5zYfH4ga5
lq2Xss4XEpgCEZi6EFlvbleXc/GGunEr8uhrKcs9IZTzpnwVd1P0n0f8ipz3UoAlGDUBNwqfhDfn
cC5RmwrWVS91srMTQpH0DoRseiupxpr5mcPhAojdi8RHWfWQ2fP28MXy0QYHWAlvCgWt

こんなになる。

おまけで高速化も図られるらしい。

MACアドレスインストールサーバーを指定したりとか、ライセンスファイル方式をやったりできるらしい。

他の競合アプリにくらべて、安く(Zend Guard高杉)、なおかつ(個人的に試した限り)遜色ない。


アプリのデキ

UIはとても簡単でよい。

ソースのディレクトリと、出力先のディレクトリを指定したら後は実行するだけ。

オプションは色々あるが、特にいじらなくてもちゃんと暗号化はされる(当然か)


HTMLや画像ファイルなど、全部ごたまぜをsrcにしてOK(これはカイテキ)、指定の拡張子だけ(phpなど)変換され、指定されていない拡張子のファイルは単にコピーされる

出力先をhtdocsなどにしておくと、再デプロイが手軽ですね。FTPで直接アップロードする機能もあるらしい。


変換はかなり高速で、200ファイルは5秒くらいで処理される(キャッシュ効いているのかな?)。

ちょいといじる > スクランブル > テスト

もそんなに苦もなくできてステキ。


他の製品と比較すると

ZendGuardにくらべて単純なUIなので使いやすい。(ZGは色々できるのはいいが、UIがむずかしすぎる、どちらも体験版がでているので試してみると良い。俺はipeのほうがよかった)


サーバー側の設定

PHPに拡張を一つ入れる必要が有る。

ファイルをコピーして、php.iniに一行加えるだけなので、簡単。


一発で全部変換できるってのはムリ

一発で全部暗号化できるかというと、さすがに何も考えずにつくったPHPアプリの一括変換はムリ。

よく拡張子phpにした非phpなコンフィグファイル(リソースファイル)などを作ると思うが、これも暗号化処理されてしまうので問題が発生する。


ただ、リソースファイルは別途の暗号化が可能であり、特殊な関数で読み込める。この辺りまとめると

PHPアプリをプリコンパイルしたもの

・テキストなどを暗号化したもの

という二パターンが生成でき、拡張子にてどちらにするか指定することができる。


だから、phpという拡張子リソースファイルは、たとえばencなどという拡張子にでもして、それに合わせて全体のコードを書き換える必要が有る。


もちろん暗号化されたファイルはそのままfile()などで読み込むとダメなので、

function readencfile($filename){
	$res = false;
	if (function_exists('ioncube_read_file')) {
		$res = ioncube_read_file($filename);
		if (is_int($res)) $res = false;
	}
	else if ( ($fd = @fopen($filename, 'rb')) ) {
		$res = ($size = filesize($filename)) ? fread($fd, $size) : '';
		fclose($fd);
	}
	return $res;
}

こんな感じのコードを書いて、ioncube_read_fileという関数を用いて復号化して上げる。

基本的に、このリソースファイルの読み込み問題が暗号化で発生するほとんどの問題である。


ipeはSmartyに対応!などと唱っているが、様は上のようなコードをSmartyClassパッチすることで、Smartyテンプレートファイルを暗号リソースファイルにして動かせる、という意味であり、まあなんというか…言った物勝ちだなーとおもったw


そのほか、引っかかったところ

一部セッション周りがそのままではうまくうごかなかった、最適化の所為だろうか。

最初にob_start()を実行することでこの問題は解決出来た。(ちなみにZGでも同じ問題がでた、ZGとIPEは実装似てるんだろうな)


暗号化した後のデバッグは不可能だとおもったほうがよい。

エラーテキスト自体は普通なのだが、行番号が0になってしまいまったく追えない。(最初にスクランブルする作業では結構手間取った。)


ipeが悪いのか、なんなのか解らないが、Apacheが落ちることがあった。


感想

簡単だし、安いし、良いのではないでしょうか*1

*1:まあ、現在絶賛移植作業中なのだが

2008-04-23

PHPerになりつつある

| 00:45

転職(?)して約1ヶ月経ちました。

在宅勤務にも慣れてきました。

PHPばかり触っていて、正直不満です。*1


Pear:DB execute(prepare($sql),$data)の罠

| 00:45

$data = ('column');
$sql = 'select * from tablename order by ? limit 1';

$statement = $db->conn->prepare($sql);
$res = $db->conn->execute($statement, $data);

って実行したら、結果にORDER BYがきかない。

普通に

select * from tablename order by column limit 1

Mysqlを叩いたら普通に動く、ので、これはPHP側でどっかおかしい。


まあ、どう見てもPrepare & Executeが怪しい

ナカヌキしてやってみた。

$sql = 'select * from tablename order by column limit 1';

$statement = $db->conn->prepare($sql);
$res = $db->conn->execute($statement);

コレだとばっちりうまくいった。

まちがいねえ、ホシはこの周りですぜ。


ってことで

Pear::DBcommon.phpのexecute()に

    function &execute($stmt, $data = array())
    {
        $realquery = $this->executeEmulateQuery($stmt, $data);
        if (DB::isError($realquery)) {
            return $realquery;
        }
      print "<br>exec:".$realquery."<br>";
        $result = $this->simpleQuery($realquery);

とかぶっ込んでみた。

結果は

select * from tablename order by column limit 1
と有るべきが
select * from tablename order by 'column' limit 1
となっていた

ゲエエーッ!

`column`でもなく'column'かよ!なんでだよ!


多分仕様なんだろう…しらんけど。

でもねー俺今あるコードを移植してんだけどね、このコードで正しくうごいてたらしいんだよね。

DBライブラリのバージョンが上がって、動作がかわったのか。いや、Mysqlが悪いのかもしれない。

タチが悪い事に、上のSQLって普通にMysqlシェルから実行してエラーんならないんだよね。

実際この挙動が正しいのかはわからないが、ORDER BYがきかないってことはカラムが選択できていないのだろうし、ORDER BYにウソのカラム名を入れるとエラーになるんだから、このSQL文はエラーになるべきだとおもうんだけどなあ、select * from 'tablename';はエラーになるし…まあ何とも言えないが。


奥さんに上の現象を説明してみた。

| 00:45

プログラムというレシピとしては同じなんだけど、使ってる道具がなぜか挙動がかわっちゃったんだよね。

たとえば材料の処理にフードプロセッサーをつかってるんだけど、なんかあたらしいバージョンのフードプロセッサーには加熱処理がおまけでついちゃったらしいんだよ。

それで前の人は問題なかったらしいけど、俺には問題が発生しちゃったみたい。

フードプロセッサーのバージョンを揃えるにも、型落ち古いフードプロセッサーを使うってのも別の不具合をはらんでいるかもしれないから、どうしようかなあ。」



ITの仕事って人に説明しづらいですね。

*1PHPが嫌いな訳ではなく、やる内容が

2008-04-07

テキストをビットマップに変換し、二次元配列におさめてく

| 14:29

TextFieldでBitmapDataにDrawして、それをgetPixcelで配列に納めていく。

どんなことに使うのかは書かないが(仕事で使うから)、なるほどこうやれば文字列の画像を生成したりできるんだなーと知った。

import flash.display.*;
import mx.core.UIComponent;
import flash.display.Sprite;
import flash.text.*;

private var bmpArray:Array = []
private var _bmd:BitmapData
private var label:TextField
private var format:TextFormat

public function genData(_string:String, _fontsize:int):Array{
	//テキストの書式を指定
    format = new TextFormat();
    format.font ="_等幅";
    format.color=0x000001;
    format.size = _fontsize

	//テキストを生成
    label = new TextField();
    label.autoSize=TextFieldAutoSize.LEFT;
	label.text = _string
	label.setTextFormat(format);

	//テキストをビットマップ描画
	_bmd = new BitmapData(label.width, label.height, true, 0xFFFFFF)
	_bmd.draw(label)
	
	//ビットマップを1ピクセルずつ拾い、配列に格納
	var pixelnum:uint = _bmd.width * _bmd.height
	for(var i:uint=0; i < pixelnum; i++){
		var ty:int = (i / _bmd.width) + 1
		var tx:int = (i % _bmd.width) + 1
		if (bmpArray[ty-1] == null){
			bmpArray[ty-1] = [];
		}
		if (_bmd.getPixel(tx,ty) > 0) {
			bmpArray[ty-1][tx-1] = 1
		}else{
			bmpArray[ty-1][tx-1] = 0
		}
	} 
	// return bmpArray
	

	//ビットマップを実際に表示するには
	//Bitmapオブジェクトを上でつくったデータを引数に生成し、
	var bmp:Bitmap = new Bitmap(_bmd)
	//FlexだとBitmapを直接Canvas等にaddchildできないので、
	//一端UIComponentでラップして
	var ui:UIComponent = new UIComponent();
	ui.addChild(bmp)
	//表示
	addChild(ui)
}


BitmapDataをつかうと、色々生な画像がいじれる。

Loaderなどと連携させれば、普通の画像を画像処理したりとか、色々出来る。

2008-04-03

4/1で新しい仕事になりました、なんかFlexとかでちんまり作ってます。

どうやら本格的にSAからPM/PGジョブチェンジの予感。

(とはいえ前職にはまだ籍があり、週4日ばかり新しい仕事をやる、という変な形態ですが)

在宅勤務になったので、こたつにはいりながら仕事をしています。

やばい、引きこもりになりそうw

きちんとマイルストーンを置けば在宅でも余りだらけずにやっていけそうです。


携帯GPSの取り方

| 13:05

勝手サイトだとGPSがとれないという話が客先で出たらしいので、実装サンプルを作った。

そのときDocomoAuSBMWillcomでの取り方を調べたのでメモ。

とりあえずGPS取得クエリを投げて、環境変数をダンプするCGI

#!/usr/local/bin/perl
print "Content-type: text/html\n\n";

print << "EOL";
<html>
<body>
<pre>gpstest
<A HREF="" lcs>DOCOMO</a>
<a href="device:gpsone?url=http://hoge/this.cgi&ver=1&datum0&unit=0&acry=0&number=0">au</a>
<A z HREF="">SBM</a>
<a href="http://location.request/dummy.cgi?my=http://hoge/this.cgi&pos=$location">
willcom</a>
EOL
foreach my $key( keys %ENV ){
    print "$key: $ENV{$key}", "\n";
}
print '</pre></body></html>';

詳細とかは

http://www.yaskey.cside.tv/mapserver/note/gps.html

http://siisise.net/gps.html

で。


超久々にPerlCGI書いたな。

最近Rubyばっかりだから、セミコロンつけ忘れるわ、foreachの書き方を微妙にわすれてるわ、ひどかったw



動画を切り抜く

| 13:05

Flexで、FLVとかを再生し、それを適当な形に切り抜く方法。

DisplayObject.maskをつかうと切り抜きができる。

http://livedocs.adobe.com/flex/2_jp/langref/flash/display/DisplayObject.html#mask

<mx:Script>
<![CDATA[
private function init():void
 {
  cv.graphics.clear();
  cv.graphics.lineStyle(0,0,0);
  cv.graphics.beginFill(0xFF0000);
  cv.graphics.moveTo(100, 100 );
  cv.graphics.lineTo(200, 100 );
  cv.graphics.lineTo(200, 200 );
  cv.graphics.lineTo(100, 200 );
  cv.graphics.endFill();	
  yp.mask = cv
 }
]]>
</mx:Script>
	<mx:VideoDisplay x="0" y="0" width="100%" height="100%"
		 source="http://hoge/hage.flv" id="yp" autoPlay="true"/>
	<mx:Canvas width="100%" height="100%" id="cv">
	</mx:Canvas>
</mx:Canvas>

超かんたんですね。

最初は上の切り抜きマスクではなく、本当のマスク(本当に上にオブジェクトをのせて隠す)で実装してたんですが、丸く切り抜きたいといわれて挫折しましたw

ただ、複雑な図形を書いて、なおかつ解像度が高いとすげえ重い。

特に円とかで切り抜くと、アンチエイリアスが効くのだろうな…。

2008-03-19

rails+jpmobileで、PCで絵文字表示(三キャリア対応編)

| 13:32

Docomoの絵文字をPCで表示(拡張絵文字も追加)Docomo(imode)は対応してたんだけど、勢いでauezweb)とSoftbankMobile(ezsky、Y!)も対応してみた。これで一応3キャリア対応です。

まあ、auSBMはまだきっちりとはテストしてないですがね。


見ての通り、railsで、ヘルパー関数に登録して、Viewで使います。

module MypageHelper
  
  # htlmのエンティティを落としてから(railsのhelperのh())
  # emojichar>htmlimgtag convert Helper を実行
  # <%=h dirtytext %> みたいに
  # <%=he dirtytext_emojichar_conbined %> とかして使います。
  def he(str)
    str = emoji2imgtag( h(str) )
  end
  
  # emojichar>htmlimgtag convert Helper
  # Unicodeの絵文字を <img href='hoge'>に変換
  # jpmobileの出力するUnicode文字列に対応。
  # jpmobileの仕様上、SBMの文字空間は0x1000シフトされている
  # http://d.hatena.ne.jp/darashi/20070623
  # 
  # 3キャリア対応
  # Imode 基本絵文字+拡張絵文字に対応
  # AUの絵文字に対応(しかし未テスト)
  # SBMの絵文字に対応(しかし未テス(略
  def emoji2imgtag(str)

    if request.mobile?
      #相手が携帯電話なら処理しない(キャリア間の変換はjpmobileに処理を全部任せる)
      return str
    else
      chars = []
      chars = str.split('')
      returnstr = ''
      chars.each{ |char|

        n = char.unpack('U')[0] #数値に変換

        # FIXME 高速化のメモ
        # ↓おおまかな絵文字コードの範囲「以外」だったら処理しない様にすることで、
        # if文を大幅にやり過ごすことができる、はず。
        # ただ、見にくくなるのがイヤなので、高速化しなければならない時点まで放置。
        
        if ( 0xE63E <= n && n <= 0xE6A5 ) || # Docomoに対応
          ( 0xE6AC <= n && n <= 0xE6AE ) ||
            ( 0xE6B1 <= n && n <= 0xE6B3 ) ||
            ( 0xE6B7 <= n && n <= 0xE6BA ) ||
            ( 0xE6CE <= n && n <= 0xE70B ) || # ここまでimode 基本絵文字 範囲
          ( 0xE70C <= n && n <= 0xE757 ) # 拡張絵文字
          
          returnstr += '<img src="/images/e/i/' + char.unpack('U').to_s + '.gif" width="12" height="12" alt="' + char.to_s + '" >'
          
        elsif ( 58472 <= n && n <= 58847 ) || #au に対応
          ( 60032 <= n && n <= 60269 ) 
          
          returnstr += '<img src="/images/e/a/' + char.unpack('U').to_s + '.gif" width="12" height="12" alt="' + char.to_s + '" >'
          
        elsif ( 61441 <= n && n <= 61530 ) || #SBM に対応
          ( 61697 <= n && n <= 61786 ) ||
            ( 61953 <= n && n <= 62035 ) ||
            ( 61957 <= n && n <= 62039 ) ||
            ( 62209 <= n && n <= 62285 ) ||
            ( 62465 <= n && n <= 62540 ) ||
            ( 62721 <= n && n <= 62775 )
          
          returnstr += '<img src="/images/e/s/' + char.unpack('U').to_s + '.gif" width="12" height="12" alt="' + char.to_s + '" >'
          
        else
          #puts NKF::nkf('-U -s', "\ne:" + char.to_s)
          returnstr += char.to_s
        end
      
      }
      return returnstr
    end

  end
end

これで使う画像ファイルは、MobilePictogramConverterに添付されているものを変換して作りました。

ライセンスとかは考えたくないので(俺は趣味で使うからイイとして)、同様の事をしたければ自分でファイルを取ってきて、自分でライセンスを判断してください(SBMは必ず申請が必要な様です、詳しくは配布サイトを見てください)。


変換(というかリネーム)するバッチと、一番作るのがめんどくさかった文字コード空間のエクセル*1は、こちらにおいておきます。一昨日くらいの俺ならのどから手がでるほど欲しいファイルでした。

http://cfe.jp/pub/http_born1981.g.hatena.ne.jp_uzulla_20080319.zip

移動させる可能性大なので、直リンクはしないでください(このエントリに張ってくれれば、対応しておきます)。


上のSnippetにも書いてありますが、jpmobileの仕様上、SBMの絵文字空間は0x1000シフトされています。

しかし、上の私の作ったエクセル+バッチファイルでは、エクセルSBMの絵文字空間を正しく(資料通り)記述してありますので、そのままつかってはいけません。エクセルの絵文字空間を利用して、コードの条件文などを書く場合には注意してくださいね。

(って俺が書いてもしかたないので、本家情報を見てください。

バッチファイルはシフト済(0xExxx>0xFxxx)です。


本家でもこういうチケットがあるので、そのうち対応されるのでしょう。

そうなったらこれらは不要になりますね。


やっぱりちょと余る…

MobilePictogramConverter添付の画像から変換していくと、画像ファイルが変換しきれずに余るんですよね…、どうやら各社「資料にのってない絵文字」ってのがあるんですね、どないせえと(;´Д`)

まあ、チケットぴあの「ぴ」マークとか、Jskyのロゴとか、あんまり変換する意味のなさそうな(変換してもだれもよろこばなそうな)物ばっかりなんですけどね。


おまけ、Rubyで文字をエンコードしたり、デコードしたり

| 15:32

今回各社の資料に載っていたUnicodeの0xFFFFの16進数形式を10進数に変換する為にRubyの書き方を覚えました。

(なんで16進数のままやらなかったかって?10進数のほうが解りやすかったから…)


文字を数値にエンコードしたりデコードするには "A".unpack('U')[0] とします。

最後の[0]は、"A".unpack('U')は配列を返すので中身を返しているだけです。

これは一文字だけなので、複数文字をやるときは

"abcdefg".unpack('U*').join('')

とやります、*は入力全てに対して処理するという意味です(指定しないとPackテンプレートの1単位しか処理されない)


要は、バイト列をunpackするとは、バイト列(というかString)をpackテンプレート引数にそった形式できりはなし(*の場合)、配列に詰めていきます。

unpack()と対になるpack()は、配列それぞれを1要素(Packテンプレートで指定された要素)であると判断して、エンコードします。

Packテンプレートの一覧はこちら、色々あって面白い。

http://www.ruby-lang.org/ja/man/html/pack_A5C6A5F3A5D7A5ECA1BCA5C8CAB8BBFACEF3.html


良くある例、URLエンコード

Perlで何度も書くURLエンコード変換について書くと

a=""

"あいうえお".each_byte {|c| a += sprintf("%%%x", c)}

p a

または

p "あいうえお".unpack('H*').join('').gsub(/[0-9a-f]{2}/, '%\&')

と、すっきり書けます、見やすくていいですね。


逆にデコードだと

chars = []

"%61%62%63".gsub(/%([0-9a-f]{2})/){ chars << $1.hex }

p chars.pack("U*")

とか書くのか?定石がわからん、デコードはちょっとめんどいか(上のはUNICODE考えてません)。

うーんPerlとまったく同じ書き方するの?


まあ、そんなことしなくても

標準ライブラリCGIをつかえば

require "cgi"

p CGI.escape('あいうえお')

p CGI.unescape('%40%23%23')

とかやるだけなんですけどね。

*1SJIS?のコードがファイル名になっているMobilePictogramConverterに添付画像から、扱いやすいUnicodeのコードへの対応表

KeyaanKeyaan2012/01/06 15:51A ltilte rationality lifts the quality of the debate here. Thanks for contributing!

|