PHP序列化与反序列化

PHP序列化与反序列化

十一月 17, 2019 (Updated: )

serialize( void ) : string

serialize函数会把一个已有的数据转化成一串字符串,该字符串从前到后依次记录该数据的类型、长度(涉及到类似数组字符串这种拥有元素个数属性的数据序列化后会返回该值)、内容,同一数据的不同内容用 “ : ” 进行分割,例如

1
2
3
$a="asd"  //a被定义为一个字符串,类型是string,长度为3,内容“asd”

$b=serialize($a) //b会成为一个字符串,其内容为“ s:3:"asd" ” s为 a 的数据类型 string 的首字母,3 为 a 的长度或者说 a 中包含的元素(字符)个数,"asd" 为a的内容

实例

各种类型序列化的例子:

变量类型 进行序列化后 标志
int i:9; i
string s:3:”asd”; s
double d:9.90001; d
bool b:1; b
array a:3:{i:0;s:3:”asd”;i:1;i:111;i:2;d:12.10001;} a
null N; N
object O:9:”classname”:0:{} O

这里要说明一些特殊个例:arrayobject

特例

array

array这种组合型数据,长度为元素个数,其内容用花括号 { }括起来

花括号内的数据不仅用每个数据相应的序列化方式进行序列化,且在该数据前面要有一个i:x

这个i:x代表的是其在数组中的下标,且下标和数据之间也要用间隔。

object

object因为是使用者自定义的,所以系统在判断的时候想要知道你这个被序列化过的数据如果是object该是什么object就必须给它加上相应object的名字,所以object类型的数据序列化之后标志后要比 array要再多一点东西,就是名字和名字的长度,不过这里不需要再用别的什么方式来表示,就只是简单的在标志 O 后面加上名字的长度和内容,中间全部都是,不需要分割。

花括号内部的数据内容不像array那样需要标注下标,但是object内部属性有自己的数据类型和名字,所以属性的名字起到了下标的作用,下标是整形数字,所以用i:x;表示,而属性名是字符串,故用s:x:"xxx";表示。

同时在object中因为存在公共属性、私有属性、保护属性,其中公共属性没有特殊点。另外两种在序列化时会有一点不一样

protected

受保护的属性其序列化后,属性名前会有一个 * ,如:

1
2
3
4
5
6
7
8
9
10
11
12
class Test
{
protected $asd;
}
$a = new Test('sadasd');
echo serialize($a);
/*
输出结果整理成清楚的格式
O:4:"Test":1:{
s:6:"%00*%00asd";
s:6:"sadasd";
}

这里需要注意,受保护的属性名字序列化后虽然是显示为形如*name这样的形式,但其实 * 前后各有一个%00填充

private

私有化的属性其序列化后,属性名前会加上object的名字 ,如:

1
2
3
4
5
6
7
8
9
10
11
12
class Test
{
private $asd;
}
$a = new Test('sadasd');
echo serialize($a);
/*
输出结果整理成清楚的格式
O:4:"Test":1:{
s:6:"%00*%00asd";
s:6:"sadasd";
}

这里需要注意,私有化的属性名字序列化后虽然是显示为形如object_name+name这样的形式,但其实 类似array 里面的* 一样object_name前后各有一个%00填充

unserialize( string $serialized )

通过序列化可以将一个数据转化成纯字符串的形式从而极大地方便了其储存和传输,既然可以将数据序列化为纯字符串,自然也需要在使用时进行反序列化将其解析为原本的数据。

反序列化暂时不了解什么特殊,只知道在web反序列化时,若其内部有魔术方法_wakeup()时,我们构造的payload对象属性个数大于实际属性个数时,_warmup()魔术方法会被绕过

例如:

1
2
3
4
5
6
7
8
9
10
11
12
class SoFun{
protected $file='index.php';
function __destruct(){
if(!empty($this->file)) {
if(strchr($this-> file,"\\")===false && strchr($this->file,'/')===false)
show_source(dirname (__FILE__).'/'.$this ->file);
else
die('Wrong filename.');
}}
function __wakeup(){ $this-> file='index.php'; }
public function __toString(){return '' ;}
}

在该例中,假如我们构造一个payloadO:5:"SoFun":2:{s:7:"%00*%00file";s:5:"1.php";}

可以看到,class SoFun中只有一个属性,而我们构造的paylaod中的属性个数故意多写了一个,此时该payload在反序列化时就会跳过_warmup(),从而泄露我们想要看到的文件的源码

隐藏