PHP序列化与反序列化
serialize( void ) : string
serialize函数会把一个已有的数据转化成一串字符串,该字符串从前到后依次记录该数据的类型、长度(涉及到类似数组字符串这种拥有元素个数属性的数据序列化后会返回该值)、内容,同一数据的不同内容用 “ : ” 进行分割,例如
1 | $a="asd" //a被定义为一个字符串,类型是string,长度为3,内容“asd” |
实例
各种类型序列化的例子:
变量类型 | 进行序列化后 | 标志 |
---|---|---|
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 |
这里要说明一些特殊个例:array
和object
特例
array
array
这种组合型数据,长度为元素个数,其内容用花括号 { }括起来
花括号内的数据不仅用每个数据相应的序列化方式进行序列化,且在该数据前面要有一个i:x
这个i:x
代表的是其在数组中的下标,且下标和数据之间也要用;
间隔。
object
object
因为是使用者自定义的,所以系统在判断的时候想要知道你这个被序列化过的数据如果是object
该是什么object
就必须给它加上相应object
的名字,所以object
类型的数据序列化之后标志后要比 array
要再多一点东西,就是名字和名字的长度,不过这里不需要再用别的什么方式来表示,就只是简单的在标志 O 后面加上名字的长度和内容,中间全部都是:,不需要;分割。
花括号内部的数据内容不像array
那样需要标注下标,但是object
内部属性有自己的数据类型和名字,所以属性的名字起到了下标的作用,下标是整形数字,所以用i:x;
表示,而属性名是字符串,故用s:x:"xxx";
表示。
同时在object中因为存在公共属性、私有属性、保护属性,其中公共属性没有特殊点。另外两种在序列化时会有一点不一样
protected
受保护的属性其序列化后,属性名前会有一个 * ,如:
1 | class Test |
这里需要注意,受保护的属性名字序列化后虽然是显示为形如*name
这样的形式,但其实 * 前后各有一个%00
填充
private
私有化的属性其序列化后,属性名前会加上object的名字 ,如:
1 | class Test |
这里需要注意,私有化的属性名字序列化后虽然是显示为形如object_name+name
这样的形式,但其实 类似array
里面的* 一样object_name
前后各有一个%00
填充
unserialize( string $serialized
)
通过序列化可以将一个数据转化成纯字符串的形式从而极大地方便了其储存和传输,既然可以将数据序列化为纯字符串,自然也需要在使用时进行反序列化将其解析为原本的数据。
反序列化暂时不了解什么特殊,只知道在web反序列化时,若其内部有魔术方法_wakeup()
时,我们构造的payload对象属性个数大于实际属性个数时,_warmup()
魔术方法会被绕过
例如:
1 | class SoFun{ |
在该例中,假如我们构造一个payload为O:5:"SoFun":2:{s:7:"%00*%00file";s:5:"1.php";}
可以看到,class SoFun
中只有一个属性,而我们构造的paylaod中的属性个数故意多写了一个,此时该payload在反序列化时就会跳过_warmup()
,从而泄露我们想要看到的文件的源码