因为最近在学java,学完基础知识想从Java的反序列化继续学习,借着这个机会来复习一下php的反序列化
一、序列化基础知识
1、序列化的定义
序列化(串行化):是将变量转换为可保存或传输的字符串的过程; 反序列化(反串行化):就是在适当的时候把这个字符串再转化成原来的变量使用; 这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性; 常见的php序列化和反序列化方式主要有:serialize,unserialize
2、表达方式

<?php
class Person
{
public $name;
public $age;
public $sex;
public function __construct($name="张三", $sex="男", $age=22)
{
$this->name = $name;
$this->sex = $sex;
$this->age = $age;
}
public function say()
{
echo "我叫:" . $this->name . ",性别:" . $this->sex . ",年龄:" . $this->age;
}
}
$Person1 = new Person();
echo serialize(($Person1));
回显为
O:6:"Person":3:{s:4:"name";s:6:"张三";s:3:"age";i:22;s:3:"sex";s:3:"男";}
O代表object
6代表对象名长度
Person代表对象名称
3代表对象中的变量个数
s和i等代表变量数据类型
s代表 string
i代表 int
变量是int型时,没有长度,不加引号
4代表变量名长度
name代表变量名

3.public、protected、private下序列化对象的区别
php v7.x反序列化的时候对访问类别不敏感
public变量
直接变量名反序列化出来
protected变量
\x00 + * + \x00 + 变量名
可以用S:5:"\00*\00op"来代替s:5:"?*?op"
private变量
\x00 + 类名 + \x00 + 变量名
反序列化中s和S的区别
如果类型是s,会调用以下函数,简单来说就是将\解释成十六进制,来转成字符
static zend_string *unserialize_str(const unsigned char **p, size_t len, size_t maxlen)
{
size_t i, j;
zend_string *str = zend_string_safe_alloc(1, len, 0, 0);
unsigned char *end = *(unsigned char **)p+maxlen;
if (end < *p) {
zend_string_efree(str);
return NULL;
}
for (i = 0; i < len; i++) {
if (*p >= end) {
zend_string_efree(str);
return NULL;
}
if (**p != '\\') {
ZSTR_VAL(str)[i] = (char)**p;
} else {
unsigned char ch = 0;
for (j = 0; j < 2; j++) {
(*p)++;
if (**p >= '0' && **p <= '9') {
ch = (ch << 4) + (**p -'0');
} else if (**p >= 'a' && **p <= 'f') {
ch = (ch << 4) + (**p -'a'+10);
} else if (**p >= 'A' && **p <= 'F') {
ch = (ch << 4) + (**p -'A'+10);
} else {
zend_string_efree(str);
return NULL;
}
}
ZSTR_VAL(str)[i] = (char)ch;
}
(*p)++;
}
ZSTR_VAL(str)[i] = 0;
ZSTR_LEN(str) = i;
return str;
}
二、反序列化特性
1、反序列化之后的内容为一个对象。
2、反序列化生成的对象里的值,由反序列化里的值提供,与原有类预定义的值无关。
3、反序列化不触发类的成员方法(成员函数)(魔术方法除外),需要调用方法后才能触发。
三、魔术方法的构造和解析
__construct() 类的构建函数
__destruct() 类的析构函数
__call() 在对象中调用一个不可以访问方法时调用
__callStatic() 用静态方式中调用一个不可以访问方法时调用
__get() 获得一个类的成员变量时调用
__isset() 当对不可访问属性调用isset()或empyt()时调用
__set() 设置一个类的成员变量时调用
__unset() 当对不可访问属性调用unset()时被调用
__sleep() 执行serialize()时,先会调用这个函数
__wakeup() 执行unserialize()时,会先调用这个函数
__toString() 类被当成字符串时的回应方法
__invoke() 调用函数的方式调用一个对象时的回应方法
__set_state() 调用var_export()导出类时,此静态方法被调用
__clone() 当对象复制完成时调用
__autoload() 尝试加载未定义的类
__debuginfo() 打印所需调式信息
1、什么是魔术方法
一个预定义好的,在特定情况下自动触发的行为方法。
2、魔术方法的作用
反序列化漏洞的成因:
反序列化的过程中,unserialize()接收的值(字符串)可控;
通过更改这个值(字符串),得到所需要的代码;
通过调用方法,触发代码执行。
魔术方法的作用就是在特定条件下自动调用相关方法,最终导致触发代码
3、魔术方法相关机制
1、触发机制(动作不同,触发的魔术方法也不同)
2、功能
3、参数(一些特殊魔术方法会传参)
4、返回值
四、常见的序列化格式
- 二进制格式
- 字节数组
- json字符串
- xml字符串