Last active
November 10, 2017 09:01
-
-
Save wangweij/2c5a87fdab22a578bc8743d96d8d33bf to your computer and use it in GitHub Desktop.
Apply ois-dump.patch to ObjectInputStream.java. If created with a PrintStream as the 2nd argument, reading will generate a dump into that PrintStream. SerialASM.java can read this dump and assemble it into a binary blob or a Java program (if -c specified).
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
// List.of(1, true, 0.75, "string") | |
STREAM_MAGIC | |
s 5 | |
TC_OBJECT | |
TC_CLASSDESC | |
"java.util.CollSer" | |
l 6309168927139932177 | |
3 | |
s 1 | |
'I' | |
"tag" | |
TC_ENDBLOCKDATA | |
TC_NULL | |
// handle 0 assigned to non proxy desc java.util.CollSer | |
oct 00000001 | |
TC_BLOCKDATA | |
4 | |
// refill 4 | |
i 5 | |
TC_OBJECT | |
TC_CLASSDESC | |
"java.lang.Integer" | |
l 1360826667806852920 | |
SC_SERIALIZABLE | |
s 1 | |
'I' | |
"value" | |
TC_ENDBLOCKDATA | |
TC_CLASSDESC | |
"java.lang.Number" | |
l -8742448824652078965 | |
SC_SERIALIZABLE | |
s 0 | |
TC_ENDBLOCKDATA | |
TC_NULL | |
// handle 3 assigned to non proxy desc java.lang.Number | |
// handle 2 assigned to non proxy desc java.lang.Integer | |
oct 00000001 | |
// handle 4 assigned to object class java.lang.Integer | |
TC_OBJECT | |
TC_CLASSDESC | |
"java.lang.Boolean" | |
l -3665804199014368530 | |
SC_SERIALIZABLE | |
s 1 | |
'Z' | |
"value" | |
TC_ENDBLOCKDATA | |
TC_NULL | |
// handle 5 assigned to non proxy desc java.lang.Boolean | |
oct 01 | |
// handle 6 assigned to object class java.lang.Boolean | |
TC_OBJECT | |
TC_CLASSDESC | |
"java.lang.Double" | |
l -9172774392245257468 | |
SC_SERIALIZABLE | |
s 1 | |
'D' | |
"value" | |
TC_ENDBLOCKDATA | |
TC_REFERENCE | |
h 3 | |
// handle 7 assigned to non proxy desc java.lang.Double | |
oct 3FE8000000000000 | |
// handle 8 assigned to object class java.lang.Double | |
TC_STRING | |
"string" | |
// handle 9 assigned to str string | |
TC_REFERENCE | |
h 9 | |
TC_ENDBLOCKDATA | |
// handle 1 assigned to object class java.util.CollSer |
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
diff --git a/src/java.base/share/classes/java/io/ObjectInputStream.java b/src/java.base/share/classes/java/io/ObjectInputStream.java | |
--- a/src/java.base/share/classes/java/io/ObjectInputStream.java | |
+++ b/src/java.base/share/classes/java/io/ObjectInputStream.java | |
@@ -29,6 +29,7 @@ | |
import java.lang.System.Logger; | |
import java.lang.ref.ReferenceQueue; | |
import java.lang.reflect.Array; | |
+import java.lang.reflect.Field; | |
import java.lang.reflect.Modifier; | |
import java.lang.reflect.Proxy; | |
import java.security.AccessControlContext; | |
@@ -243,6 +244,8 @@ | |
"double", double.class, | |
"void", void.class); | |
+ private static final char[] hchars = "0123456789ABCDEF".toCharArray(); | |
+ | |
private static class Caches { | |
/** cache of subclass security audit results */ | |
static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits = | |
@@ -333,9 +336,13 @@ | |
* @see ObjectOutputStream#ObjectOutputStream(OutputStream) | |
*/ | |
public ObjectInputStream(InputStream in) throws IOException { | |
+ this(in, null); | |
+ } | |
+ | |
+ public ObjectInputStream(InputStream in, PrintStream dump) throws IOException { | |
verifySubclass(); | |
- bin = new BlockDataInputStream(in); | |
- handles = new HandleTable(10); | |
+ bin = new BlockDataInputStream(in, dump); | |
+ handles = new HandleTable(10, dump); | |
vlist = new ValidationList(); | |
serialFilter = ObjectInputFilter.Config.getSerialFilter(); | |
enableOverride = false; | |
@@ -1693,7 +1700,7 @@ | |
if (bin.readByte() != TC_REFERENCE) { | |
throw new InternalError(); | |
} | |
- passHandle = bin.readInt() - baseWireHandle; | |
+ passHandle = bin.readHandle(); | |
if (passHandle < 0 || passHandle >= handles.size()) { | |
throw new StreamCorruptedException( | |
String.format("invalid handle value: %08X", passHandle + | |
@@ -1734,6 +1741,7 @@ | |
handles.markException(passHandle, resolveEx); | |
} | |
+ handles.describe(passHandle, "class " + desc.getName()); | |
handles.finish(passHandle); | |
return cl; | |
} | |
@@ -1840,6 +1848,7 @@ | |
} | |
handles.finish(descHandle); | |
+ handles.describe(descHandle, "proxy desc " + desc.getName()); | |
passHandle = descHandle; | |
return desc; | |
} | |
@@ -1897,6 +1906,7 @@ | |
} | |
handles.finish(descHandle); | |
+ handles.describe(descHandle, "non proxy desc " + desc.getName()); | |
passHandle = descHandle; | |
return desc; | |
@@ -1923,6 +1933,7 @@ | |
String.format("invalid type code: %02X", tc)); | |
} | |
passHandle = handles.assign(unshared ? unsharedMarker : str); | |
+ handles.describe(passHandle, "str " + str); | |
handles.finish(passHandle); | |
return str; | |
} | |
@@ -1987,6 +1998,7 @@ | |
} | |
handles.finish(arrayHandle); | |
+ handles.describe(arrayHandle, "array of " + ccl); | |
passHandle = arrayHandle; | |
return array; | |
} | |
@@ -2030,6 +2042,7 @@ | |
} | |
handles.finish(enumHandle); | |
+ handles.describe(enumHandle, "enum " + result); | |
passHandle = enumHandle; | |
return result; | |
} | |
@@ -2078,6 +2091,7 @@ | |
readSerialData(obj, desc); | |
} | |
+ handles.describe(passHandle, "object " + obj.getClass()); | |
handles.finish(passHandle); | |
if (obj != null && | |
@@ -2831,6 +2845,8 @@ | |
private final byte[] hbuf = new byte[MAX_HEADER_SIZE]; | |
/** char buffer for fast string reads */ | |
private final char[] cbuf = new char[CHAR_BUF_SIZE]; | |
+ /** dump */ | |
+ private final PrintStream dump; | |
/** block data mode */ | |
private boolean blkmode = false; | |
@@ -2852,9 +2868,13 @@ | |
* Creates new BlockDataInputStream on top of given underlying stream. | |
* Block data mode is turned off by default. | |
*/ | |
- BlockDataInputStream(InputStream in) { | |
+ BlockDataInputStream(InputStream in, PrintStream dump) { | |
this.in = new PeekInputStream(in); | |
+ this.dump = dump; | |
din = new DataInputStream(this); | |
+// if (dump != null) { | |
+// dump.println("// Start stream"); | |
+// } | |
} | |
/** | |
@@ -2865,6 +2885,9 @@ | |
* block data is still present in the stream. | |
*/ | |
boolean setBlockDataMode(boolean newmode) throws IOException { | |
+// if (dump != null) { | |
+// dump.println("// blockmode " + newmode); | |
+// } | |
if (blkmode == newmode) { | |
return blkmode; | |
} | |
@@ -2893,6 +2916,9 @@ | |
* mode, throws an IllegalStateException. | |
*/ | |
void skipBlockData() throws IOException { | |
+// if (dump != null) { | |
+// dump.println("// skipBlockData"); | |
+// } | |
if (!blkmode) { | |
throw new IllegalStateException("not in block data mode"); | |
} | |
@@ -2932,6 +2958,10 @@ | |
return HEADER_BLOCKED; | |
} | |
in.readFully(hbuf, 0, 2); | |
+ if (dump != null) { | |
+ dump.println("TC_BLOCKDATA"); | |
+ dump.println(hbuf[1] & 0xFF); | |
+ } | |
return hbuf[1] & 0xFF; | |
case TC_BLOCKDATALONG: | |
@@ -2940,6 +2970,10 @@ | |
} | |
in.readFully(hbuf, 0, 5); | |
int len = Bits.getInt(hbuf, 1); | |
+ if (dump != null) { | |
+ dump.println("TC_BLOCKDATALONG"); | |
+ dump.println("i " + len); | |
+ } | |
if (len < 0) { | |
throw new StreamCorruptedException( | |
"illegal block data header length: " + | |
@@ -2954,6 +2988,9 @@ | |
* reads may span data blocks separated by a TC_RESET. | |
*/ | |
case TC_RESET: | |
+ if (dump != null) { | |
+ dump.println("TC_RESET"); | |
+ } | |
in.read(); | |
handleReset(); | |
break; | |
@@ -2987,6 +3024,9 @@ | |
if (unread > 0) { | |
int n = | |
in.read(buf, 0, Math.min(unread, MAX_BLOCK_SIZE)); | |
+ if (dump != null) { | |
+ dump.println("// refill " + n); | |
+ } | |
if (n >= 0) { | |
end = n; | |
unread -= n; | |
@@ -3065,6 +3105,7 @@ | |
*/ | |
public int read() throws IOException { | |
+ // no dump. called by others. | |
if (blkmode) { | |
if (pos == end) { | |
refill(); | |
@@ -3076,10 +3117,16 @@ | |
} | |
public int read(byte[] b, int off, int len) throws IOException { | |
+ if (dump != null) { | |
+ dump.println(">> read"); | |
+ } | |
return read(b, off, len, false); | |
} | |
public long skip(long len) throws IOException { | |
+ if (dump != null) { | |
+ dump.println(">> skip"); | |
+ } | |
long remain = len; | |
while (remain > 0) { | |
if (blkmode) { | |
@@ -3193,6 +3240,7 @@ | |
public void readFully(byte[] b, int off, int len, boolean copy) | |
throws IOException | |
{ | |
+ int oldOff = off; | |
while (len > 0) { | |
int n = read(b, off, len, copy); | |
if (n < 0) { | |
@@ -3201,9 +3249,20 @@ | |
off += n; | |
len -= n; | |
} | |
+ if (dump != null && off != oldOff) { | |
+ StringBuilder sb = new StringBuilder("oct "); | |
+ for (int i = oldOff; i < off; i++) { | |
+ sb.append(hchars[(b[i]>>4)&0xf]); | |
+ sb.append(hchars[b[i]&0xf]); | |
+ } | |
+ dump.println(sb.toString()); | |
+ } | |
} | |
public int skipBytes(int n) throws IOException { | |
+ if (dump != null) { | |
+ dump.println(">> skipBytes"); | |
+ } | |
return din.skipBytes(n); | |
} | |
@@ -3212,6 +3271,9 @@ | |
if (v < 0) { | |
throw new EOFException(); | |
} | |
+ if (dump != null) { | |
+ dump.println(v != 0); | |
+ } | |
return (v != 0); | |
} | |
@@ -3220,6 +3282,31 @@ | |
if (v < 0) { | |
throw new EOFException(); | |
} | |
+ if (dump != null) { | |
+ boolean found = false; | |
+ try { | |
+ for (Field f : ObjectStreamConstants.class.getFields()) { | |
+ if (f.getType() == Byte.TYPE && f.getByte(null) == v) { | |
+ String n = f.getName(); | |
+ if (n.equals("TC_BASE")) { | |
+ n = "TC_NULL"; | |
+ } | |
+ dump.println(n); | |
+ found = true; | |
+ break; | |
+ } | |
+ } | |
+ } catch (Exception e) { | |
+ // ignored | |
+ } | |
+ if (!found) { | |
+ if (v > ' ' && v <= '~') { | |
+ dump.println("'" + (char)v + "'"); | |
+ } else { | |
+ dump.println(v); | |
+ } | |
+ } | |
+ } | |
return (byte) v; | |
} | |
@@ -3228,6 +3315,9 @@ | |
if (v < 0) { | |
throw new EOFException(); | |
} | |
+ if (dump != null) { | |
+ dump.println(v); | |
+ } | |
return v; | |
} | |
@@ -3240,22 +3330,40 @@ | |
} | |
char v = Bits.getChar(buf, pos); | |
pos += 2; | |
+ if (dump != null) { | |
+ dump.println("c " + (int)v); | |
+ } | |
return v; | |
} | |
public short readShort() throws IOException { | |
+ short v; | |
if (!blkmode) { | |
pos = 0; | |
in.readFully(buf, 0, 2); | |
+ v = Bits.getShort(buf, pos); | |
+ pos += 2; | |
} else if (end - pos < 2) { | |
- return din.readShort(); | |
+ v = din.readShort(); | |
+ } else { | |
+ v = Bits.getShort(buf, pos); | |
+ pos += 2; | |
} | |
- short v = Bits.getShort(buf, pos); | |
- pos += 2; | |
+ if (dump != null) { | |
+ if (v == STREAM_MAGIC) { | |
+ dump.println("STREAM_MAGIC"); | |
+ } else { | |
+ dump.println("s " + v); | |
+ } | |
+ } | |
return v; | |
} | |
public int readUnsignedShort() throws IOException { | |
+ return readUnsignedShortInternal(false); | |
+ } | |
+ | |
+ private int readUnsignedShortInternal(boolean silent) throws IOException { | |
if (!blkmode) { | |
pos = 0; | |
in.readFully(buf, 0, 2); | |
@@ -3264,10 +3372,29 @@ | |
} | |
int v = Bits.getShort(buf, pos) & 0xFFFF; | |
pos += 2; | |
+ if (!silent && dump != null) { | |
+ dump.println("s " + v); | |
+ } | |
+ return v; | |
+ } | |
+ | |
+ public int readHandle() throws IOException { | |
+ int v = readIntInternal() - baseWireHandle; | |
+ if (dump != null) { | |
+ dump.println("h " + v); | |
+ } | |
return v; | |
} | |
public int readInt() throws IOException { | |
+ int v = readIntInternal(); | |
+ if (dump != null) { | |
+ dump.println("i " + v); | |
+ } | |
+ return v; | |
+ } | |
+ | |
+ private int readIntInternal() throws IOException { | |
if (!blkmode) { | |
pos = 0; | |
in.readFully(buf, 0, 4); | |
@@ -3288,10 +3415,17 @@ | |
} | |
float v = Bits.getFloat(buf, pos); | |
pos += 4; | |
+ if (dump != null) { | |
+ dump.println("f " + v); | |
+ } | |
return v; | |
} | |
public long readLong() throws IOException { | |
+ return readLongInternal(false); | |
+ } | |
+ | |
+ public long readLongInternal(boolean silent) throws IOException { | |
if (!blkmode) { | |
pos = 0; | |
in.readFully(buf, 0, 8); | |
@@ -3300,6 +3434,9 @@ | |
} | |
long v = Bits.getLong(buf, pos); | |
pos += 8; | |
+ if (!silent && dump != null) { | |
+ dump.println("l " + v); | |
+ } | |
return v; | |
} | |
@@ -3312,11 +3449,14 @@ | |
} | |
double v = Bits.getDouble(buf, pos); | |
pos += 8; | |
+ if (dump != null) { | |
+ dump.println("d " + v); | |
+ } | |
return v; | |
} | |
public String readUTF() throws IOException { | |
- return readUTFBody(readUnsignedShort()); | |
+ return readUTFBody(readUnsignedShortInternal(true)); | |
} | |
@SuppressWarnings("deprecation") | |
@@ -3333,6 +3473,9 @@ | |
*/ | |
void readBooleans(boolean[] v, int off, int len) throws IOException { | |
+ if (dump != null) { | |
+ dump.println("// readBooleans"); | |
+ } | |
int stop, endoff = off + len; | |
while (off < endoff) { | |
if (!blkmode) { | |
@@ -3341,19 +3484,30 @@ | |
stop = off + span; | |
pos = 0; | |
} else if (end - pos < 1) { | |
- v[off++] = din.readBoolean(); | |
+ boolean b = din.readBoolean(); | |
+ if (dump != null) { | |
+ dump.println(b); | |
+ } | |
+ v[off++] = b; | |
continue; | |
} else { | |
stop = Math.min(endoff, off + end - pos); | |
} | |
while (off < stop) { | |
- v[off++] = Bits.getBoolean(buf, pos++); | |
+ boolean b = Bits.getBoolean(buf, pos++); | |
+ if (dump != null) { | |
+ dump.println(b); | |
+ } | |
+ v[off++] = b; | |
} | |
} | |
} | |
void readChars(char[] v, int off, int len) throws IOException { | |
+ if (dump != null) { | |
+ dump.println("// readChars"); | |
+ } | |
int stop, endoff = off + len; | |
while (off < endoff) { | |
if (!blkmode) { | |
@@ -3362,20 +3516,31 @@ | |
stop = off + span; | |
pos = 0; | |
} else if (end - pos < 2) { | |
- v[off++] = din.readChar(); | |
+ char c = din.readChar(); | |
+ if (dump != null) { | |
+ dump.println("c " + (int)c); | |
+ } | |
+ v[off++] = c; | |
continue; | |
} else { | |
stop = Math.min(endoff, off + ((end - pos) >> 1)); | |
} | |
while (off < stop) { | |
- v[off++] = Bits.getChar(buf, pos); | |
+ char c = Bits.getChar(buf, pos); | |
+ if (dump != null) { | |
+ dump.println("c " + (int)c); | |
+ } | |
+ v[off++] = c; | |
pos += 2; | |
} | |
} | |
} | |
void readShorts(short[] v, int off, int len) throws IOException { | |
+ if (dump != null) { | |
+ dump.println("// readShorts"); | |
+ } | |
int stop, endoff = off + len; | |
while (off < endoff) { | |
if (!blkmode) { | |
@@ -3384,20 +3549,31 @@ | |
stop = off + span; | |
pos = 0; | |
} else if (end - pos < 2) { | |
- v[off++] = din.readShort(); | |
+ short s = din.readShort(); | |
+ if (dump != null) { | |
+ dump.println("s " + s); | |
+ } | |
+ v[off++] = s; | |
continue; | |
} else { | |
stop = Math.min(endoff, off + ((end - pos) >> 1)); | |
} | |
while (off < stop) { | |
- v[off++] = Bits.getShort(buf, pos); | |
+ short s = Bits.getShort(buf, pos); | |
+ if (dump != null) { | |
+ dump.println("s " + s); | |
+ } | |
+ v[off++] = s; | |
pos += 2; | |
} | |
} | |
} | |
void readInts(int[] v, int off, int len) throws IOException { | |
+ if (dump != null) { | |
+ dump.println("// readInts"); | |
+ } | |
int stop, endoff = off + len; | |
while (off < endoff) { | |
if (!blkmode) { | |
@@ -3406,20 +3582,31 @@ | |
stop = off + span; | |
pos = 0; | |
} else if (end - pos < 4) { | |
- v[off++] = din.readInt(); | |
+ int i = din.readInt(); | |
+ if (dump != null) { | |
+ dump.println("i " + i); | |
+ } | |
+ v[off++] = i; | |
continue; | |
} else { | |
stop = Math.min(endoff, off + ((end - pos) >> 2)); | |
} | |
while (off < stop) { | |
- v[off++] = Bits.getInt(buf, pos); | |
+ int i = Bits.getInt(buf, pos); | |
+ if (dump != null) { | |
+ dump.println("i " + i); | |
+ } | |
+ v[off++] = i; | |
pos += 4; | |
} | |
} | |
} | |
void readFloats(float[] v, int off, int len) throws IOException { | |
+ if (dump != null) { | |
+ dump.println("// readFloats"); | |
+ } | |
int span, endoff = off + len; | |
while (off < endoff) { | |
if (!blkmode) { | |
@@ -3427,19 +3614,31 @@ | |
in.readFully(buf, 0, span << 2); | |
pos = 0; | |
} else if (end - pos < 4) { | |
- v[off++] = din.readFloat(); | |
+ float f = din.readFloat(); | |
+ if (dump != null) { | |
+ dump.println("f " + f); | |
+ } | |
+ v[off++] = f; | |
continue; | |
} else { | |
span = Math.min(endoff - off, ((end - pos) >> 2)); | |
} | |
bytesToFloats(buf, pos, v, off, span); | |
+ if (dump != null) { | |
+ for (int i = 0; i < span; i++) { | |
+ dump.println("f " + v[off + i]); | |
+ } | |
+ } | |
off += span; | |
pos += span << 2; | |
} | |
} | |
void readLongs(long[] v, int off, int len) throws IOException { | |
+ if (dump != null) { | |
+ dump.println("// readLongs"); | |
+ } | |
int stop, endoff = off + len; | |
while (off < endoff) { | |
if (!blkmode) { | |
@@ -3448,20 +3647,31 @@ | |
stop = off + span; | |
pos = 0; | |
} else if (end - pos < 8) { | |
- v[off++] = din.readLong(); | |
+ long l = din.readLong(); | |
+ if (dump != null) { | |
+ dump.println("l " + l); | |
+ } | |
+ v[off++] = l; | |
continue; | |
} else { | |
stop = Math.min(endoff, off + ((end - pos) >> 3)); | |
} | |
while (off < stop) { | |
- v[off++] = Bits.getLong(buf, pos); | |
+ long l = Bits.getLong(buf, pos); | |
+ if (dump != null) { | |
+ dump.println("l " + l); | |
+ } | |
+ v[off++] = l; | |
pos += 8; | |
} | |
} | |
} | |
void readDoubles(double[] v, int off, int len) throws IOException { | |
+ if (dump != null) { | |
+ dump.println("// readDoubles"); | |
+ } | |
int span, endoff = off + len; | |
while (off < endoff) { | |
if (!blkmode) { | |
@@ -3469,13 +3679,22 @@ | |
in.readFully(buf, 0, span << 3); | |
pos = 0; | |
} else if (end - pos < 8) { | |
- v[off++] = din.readDouble(); | |
+ double d = din.readDouble(); | |
+ if (dump != null) { | |
+ dump.println("d " + d); | |
+ } | |
+ v[off++] = d; | |
continue; | |
} else { | |
span = Math.min(endoff - off, ((end - pos) >> 3)); | |
} | |
bytesToDoubles(buf, pos, v, off, span); | |
+ if (dump != null) { | |
+ for (int i = 0; i < span; i++) { | |
+ dump.println("d " + v[off + i]); | |
+ } | |
+ } | |
off += span; | |
pos += span << 3; | |
} | |
@@ -3487,7 +3706,7 @@ | |
* (instead of the standard 2 bytes) to convey the UTF encoding length. | |
*/ | |
String readLongUTF() throws IOException { | |
- return readUTFBody(readLong()); | |
+ return readUTFBody(readLongInternal(true)); | |
} | |
/** | |
@@ -3529,7 +3748,11 @@ | |
} | |
} | |
- return sbuf.toString(); | |
+ String s = sbuf.toString(); | |
+ if (dump != null) { | |
+ dump.println("\"" + s + "\""); | |
+ } | |
+ return s; | |
} | |
/** | |
@@ -3723,13 +3946,17 @@ | |
/** number of handles in table */ | |
int size = 0; | |
+ /** dump */ | |
+ final PrintStream dump; | |
+ | |
/** | |
* Creates handle table with the given initial capacity. | |
*/ | |
- HandleTable(int initialCapacity) { | |
+ HandleTable(int initialCapacity, PrintStream d) { | |
status = new byte[initialCapacity]; | |
entries = new Object[initialCapacity]; | |
deps = new HandleList[initialCapacity]; | |
+ dump = d; | |
} | |
/** | |
@@ -3747,6 +3974,12 @@ | |
return size++; | |
} | |
+ void describe(int n, String label) { | |
+ if (dump != null) { | |
+ dump.printf("// handle %d assigned to %s\n", n, label); | |
+ } | |
+ } | |
+ | |
/** | |
* Registers a dependency (in exception status) of one handle on | |
* another. The dependent handle must be "open" (i.e., assigned, but |
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
import java.io.BufferedReader; | |
import java.io.DataOutputStream; | |
import java.io.InputStreamReader; | |
import java.io.ObjectStreamClass; | |
import java.io.ObjectStreamConstants; | |
import java.lang.reflect.Field; | |
// This class assembles a serialization description into a byte blob. | |
// "//" comment can appear anywhere (hopefully not in a string); | |
// A line can be "string" or 'A' (byte); | |
// or a number as "type number", type can be 1st char of char, short, int, | |
// long, float, or double, number can be hex, long can end with 'l'; | |
// or "oct hex" for a binary blob; | |
// or "svuid" means serialVersionUID of the string above; | |
// or "h handle" where handle is a number; | |
// or a ObjectStreamConstants constant; | |
// or "true", or "false"; | |
// or a byte. | |
// | |
// Attention: if you want to use "h number", all "nh" must be provided, | |
// This tool does not support auto handle counting (yet). | |
public class SerialASM { | |
static void codi(String f, String v) { | |
StringBuilder sb = new StringBuilder(" dos."); | |
sb.append(f).append('('); | |
if (f.equals("writeUTF")) { | |
sb.append('"'); | |
} | |
sb.append(v); | |
if (f.equals("writeLong") && (!v.endsWith("l") && !v.endsWith("L"))) { | |
sb.append('L'); | |
} | |
if (f.equals("writeUTF")) { | |
sb.append('"'); | |
} | |
sb.append(");\n"); | |
System.out.print(sb.toString()); | |
} | |
public static void main(String[] args) throws Exception { | |
boolean code = args.length == 1 && args[0].equals("-c"); | |
if (code) { | |
System.out.println("import java.io.*;\n" | |
+ "\n" | |
+ "public class App implements ObjectStreamConstants {\n" | |
+ "\n" | |
+ " static byte[] hex(String in) {\n" | |
+ " int len = in.length()/2;\n" | |
+ " byte[] out = new byte[len];\n" | |
+ " for (int i=0; i<len; i++) {\n" | |
+ " out[i] = (byte)Integer.parseInt(in.substring(i*2, i*2+2), 16);\n" | |
+ " }\n" | |
+ " return out;\n" | |
+ " }\n" | |
+ "\n" | |
+ " public static void main(String[] args) throws Exception {\n" | |
+ " ByteArrayOutputStream baos = new ByteArrayOutputStream();\n" | |
+ " DataOutputStream dos = new DataOutputStream(baos);\n"); | |
} | |
try (DataOutputStream dos = code ? null : new DataOutputStream(System.out); | |
BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) { | |
String lastString = null; | |
while (true) { | |
String s = br.readLine(); | |
if (s == null) { | |
break; | |
} | |
int pos = s.indexOf("//"); | |
if (pos >= 0) { | |
s = s.substring(0, pos); | |
} | |
s = s.trim(); | |
if (s.isEmpty()) { | |
continue; | |
} | |
char h = s.charAt(0); | |
if (h == '\'') { | |
if (code) { | |
codi("writeByte", "" + (int)s.charAt(1)); | |
} else { | |
dos.writeByte(s.charAt(1)); | |
} | |
} else if (h == '"') { | |
lastString = s.substring(1, s.length() - 1); | |
if (code) { | |
codi("writeUTF", lastString); | |
} else { | |
dos.writeUTF(lastString); | |
} | |
} else { | |
String[] pair = s.split(" ", 2); | |
String k = pair[0]; | |
String v = pair.length == 2 ? pair[1] : pair[0]; | |
int radix = 10; | |
String oldv = v; | |
if (v.startsWith("0x") || v.startsWith("0X")) { | |
v = v.substring(2); | |
radix = 16; | |
} | |
switch (k) { | |
case "c": | |
if (code) { | |
codi("writeChar", oldv); | |
} else { | |
dos.writeChar(Short.parseShort(v, radix)); | |
} | |
break; | |
case "s": | |
if (code) { | |
codi("writeShort", oldv); | |
} else { | |
dos.writeShort(Short.parseShort(v, radix)); | |
} | |
break; | |
case "i": | |
if (code) { | |
codi("writeInt", oldv); | |
} else { | |
dos.writeInt(Integer.parseInt(v, radix)); | |
} | |
break; | |
case "h": | |
if (code) { | |
codi("writeInt", "ObjectStreamConstants.baseWireHandle + " + oldv); | |
} else { | |
dos.writeInt(ObjectStreamConstants.baseWireHandle | |
+ Integer.parseInt(v, radix)); | |
} | |
break; | |
case "l": | |
if (v.endsWith("l") || v.endsWith("L")) { | |
v = v.substring(0, v.length() - 1); | |
} | |
if (code) { | |
codi("writeLong", oldv); | |
} else { | |
dos.writeLong(Long.parseLong(v, radix)); | |
} | |
break; | |
case "f": | |
if (code) { | |
codi("writeFloat", v); | |
} else { | |
dos.writeFloat(Float.parseFloat(v)); | |
} | |
break; | |
case "d": | |
if (code) { | |
codi("writeDouble", oldv); | |
} else { | |
dos.writeDouble(Double.parseDouble(v)); | |
} | |
break; | |
case "oct": | |
if (code) { | |
codi("write", "hex(\"" + v + "\")"); | |
} else { | |
dos.write(xeh(v)); | |
} | |
break; | |
case "true": | |
case "false": | |
if (code) { | |
codi("writeBoolean", v); | |
} else { | |
dos.writeBoolean(v.equals("true")); | |
} | |
break; | |
case "svuid": | |
Class<?> cl = Class.forName(lastString, false, null); | |
ObjectStreamClass desc = ObjectStreamClass.lookup(cl); | |
if (desc != null) { | |
if (code) { | |
codi("writeLong", "" + desc.getSerialVersionUID()); | |
} else { | |
dos.writeLong(desc.getSerialVersionUID()); | |
} | |
} else { | |
throw new Exception("svuid for " + lastString); | |
} | |
break; | |
default: | |
if (pair.length == 2) { | |
throw new Exception("Unknown " + s); | |
} | |
if (Character.isDigit(h)) { | |
if (code) { | |
codi("writeByte", oldv); | |
} else { | |
dos.writeByte(Byte.parseByte(v, radix)); | |
} | |
} else { | |
Field f = ObjectStreamConstants.class.getField(s); | |
if (f.getType() == Byte.TYPE) { | |
if (code) { | |
codi("writeByte", s); | |
} else { | |
dos.writeByte(f.getByte(null)); | |
} | |
} else if (f.getType() == Short.TYPE) { | |
if (code) { | |
codi("writeShort", s); | |
} else { | |
dos.writeShort(f.getShort(null)); | |
} | |
} else { | |
throw new Exception("Const? " + s); | |
} | |
} | |
} | |
} | |
} | |
} | |
if (code) { | |
System.out.println("\n" | |
+ " System.out.write(baos.toByteArray());\n" | |
+ " }\n" | |
+ "}\n"); | |
System.out.flush(); | |
} | |
} | |
static byte[] xeh(String in) { | |
int len = in.length()/2; | |
byte[] out = new byte[len]; | |
for (int i=0; i<len; i++) { | |
out[i] = (byte)Integer.parseInt(in.substring(i*2, i*2+2), 16); | |
} | |
return out; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment