Skip to content

Instantly share code, notes, and snippets.

@Ryex
Created July 27, 2015 19:27
Show Gist options
  • Save Ryex/1a4758ccaa01af496557 to your computer and use it in GitHub Desktop.
Save Ryex/1a4758ccaa01af496557 to your computer and use it in GitHub Desktop.
Ruby Marshal version 4.8 Reader in Python
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