Beruflich Dokumente
Kultur Dokumente
de
Stefan Esser
• aus Köln / Deutschland
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 2
Teil I
Einführung
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 3
Einführung
• PHP Bytecode
• ist die compilierte Form von PHP
PHP Skript
• gibt guten Einblick in Funktionsweise von PHP
PHP
• erlaubt Analysen auf der Ausführungsebene Quelltext
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 4
Bytekit
• PHP Erweiterung
• gibt Zugriff auf PHP Bytecode Rohdaten (ähnlich parsekit)
• enthält PHP Bytecode Disassembler (ähnlich vld)
• gibt Control Flow Informationen
• Zuordnung zu Basic Blocks
• Control Flow Graph
• hilft bei Visualisierung
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 5
Teil II
Bytekit - API
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 6
Funktionen
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 7
Konstanten
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 8
bytekit_get_baseaddress()
int
bytekit_get_baseaddress()
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 9
bytekit_set_baseaddress()
void
bytekit_set_baseaddress(int baseaddress)
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 10
bytekit_disassemble_file()
array
bytekit_disassemble_file(string filename[,
array &errors[, mixed options]])
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 11
bytekit_disassemble_file() - Optionen
• „flags“
➡ BYTEKIT_SHOW_SPECIALS
• „dependencies“
• Array von Abhängigkeiten
• Liste der Dateien die zuvor compiliert werden müssen
• Erlaubt Auflösen von Vererbungshierarchien
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 12
Rückgabe von bytekit_disassemble_file()
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 13
Funktionen
'functions' =>
array (
'main' =>
array (
'code' => ..., // disassemblierter Code
'cfg' => ..., // Code Flow Graph
'entries' => ..., // Eingehende Kanten
'exits' => ..., // Ausgehende Kanten
'bb' => ..., // Zuweisung der Basic Blocks
'next_t' => ..., // Wahr - Kanten
'next_f' => ..., // Falsch - Kanten
'exception' => ..., // Exception - Kanten
'raw' => ... // Raw op_array data
),
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 14
Funktionen - Disassemblierter Code
'code' =>
array (
0 =>
array (
'address' => ..., // Adresse für Disassembler
'opline' => ..., // zugehörige Opline
'mnemonic' => ..., // Mnemonic - FETCH_CLASS
'operands' =>
array (
0 =>
array (
'string' => ..., // '~4'
'value' => ..., // 4
'type' => ..., // BYTEKIT_TYPE_TMP_VAR
),
1 => ...,
)
)
1 => ...,
)
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 15
Funktionen - Code Flow Graph
2 =>
array (
3 => BYTEKIT_EDGE_NORMAL
),
3 =>
array (
)
)
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 16
Funktionen - Basic Block Zuweisung
'bb' =>
array (
0 => 1,
1 => 1,
2 => 1,
3 => 1,
4 => 1, Weist jede disassemblierte
5 => 2, Codezeile einem Basic Block zu
6 => 2,
7 => 3,
8 => 3,
9 => 3,
10 => 3
)
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 17
Funktionen - Kanten
'entries' => 'exits' => 'next_t' => 'next_f' => 'exception' =>
array ( array ( array ( array ( array (
0 => 0, 0 => 1, 0 => 1, 0 => -1, 0 => -1,
1 => 1, 1 => 1, 1 => 2, 1 => -1, 1 => -1,
2 => 1, 2 => 1, 2 => 3, 2 => -1, 2 => -1,
3 => 1, 3 => 1, 3 => 4, 3 => -1, 3 => -1,
4 => 1, 4 => 2, 4 => 7, 4 => 5, 4 => -1,
5 => 1, 5 => 1, 5 => 6, 5 => -1, 5 => -1,
6 => 1, 6 => 1, 6 => 7, 6 => -1, 6 => -1,
7 => 2, 7 => 1, 7 => 8, 7 => -1, 7 => -1,
8 => 1, 8 => 1, 8 => 9, 8 => -1, 8 => -1,
9 => 1, 9 => 1, 9 => 10, 9 => -1, 9 => -1,
10 => 1 10 => 0 10 => -1 10 => -1 10 => -1
) ) ) ) )
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 18
Funktionen - Raw op_array Daten
'raw' =>
array (
'type' => ...,
'function_name' => ...,
'scope' => ...,
'fn_flags' => ...,
'num_args' => ...,
'required_num_args' => ...,
'pass_rest_by_reference' => ...,
'try_catch_array' => ...,
'line_start' => ...,
'line_end' => ...,
'doc_comment'
'return_reference'
=>
=>
...,
...,
Ausgabe der
'refcount'
'last'
=>
=>
...,
...,
op_array Rohdaten
'size'
'T'
=>
=>
...,
...,
wie bei pecl/parsekit
'last_brk_cont' => ...,
'current_brk_cont' => ...,
'backpatch_count' => ...,
'done_pass_two' => ...,
'brk_cont_array' => ...,
'cv' => ...,
'static_variables' => ...,
'start_op' => ...,
'filename' => ...,
'opcodes' => ...
)
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 19
Funktionen - Raw opline Daten
'opcodes' =>
array (
0 =>
array (
'opcode' => 99,
'flags' => 132137,
'result' =>
array (
'type' => 2,
'EA.var' => 20,
'EA.type' => 0,
'var' => 1,
'constant' => 20,
),
'op1' => Ausgabe der
array (
'type'
'EA.var'
=> 8,
=> 0,
opline Rohdaten
'EA.type' => 0,
'var' => 0,
wie bei pecl/parsekit
'constant' => 0,
),
'op2' =>
array (
'type' => 1,
'EA.var' => 8080000,
'EA.type' => 5,
'constant' => 'E_ALL',
),
'extended_value' => 0,
'lineno' => 11,
)
)
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 20
Klassen
'classes' =>
array (
'TestKlasse' =>
array (
'type' => 2, Ausgabe der
'name' => 'TestKlasse',
'parent' => NULL, class_entry Rohdaten
'constants_updated' => false,
'ce_flags' => 0, wie bei pecl/parsekit
'constructor' => '__construct',
'clone' => NULL,
'__get' => NULL,
'__set' => NULL,
'__call' => NULL,
'properties_info' => NULL,
'static_members' => NULL,
'constants_table' => NULL,
'interfaces' => NULL,
'filename' => '/var/www/bytekit/examples/test.php',
'line_start' => 3,
'line_end' => 9,
'doc_comment' => NULL,
'refcount' => 1,
'methods' =>
array (
0 => '__construct',
),
'default_properties' => NULL,
),
)
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 21
Teil III
Bytekit - Beispiele
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 22
Einfacher Disassembler
<?php
$filename = $_SERVER['argv'][1];
if (!file_exists($filename)) {
die("File not found.");
}
$res = bytekit_disassemble_file($filename);
$operands = "";
for ($j=0; $j<count($c['operands']); $j++) {
$operands .= ($j > 0 ? ", " : "") . $c['operands'][$j]['string'];
}
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 23
Einfacher Disassembler - Ausgabe
main:
FETCH_R $0, '_SERVER'
FETCH_DIM_R $1, $0, 'argv'
FETCH_DIM_R $2, $1, 1
ASSIGN !0, $2
SEND_VAR !0, 1
DO_FCALL $4, 'file_exists'
BOOL_NOT ~5, $4
JMPZ ~5, loc_40a
EXIT 'File not found.'
JMP loc_40a
SEND_VAR !0, 1
DO_FCALL $6, 'bytekit_disassemble_file'
ASSIGN !1, $6
FETCH_DIM_W $8, !1, 'functions'
FE_RESET $9, $8, loc_444
FE_FETCH $10, ~11, $9, loc_444
ASSIGN_REF !3, $10, 0
ASSIGN !2, ~11
ECHO !2
ECHO ':\n'
ASSIGN !4, 0
FETCH_DIM_R $14, !3, 'code'
SEND_VAR $14, 1
DO_FCALL $15, 'count'
IS_SMALLER ~16, !4, $15
JMPZNZ ~16, loc_442, loc_41e
POST_INC ~17, !4
FREE ~17
JMP loc_416
...
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 24
Disassembler - Sammeln der Sprunglabels
<?php
...
$jumptargets = array();
for ($i=0; $i<count($function['code']); $i++) {
$c = &$function['code'][$i];
potentielles
for ($j=0; $j<count($c['operands']); $j++) { Sprungziel
$op = &$c['operands'][$j];
if ($op['type'] == BYTEKIT_TYPE_SYMBOL) {
$jumptargets[$op['value']] = $op['string'];
}
}
}
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 25
Disassembler - Ausgabe mit Sprunglabels
main:
FETCH_R $0, '_SERVER'
FETCH_DIM_R $1, $0, 'argv'
FETCH_DIM_R $2, $1, 1
ASSIGN !0, $2
SEND_VAR !0, 1
DO_FCALL $4, 'file_exists'
BOOL_NOT ~5, $4
JMPZ ~5, loc_40a
EXIT 'File not found.'
JMP loc_40a
loc_40a:
SEND_VAR !0, 1
DO_FCALL $6, 'bytekit_disassemble_file'
ASSIGN !1, $6
FETCH_DIM_W $8, !1, 'functions'
FE_RESET $9, $8, loc_444
FE_FETCH $10, ~11, $9, loc_444
loc_40f:
ASSIGN_REF !3, $10, 0
ASSIGN !2, ~11
ECHO !2
ECHO ':\n'
ASSIGN !4, 0
loc_416:
FETCH_DIM_R $14, !3, 'code'
SEND_VAR $14, 1
DO_FCALL $15, 'count'
IS_SMALLER ~16, !4, $15
JMPZNZ ~16, loc_442, loc_41e
loc_41b:
POST_INC ~17, !4
FREE ~17
JMP loc_416
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 26
Visualisierung des Bytecodes (I)
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 27
Visualisierung des Bytecodes (II)
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 28
Visualisierung des Bytecodes (III)
• Basic Blocks sind als HTML Nodes definiert
"bb_1" [
label =<<TABLE BORDER="2" CELLBORDER="0" CELLSPACING="0" BGCOLOR="#ffffff">
<TR>
<TD BGCOLOR="#FF8888" colspan="3" ALIGN="LEFT">
<FONT face="Courier-Bold" point-size="12">00000400:</FONT></TD>
</TR>
<TR>
<TD ALIGN="LEFT">00000400</TD>
<TD ALIGN="LEFT">FETCH_R</TD>
<TD ALIGN="LEFT">$0, '_SERVER'</TD>
</TR>
<TR>
<TD ALIGN="LEFT">00000401</TD>
<TD ALIGN="LEFT">FETCH_DIM_R</TD>
<TD ALIGN="LEFT">$1, $0, 'argv'</TD>
</TR>
<TR>
<TD ALIGN="LEFT">00000402</TD>
<TD ALIGN="LEFT">FETCH_DIM_R</TD>
<TD ALIGN="LEFT">$2, $1, 1</TD>
</TR>
<TR>
<TD ALIGN="LEFT">00000403</TD>
<TD ALIGN="LEFT">ASSIGN</TD>
<TD ALIGN="LEFT">!0, $2</TD>
</TR>
...
</TABLE>>
];
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 29
Visualisierung - Aufbau der Nodes
$code = &$function['code'];
$bb = &$function['bb'];
$bbid = -1;
for ($i=0; $i<count($code); $i++) {
$instruction = &$code[$i];
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 30
Visualisierung - Ausgabe der Nodes
foreach($nodes[$key]['instructions'] as $instr) {
fprintf($fout, "<TR><TD ALIGN=\"LEFT\">%s</TD><TD ALIGN=\"LEFT\">%s</TD> ".
"<TD ALIGN=\"LEFT\">%s</TD></TR>\n",
htmlentities(sprintf("%08x",$instr['address'])),
htmlentities($instr['mnemonic']),
htmlentities($instr['operands']));
}
fprintf($fout, "</TABLE>>\n");
fprintf($fout, "];\n");
}
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 31
Visualisierung - Ausgabe der Kanten
$cfg = &$function['cfg'];
switch ($value) {
case BYTEKIT_EDGE_TRUE:
$style = 'color="#00ff00"';
break;
case BYTEKIT_EDGE_FALSE:
$style = 'color="#ff0000"';
break;
case BYTEKIT_EDGE_NORMAL:
$style = 'color="#000000"';
break;
case BYTEKIT_EDGE_EXCEPTION:
$style = 'style=dotted, penwidth=3.0, color="#0000ff"';
break;
default:
$style = 'color="#0000ff"';
}
fprintf($fout, '"bb_%d" -> "bb_%d" [%s];'."\n", $i, $key, $style);
}
}
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 32
Suche nach eval() - Traditionell
wp-admin/import/blogger.php: eval('this.status='+text);
wp-admin/includes/class-pclzip.php: eval('$v_result = '.$p_options[PCLZIP_CB_PRE_ADD].'(PCL...
wp-admin/includes/class-pclzip.php: eval('$v_result = '.$p_options[PCLZIP_CB_POST_ADD].'(PC...
wp-admin/includes/class-pclzip.php: eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'...
wp-admin/includes/class-pclzip.php: eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT]....
wp-admin/includes/class-pclzip.php: eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].....
wp-admin/includes/class-pclzip.php: eval('$v_result = '.$p_options[PCLZIP_CB_POST_EXTRACT]....
wp-admin/press-this.php: var my_src = eval(
wp-admin/press-this.php: var my_src = eval(
wp-includes/classes.php: eval("@\$query = \"" . addslashes($query) . "\";");
wp-includes/functions.php: if ( doubleval($bytes) >= $mag )
wp-includes/js/jquery/interface.js:eval(function(p,a,c,k,e,r){e=functi...
wp-includes/js/prototype.js: return this.extractScripts().map(function(script){return eval(script)});
wp-includes/js/prototype.js: if (!sanitize || json.isJSON()) return eval('(' + json + ')');
...
wp-includes/js/tinymce/tiny_mce_popup.js: eval(s);
wp-includes/js/tw-sack.js: eval(this.response);
wp-includes/rewrite.php: eval("\$query = \"" . addslashes($query) . "\";");
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 33
Suche nach eval() im Bytecode
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 34
Einfacher Include/Require/Eval Scanner (I)
• beschränkte Datenflussanalyse
• suche nur im gleichen Basic Block
• Emulation von einigen wenigen Opcodes
• bei Unbekannten Opcodes wird „Manipulierbarkeit“ angenommen
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 35
Einfacher Include/Require/Eval Scanner (II)
• Emuliert wird
• INCLUDE_OR_EVAL
• ASSIGN
• ASSIGN_CONCAT
• CONCAT
• FETCH_CONSTANT
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 36
Suche nach Include/Require/Eval im Bytecode
if ($is_safe == BYTEKIT_IS_UNSAFE) {
$found[] = $o[$i]['lineno'];
}
}
}
}
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 37
Rückwärts Datenfluss Analyse (I)
<?php
function bytekit_trace_backward(&$func, $start, $end, &$s)
{
$o = &$func['raw']['opcodes'];
if ($s['type'] == BYTEKIT_IS_CONST) {
return BYTEKIT_IS_SAFE;
}
$i = $start;
while ($i >= $end)
{
$ol = &$o[$i];
$opcode = $ol['opcode'];
switch ($opcode) {
/*******************/
/* EMULATE OPCODES */
/*******************/
return BYTEKIT_IS_UNSAFE;
}
?>
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 38
Rückwärts Datenfluss Analyse (II)
case BYTEKIT_INCLUDE_OR_EVAL:
if (bytekit_equal_node($s, $ol['result'])) {
return BYTEKIT_IS_UNSAFE;
}
break;
case BYTEKIT_FETCH_CONSTANT:
if (bytekit_equal_node($s, $ol['result'])) {
return BYTEKIT_IS_SAFE;
}
break;
case BYTEKIT_ASSIGN:
if (bytekit_equal_node($s, $ol['result']) || bytekit_equal_node($s, $ol['op1'])) {
return bytekit_trace_backward($func, $i-1, $end, $ol['op2']);
}
break;
case BYTEKIT_ASSIGN_CONCAT:
if (bytekit_equal_node($s, $ol['op1'])) {
$res1 = bytekit_trace_backward($func, $i-1, $end, $ol['op1']) == BYTEKIT_IS_SAFE;
$res2 = bytekit_trace_backward($func, $i-1, $end, $ol['op2']) == BYTEKIT_IS_SAFE;
return ($res1 && $res2) ? BYTEKIT_IS_SAFE : BYTEKIT_IS_UNSAFE;
}
break;
case BYTEKIT_CONCAT:
if (bytekit_equal_node($s, $ol['result'])) {
$res1 = bytekit_trace_backward($func, $i-1, $end, $ol['op1']) == BYTEKIT_IS_SAFE;
$res2 = bytekit_trace_backward($func, $i-1, $end, $ol['op2']) == BYTEKIT_IS_SAFE;
return ($res1 && $res2) ? BYTEKIT_IS_SAFE : BYTEKIT_IS_UNSAFE;
}
break;
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 39
Include Checker in Aktion
<?php
define("MODULE", "module1");
define("BASE_PATH", "/directory/");
include "/konstante.php";
include BASE_PATH . MODULE . ".php";
include $x . ".php";
$x = MODULE . "1.";
$y = $x;
include $y . ".php";
?>
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 40
Danke fürs Zuhören...
Fragen ?
http://www.bytekit.org
Stefan Esser • Bytekit - Ein Werkzeug zur Arbeit mit PHP Bytecode • Mai 2009 • 41