PHP序列化与反序列化(__sleep与__wakeup)

前言

总结一下序列化和反序列化的入门知识主要介绍__sleep与__wakeup

简单介绍

1、序列化
将一个对象转换为字符串
2、反序列化
将一个字符串恢复成对象
3、常见魔术方法

__construct: 在创建对象时候初始化对象,一般用于对变量赋初值。
__destruct: 和构造函数相反,当对象所在函数调用完毕后执行。
__toString:当对象被当做一个字符串使用时调用。
__sleep:序列化对象之前就调用此方法(其返回需要一个数组)
__wakeup:反序列化恢复对象之前调用该方法
__call:当调用对象中不存在的方法会自动调用该方法。
__get:在调用私有属性的时候会自动执行
__isset():在不可访问的属性上调用isset()或empty()触发
__unset():在不可访问的属性上使用unset()时触发

PHP序列化

php序列化的函数为serialize()
例:

<?php
class peak{
	public $name = "1stPeak";
	protected $sex = "man";
	private $age = "18";
	}
$a = new peak;
echo serialize($a);
?>

序列化后的结果为:

O:4:"peak":3:{s:4:"name";s:7:"1stPeak";s:6:"*sex";s:3:"man";s:9:"peakage";s:2:"18";}

解释一下上面所代表的的是什么:
O:表示对象(object)
7:对象名称的长度
peak:对象名称
3:表示对象里属性的个数,这里的属性为warn
s:属性名类型
4:属性名长度
name:属性的名称
s:属性值的类型
7:属性值的长度
1stPeak:属性值
s:属性名类型
6:属性名长度
*sex:属性的名称
s:属性值的类型
3:属性值的长度
man:属性值
s:属性名类型
9:属性名长度
peakage:属性的名称
s:属性值的类型
2:属性值的长度
18:属性值
注:这个属性的值可有可无,没有赋值,那么序列化后就没有该值
这里可能不理解序列化后的sex和age,这个是由访问控制修饰符导致的(访问修饰符的不同,序列化后的属性名的长度和属性名会有所不同)
例:

public(公有)
protected(受保护)
private(私有)
各访问修饰符序列化后的区别:
public:属性被序列化的时候属性名还是原来的属性名,没有任何改变
protected:属性被序列化的时候属性名会变成%00*%00属性名,长度跟随属性名长度而改变
private:属性被序列化的时候属性名会变成%00类名%00属性名,长度跟随属性名长度而改变

注:这里的%00也就是表示空字符,在url中用%00表示,在hex中表示\x00
Tip:%00和\x00也就是我们常说的00截断

Tip:
serialize()函数有个规定,在序列化对象时,如果对象存在有__sleep魔术方法,那么,在序列化对象时__sleep魔术方法会优先调用,然后再继续执行序列化操作
例:

<?php
class peak{
    public $name = "1stPeak";
    public $sex = "man";
    public $age = "18";
    public function __sleep(){
        return array('age');
    }
}
$a = new peak;
echo serialize($a);
?>

结果:O:4:"peak":1:{s:3:"age";s:2:"18";}
如上所示:只要序列化时执行了__sleep,就只会序列化__sleep中的属性

PHP反序列化

反序列化,顾名思义,就是将序列化后的字符串还原
unserialize()也有和serialize()相对应的魔术方法__wakeup,反序列化时,会优先检查是否存在__wakeup魔术方法,如果存在,就会优先调用__wakeup方法
例如:

<?php
class Demo
{
    public $flag = 'flag{aaaaaa}';
    public function __wakeup()
    {
        $this->flag = "bbbbbb";
        echo $this->flag;
    }
}
$a = new Demo();
$b = serialize($a);
echo $b."\n";
echo unserialize($b);
?>
输出:
O:4:"Demo":1:{s:4:"flag";s:12:"flag{aaaaaa}";}
bbbbbb

一般做CTF题目时绕过的方法就是:先序列化字符串,然后使序列化后字符串中属性的个数大于真实对象中属性的个数,即可绕过,例如XCTF的那道题:
题目为:

class xctf{
public $flag = '111';
public function __wakeup(){
exit('bad requests');
}
?code=

我们对其进行序列化:

<?php
class xctf{
public $flag = '111';
public function __wakeup(){
exit('bad requests');
    }
}
$a = new xctf;
echo serialize($a);
?>

结果为:O:4:"xctf":1:{s:4:"flag";s:3:"111";},然后将序列化后字符串中属性的个数由1改为2,大于真实属性个数1,即可绕过__wakeup
注:修改s(属性类型)或属性名长度也可绕过执行__wakeup,因为你构造的属性和真实的属性不符;但修改属性值是不会绕过__wakeup的,所以综述:只有改变属性个数、属性类型、属性名才可以绕过!

总结:这只是简单的php序列化和反序列化中__sleep和__wakeup简单的入门,后面的还有很多其它知识点,学完了再写blog记录一下。
参考:
https://www.freebuf.com/articles/web/221213.html
https://www.cnblogs.com/tr1ple/p/11156279.html

发布了148 篇原创文章 · 获赞 61 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/qq_41617034/article/details/105270864