Skip to content

Commit 5bd3b2b

Browse files
author
vbuell
committed
Primitive types supported
1 parent ecad27e commit 5bd3b2b

File tree

2 files changed

+220
-71
lines changed

2 files changed

+220
-71
lines changed

javaobj.py

Lines changed: 188 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
def loads(object):
55
"""
66
Deserializes Java primitive data and objects serialized by ObjectOutputStream
7+
from a string.
78
"""
89
f = StringIO.StringIO(object)
9-
marshaller = JavaObjectMarshaller(f)
10-
return marshaller.readObject()
10+
marshaller = JavaObjectMarshaller()
11+
return marshaller.load_stream(f)
1112
# ba = f.read(4)
1213
# (magic, version) = struct.unpack(">HH", ba)
1314
# print magic
@@ -18,6 +19,33 @@ def loads(object):
1819
#
1920
# print type(object), Magic
2021

22+
def dumps(object):
23+
"""
24+
Serializes Java primitive data and objects unmarshaled by load(s) before into string.
25+
"""
26+
marshaller = JavaObjectMarshaller()
27+
return marshaller.dump(object)
28+
29+
30+
class JavaClass(object):
31+
def __init__(self):
32+
self.name = None
33+
self.serialVersionUID = None
34+
self.flags = None
35+
self.fields_names = []
36+
self.fields_types = []
37+
38+
def __str__(self):
39+
return "[%s:0x%X]" % (self.name, self.serialVersionUID)
40+
41+
42+
class JavaObject(object):
43+
classdesc = None
44+
45+
def get_class(self):
46+
return self.classdesc
47+
48+
2149
class JavaObjectMarshaller:
2250

2351
STREAM_MAGIC = 0xaced
@@ -40,7 +68,14 @@ class JavaObjectMarshaller:
4068
TC_ENUM = 0x7E
4169
TC_MAX = 0x7E
4270

43-
def __init__(self, stream):
71+
# classDescFlags
72+
SC_WRITE_METHOD = 0x01 # if SC_SERIALIZABLE
73+
SC_BLOCK_DATA = 0x08 # if SC_EXTERNALIZABLE
74+
SC_SERIALIZABLE = 0x02
75+
SC_EXTERNALIZABLE = 0x04
76+
SC_ENUM = 0x10
77+
78+
def __init__(self):
4479
self.opmap = {
4580
self.TC_NULL: self.do_null,
4681
self.TC_CLASSDESC: self.do_classdesc,
@@ -50,26 +85,33 @@ def __init__(self, stream):
5085
self.TC_BLOCKDATA: self.do_blockdata,
5186
self.TC_REFERENCE: self.do_reference
5287
}
88+
self.current_object = None
89+
90+
def load_stream(self, stream):
5391
self.object_stream = stream
5492
self._readStreamHeader()
55-
self.finalValue = True
56-
self.current_object = None
93+
return self.readObject()
5794

5895
def _readStreamHeader(self):
5996
(magic, version) = self._readStruct(">HH", 4)
6097
if magic != self.STREAM_MAGIC or version != self.STREAM_VERSION:
6198
raise IOError("The stream is not java serialized object. Invalid stream header: %04X%04X" % (magic, version))
6299

63100
def readObject(self):
64-
(opid, ) = self._readStruct(">B", 1)
65-
print "OpCode: 0x%X" % opid
66-
res = self.opmap.get(opid, self.do_default_stuff)()
101+
res = self.read_and_exec_opcode(ident=0) # TODO: add expects
102+
103+
the_rest = self.object_stream.read()
104+
if len(the_rest):
105+
print "Warning!!!!: Stream still has %s bytes left." % str(the_rest)
106+
67107
return res
68108

