Upload
moriyoshi-koizumi
View
13.148
Download
1
Embed Size (px)
DESCRIPTION
Citation preview
PHPを“いじり倒す”
10の方法10 ways to “exploit” PHP that you might not know
小泉 守義 (Moriyoshi Koizumi) <[email protected]>
復 習しましょう PHPとは何かテンプレートエンジン界のオーバーテクノロジーThe most overengineered template engine ever.
文法上の特徴により、よくプログラミング言語と勘違いされるOften mistaken as a sort of programming language due to its “your-favorite-language-like” syntatic features.
世界で初めてテンプレートエンジンでテンプレートエンジンが実装されたThe world’s first template engine upon which another template engine is implemented.
brushup: What is PHP?
Uh, so... do you mean PHP is not a programming language?
テンプレートエンジンをカスタマイズするのはごく自然な行為
です から
PHPもカスタマイズして当然
Why not customize PHP so it would fit more to your project?
と、いう話です。
PHPの内部構造を知ろう!
PHPのアーキテクチャ
ZendEngine2SAPI
SAPI module
Extensions
SAPI module
ZendEngine2SAPI
SAPI module
Extensions
Webサーバの実装とのglue(のり)となる部分
コマンドライン用のPHPもSAPI moduleとして実装されている。
SAPI
ZendEngine2SAPI
SAPI module
Extensions
SAPI moduleと各extensionとのインターフェイス
通常拡張モジュールは直接SAPI moduleに触れることはない
ZendEngine
ZendEngine2SAPI
SAPI module
Extensions
言わずと知れたPHPのコア
言語パーサの他に、設定ファイル (.ini) のパーサなどいろいろなものが含まれている。
Extensions
ZendEngine2SAPI
SAPI module
Extensions
新たな関数を追加したりクラスを追加したりする通常のextensionと、ZendEngine自体を拡張するzend extensionの2種類がある。
厳密な違いではない
その他の構成要素: TSRMThread-Safe Resource Managerの頭文字を取ったもの。正体はクロスプラットフォームのTLS (thread-local-storage) ライブラリfork型のWebサーバをターゲットに作られたPHPを無理矢理multi-threadedなWebサーバに対応させるために誕生ソースコード中に登場するTSRMLS_CやTSRMLS_Dといったマクロは、TLSのスロットへのポインタを関数引数として引き回してパフォーマンスを向上する工夫。
TSRMのイメージSlot #1
Slot #2
Slot #n
module global
threads
TLS
TLS
TLS
ZendEngineの構造
Allocator
Parser
ini parser
Lexer
Hashtable
basic data structure
Stack Linked List
Utilities
Parser
Garbage Collector
language core
Opcode emitter
Virtual Machine
Objects API
Lexer
zend_alloc.c
zend_API.czend_float.czend_stream.czend_qsort.c
zend_stack.czend_ptr_stack.czend_llist.czend_hash.c
zend_ini.czend_ini_parser.yzend_ini_scanner.c
zend_objects.czend_object_handlers.czend_objects_API.c
zend_execute.czend_execute_API.czend_vm_execute.hzend_operators.c
zend_gc.c
zend_compile.czend_opcode.c
zend_language_parser.y zend_language_scanner.l
Compiler
<?php$a = 1;$b = 2;$c = $a + $b;?> ?
Compiler<?php$a = 1;$b = 2;$c = $a + $b;?>
Lexer
T_OPEN_TAGT_VARIABLE‘=’T_LNUMBER‘;’T_VARIABLE‘=’T_LNUMBER‘;’T_VARIABLE‘=’T_VARIABLE‘+’T_VARIABLE‘;’T_CLOSE_TAG
CompilerT_OPEN_TAGT_VARIABLE‘=’T_LNUMBER‘;’T_VARIABLE‘=’T_LNUMBER‘;’T_VARIABLE‘=’T_VARIABLE‘+’T_VARIABLE‘;’T_CLOSE_TAG
ParserOpcode emitter
zend_op
zend_op_array
ASSIGN
ASSIGN
ADD
zend_op
zend_op
zend_opASSIGN
zend_op_arrayopcodeを格納する配列を持つ構造体opcode以外にもopcode列に対応する関数の情報などが格納される詳細は割愛
zend_op1つのopcodeを表す構造体opcode: opcode番号を表すhandler: opcode番号を関数ポインタに解決したものop1, op2, extented_value:オペランドを表すメンバresult: 実行結果の格納先を表す
op1 op2
extended_value
result
zend_op
opcode handler
znode
op_type
constant varopline_num
jmp_addr
op_array
構文解析のときにsemantic valueとして使われる。opcodeのオペランドとして流用される。正体は複雑怪奇な共用体 (union)constant: 定数を表すvar: tmp_var番号を表すopline_num / jmp_addr:JMP命令の飛び先を表すop_array: 関数の呼び出し先を表す
TMP_VARについて実際のCPUにおけるレジスタのようなものop_arrayに格納されたopcodeを実行するたびにVMにより確保され、実行が終わった時点で解放されるzend_op_array構造体の”T”という変数に使用数が格納されているopcodeの結果を格納して別のopcodeに引き渡すのに使われるzend_opをノードとして表現された抽象構文木 (AST) のエッジにあたるものと思うと分かりやすい
TMP_VARとzend_op
op1 op2
result
ADD
op1 op2
result
ADD
op1 op2
result
ASSIGN
#0変数: $b
#1変数: $c
#3変数: $d
#5変数: $a
TMP_VAR
$a = $b + $c + $d;
ADD
ASSIGN
ADD実行順
zend_opの抽象構文木的な
表現
#2一時変数
#4一時変数
opcodeとしての表現
そのほかcompilerで使われる構造体 (一部)compiler_globals専らコンパイルで使う一時的な情報が格納される
zend_function関数を表す構造体
zend_arg_info関数引数に関する情報が格納されている。
zend_compiled_variable「コンパイル済み変数」のエントリを表す
そのほかcompilerで使われる構造体 (一部)zend_brk_cont_elementbreak や continue の分岐情報を管理する
zend_try_catch_elementtry - catch 節でのcatch先の分岐情報を管理する
Virtual Machine
zend_op_array
ASSIGN
ASSIGN
ADD
zend_op
zend_op
zend_opASSIGN
zend_op ASSIGN
FETCH_R
FETCH_W
ECHO
ADD
FETCH_DIM_R
FETCH_DIM_W
handlers
Virtual machineで使われる構造体executor_globalsVMの状態が格納される
execute_dataVMの実行フレームを表す
zend_function_state呼び出し中の関数の状態を表す構造体
てなわけで…
改造しましょう!
改造 #1: 関数を追加する htmlspecialchars()と打つのがめんどくさい!第2引数のデフォルトがENT_COMPATになっているのがうざい!もっというと第3引数のデフォルトはUTF-8でよくない?新しい関数を追加しよう!
改造のポイントhtmlspecialchars()を実装している箇所を探すgrep -r “PHP_FUNCTION(htmlspecialchars)”とか grep -r “PHP_FE(htmlspecialchars)”
中身を見る真似して関数を実装するPHP_FUNCTION(...)を追加PHP_FE(...) を適当な場所に追加ZEND_BEGIN_ARG_INFO() ~ ZEND_END_ARG_INFO() を追加
...
改造 #2: autoboxを追加PHPでもRubyみたいにスカラやarrayとオブジェクトを分け隔てなく使いたい!たとえばこんな感じarray(1, 2, 3, 4, 5)->join(’,’)JavaにあるautoboxingをPHPでも実装すればいいんじゃ?autoboxが必要なときに、__autobox()というグローバル関数を、対象の値を引数に呼び出すようにする。
改造のポイントとりあえず「->」演算子を評価し、オブジェクトのメソッドが呼ばれる箇所 zend_vm_def.h の中から見つける。
zend_vm_def.h は前述のhandlerが定義されている場所zend_vm_execute.h は zend_vm_def.h から自動生成されるものなのでいじっても無駄ッ!無駄ッ!
「->」演算子がパースされている箇所も見ておく普通 1->toString() とか書けないもんね多分 zend_language_parser.y もいじる必要あり
...
改造 #3: テンプレートエンジンとしてのPHPを強化PHPってテンプレートエンジンでしょ、Smartyとか意味あるの?論理構造を無視したテンプレートエンジンって使いづらいPHPのパーサでなんとかならんのかじゃあ拡張しよう
<?php $a = << ?><?html><body> <?div id=”{$id}”>test</?div></body></?html>
<?php// $a には構造がDOMみたいに格納されるvar_dump($a);?>
改造のポイントzend_language_scanner.l にタグのパースに必要なトークンを生成するような記述を追加zend_language_parser.y にタグのパースのためのルールを追加基本的に見よう見まねしましょう!
...
改造にあたってのTips--enable-debug つきで configure しよう!phpのソースツリーの一番上にある .gdbinit を活用しよう!printzvprintzopszbacktraceなどなど
閑話休題
PHPのextensionを簡単に作る方法教えます!!
これまでのextension作成手法ext_skelを使う使い方がどこにも書いてない
PECL_CodeGenを使うそれでも面倒くさい
じゃあ、あと何がある?
Boost.PHP
Boost.PHPとは?C++の各種機能を利用して、簡単にPHPの拡張モジュールを書けるようにするライブラリCodeGen_Pecl? ext_skel? なにそれおいしいの?Boost.Python に強く影響されつつ作りましたBoost.* を名乗っているものの、まだ公式にBoost library suiteの一員ではありません (サーセン!)C++を知らなくても使えます(たぶん)http://github.com/moriyoshi/boost.php/
どのくらい簡単?#include "boost/php/module.hpp"#include "boost/php/function.hpp"
using namespace boost;
class m001_module : public php::module, public php::function_container<m002_module> {public: class handler : public php::module::handler { public: handler(m001_module* mod) :php::module::handler(mod) {} };public: m001_module(zend_module_entry* entry) : php::module(entry) { // entry->functions = defun("your_function", &handler::your_function); }};
#define BOOST_PHP_MODULE_NAME m001#define BOOST_PHP_MODULE_CAPITALIZED_NAME M001#define BOOST_PHP_MODULE_VERSION "0.1"#define BOOST_PHP_MODULE_CLASS_NAME m001_module
#include "boost/php/module_def.hpp"
これがテンプレです
...
モジュール定義次の#defineを書くBOOST_PHP_MODULE_NAMEBOOST_PHP_MODULE_CAPITALIZED_NAMEBOOST_PHP_MODULE_VERSIONBOOST_PHP_MODULE_CLASS_NAME
module classを定義する(名前は BOOST_PHP_MODULE_CLASS_NAME で指定したもの)
関数を定義する
moduleクラスのコンストラクタに渡されるentry構造体のfunctionsにdefun(”function_name”, 関数ポインタ)をアサインする以上
クラスを定義する
module classの__initialize()メソッドの中身にdef_class<C++のクラス名>(”PHPのクラス名”, ...)を追加する。def_class()の戻り値に対して、先ほどと同じ要領でdefun()を呼び出す。以上
ね、簡単でしょう?
ご清聴ありがとうございましたThank you for listening!
Questions?