少女祈祷中...
秋月竹云的博客
代码审计CTF

PHP反序列化——基础知识

序列化和反序列化

序列化:把程序里的数据结构变成一串可保存、可传输的文本或字节流。比如一个数组、对象、字典,原本存在内存里,序列化后可以写进文件、数据库,或者通过网络发送。

反序列化:把这串文本或字节流再还原回原来的数据结构。也就是把“保存起来的样子”重新变回程序能直接使用的变量、数组、对象。

php对象的序列化

PHP中通过 serialize() 函数实现序列化

class User {
    public $name = "Tom";
    public $age = 18;
}

$user = new User();
echo serialize($user);

结果为

O:4:"User":2:{s:4:"name";s:3:"Tom";s:3:"age";i:18;}

解释说明

  • O:表示这是一个对象

  • 4:"User":类名是 User,长度是 4

  • 2:有 2 个属性

  • s:4:"name":属性名 name

  • s:3:"Tom":字符串值 Tom

  • i:18:整数值 18

数组

$a = ["name" => "Tom", "age" => 18];
echo serialize($a);

结果为

a:2:{s:4:"name";s:3:"Tom";s:3:"age";i:18;}

解释

  • a:array,表示数组

  • 2:有 2 个元素

  • s:4:"name":键名是字符串 name,长度 4

  • s:3:"Tom":值是字符串 Tom,长度 3

  • s:3:"age":键名是字符串 age,长度 3

  • i:18:值是整数 18

数组序列化后以 a 开头
对象序列化后以 O 开头

php对象的反序列化

简单来说,反序列化就是序列化的逆过程。

通过 unserialize() 函数实现

class User {
    public $name = "Tom";
    public $age = 18;
}

$user = new User();
echo serialize($user);

序列化的结果为

O:4:"User":2:{s:4:"name";s:3:"Tom";s:3:"age";i:18;}

使用 unserialize 反序列化为User对象

$obj = unserialize('O:4:"User":2:{s:4:"name";s:3:"Tom";s:3:"age";i:18;}');
var_dump($obj);

结果为

object(User)#1 (2) { ["name"]=> string(3) "Tom" ["age"]=> int(18) }

访问控制修饰符

public(公开):类外、类内、子类都能访问
protected(受保护):类内和子类可以访问,类外不能直接访问
private(私有):只能在当前类内部访问,子类和类外都不能直接访问

在 PHP 序列化里,publicprotectedprivate 属性的写法不一样。

class User {
    public $name = "Tom";
    protected $age = 18;
    private $id = 1;
}

echo urlencode(serialize(new User()));

序列化的结果为

O:4:"User":3:{s:4:"name";s:3:"Tom";s:6:"\0*\0age";i:18;s:8:"\0User\0id";i:1;}

由于存在不可见字符 \0,所以直接打印会变成

O:4:"User":3:{s:4:"name";s:3:"Tom";s:6:"*age";i:18;s:8:"Userid";i:1;}

需要使用urlencode进行编码

O%3A4%3A%22User%22%3A3%3A%7Bs%3A4%3A%22name%22%3Bs%3A3%3A%22Tom%22%3Bs%3A6%3A%22%00%2A%00age%22%3Bi%3A18%3Bs%3A8%3A%22%00User%00id%22%3Bi%3A1%3B%7D
  • public -> 直接是属性名

  • protected -> \0*\0属性名

  • private -> \0类名\0属性名

魔术方法

魔术方法

触发时机

常见用途

__construct()

创建对象时

初始化属性

__destruct()

对象销毁时

资源释放、收尾

__wakeup()

反序列化后

恢复对象状态

__toString()

对象被当字符串时

输出字符串表示

__get()

访问不存在/不可访问属性时

动态取值

__set()

给不存在/不可访问属性赋值时

动态赋值

__call()

调用不存在的方法时

动态方法分发

__invoke()

对象被当函数调用时

让对象可调用

评论