69-
def read_and_exec_opcode(self):
109+
def read_and_exec_opcode(self, ident=0, expect=None):
70110
(opid, ) = self._readStruct(">B", 1)
71-
print "OpCode: 0x%X" % opid
72-
return self.opmap.get(opid, self.do_default_stuff)()
111+
self.print_ident("OpCode: 0x%X" % opid, ident)
112+
if expect and opid not in expect:
113+
raise IOError("Unexpected opcode 0x%X" % opid)
114+
return self.opmap.get(opid, self.do_default_stuff)(ident=ident)
73115

74116
def _readStruct(self, unpack, length):
75117
ba = self.object_stream.read(length)
@@ -80,7 +122,7 @@ def _readString(self):
80122
ba = self.object_stream.read(length)
81123
return ba
82124

83-
def do_classdesc(self, parent=None):
125+
def do_classdesc(self, parent=None, ident=0):
84126
# TC_CLASSDESC className serialVersionUID newHandle classDescInfo
85127
# classDescInfo:
86128
# classDescFlags fields classAnnotation superClassDesc
@@ -96,99 +138,176 @@ def do_classdesc(self, parent=None):
96138
# prim_typecode fieldName
97139
# objectDesc:
98140
# obj_typecode fieldName className1
99-
class JavaClass(object):
100-
def __init__(self):
101-
self.name = None
102-
self.serialVersionUID = None
103-
self.flags = None
104-
self.fields_names = []
105-
self.fields_types = []
106-
107-
def __str__(self):
108-
return "[%s:0x%X]" % (self.name, self.serialVersionUID)
109-
110141
clazz = JavaClass()
111-
print "[classdesc]"
142+
self.print_ident("[classdesc]", ident)
112143
ba = self._readString()
113144
clazz.name = ba
114-
print "Class name:", ba
145+
self.print_ident("Class name: %s" % ba, ident)
115146
(serialVersionUID, newHandle, classDescFlags) = self._readStruct(">LLB", 4+4+1)
116147
clazz.serialVersionUID = serialVersionUID
117148
clazz.flags = classDescFlags
118-
print "Serial: 0x%X newHanle: 0x%X. classDescFlags: 0x%X" % (serialVersionUID, newHandle, classDescFlags)
149+
self.print_ident("Serial: 0x%X newHanle: 0x%X. classDescFlags: 0x%X" % (serialVersionUID, newHandle, classDescFlags), ident)
119150
(length, ) = self._readStruct(">H", 2)
120-
print "Fields num: 0x%X" % length
151+
self.print_ident("Fields num: 0x%X" % length, ident)
121152

122-
fields_names = []
123-
fields_types = []
153+
clazz.fields_names = []
154+
clazz.fields_types = []
124155
for fieldId in range(length):
125156
(type, ) = self._readStruct(">B", 1)
126-
ba = self._readString()
127-
print "FieldType: 0x%X" % type, ba
128-
(opid, ) = self._readStruct(">B", 1)
129-
print "OpCode: 0x%X" % opid
130-
res = self.opmap.get(opid, self.do_default_stuff)()
131-
fields_names.append(ba)
132-
fields_types.append(res)
157+
field_name = self._readString()
158+
field_type = None
159+
if type == 0x44: # 'D': Double
160+
field_type = "double"
161+
elif type == 0x49: # 'I': Integer
162+
field_type = "integer"
163+
elif type == 0x4A: # 'J': Long
164+
field_type = "long"
165+
elif type == 0x5A: # 'Z': Boolean
166+
field_type = "boolean"
167+
elif type == 0x5B: # '[': Array
168+
field_type = self.read_and_exec_opcode(ident=ident+1, expect=[self.TC_STRING, self.TC_REFERENCE])
169+
field_type = "array of " + field_type
170+
elif type == 0x42: # 'B': Byte
171+
field_type = "byte"
172+
elif type == 0x4C: # 'L': Object
173+
field_type = self.read_and_exec_opcode(ident=ident+1, expect=[self.TC_STRING, self.TC_REFERENCE])
174+
else:
175+
raise NotImplementedError("type 0x%X isn't implemented yet" % type)
176+
self.print_ident("FieldName: 0x%X" % type + " " + str(field_name) + " " + str(field_type), ident)
177+
clazz.fields_names.append(field_name)
178+
clazz.fields_types.append(field_type)
133179
if parent:
134-
parent.__fields = fields_names
135-
parent.__types = fields_types
180+
parent.__fields = clazz.fields_names
181+
parent.__types = clazz.fields_types
136182
# classAnnotation
137183
(opid, ) = self._readStruct(">B", 1)
138184
if opid != self.TC_ENDBLOCKDATA:
139185
raise NotImplementedError("classAnnotation isn't implemented yet")
140-
print "OpCode: 0x%X" % opid
186+
self.print_ident("OpCode: 0x%X" % opid, ident)
141187
# superClassDesc
142-
(opid, ) = self._readStruct(">B", 1)
143-
print "OpCode: 0x%X" % opid
144-
self.opmap.get(opid, self.do_default_stuff)()
188+
superclassdesc = self.read_and_exec_opcode(ident=ident+1, expect=[self.TC_CLASSDESC, self.TC_NULL])
189+
print superclassdesc
145190

