序列化对象 - 在会话中存放对象

所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialize()函数能够重新把字符串变回php原来的值。 序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。

为了能够unserialize()一个对象,这个对象的类必须已经定义过。如果序列化类A的一个对象,将会返回一个跟类A相关,而且包含了对象所有变量值的字符串。 如果要想在另外一个文件中解序列化一个对象,这个对象的类必须在解序列化之前定义,可以通过包含一个定义该类的文件或使用函数spl_autoload_register()来实现。

上面是官方文档中对序列化的解释。简单来说,序列化就是将一个类中的所有变量保存在一个字符串中。那么反序列化当然就是把这个字符串转化为类啦~(前提是这个类已经定义了: D)

一个小例子

网上随便找的例子:

源码

<?php
class chybeta{
    var $test = '123';
}
$class1 = new chybeta;
$class1_ser = serialize($class1);
print_r($class1_ser);
?>

上面的源码输出如下:

O:7:"chybeta":1:{s:4:"test";s:3:"123";}

下面就逐个解释这些都是什么意思吧。

  • O 代表储存的是一个对象(object)
  • 7 表示对象的名称有7个字符。
  • "chybeta" 表示对象的名称。
  • 1 表示有一个值。
  • {s:4:"test";s:3:"123";} 中:

    • s 表示字符串
    • 4 表示该字符串的长度
    • "test" 为字符串的名称,之后的类似。
这里提示一下:序列化的时候会调用 __sleep() 这个函数,反序列化的时候会调用 __wakeup() 这个函数。(如果有的话)

如果对象中含有私有成员的话,序列化出来的成员名会包含两个空字符。举例如下:

源码

<?php 
    class test
    {
        private $flag = "flag{233}";
        public $a = "aaa";
        static $b = "bbb";
    }

    $test = new test;
    $data = serialize($test);
    echo $data;
 ?>

运行结果

O:4:"test":2:{s:10:"testflag";s:9:"flag{233}";s:1:"a";s:3:"aaa";}

可以看到 flag 成员的名称长度为 10 。转换成文件可以看到 test 两边有两个空字符。

补充

在一次内部赛的时候碰到的知识点:
漏洞有效的PHP版本未知。我本地环境用的是PHP5.4 。PHP在反序列化对象的时候,会先检测 O: ,然后再检测是否有 + 。如果是 + 的话就忽略 + 继续检测后面的数字。这就导致了可以绕过一些 waf 如 /O:\d/ 之类的正则。
参考资料:http://sec-redclub.com/archives/962/

最后修改:2019 年 06 月 12 日
如果觉得我的文章对你有用,请随意赞赏