PHPをハックしよう(第二回)

2014-10-22

PHP嫌いのハッカーがPHPをハックする連載の第二回目です。 前回はこちら

前回見つけた、opdumperというPHP拡張でopcodeをdumpしてみましょう。

foo.phpとして以下の内容のファイルを作成します。

<?php

function f($a, $b) {
    return $a + $b;
}

echo f(1,2);

このファイルのopcodeをdumpしてみましょう。

$ php -d opdumper.active=1 foo.php
line: 3
opcode: ZEND_NOP
op1_type: UNUSED
op2_type: UNUSED
result_type: UNUSED
op1: UNUSED
op2: UNUSED
result: UNUSED
==============================
line: 7
opcode: ZEND_SEND_VAL
op1_type: CONST
op2_type: UNUSED
result_type: UNUSED
op1: 1
op2: UNUSED
result: UNUSED
==============================
line: 7
opcode: ZEND_SEND_VAL
op1_type: CONST
op2_type: UNUSED
result_type: UNUSED
op1: 2
op2: UNUSED
result: UNUSED
==============================
line: 7
opcode: ZEND_DO_FCALL
op1_type: CONST
op2_type: UNUSED
result_type: VAR
op1: f
op2: UNUSED
result: $4294967264
==============================
line: 7
opcode: ZEND_ECHO
op1_type: VAR
op2_type: UNUSED
result_type: UNUSED
op1: $4294967264
op2: UNUSED
result: UNUSED
==============================
line: 8
opcode: ZEND_RETURN
op1_type: CONST
op2_type: UNUSED
result_type: UNUSED
op1: 1
op2: UNUSED
result: UNUSED
$

ちなみに、失敗する場合にはPHP拡張が読み込めていない可能性があります。 その場合にはphp.iniextension=opdumper.soを追加するなどしてみてください。

opcodeとソースを見比べるとなんとなくわかりますね。 ZEND_SEND_VALで関数の引数をスタックに積んで、ZEND_DO_FCALLで関数を呼び出しているのがわかります。 で、その結果をZEND_ECHOで出力しています。

しかしですね、関数fの定義のopcodeが見当たらない。。。 調べてみると、PHPのコンパイラはtwo-passのコンパイラ、つまり2回ソースをスキャンするタイプのコンパイラなんです。 1回目で関数の宣言とかそういうのをスキャンして、2回目で実際の関数の呼び出しのopcodeを生成しているようです。 つまり、この出力されているのは2回目のスキャンの結果のようです。 1回目のスキャン結果のopcodeはどうやって取得すればよいのやら。。。 どうやらPHPのソースをハックする必要がありそうです。 なんか深みにはまりそうな予感。。。

続きは次回。