146191
return clazz
147192

148-
def do_blockdata(self, parent=None):
193+
def do_blockdata(self, parent=None, ident=0):
149194
# TC_BLOCKDATA (unsigned byte)<size> (byte)[size]
150-
print "[blockdata]"
195+
self.print_ident("[blockdata]", ident)
151196
(length, ) = self._readStruct(">B", 1)
152197
ba = self.object_stream.read(length)
153198
return ba
154199

155-
def do_class(self, parent=None):
200+
def do_class(self, parent=None, ident=0):
156201
# TC_CLASS classDesc newHandle
157-
print "[class]"
158-
clazz = self.read_and_exec_opcode()
159-
print "Class:", clazz
160-
return clazz
202+
self.print_ident("[class]", ident)
161203

162-
def do_object(self, parent=None):
204+
# TODO: what to do with "(ClassDesc)prevObject". (see 3rd line for classDesc:)
205+
classdesc = self.read_and_exec_opcode(ident=ident+1, expect=[self.TC_CLASSDESC, self.TC_PROXYCLASSDESC, self.TC_NULL])
206+
self.print_ident("Classdesc: %s" % classdesc, ident)
207+
return classdesc
208+
209+
def do_object(self, parent=None, ident=0):
163210
# TC_OBJECT classDesc newHandle classdata[] // data for each class
164-
class JavaObject(object):
165-
pass
166-
self.current_object = JavaObject()
167-
print "[object]"
168-
(opid, ) = self._readStruct(">B", 1)
169-
print "OpCode: 0x%X" % opid
170-
res = self.opmap.get(opid, self.do_default_stuff)(self.current_object)
171-
self.finalValue = res
211+
java_object = JavaObject()
212+
self.print_ident("[object]", ident)
213+
214+
# TODO: what to do with "(ClassDesc)prevObject". (see 3rd line for classDesc:)
215+
classdesc = self.read_and_exec_opcode(ident=ident+1, expect=[self.TC_CLASSDESC, self.TC_PROXYCLASSDESC, self.TC_NULL])
216+
172217
# classdata[]
173218

174-
for field_name in self.current_object.__fields:
175-
(opid, ) = self._readStruct(">B", 1)
176-
print "OpCode: 0x%X" % opid
177-
res = self.opmap.get(opid, self.do_default_stuff)(self.current_object)
178-
self.current_object.__setattr__(field_name, res)
179-
return self.current_object
219+
# Store classdesc of this object
220+
java_object.classdesc = classdesc
180221

