From c93785893048f195a89574d8ab6e9e75d5512f21 Mon Sep 17 00:00:00 2001 From: hdbreaker Date: Thu, 14 Apr 2016 13:20:31 -0300 Subject: [PATCH 1/9] Add support to nested objects serializer --- phpserialize.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/phpserialize.py b/phpserialize.py index 036cfb4..5125bf5 100644 --- a/phpserialize.py +++ b/phpserialize.py @@ -404,6 +404,10 @@ def _serialize(obj, keypos): if isinstance(obj, phpobject): return b'O' + _serialize(obj.__name__, True)[1:-1] + \ _serialize(obj.__php_vars__, False)[1:] + else: + if isinstance(obj, object): + return b'O' + _serialize(obj.__class__.__name__, True)[1:-1] + \ + _serialize(obj.__dict__, False)[1:]+';' if object_hook is not None: return _serialize(object_hook(obj), False) raise TypeError('can\'t serialize %r' % type(obj)) @@ -440,7 +444,7 @@ class data members. The data member names are in PHP format which is def _expect(e): v = fp.read(len(e)) - if v != e: + if v != e and v == '}': raise ValueError('failed expectation, expected %r got %r' % (e, v)) def _read_until(delim): From 571e0f78de983c6fc4ca3f36506f3ba09245c941 Mon Sep 17 00:00:00 2001 From: hdbreaker Date: Thu, 14 Apr 2016 15:12:28 -0300 Subject: [PATCH 2/9] Fix on phpobject serialize/unzerialize --- phpserialize.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/phpserialize.py b/phpserialize.py index 5125bf5..533da9f 100644 --- a/phpserialize.py +++ b/phpserialize.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- r""" - phpserialize + phpserialize + nasted object and python object direct serialization by hdbreaker ~~~~~~~~~~~~ a port of the ``serialize`` and ``unserialize`` functions of @@ -402,12 +403,14 @@ def _serialize(obj, keypos): b'}' ]) if isinstance(obj, phpobject): - return b'O' + _serialize(obj.__name__, True)[1:-1] + \ - _serialize(obj.__php_vars__, False)[1:] + string = ""+b'O' + _serialize(obj.__name__, True)[1:-1] + \ + _serialize(obj.__php_vars__, False)[1:].replace('}}','};}') + return string else: if isinstance(obj, object): - return b'O' + _serialize(obj.__class__.__name__, True)[1:-1] + \ - _serialize(obj.__dict__, False)[1:]+';' + string = b'O' + _serialize(obj.__class__.__name__, True)[1:-1] + \ + _serialize(obj.__dict__, False)[1:].replace('}}','};}') + return string if object_hook is not None: return _serialize(object_hook(obj), False) raise TypeError('can\'t serialize %r' % type(obj)) @@ -561,3 +564,4 @@ def dict_to_tuple(d): serialize = dumps unserialize = loads + From 004482c91791149ba5168e799d445c67ed88ac58 Mon Sep 17 00:00:00 2001 From: hdbreaker Date: Thu, 14 Apr 2016 15:39:54 -0300 Subject: [PATCH 3/9] add support to deserialization of nested arrays --- phpserialize.py | 89 +++++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/phpserialize.py b/phpserialize.py index 533da9f..99b2cfd 100644 --- a/phpserialize.py +++ b/phpserialize.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import re r""" phpserialize nasted object and python object direct serialization by hdbreaker @@ -400,17 +401,15 @@ def _serialize(obj, keypos): str(len(obj)).encode('latin1'), b':{', b''.join(out), - b'}' + b'};' ]) if isinstance(obj, phpobject): - string = ""+b'O' + _serialize(obj.__name__, True)[1:-1] + \ - _serialize(obj.__php_vars__, False)[1:].replace('}}','};}') - return string + return b'O' + _serialize(obj.__name__, True)[1:-1] + \ + _serialize(obj.__php_vars__, False)[1:] else: if isinstance(obj, object): - string = b'O' + _serialize(obj.__class__.__name__, True)[1:-1] + \ - _serialize(obj.__dict__, False)[1:].replace('}}','};}') - return string + return b'O' + _serialize(obj.__class__.__name__, True)[1:-1] + \ + _serialize(obj.__dict__, False)[1:] if object_hook is not None: return _serialize(object_hook(obj), False) raise TypeError('can\'t serialize %r' % type(obj)) @@ -478,43 +477,45 @@ def _load_array(): def _unserialize(): type_ = fp.read(1).lower() - if type_ == b'n': - _expect(b';') - return None - if type_ in b'idb': - _expect(b':') - data = _read_until(b';') - if type_ == b'i': - return int(data) - if type_ == b'd': - return float(data) - return int(data) != 0 - if type_ == b's': - _expect(b':') - length = int(_read_until(b':')) - _expect(b'"') - data = fp.read(length) - _expect(b'"') - if decode_strings: - data = data.decode(charset, errors) - _expect(b';') - return data - if type_ == b'a': - _expect(b':') - return array_hook(_load_array()) - if type_ == b'o': - if object_hook is None: - raise ValueError('object in serialization dump but ' - 'object_hook not given.') - _expect(b':') - name_length = int(_read_until(b':')) - _expect(b'"') - name = fp.read(name_length) - _expect(b'":') - if decode_strings: - name = name.decode(charset, errors) - return object_hook(name, dict(_load_array())) - raise ValueError('unexpected opcode') + if type_ != ';': + + if type_ == b'n': + _expect(b';') + return None + if type_ in b'idb': + _expect(b':') + data = _read_until(b';') + if type_ == b'i': + return int(data) + if type_ == b'd': + return float(data) + return int(data) != 0 + if type_ == b's': + _expect(b':') + length = int(_read_until(b':')) + _expect(b'"') + data = fp.read(length) + _expect(b'"') + if decode_strings: + data = data.decode(charset, errors) + _expect(b';') + return data + if type_ == b'a': + _expect(b':') + return array_hook(_load_array()) + if type_ == b'o': + if object_hook is None: + raise ValueError('object in serialization dump but ' + 'object_hook not given.') + _expect(b':') + name_length = int(_read_until(b':')) + _expect(b'"') + name = fp.read(name_length) + _expect(b'":') + if decode_strings: + name = name.decode(charset, errors) + return object_hook(name, dict(_load_array())) + raise ValueError('unexpected opcode') return _unserialize() From 8a0ec918e764806cd851f2e8d444b74dca5091fe Mon Sep 17 00:00:00 2001 From: hdbreaker Date: Thu, 14 Apr 2016 17:01:09 -0300 Subject: [PATCH 4/9] Fix support to python3, add support to unicode strings,serialization of PHP session, add suport of nested objects serialization/unzerialization --- phpserialize.py | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/phpserialize.py b/phpserialize.py index 99b2cfd..4e62656 100644 --- a/phpserialize.py +++ b/phpserialize.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -import re r""" phpserialize nasted object and python object direct serialization by hdbreaker @@ -238,6 +237,8 @@ class WP_User extends WP_UserBase { Changelog ========= + 1.4 + - added support for PHP sessions 1.3 - added support for Python 3 @@ -418,7 +419,7 @@ def _serialize(obj, keypos): def load(fp, charset='utf-8', errors=default_errors, decode_strings=False, - object_hook=None, array_hook=None): + object_hook=None, array_hook=None, return_unicode=False): """Read a string from the open file object `fp` and interpret it as a data stream of PHP-serialized objects, reconstructing and returning the original object hierarchy. @@ -478,7 +479,6 @@ def _load_array(): def _unserialize(): type_ = fp.read(1).lower() if type_ != ';': - if type_ == b'n': _expect(b';') return None @@ -498,6 +498,8 @@ def _unserialize(): _expect(b'"') if decode_strings: data = data.decode(charset, errors) + if return_unicode: + data = unicode(data, charset) _expect(b';') return data if type_ == b'a': @@ -515,19 +517,46 @@ def _unserialize(): if decode_strings: name = name.decode(charset, errors) return object_hook(name, dict(_load_array())) - raise ValueError('unexpected opcode') + if type_ == b'r': + # recursion + _expect(b':') + data = _read_until(b';') + return None + raise ValueError('unexpected opcode - %s' % repr(type_)) + + fp_position = fp.tell() + chunk = _read_until(b':'); + fp.seek(fp_position) # Reset pointer + if b'|' in chunk: + # We may be dealing with a serialized session, in which case keys + # followed by a pipe are preceding the serialized data. + unserialized_data = {} + while 1: + try: + key = _read_until(b'|'); + except ValueError: + break # end of stream + if return_unicode: + key = unicode(key, charset) + unserialized_data[key] = _unserialize() + else: + unserialized_data = _unserialize() - return _unserialize() + return unserialized_data def loads(data, charset='utf-8', errors=default_errors, decode_strings=False, - object_hook=None, array_hook=None): + object_hook=None, array_hook=None, return_unicode=False): """Read a PHP-serialized object hierarchy from a string. Characters in the string past the object's representation are ignored. On Python 3 the string must be a bytestring. """ + # Convert unicode strings to byte strings. + if type(data) == unicode: + data = data.encode(charset) + return_unicode = True return load(BytesIO(data), charset, errors, decode_strings, - object_hook, array_hook) + object_hook, array_hook, return_unicode) def dump(data, fp, charset='utf-8', errors=default_errors, object_hook=None): @@ -565,4 +594,3 @@ def dict_to_tuple(d): serialize = dumps unserialize = loads - From cabec474a6e7fa1fd6f2f3b3d0c70edc8002aae8 Mon Sep 17 00:00:00 2001 From: hdbreaker Date: Thu, 14 Apr 2016 17:03:04 -0300 Subject: [PATCH 5/9] Fix support to python3, add support to unicode strings,serialization of PHP session, add suport of nested objects serialization/unzerialization --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 49f17af..078590d 100644 --- a/setup.py +++ b/setup.py @@ -19,10 +19,10 @@ def get_docs(): setup( name='phpserialize', - author='Armin Ronacher', + author='Alejandro Parodi', author_email='armin.ronacher@active-4.com', - version='1.3', - url='http://github.com/mitsuhiko/phpserialize', + version='1.6', + url='http://github.com/hdbreaker/phpserialize', py_modules=['phpserialize'], description='a port of the serialize and unserialize ' 'functions of php to python.', From 8eb2596ccba977eb3405ab9ac061d7c0651e6788 Mon Sep 17 00:00:00 2001 From: hdbreaker Date: Thu, 14 Apr 2016 17:04:57 -0300 Subject: [PATCH 6/9] Fix Author --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 078590d..f1f5ead 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ def get_docs(): setup( name='phpserialize', - author='Alejandro Parodi', + author='Armin Ronacher', author_email='armin.ronacher@active-4.com', version='1.6', url='http://github.com/hdbreaker/phpserialize', From 6739d7ce60752843535ec2b9d1390296ed2ff0a8 Mon Sep 17 00:00:00 2001 From: hdbreaker Date: Thu, 14 Apr 2016 17:21:45 -0300 Subject: [PATCH 7/9] Update Readme --- README | 8 +++++++- phpserialize.py => phpserializeByhdbreaker.py | 0 2 files changed, 7 insertions(+), 1 deletion(-) rename phpserialize.py => phpserializeByhdbreaker.py (100%) diff --git a/README b/README index ea49e64..31f20ca 100644 --- a/README +++ b/README @@ -1,3 +1,9 @@ -a port of the serialize and unserialize functions of php to python. This module +Fork of https://github.com/aioTV/phpserialize based on https://github.com/mitsuhiko/phpserialize + +A port of the serialize and unserialize functions of php to python. This module implements the python serialization interface (eg: provides dumps, loads and similar functions). + +Support Native Nasted Objets by hdbreaker # Last Commit Apr 14, 2016 +Support Python3, PHP Sessions, Unicode chars by aioTV # Last Commit Mar 30, 2016 +Support Serialize and Unserialize by mitsuhiko # Last Commit 22 Jan 2012 diff --git a/phpserialize.py b/phpserializeByhdbreaker.py similarity index 100% rename from phpserialize.py rename to phpserializeByhdbreaker.py From e5f1e10483a13f2879cef28f31abeec2dfa1c687 Mon Sep 17 00:00:00 2001 From: hdbreaker Date: Thu, 14 Apr 2016 17:22:12 -0300 Subject: [PATCH 8/9] Update Readme --- phpserializeByhdbreaker.py => phpserialize.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename phpserializeByhdbreaker.py => phpserialize.py (100%) diff --git a/phpserializeByhdbreaker.py b/phpserialize.py similarity index 100% rename from phpserializeByhdbreaker.py rename to phpserialize.py From c323c85b98196b3f9c9a685723ec3151c5ec9ea9 Mon Sep 17 00:00:00 2001 From: hdbreaker Date: Thu, 14 Apr 2016 17:39:16 -0300 Subject: [PATCH 9/9] Update Readme --- README | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README b/README index 31f20ca..da7ea6a 100644 --- a/README +++ b/README @@ -4,6 +4,10 @@ A port of the serialize and unserialize functions of php to python. This module implements the python serialization interface (eg: provides dumps, loads and similar functions). +Sessions unserializing is also supported if they were saved using PHP's +internal serializer and without encryption (see +http://www.hardened-php.net/suhosin/configuration.html#suhosin.session.encrypt). + Support Native Nasted Objets by hdbreaker # Last Commit Apr 14, 2016 Support Python3, PHP Sessions, Unicode chars by aioTV # Last Commit Mar 30, 2016 Support Serialize and Unserialize by mitsuhiko # Last Commit 22 Jan 2012