| Home / Indice sezione | www.icosaedro.it | ![]() |
Offuscatore di codice PHPUltimo aggiornamento: 2008-02-07
Qui presento un programma Bash che rende irriconoscibile il sorgente di PHP eliminando spazi, commenti, cambiando il nome a funzioni e variabili. Ovviamente, ciò non impedisce la copia del programma offuscato, e neppure impedisce a persone determinate di modificarlo a loro piacimento. Inoltre, le stringhe non vengono crittate nè oscurate in alcun modo, sicché le informazioni riservate come le password rimangono in chiaro. Tuttavia penso che possa essere una soluzione sufficiente in molte occasioni, ed inoltre non richiede di installare nulla sul server.
Che cosa fa php-obfuscator
Funzionalità e limitazioni
Opzioni del programma
Esempio di programma offuscato
Download
Altri offuscatori e compilatori per PHP
Riferimenti
Bisogna indicare da linea di comando tutti i file .php che compongono l'applicazione. php-obfuscator analizza questi file uno per uno e raccoglie l'elenco degli identificatori in essi definiti, per il momento solo variabili e funzioni. Quindi sostituisce ogni identificatore con un nome anonimo ed elimina commenti e spaziature. Il sorgente così ottenuto risulta poco leggibile e difficilmente modificabile. Notare che i riferimenti a variabili o funzioni esterne non vengono offuscati, altrimenti questo comprometterebbe la possibilità di interfacciarsi con questi componenti non offuscati. Ovviamente il codice HTML che circonda il codice PHP non viene toccato.
In definitiva, i vari file che compongono l'applicazione WEB vengono resi (quasi) illeggibili, ma rimangono pur sempre normali programmi PHP e non richiedono alcun particolare ambiente di esecuzione o libreria di runtime.
Attenzione! Offuscare non impedisce di copiare il sorgente, e neppure impedisce del tutto che venga modificato. Alcuni prodotti elencati più avanti offrono un livello maggiore di protezione.
$v_1, $v_2, ecc.f_1,
f_2, ecc.
Questo offuscatore non ha alcuna pretesa di essere completo nè
inviolabile. Inoltre questo programma non implementa un parser del
linguaggio PHP, ma opera solo sulle stringhe. Ad esempio, tutto quello che
segue il carattere $ è il nome di una variabile; tutto
quello che sta tra la parola function e la parentesi tonda aperta
è il nome di una funzione; ecc. Limiti e restrizioni:
${X}.$$X.$x().pg_fetch_object() che
restituisce un oggetto i cui campi sono i nomi dei campi di una riga
della tabella.eval().function
nomefunzione(... con le parole separate da spazi o caratteri
di tabulazione. Ad esempio, non si può mettere un a-capo tra la
parola riservata function e il nome della funzione.function.
Si risolve, ad esempio, scrivendo un commento vuoto all'inizio:
/**/ function RollIcon()$quantità non è valido.
Un aspetto interessante di questo programma è il modo in cui risolve
il problema dei riferimenti incrociati tra file inclusi. Se ad esempio ho un
modulo A.php che importa B.php, è necessario
che gli identificatori dichiarati nei due moduli corrispondano. Ad esempio,
se in B.php dichiaro una funzione di nome b che a
sua volta viene richiamata all'interno dell'altro modulo A.php,
è necessario che il nome della funzione venga convertito nello stesso
nome in entrambi i file. A questo scopo l'offuscatore richiede di specificare
tutti i file coinvolti nel progetto, in modo da poter collezionare tutti
gli identificatori coinvolti: a nome in chiaro uguale corrisponderà
un uguale nome offuscato.
L'offuscatore accetta uno o più file da trattare. Questi file verranno sostituiti dalle versioni offuscate. Quindi bisogna fare preventivamente una copia dei file originali! Tipicamente, per ogni applicazioni dovremo confezionare un apposito programma per il rilascio e il deploy del progetto. Questo programma dovrebbe occuparsi di creare una copia dei file necessari in una directory provvisoria, quindi dovrebbe richiamare l'offuscatore sui file di questa directory prima di eseguire l'upload. La sintassi del comando che avvia l'offuscatore è:
php-obfuscator opzioni files...
dove le opzioni principali sono:
-rRimuove commenti, spazi e a-capo non essenziali. -vOffusca i nomi delle variabili e i campi delle classi. -fOffusca i nomi delle funzioni dichiarate nei file indicati. -aOffusca tutto. Equivale alle opzioni -r -v -f.-hStampa un riassunto delle opzioni disponibili.
Questo è un piccolo programma usato in questo sito:
<?
$i = 0;
$examples[$i++] = array("x", "--");
$examples[$i++] = array("bugged-web-page.txt",
"Very bugged WEB page");
$examples[$i++] = array("../php/esempio1.cgi",
"Call-back handling in WEB pages");
$examples[$i++] = array("../php/esempio2.cgi",
"Secure call-back handling in WEB pages");
$examples[$i++] = array("index.html",
"The index page of the section dedicated to PHPLint");
$examples[$i++] = array("phplint-on-line.html",
"The code of this same WEB page!");
$n_examples = $i;
class FormData {
public $example_n = 0;
public $width = 80;
public $height = 12;
public $check_extended = TRUE;
public $check_controls = TRUE;
public $print_context = TRUE;
public $errors = TRUE;
public $warnings = TRUE;
public $notices = FALSE;
public $show_source = 1; # 0=no, 1=text+line no., 2=colors
public $src = "";
}
function PrintSizer()
{
echo '<table cellspacing=0 cellpadding=0><tr><td valign=center>';
Button('<', 'SizeButton', 'w');
echo '</td><td>';
Button('^', 'SizeButton', 'n'); echo BR;
Button('V', 'SizeButton', 's');
echo '</td><td valign=center>';
Button('>', 'SizeButton', 'e');
echo '</td></tr></table>';
}
function Parse($fd)
{
$src_file = "/tmp/phplint-" . time();
#$err_file = "/tmp/phplint-b";
$f = @fopen($src_file, "w");
if( $f == FALSE ){
trigger_error("fopen() failed: $php_errormsg");
}
fwrite($f, $fd->src);
fclose($f);
echo '<h2>PHPLint report</h2><pre>';
$opt = "--version --no-file-name --tab-size ". TAB_SIZE;
if( $fd->debug == 1 )
$opt .= " -d";
if( ! $fd->check_extended ) $opt .= " --no-ascii-ext-check";
if( ! $fd->check_controls ) $opt .= " --no-ctrl-check";
if( $fd->print_context ) $opt .= " --print-context";
if( ! $fd->errors ) $opt .= " --no-errors";
if( ! $fd->warnings ) $opt .= " --no-warnings";
if( ! $fd->notices ) $opt .= " --no-notices";
$cmd = PHPLINT ." $opt $src_file";
#system("$cmd 2>&1 | ". CODE2HTML . ' | ' . NL2BR);
system("$cmd 2>&1 | expand -t ". TAB_SIZE ." | ". TEXT2HTML);
echo '</pre>';
/***
if( file_exists($err_file) && filesize($err_file) > 0 ){
echo("Errors:<blockquote><pre>");
system("cat $err_file");
echo("</pre></blockquote>");
}
***/
if( $fd->show_source == 1 ){
echo '<h2>Source text for line references</h2><pre>';
system("nl -ba $src_file | expand -t ". TAB_SIZE ." | ". CODE2HTML);
echo '</pre>';
} else if( $fd->show_source == 2 ){
echo '<h2>Source with highlighted syntax</h2>';
highlight_file($src_file);
}
unlink($src_file);
}
function DrawForm($fd)
{
global $examples, $n_examples;
echo '<div style="background-color: #cccccc; border: 10px solid #cccccc;">';
Form();
echo '<table cellspacing=0 cellpadding=0 width=100%><tr><td>';
echo '<b>PHPLint on-line - PHP source parser and validator</b>';
echo '<p>Choose an example, or paste your code here.</p>';
echo 'Example: ';
Select('example_n');
for($i=0; $i<$n_examples; $i++)
Option($i, $fd->example_n, TextToHtml($examples[$i][1]));
Select_();
echo ' ';
Button("See Example", "SeeButton");
echo '</td><td valign=bottom align=right>';
PrintSizer();
echo '</td></tr></table>';
# Form del sorgente:
Hidden('width', $fd->width);
Hidden('height', $fd->height);
echo '<TEXTAREA name=src',
' cols=', $fd->width,
' rows=', $fd->height, '>',
TextToHtml($fd->src),
'</TEXTAREA><br>';
echo '<table width=100% cellspacing=0 cellpadding=0>',
'<tr><td valign=top>';
Box("Report:");
CheckBox("errors", $fd->errors, "Show Errors"); echo BR;
CheckBox("warnings", $fd->warnings, "Show Warnings"); echo BR;
CheckBox("notices", $fd->notices, "Show Notices"); echo P;
CheckBox("print_context", $fd->print_context, 'Print a line of context');
Box_();
echo '</td><td valign=top>';
Box('Optional checks:');
CheckBox("check_extended", $fd->check_extended,
'Presence of extended-ASCII characters'); echo BR;
CheckBox("check_controls", $fd->check_controls,
'Presence of ASCII control characters'); echo BR;
Box_();
Box("Show:");
RadioButton('show_source', $fd->show_source, 0,
"Report"); echo BR;
RadioButton('show_source', $fd->show_source, 1,
"Report + source with line numbers"); echo BR;
RadioButton('show_source', $fd->show_source, 2,
"Report + source with syntax highlighted");
Box_();
#CheckBox('debug', $fd->debug, "Debug");
echo '</td></tr></table>';
echo P, CENTER;
Button("Clear", "ClearButton");
echo HSPACE, HSPACE;
Button("Parse", "ParseButton");
echo CENTER_, FORM_;
echo("</div>");
}
function GetPost()
{
global $n_examples;
$fd = new FormData();
$fd->example_n = GetSelect('example_n', $n_examples);
$fd->width = min(max((int) $_POST['width'], 20), 150);
$fd->height = min(max((int) $_POST['height'], 5), 100);
$fd->check_extended = GetCheckBox('check_extended');
$fd->check_controls = GetCheckBox('check_controls');
$fd->print_context = GetCheckBox('print_context');
$fd->errors = GetCheckBox('errors');
$fd->warnings = GetCheckBox('warnings');
$fd->notices = GetCheckBox('notices');
$fd->debug = GetCheckBox('debug');
$fd->show_source = GetRadioButton('show_source', 3);
$fd->src = $_POST['src'];
return $fd;
}
function SeeButton()
{
global $examples, $n_examples;
$fd = GetPost();
if( $fd->example_n > 0 ){
$f = fopen($examples[$fd->example_n][0], "r");
$fd->src = fread($f, 100000);
fclose($f);
}
DrawForm($fd);
Piede();
}
function ClearButton()
{
$fd = GetPost();
$fd->example_n = 0;
$fd->src = "";
DrawForm($fd);
Piede();
}
function SizeButton($size)
{
$fd = GetPost();
switch( $size ){
case 'n': $fd->height -= 5; break;
case 's': $fd->height += 5; break;
case 'w': $fd->width -= 10; break;
case 'e': $fd->width += 10; break;
}
$fd->width = min(max($fd->width, 20), 150);
$fd->height = min(max($fd->height, 5), 100);
DrawForm($fd);
Piede();
}
function ParseButton()
{
$fd = GetPost();
DrawForm($fd);
Parse($fd);
Piede();
}
if( ! RequestHandler() ){
DrawForm(new FormData());
Piede();
}
?>
Ed ecco la versione offuscata con il mio programma, ottenuta usando
l'opzione -a (ho inserito gli a-capo in modo da troncare le
righe a una lunghezza ragionevole, perché l'offuscatore restituisce
tutto su di una sola riga):
<? $v_2 = 0; $v_3[$v_2++] = array("x", "--"); $v_3[$v_2++] =
array("bugged-web-page.txt", "Very bugged WEB page"); $v_3[$v_2++]
= array("../php/esempio1.cgi", "Call-back handling in WEB pages");
$v_3[$v_2++] = array("../php/esempio2.cgi", "Secure call-back
handling in WEB pages"); $v_3[$v_2++] = array("index.html", "The
index page of the section dedicated to PHPLint"); $v_3[$v_2++] =
array("phplint-on-line.html", "The code of this same WEB page!");
$v_7 = $v_2; class FormData { public $v_12 = 0; public $v_a = 80;
public $v_11 = 12; public $v_14 = TRUE; public $v_15 = TRUE; public
$v_e = TRUE; public $v_13 = TRUE; public $v_b = TRUE; public $v_10
= FALSE; public $v_d = 1; public $v_c = ""; } function f_6() { echo
'<table cellspacing=0 cellpadding=0><tr><td valign=center>'; Button('<',
'SizeButton', 'w'); echo '</td><td>'; Button('^', 'SizeButton', 'n');
echo BR; Button('V', 'SizeButton', 's'); echo '</td><td valign=center>';
Button('>', 'SizeButton', 'e'); echo '</td></tr></table>'; } function
f_4($v_1) { $v_6 = "/tmp/phplint-" . time(); $v_5 = @fopen($v_6, "w"); if(
$v_5 == FALSE ){ trigger_error("fopen() failed: $v_f"); } fwrite($v_5,
$v_1->v_c); fclose($v_5); echo '<h2>PHPLint report</h2><pre>'; $v_4 =
"--version --no-file-name --tab-size ". TAB_SIZE; if( $v_1->debug ==
1 ) $v_4 .= " -d"; if( ! $v_1->v_14 ) $v_4 .= " --no-ascii-ext-check";
if( ! $v_1->v_15 ) $v_4 .= " --no-ctrl-check"; if( $v_1->v_e ) $v_4 .=
" --print-context"; if( ! $v_1->v_13 ) $v_4 .= " --no-errors"; if(
! $v_1->v_b ) $v_4 .= " --no-warnings"; if( ! $v_1->v_10 ) $v_4 .= "
--no-notices"; $v_9 = PHPLINT ." $v_4 $v_6"; system("$v_9 2>&1 | expand
-t ". TAB_SIZE ." | ". TEXT2HTML); echo '</pre>'; if( $v_1->v_d == 1 ){
echo '<h2>Source text for line references</h2><pre>'; system("nl -ba
$v_6 | expand -t ". TAB_SIZE ." | ". CODE2HTML); echo '</pre>'; } else
if( $v_1->v_d == 2 ){ echo '<h2>Source with highlighted syntax</h2>';
highlight_file($v_6); } unlink($v_6); } function f_2($v_1) { global
$v_3, $v_7; echo '<div style="background-color: #cccccc; border: 10px
solid #cccccc;">'; Form(); echo '<table cellspacing=0 cellpadding=0
width=100%><tr><td>'; echo '<b>PHPLint on-line - PHP source parser and
validator</b>'; echo '<p>Choose an example, or paste your code here.</p>';
echo 'Example: '; Select('example_n'); for($v_2=0; $v_2<$v_7; $v_2++)
Option($v_2, $v_1->v_12, TextToHtml($v_3[$v_2][1])); Select_(); echo '
'; Button("See Example", "SeeButton"); echo '</td><td valign=bottom
align=right>'; f_6(); echo '</td></tr></table>'; Hidden('width',
$v_1->v_a); Hidden('height', $v_1->v_11); echo '<TEXTAREA name=src',
' cols=', $v_1->v_a, ' rows=', $v_1->v_11, '>', TextToHtml($v_1->v_c),
'</TEXTAREA><br>'; echo '<table width=100% cellspacing=0 cellpadding=0>',
'<tr><td valign=top>'; Box("Report:"); CheckBox("errors", $v_1->v_13,
"Show Errors"); echo BR; CheckBox("warnings", $v_1->v_b, "Show
Warnings"); echo BR; CheckBox("notices", $v_1->v_10, "Show Notices");
echo P; CheckBox("print_context", $v_1->v_e, 'Print a line of
context'); Box_(); echo '</td><td valign=top>'; Box('Optional
checks:'); CheckBox("check_extended", $v_1->v_14, 'Presence of
extended-ASCII characters'); echo BR; CheckBox("check_controls",
$v_1->v_15, 'Presence of ASCII control characters'); echo BR;
Box_(); Box("Show:"); RadioButton('show_source', $v_1->v_d, 0,
"Report"); echo BR; RadioButton('show_source', $v_1->v_d, 1, "Report
+ source with line numbers"); echo BR; RadioButton('show_source',
$v_1->v_d, 2, "Report + source with syntax highlighted"); Box_(); echo
'</td></tr></table>'; echo P, CENTER; Button("Clear", "ClearButton");
echo HSPACE, HSPACE; Button("Parse", "ParseButton"); echo CENTER_, FORM_;
echo("</div>"); } function f_3() { global $v_7; $v_1 = new FormData();
$v_1->v_12 = GetSelect('example_n', $v_7); $v_1->v_a = min(max((int)
$_POST['width'], 20), 150); $v_1->v_11 = min(max((int) $_POST['height'],
5), 100); $v_1->v_14 = GetCheckBox('check_extended'); $v_1->v_15 =
GetCheckBox('check_controls'); $v_1->v_e = GetCheckBox('print_context');
$v_1->v_13 = GetCheckBox('errors'); $v_1->v_b = GetCheckBox('warnings');
$v_1->v_10 = GetCheckBox('notices'); $v_1->debug = GetCheckBox('debug');
$v_1->v_d = GetRadioButton('show_source', 3); $v_1->v_c = $_POST['src'];
return $v_1; } function f_7() { global $v_3, $v_7; $v_1 = f_3(); if(
$v_1->v_12 > 0 ){ $v_5 = fopen($v_3[$v_1->v_12][0], "r"); $v_1->v_c =
fread($v_5, 100000); fclose($v_5); } f_2($v_1); Piede(); } function f_1()
{ $v_1 = f_3(); $v_1->v_12 = 0; $v_1->v_c = ""; f_2($v_1); Piede(); }
function f_8($v_8) { $v_1 = f_3(); switch( $v_8 ){ case 'n': $v_1->v_11
-= 5; break; case 's': $v_1->v_11 += 5; break; case 'w': $v_1->v_a -= 10;
break; case 'e': $v_1->v_a += 10; break; } $v_1->v_a = min(max($v_1->v_a,
20), 150); $v_1->v_11 = min(max($v_1->v_11, 5), 100); f_2($v_1); Piede();
} function f_5() { $v_1 = f_3(); f_2($v_1); f_4($v_1); Piede(); } if(
! RequestHandler() ){ f_2(new FormData()); Piede(); }
?>
Alcune cose da osservare in questo esempio:
php-obfuscator.txt - Questo
è il testo sorgente del programma Bash. Salvare nel file
php-obfuscator e rendere eseguibile con chmod +x
php-obfuscator. REQUISITI: occorre avere l'interprete Bash
(è la shell comunemente installata in Linux) *e* l'interprete
PHP stand-alone (cioè la versione CGI o CLI del PHP).
Ecco alcuni prodotti che offuscano o altrimenti rendono illeggibile il sorgente del programma. Alcuni cambiano i nomi delle variabili, delle funzioni ecc. Altri intercettano il bytecode prodotto dall'interprete PHP e quindi sono una specia di "compilatori". Altri applicano algoritmi di crittografia al codice sorgente e poi lo decrittano al volo per eseguirlo. Alcuni combinano due o più di questi metodi.
Offuscatori a parte, ci sono anche i compilatori. Un compilatore ha come effetto collaterale di eliminare del tutto il codice sorgente. In generale questi compilatori non producono codice del processore, ma intercettano il bytecode prodotto dall'interprete PHP ed eseguoni poi quello, con un vantaggio prestazionale comunque notevole. Il problema della compilazione di un linguaggio interpretato a tipizzazione dinamica come PHP viene discusso nell'articolo PHP Analisi Critica (www.icosaedro.it/articoli/php-analisi-critica.html).
Ovviamente il server deve essere configurato opportunamente per poter eseguire il prodotto di questi compilatori. A volte basta patchare l'interprete PHP con una estensione, a volte è richiesta l'installazione di ulteriore software o una particolare configurazione del WEB server.
| Umberto Salsi | Contatto | Mappa | Home / Indice sezione |