PHP序列化以及反序列化系列[1]--PHP序列化格式的写法

反序列化:对单一的已序列化的变量进行操作,将其转换回 PHP 的值(zval)。

PHP序列化方式

PHP在序列化的时候会将相应的变量以对应的键值进行储存。

将一个类序列化的话,处理代码主要的 文件:ext/standard/var.c 中,如下。

php_var_serialize_class()函数:

static void php_var_serialize_class(smart_str *buf, zval *struc, zval *retval_ptr, HashTable *var_hash TSRMLS_DC) /* {{{ */ { ... incomplete_class = php_var_serialize_class_name(buf, struc TSRMLS_CC); ...

php_var_serialize_class_name()函数:

static inline zend_bool php_var_serialize_class_name(smart_str *buf, zval *struc TSRMLS_DC) /* {{{ */ { PHP_CLASS_ATTRIBUTES; PHP_SET_CLASS_ATTRIBUTES(struc); smart_str_appendl(buf, "O:", 2); smart_str_append_long(buf, (int)name_len); smart_str_appendl(buf, ":\"", 2); smart_str_appendl(buf, class_name, name_len); smart_str_appendl(buf, "\":", 2); PHP_CLEANUP_CLASS_ATTRIBUTES(); return incomplete_class; }

需要序列化一个类的话,首先PHP会先将类名序列化。格式为

O:类名长度:"类名":值:{} <?php class test { public function show_one() { echo $this->one; } public function show_two() { echo "123"; } }

例:如果一个类名叫做 test 的类没有定义任何变量的话,序列化之后结果如下:

O:4:"test":0:{}

我们可以看到,这个类中的方法没有在序列化字符串中出现,也体现了开头的“序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。”。

其中还有比较特殊的序列化就是 数组中的引用(&) 的序列化与实例化后对象中自身的二次赋值。

我们在这用PHP Internal Book中的例子。

例1:

<?php /** * User: LonelyRain * Date: 16/9/14 * Time: 下午12:53 */ $a = ["foo"]; $a[1] =& $a[0]; $s = serialize($a); print $s;

以上代码的序列化结果是

a:2:{i:0;s:3:"foo";i:1;R:2;}

这里的 R:2; 部分意味着"指向第二个值".什么是第二个值?整个数组代表第一个值, (s:3:"foo") 代表第二个值.

<?php /** * User: LonelyRain * Date: 16/9/14 * Time: 下午12:53 */ $o = new stdClass; $o->foo = $o; $s = serialize($o); print $s;

以上代码的序列化结果是

O:8:"stdClass":1:{s:3:"foo";r:1;}

以下是zval对应的类型和键对照表

序列化键名对照表: 数组中二次赋值(&): R; 对象二次赋值 : r; NULL : N; true : b:1; false : b:0; Long : i; Double : d; String : s/S; Class : C; Array : a; Object : O;

变量不同的属性也有着不同的格式

public : key; protected : \0*\0key; private : \0key\0;

通过实例来观察:

<?php /** * User: LonelyRain * Date: 16/9/14 * Time: 下午12:53 */ class Test { public $public = 1; protected $protected = 2; private $private = 3; } $a = new Test(); $s = serialize($a); var_dump($s);

结果:

"O:4:"Test":3:{s:6:"public";i:1;s:12:"*protected";i:2;s:13:"Testprivate";i:3;}"

再来看一看反序列化的相关知识。大家应该注意到了String对应着两个键,s与S。

serialize()与unserialize()处理有着一些差异。PHP源码serialize()中是没有相关序列化是以S为标识的,但是在unserialize中又有对S键的相关处理,下面我把相关部分代码贴出来供读者参考。

case ‘S‘: goto yy10; ... yy10: yych = *(YYMARKER = ++YYCURSOR); if (yych == ‘:‘) goto yy39; goto yy3; ... yy39: yych = *++YYCURSOR; if (yych == ‘+‘) goto yy40; if (yych <= ‘/‘) goto yy18; if (yych <= ‘9‘) goto yy41; goto yy18; case ‘s‘: goto yy9; ... yy9: yych = *(YYMARKER = ++YYCURSOR); if (yych == ‘:‘) goto yy46; goto yy3; ... yy46: yych = *++YYCURSOR; if (yych == ‘+‘) goto yy47; if (yych <= ‘/‘) goto yy18; if (yych <= ‘9‘) goto yy48; goto yy18; ... ...

如果大家继续看接下去的代码下去,会发现s和S的就会发现两个键的处理方式是一模一样的。

如果大家看了phpcodz 10,里面写道 a:1:{s:8:"ryatsyne"tO:8:"ryatsyne":0:{}} 这样可以突破

static public function safeUnserialize( $serialized ) { // unserialize will return false for object declared with small cap o // as well as if there is any ws between O and : if ( is_string( $serialized ) && strpos( $serialized, "\0" ) === false ) { if ( strpos( $serialized, ‘O:‘ ) === false ) { // the easy case, nothing to worry about // let unserialize do the job return @unserialize( $serialized ); } else if ( ! preg_match(‘/(^|;|{|})O:[+\-0-9]+:"/‘, $serialized ) ) { // in case we did have a string with O: in it, // but it was not a true serialized object return @unserialize( $serialized ); } } return false; }

这个payload在php5.6.23中失效,看以下代码

yy48: ++YYCURSOR; if ((YYLIMIT - YYCURSOR) < 2) YYFILL(2); yych = *YYCURSOR; if (yych <= ‘/‘) goto yy18; if (yych <= ‘9‘) goto yy48; if (yych >= ‘;‘) goto yy18; yych = *++YYCURSOR; if (yych != ‘"‘) goto yy18; ++YYCURSOR; { size_t len, maxlen; char *str; len = parse_uiv(start + 2); maxlen = max - YYCURSOR; if (maxlen < len) { *p = start + 2; return 0; } str = (char*)YYCURSOR; YYCURSOR += len; if (*(YYCURSOR) != ‘"‘) { *p = YYCURSOR; return 0; } if (*(YYCURSOR + 1) != ‘;‘) { *p = YYCURSOR + 1; return 0; } YYCURSOR += 2; *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_STRINGL(*rval, str, len, 1); return 1; }

代码中已经多加了分号符号校验,这个tricky在这个php版本中是无效的。

if (*(YYCURSOR + 1) != ‘;‘) { *p = YYCURSOR + 1; return 0; } WDDX序列化方式

序列化本质就是将程序的值以相应的格式保存下来,所以我们不止单单可以用上面的serialize函数进行序列化。PHP还提供了另外一种序列化格式为Web分布式数据交换(WDDX)。WDDX是XML的子集,所以符合WDDX的序列化过后的字符串格式是符合xml的规范的。

演示代码:

<?php /** * User: LonelyRain * Date: 16/9/14 * Time: 下午12:53 */ $a = ["foo"]; $a[1] =& $a[0]; echo wddx_serialize_value($a); ?>

结果:

<wddxPacket version=‘1.0‘><header/><data><array length=‘2‘><string>foo</string><string>foo</string></array></data></wddxPacket>

可以看到才用wddx_serialize_value()函数处理的$a和之前使用serialize()函数处理的值都被保存下来了,只不过遵守的格式有着相应的区别。

WDDX序列化反序列化相关函数:

wddx_serialize_value: 将单一值连续化。 wddx_serialize_vars : 将多值连续化。 wddx_packet_start : 开始新的 WDDX 封包。 wddx_packet_end : 结束的 WDDX 封包。 wddx_add_vars : 将 WDDX 封包连续化。 wddx_deserialize : 将 WDDX 封包解连续化。

这一篇主要讲了序列化后数据的格式,下一次会写PHP序列化中一块重要的内容,PHP的魔术方法等内容。

Reference:

PHP内核

PHP string序列化与反序列化语法解析不一致带来的安全隐患

时间: 2024-10-17 12:24:57

PHP序列化以及反序列化系列[1]--PHP序列化格式的写法的相关文章

序列化、反序列化的版本控制以及序列化、反序列化集合对象

当涉及到跨进程甚至是跨域传输数据的时候,我们需要把对象序列化和反序列化. 首先可以使用Serializable特性. [Serializable] public class Person { public string _firstName; public string _secondName; //序列化 [OnSerializing] internal void OnSerializing(StreamingContext context) { _firstName = _firstName

给Ajax一个漂亮的嫁衣——Ajax系列之五(下)之序列化和反序列化

给Ajax一个漂亮的嫁衣——Ajax系列之五(下)之序列化和反序列化 标签: ajaxdictionaryjsonobject服务器function 2012-07-25 18:41 2242人阅读 评论(6) 收藏 举报  分类: Ajax(6)  版权声明:本文为博主原创文章,未经博主允许不得转载. Ajax最强悍的功能莫过于服务器和客户端之间的异步交互,他们在交互的时候不是通过soap协议等,而是通过回调函数,以Json的格式传送数据. 由于Json格式的限制,在很多情况下,稍微复杂一些的

python学习day4之路文件的序列化和反序列化

json和pickle序列化和反序列化 json是用来实现不同程序之间的文件交互,由于不同程序之间需要进行文件信息交互,由于用python写的代码可能要与其他语言写的代码进行数据传输,json支持所有程序之间的交互,json将取代XML,由于XML格式稍微比较复杂.现在程序之间的交互都是用json来进行文件信息的交互. 在使用json序列化和反序列化的时候,dump一次,就要load一次,不能操作. json序列化的过程,就是写入文件中,让另外一个编程语言进行调用: import json in

【Java基础】序列化与反序列化深入分析

一.前言 复习Java基础知识点的序列化与反序列化过程,整理了如下学习笔记. 二.为什么需要序列化与反序列化 程序运行时,只要需要,对象可以一直存在,并且我们可以随时访问对象的一些状态信息,如果程序终止,那么对象是肯定不会存在的,但是有时候,我们需要再程序终止时保存对象的状态信息,之后程序再次运行时可以重新恢复到之前的状态,如,玩家玩游戏退出时,需要保存玩家的状态信息(如等级.装备等等),之后玩家再此登入时,必须要恢复这些状态信息.我们可以通过数据库手段来达到这个保存状态的目的,在Java中,我

java 对象序列化与反序列化

Java序列化与反序列化是什么? 为什么需要序列化与反序列化? 如何实现Java序列化与反序列化? 本文围绕这些问题进行了探讨. 1.Java序列化与反序列化  Java序列化是指把Java对象转换为字节序列的过程: Java反序列化是指把字节序列恢复为Java对象的过程. 2.为什么需要序列化与反序列化 我们知道,当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本.图片.音频.视频等, 而这些数据都会以二进制序列的形式在网络上传送.那么当两个Java进程进行通信时,能否实现进程间的

python基础之继承组合应用、对象序列化和反序列化,选课系统综合示例

继承+组合应用示例 1 class Date: #定义时间类,包含姓名.年.月.日,用于返回生日 2 def __init__(self,name,year,mon,day): 3 self.name = name 4 self.year=year 5 self.mon=mon 6 self.day=day 7 def tell_birth(self): 8 print('%s:%s-%s-%s'%(self.name,self.year,self.mon,self.day)) 9 10 11

Java IO详解(六)------序列化与反序列化(对象流)

File 类的介绍:http://www.cnblogs.com/ysocean/p/6851878.html Java IO 流的分类介绍:http://www.cnblogs.com/ysocean/p/6854098.html Java IO 字节输入输出流:http://www.cnblogs.com/ysocean/p/6854541.html Java IO 字符输入输出流:https://i.cnblogs.com/EditPosts.aspx?postid=6859242 Jav

初识序列化和反序列化,使用BinaryFormatter类、ISerializable接口、XmlSerializer类进行序列化和反序列化

序列化是将对象转换成字节流的过程,反序列化是把字节流转换成对象的过程.对象一旦被序列化,就可以把对象状态保存到硬盘的某个位置,甚至还可以通过网络发送给另外一台机器上运行的进程.本篇主要包括: ● 使用BinaryFormatter类进行序列化和反序列化● 使用ISerializable接口自定义序列化过程● 使用XmlSerializer类进行序列化和反序列化 □ 使用BinaryFormatter类进行序列化和反序列化 首先把需要序列化的类打上[Serializable]特性,如果某个字段不需

Python_日记 序列化和反序列化

Python序列化和反序列化 通过将对象序列化可以将其存储在变量或者文件中,可以保存当时对象的状态,实现其生命周期的延长.并且需要时可以再次将这个对象读取出来.Python中有几个常用模块可实现这一功能. pickle模块 存储在变量中 dumps(obj)返回存入的字节 dic = {'age': 23, 'job': 'student'} byte_data = pickle.dumps(dic) # out -> b'\x80\x03}q\x00(X\x03\x00\x00\...' pr