Created
July 27, 2015 19:27
-
-
Save Ryex/1a4758ccaa01af496557 to your computer and use it in GitHub Desktop.
Ruby Marshal version 4.8 Reader in Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from struct import pack, unpack | |
#============================================================================================ | |
# RubyMarshal | |
#-------------------------------------------------------------------------------------------- | |
# This class is able to read and write Ruby Marshal format. | |
#============================================================================================ | |
class RubyMarshal: | |
MARSHAL_MAJOR = 4 | |
MARSHAL_MINOR = 8 | |
TYPE_NIL = '0' | |
TYPE_TRUE = 'T' | |
TYPE_FALSE = 'F' | |
TYPE_FIXNUM = 'i' | |
TYPE_EXTENDED = 'e' # not implemented | |
TYPE_UCLASS = 'C' # not implemented | |
TYPE_OBJECT = 'o' | |
TYPE_DATA = 'd' # not implemented | |
TYPE_USERDEF = 'u' | |
TYPE_USRMARSHAL = 'U' # not implemented | |
TYPE_FLOAT = 'f' # not implemented | |
TYPE_BIGNUM = 'l' | |
TYPE_STRING = '"' | |
TYPE_REGEXP = '/' # not implemented | |
TYPE_ARRAY = '[' | |
TYPE_HASH = '{' | |
TYPE_HASH_DEF = '}' # not implemented | |
TYPE_STRUCT = 'S' # not implemented | |
TYPE_MODULE_OLD = 'M' # not implemented | |
TYPE_CLASS = 'c' # not implemented | |
TYPE_MODULE = 'm' # not implemented | |
TYPE_SYMBOL = ':' | |
TYPE_SYMLINK = ';' | |
TYPE_IVAR = 'I' # not implemented | |
TYPE_LINK = '@' # not implemented | |
__Version = "\x04\x08" | |
__io = None | |
__symbols = [] | |
@staticmethod | |
def generate(io): | |
pass | |
@staticmethod | |
def dump(object, io): | |
pass | |
@staticmethod | |
def load(io): | |
RubyMarshal.__io = io | |
try: | |
major = RubyMarshal.__r_byte() | |
minor = RubyMarshal.__r_byte() | |
if (major != RubyMarshal.MARSHAL_MAJOR or minor != RubyMarshal.MARSHAL_MINOR): | |
raise "incompatible marshal file format (can't be read)\n\ | |
\tformat version %d.%d required; %d.%d given" %\ | |
(RubyMarshal.MARSHAL_MAJOR, RubyMarshal.MARSHAL_MINOR, major, minor) | |
obj = RubyMarshal.__r_object() | |
except: | |
raise | |
finally: | |
RubyMarshal.__io = None | |
RubyMarshal.__symbols = [] | |
return obj | |
@staticmethod | |
def __r_object(): | |
objectType = chr(RubyMarshal.__r_byte()) | |
print "type: " + str(objectType) | |
if objectType == RubyMarshal.TYPE_LINK: | |
index = RubyMarshal.__r_long() | |
try: | |
return RubyMarshal.__symbols[index] | |
except: | |
raise "dump format error (unlinked %d of %d at 0x%x)" %\ | |
(index, len(RubyMarshal.__symbols), RubyMarshal.__io.tell()) | |
pass | |
if objectType == RubyMarshal.TYPE_NIL: | |
return None | |
if objectType == RubyMarshal.TYPE_TRUE: | |
return True | |
if objectType == RubyMarshal.TYPE_FALSE: | |
return False | |
if objectType == RubyMarshal.TYPE_FIXNUM: | |
return RubyMarshal.__r_long() | |
if objectType == RubyMarshal.TYPE_BIGNUM: | |
sign = (RubyMarshal.__r_byte() == '+') | |
data = RubyMarshal.__r_bytes() | |
result = 0 | |
while length > 0: | |
shift = 0 | |
for i in xrange(4): | |
value |= data[i] << shift | |
shift += 8 | |
length -= 1 | |
if not sign: | |
result = -result | |
RubyMarshal.__r_entry(result) | |
return result | |
if objectType == RubyMarshal.TYPE_STRING: | |
result = RubyMarshal.__r_bytes() | |
RubyMarshal.__r_entry(result) | |
return result | |
if objectType == RubyMarshal.TYPE_ARRAY: | |
result = RubyMarshal.__r_array() | |
RubyMarshal.__r_entry(result) | |
return result | |
if objectType == RubyMarshal.TYPE_HASH: | |
result = RubyMarshal.__r_hash() | |
RubyMarshal.__r_entry(result) | |
return result | |
if objectType == RubyMarshal.TYPE_USERDEF: | |
#try: | |
result = RubyMarshal.__r_unique() | |
result._load(RubyMarshal.__io) | |
#RubyMarshal.__r_entry(result) | |
return result | |
#except: | |
# raise "class %s needs to have method '_load'" % klass | |
#pass | |
if objectType == RubyMarshal.TYPE_OBJECT: | |
print "__symbols: " + str(RubyMarshal.__symbols) | |
result = RubyMarshal.__r_unique() | |
print "result: " + str(result) | |
length = RubyMarshal.__r_long() | |
attributes = {} | |
while (length > 0): | |
print "get key" | |
key = RubyMarshal.__r_symbol() | |
print "get value" | |
value = RubyMarshal.__r_object() | |
print "key, value: " + str(key) + " " + str(value) | |
attributes[key] = value | |
length -= 1 | |
print str(attributes) | |
for symbol in attributes.keys(): | |
setattr(result, symbol.replace("@", ""), attributes[symbol]) | |
RubyMarshal.__r_entry(result) | |
return result | |
if objectType == RubyMarshal.TYPE_SYMBOL: | |
result = RubyMarshal.__r_symreal() | |
RubyMarshal.__r_entry(result) | |
return result | |
if objectType == RubyMarshal.TYPE_SYMLINK: | |
result = RubyMarshal.__r_symlink() | |
RubyMarshal.__r_entry(result) | |
return result | |
raise "dump format error(0x%x at 0x%x)" % (ord(objectType), RubyMarshal.__io.tell()) | |
@staticmethod | |
def __r_byte(): | |
return ord(RubyMarshal.__io.read(1)) | |
@staticmethod | |
def __r_bytes(): | |
return RubyMarshal.__r_bytes0(RubyMarshal.__r_long()) | |
@staticmethod | |
def __r_bytes0(length): | |
if (length == 0): | |
return '' | |
return RubyMarshal.__io.read(length) | |
@staticmethod | |
def __r_array(): | |
length = RubyMarshal.__r_long() | |
result = [] | |
while (length > 0): | |
result.append(RubyMarshal.__r_object()) | |
length -= 1 | |
return result | |
@staticmethod | |
def __r_hash(): | |
length = RubyMarshal.__r_long() | |
result = {} | |
while (length > 0): | |
key = RubyMarshal.__r_object() | |
value = RubyMarshal.__r_object() | |
try: | |
result[key] = value | |
except TypeError: | |
result[tuple(key)] = value | |
length -= 1 | |
return result | |
@staticmethod | |
def __r_long(): | |
c = RubyMarshal.__r_byte() | |
if c > 127: | |
c -= 256 | |
if (c == 0): | |
return 0 | |
if (c > 0): | |
if (4 < c and c < 128): | |
return (c - 5) | |
result = 0 | |
for i in xrange(c): | |
result |= RubyMarshal.__r_byte() << (8 * i) | |
return result | |
if (-129 < c and c < -4): | |
return (c + 5) | |
c = -c | |
result = -1 | |
for i in xrange(c): | |
result &= ~(0xFF << (8 * i)) | |
result |= RubyMarshal.__r_byte() << (8 * i) | |
return result | |
@staticmethod | |
def __r_symreal(): | |
symbol = RubyMarshal.__r_bytes() | |
print "symreal: " + str(symbol) | |
RubyMarshal.__r_entry(symbol) | |
return symbol | |
@staticmethod | |
def __r_symlink(): | |
index = RubyMarshal.__r_long() | |
if index >= len(RubyMarshal.__symbols): | |
raise "bad symbol (0x%x)" % RubyMarshal.__io.tell() | |
print "symlink: " + str(index) + " " + str(RubyMarshal.__symbols[index]) | |
return RubyMarshal.__symbols[index] | |
@staticmethod | |
def __r_unique(): | |
return RubyMarshal.__id2name(RubyMarshal.__r_symbol()) | |
@staticmethod | |
def __r_symbol(): | |
if chr(RubyMarshal.__r_byte()) == RubyMarshal.TYPE_SYMLINK: | |
return RubyMarshal.__r_symlink() | |
return RubyMarshal.__r_symreal() | |
@staticmethod | |
def __r_entry(value): | |
RubyMarshal.__symbols.append(value) | |
return value | |
@staticmethod | |
def __id2name(name): | |
print "idtoname: " + str(name) | |
return eval(name.replace("::", ".") + "()") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment