php-serialized-formatter 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '059244a56e51ddb0792c2c037873f656019ea069dd6c8b3617e85595b4fb6a53'
4
+ data.tar.gz: ad4c47309502acb762b9cf8dfb804d3ac5040914aa8fb4d5cb141648a0ea5f64
5
+ SHA512:
6
+ metadata.gz: e5f968144bbf15c9cf8d0b3d3e546e86c4e76d4c51b9327f81e690d24aa5f683a06803de8a0dbef7437c98256c4561ada0ee62db6f204f6ad29c1902c45da2c6
7
+ data.tar.gz: be61df6b4acb9dbaf91a7aef59642aea213502f753e16b10a207d5a63392b03e290832175fcaa4910f42178e54ec5b0399e8def567a5a3ab17badd2bf720c3c1
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Alexandre ZANNI (noraj)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/bin/psf ADDED
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'psf'
5
+ require 'docopt'
6
+
7
+ doc = <<~DOCOPT
8
+ php-serialized-formatter (psf) v#{Psf::VERSION}
9
+
10
+ Usage:
11
+ psf unserialize [--session] (--string <string> | --file <file> ) [--format <format>]
12
+ psf -h | --help
13
+ psf --version
14
+
15
+ Commands:
16
+ unserialize Unserialize PHP serialized objects
17
+
18
+ Options:
19
+ --session Session mode [default: false]
20
+ -s <string>, --string <string> Serialized content string, read from STDIN if equal to "-"
21
+ -f <file>, --file <file> Serialized content file
22
+ --format <format> Output format (hash, json, json_pretty, yaml) [default: hash]
23
+ --debug Display arguments
24
+ -h, --help Show this screen
25
+ --version Show version
26
+
27
+ Examples:
28
+ psf unserialize --session -f /var/lib/php/session/sess_3cebqoq314pcnc2jgqiu840h0k
29
+ psf unserialize -s 'O:6:"Person":2:{s:10:"first_name";s:4:"John";s:9:"last_name";s:3:"Doe";}' --format yaml
30
+
31
+ Project:
32
+ author (https://pwn.by/noraj / https://twitter.com/noraj_rawsec)
33
+ source (https://github.com/noraj/php-serialized-formatter)
34
+ documentation (https://noraj.github.io/php-serialized-formatter)
35
+ DOCOPT
36
+
37
+ begin
38
+ args = Docopt.docopt(doc, version: Psf::VERSION)
39
+ puts args if args['--debug']
40
+ # use case 1, using the tool
41
+ if args['unserialize']
42
+ # File or string ?
43
+ serialized_data = ''
44
+ if args['--string']
45
+ args['--string'] = $stdin.read.chomp if args['--string'] == '-'
46
+ serialized_data = args['--string']
47
+ elsif args['--file']
48
+ serialized_data = File.read(args['--file'])
49
+ end
50
+ # Session or regular object ?
51
+ unserialized_data = if args['--session']
52
+ Psf.unserialize_session(serialized_data)
53
+ else
54
+ Psf.unserialize(serialized_data)
55
+ end
56
+ # Format ?
57
+ case args['--format']
58
+ when 'hash'
59
+ require 'pp'
60
+ pp unserialized_data
61
+ when 'json'
62
+ require 'json'
63
+ puts unserialized_data.to_json
64
+ when 'json_pretty'
65
+ require 'json'
66
+ puts JSON.pretty_generate(unserialized_data)
67
+ when 'yaml'
68
+ require 'yaml'
69
+ puts unserialized_data.to_yaml
70
+ else
71
+ raise 'Unexisting format specified'
72
+ end
73
+ end
74
+ # use case 2, help: already handled by docopt
75
+ # use case 3, version: already handled by docopt
76
+ rescue Docopt::Exit => e
77
+ puts e.message
78
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'psf/string_utils'
4
+
5
+ # PHP serialization library that can parse and generate PHP's serialization
6
+ # format including PHP sessions specific format.
7
+ #
8
+ # Thos module is a patched, modified and enhanced version of
9
+ # [jurias/php-serial](https://github.com/jurias/php-serial/blob/master/lib/php-serial.rb)
10
+ # (under [MIT license](https://github.com/jurias/php-serial/blob/master/LICENSE)).
11
+ module Psf
12
+ # Serializes a ruby object into PHP serialized format.
13
+ # @param var [NilClass|Fixnum|Float|TrueClass|FalseClass|String|Array|Hash|Object]
14
+ # @return [String]
15
+ def self.serialize(var)
16
+ val = ''
17
+ case var.class.to_s
18
+ when 'NilClass'
19
+ val = 'N;'
20
+ when 'Integer', 'Fixnum', 'Bignum'
21
+ val = "i:#{var};"
22
+ when 'Float'
23
+ val = "d:#{var};"
24
+ when 'TrueClass'
25
+ val = 'b:1;'
26
+ when 'FalseClass'
27
+ val = 'b:0;'
28
+ when 'String', 'Symbol'
29
+ val = "s:#{var.to_s.bytesize}:\"#{var}\";"
30
+ when 'Array'
31
+ val = "a:#{var.length}:{"
32
+ var.length.times do |index|
33
+ val += serialize(index) + serialize(var[index])
34
+ end
35
+ val += '}'
36
+ when 'Hash'
37
+ val = "a:#{var.length}:{"
38
+ var.each do |item_key, item_value|
39
+ val += serialize(item_key) + serialize(item_value)
40
+ end
41
+ val += '}'
42
+ else
43
+ klass = var.class.to_s
44
+ val = "O:#{klass.length}:\"#{klass}\":#{var.instance_variables.length}:{"
45
+ var.instance_variables.each do |ivar|
46
+ ivar = ivar.to_s
47
+ ivar.slice!(0)
48
+ val += serialize(ivar) + serialize(var.send(ivar))
49
+ end
50
+ val += '}'
51
+ end
52
+ val
53
+ end
54
+
55
+ # Serializes a hash into PHP session.
56
+ # @param hash [Hash]
57
+ # @return [String]
58
+ def self.serialize_session(hash)
59
+ serialized_session = ''
60
+ hash.each do |key, value|
61
+ serialized_session += "#{key}|#{serialize(value)}"
62
+ end
63
+ serialized_session
64
+ end
65
+
66
+ # Unserializes a PHP session.
67
+ # @param data [String]
68
+ # @return [Hash]
69
+ def self.unserialize_session(data)
70
+ begin
71
+ data = data.strip
72
+ rescue Encoding::CompatibilityError
73
+ data = data.encode('UTF-8', invalid: :replace, undef: :replace).strip if data.encoding == Encoding::UTF_8
74
+ end
75
+ hash = {}
76
+
77
+ until data.empty?
78
+ key = extract_until!(data, '|')
79
+ hash[key] = unserialize!(data)
80
+ end
81
+ hash
82
+ end
83
+
84
+ # Unserializes a string up to the first valid serialized instance.
85
+ # @param data [String]
86
+ # @return [NilClass|Fixnum|Float|TrueClass|FalseClass|String|Array|Hash|Object]
87
+ def self.unserialize(data = '')
88
+ unserialize!(data.strip)
89
+ end
90
+
91
+ # Unserializes recursively. Consumes the input string.
92
+ # @param data [String]
93
+ # @return [NilClass|Fixnum|Float|TrueClass|FalseClass|String|Array|Hash|Object]
94
+ def self.unserialize!(data = '')
95
+ var_type = data.slice!(0)
96
+ data.slice!(0)
97
+ case var_type
98
+ when 'N'
99
+ value = nil
100
+ when 'b'
101
+ value = (extract_until!(data, ';') == '1')
102
+ when 's'
103
+ length = extract_until!(data, ':').to_i
104
+ extract_until!(data, '"')
105
+ value = data.byteslice!(0, length)
106
+ extract_until!(data, ';')
107
+ when 'i'
108
+ value = extract_until!(data, ';').to_i
109
+ when 'd'
110
+ value = extract_until!(data, ';').to_f
111
+ when 'a'
112
+ value = {}
113
+ length = extract_until!(data, ':').to_i
114
+ extract_until!(data, '{')
115
+ length.times do
116
+ key = unserialize!(data)
117
+ value[key] = unserialize!(data)
118
+ end
119
+ extract_until!(data, '}')
120
+ # if keys are sequential numbers, return array
121
+ value = value.values if (Array(0..value.length - 1) == value.keys) && !value.empty?
122
+ when 'O'
123
+ value = {}
124
+ length = extract_until!(data, ':').to_i
125
+ extract_until!(data, '"')
126
+ value['class'] = data.slice!(0, length)
127
+ extract_until!(data, ':')
128
+ length = extract_until!(data, ':').to_i
129
+ extract_until!(data, '{')
130
+ length.times do
131
+ key = unserialize!(data)
132
+ value[key] = unserialize!(data)
133
+ end
134
+ end
135
+ value
136
+ end
137
+
138
+ # Return all characters up to the first occurrence of char.
139
+ # Truncates those characters from input string.
140
+ # @param str [String]
141
+ # @param char [String]
142
+ # @return [String]
143
+ def self.extract_until!(str, char)
144
+ extracted = ''
145
+ while (c = str.slice!(0))
146
+ break if c == char
147
+
148
+ extracted << c
149
+ end
150
+ extracted
151
+ end
152
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Extending String class with new methods
4
+ class String
5
+ # In place version of String#byteslice(index, length)
6
+ def byteslice!(start_index, length = nil)
7
+ byte_start_index = start_index
8
+ byte_length = length || (bytesize - byte_start_index)
9
+
10
+ byte_start_index = bytesize + byte_start_index if byte_start_index.negative?
11
+ return nil if byte_start_index.negative? || byte_start_index >= bytesize
12
+
13
+ byte_length = bytesize - byte_start_index if byte_start_index + byte_length > bytesize
14
+
15
+ out = byteslice(byte_start_index, byte_length)
16
+ replace(byteslice(0, byte_start_index) + byteslice(byte_start_index + byte_length..-1))
17
+ out
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Psf
4
+ # Version of php-serialized-formatter library and app
5
+ VERSION = '0.0.1'
6
+ end
data/lib/psf.rb ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'psf/version'
4
+ require 'psf/php_serial'
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: php-serialized-formatter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alexandre ZANNI
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2025-05-04 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: docopt
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.6'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.6'
26
+ description: PHP serialization library that can parse and generate PHP'sserialization
27
+ format including PHP sessions specific format.
28
+ email: alexandre.zanni@europe.com
29
+ executables:
30
+ - psf
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - LICENSE
35
+ - bin/psf
36
+ - lib/psf.rb
37
+ - lib/psf/php_serial.rb
38
+ - lib/psf/string_utils.rb
39
+ - lib/psf/version.rb
40
+ homepage: https://github.com/noraj/php-serialized-formatter
41
+ licenses:
42
+ - MIT
43
+ metadata:
44
+ yard.run: yard
45
+ bug_tracker_uri: https://github.com/noraj/php-serialized-formatter/issues
46
+ changelog_uri: https://github.com/noraj/php-serialized-formatter/releases
47
+ documentation_uri: https://noraj.github.io/php-serialized-formatter/
48
+ homepage_uri: https://github.com/noraj/php-serialized-formatter
49
+ source_code_uri: https://github.com/noraj/php-serialized-formatter/
50
+ rubygems_mfa_required: 'true'
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 3.1.0
59
+ - - "<"
60
+ - !ruby/object:Gem::Version
61
+ version: '4.0'
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubygems_version: 3.6.2
69
+ specification_version: 4
70
+ summary: Serialize and unserialize to|from PHP session|objects.
71
+ test_files: []