181-
def do_string(self, parent=None):
182-
print "[string]"
222+
# classdata[]
223+
# TODO: nowrclass, wrclass, externalContents, objectAnnotation
224+
if classdesc.flags & self.SC_SERIALIZABLE and not (classdesc.flags & self.SC_WRITE_METHOD):
225+
pass
226+
else:
227+
raise NotImplementedError("only nowrclass is implemented.")
228+
for field_name in classdesc.fields_names:
229+
res = self.read_and_exec_opcode(ident=ident+1)
230+
java_object.__setattr__(field_name, res)
231+
return java_object
232+
233+
def do_string(self, parent=None, ident=0):
234+
self.print_ident("[string]", ident)
183235
ba = self._readString()
184236
# (handle, ) = self._readStruct(">B", 1)
185237
return str(ba)
186238

187-
def do_reference(self, parent=None):
239+
def do_reference(self, parent=None, ident=0):
188240
(handle, reference) = self._readStruct(">HH", 4)
189241

190-
def do_null(self, parent=None):
242+
def do_null(self, parent=None, ident=0):
191243
return None
192244

193-
def do_default_stuff(self, parent=None):
245+
def do_default_stuff(self, parent=None, ident=0):
194246
raise RuntimeError("Unknown opcode")
247+
248+
def print_ident(self, message, ident):
249+
print " " * ident + str(message)
250+
# =====================================================================================
251+
252+
def dump(self, obj):
253+
self.object_obj = obj
254+
self.object_stream = StringIO.StringIO()
255+
self._writeStreamHeader()
256+
self.writeObject(obj)
257+
return self.object_stream.getvalue()
258+
259+
def _writeStreamHeader(self):
260+
self._writeStruct(">HH", 4, (self.STREAM_MAGIC, self.STREAM_VERSION))
261+
262+
def writeObject(self, obj):
263+
print type(obj)
264+
print obj
265+
if type(obj) is JavaObject:
266+
print "This is java object!"
267+
self.write_object(obj)
268+
elif type(obj) is str:
269+
print "This is string."
270+
self.write_blockdata(obj)
271+
# (opid, ) = self._readStruct(">B", 1)
272+
# print "OpCode: 0x%X" % opid
273+
# res = self.opmap.get(opid, self.do_default_stuff)()
274+
# return res
275+
276+
def _writeStruct(self, unpack, length, args):
277+
ba = struct.pack(unpack, *args)
278+
self.object_stream.write(ba)
279+
280+
def _writeString(self, string):
281+
len = len(string)
282+
self._writeStruct(">H", 2, (len, ))
283+
self.object_stream.write(string)
284+
285+
def write_blockdata(self, obj, parent=None):
286+
self._writeStruct(">B", 1, (self.TC_BLOCKDATA, ))
287+
# TC_BLOCKDATA (unsigned byte)<size> (byte)[size]
288+
if type(obj) is str:
289+
print "This is string."
290+
self._writeStruct(">B", 1, (len(obj), ))
291+
self.object_stream.write(obj)
292+
293+
def write_object(self, obj, parent=None):
294+
# TC_OBJECT classDesc newHandle classdata[] // data for each class
295+
# self.current_object = JavaObject()
296+
# print "[object]"
297+
self._writeStruct(">B", 1, (self.TC_OBJECT, ))
298+
self._writeStruct(">B", 1, (self.TC_CLASSDESC, ))
299+
300+
# print "OpCode: 0x%X" % opid
301+
# classdesc = self.opmap.get(opid, self.do_default_stuff)(self.current_object)
302+
# self.finalValue = classdesc
303+
# # classdata[]
304+
#
305+
# # Store classdesc of this object
306+
# self.current_object.classdesc = classdesc
307+
#
308+
# for field_name in self.current_object.__fields:
309+
# (opid, ) = self._readStruct(">B", 1)
310+
# print "OpCode: 0x%X" % opid
311+
# res = self.opmap.get(opid, self.do_default_stuff)(self.current_object)
312+
# self.current_object.__setattr__(field_name, res)
313+
# return self.current_object

0 commit comments

Comments
 (0)