From 6448f0e32bb1599d73b7558973c149a7835e2285 Mon Sep 17 00:00:00 2001 From: Skylot Date: Sun, 22 May 2022 14:21:25 +0100 Subject: [PATCH] fix: use variable length encoding instead short for offsets (can overflow) (#1489) --- .../src/main/java/jadx/api/JavaClass.java | 1 - .../codecache/disk/CodeMetadataAdapter.java | 14 +++-- .../utils/codecache/disk/DiskCodeCache.java | 2 +- .../disk/adapters/BaseDataAdapter.java | 26 -------- .../disk/adapters/DataAdapterHelper.java | 63 +++++++++++++++++++ .../disk/adapters/InsnCodeOffsetAdapter.java | 4 +- .../disk/adapters/NodeDeclareRefAdapter.java | 4 +- .../disk/adapters/VarNodeAdapter.java | 15 +++-- .../disk/adapters/VarRefAdapter.java | 10 ++- .../disk/adapters/DataAdapterHelperTest.java | 40 ++++++++++++ 10 files changed, 130 insertions(+), 49 deletions(-) delete mode 100644 jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/BaseDataAdapter.java create mode 100644 jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/DataAdapterHelper.java create mode 100644 jadx-gui/src/test/java/jadx/gui/utils/codecache/disk/adapters/DataAdapterHelperTest.java diff --git a/jadx-core/src/main/java/jadx/api/JavaClass.java b/jadx-core/src/main/java/jadx/api/JavaClass.java index 4032c87a..207596a4 100644 --- a/jadx-core/src/main/java/jadx/api/JavaClass.java +++ b/jadx-core/src/main/java/jadx/api/JavaClass.java @@ -206,7 +206,6 @@ public final class JavaClass implements JavaNode { // ignore declarations and offset annotations continue; } - // ignore declarations JavaNode annNode = rootDec.getJavaNodeByCodeAnnotation(codeInfo, ann); if (annNode == null && LOG.isDebugEnabled()) { LOG.debug("Failed to resolve code annotation, cls: {}, pos: {}, ann: {}", this, entry.getKey(), ann); diff --git a/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/CodeMetadataAdapter.java b/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/CodeMetadataAdapter.java index dff3b6b8..41d70df9 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/CodeMetadataAdapter.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/CodeMetadataAdapter.java @@ -25,6 +25,8 @@ import jadx.core.dex.nodes.RootNode; import jadx.core.utils.files.FileUtils; import jadx.gui.utils.codecache.disk.adapters.CodeAnnotationAdapter; +import static jadx.gui.utils.codecache.disk.adapters.DataAdapterHelper.readUVInt; +import static jadx.gui.utils.codecache.disk.adapters.DataAdapterHelper.writeUVInt; import static java.nio.file.StandardOpenOption.CREATE; import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; import static java.nio.file.StandardOpenOption.WRITE; @@ -68,8 +70,8 @@ public class CodeMetadataAdapter { private void writeLines(DataOutput out, Map lines) throws IOException { out.writeInt(lines.size()); for (Map.Entry entry : lines.entrySet()) { - out.writeShort(entry.getKey()); - out.writeShort(entry.getValue()); + writeUVInt(out, entry.getKey()); + writeUVInt(out, entry.getValue()); } } @@ -80,8 +82,8 @@ public class CodeMetadataAdapter { } Map lines = new HashMap<>(size); for (int i = 0; i < size; i++) { - int key = in.readShort(); - int value = in.readShort(); + int key = readUVInt(in); + int value = readUVInt(in); lines.put(key, value); } return lines; @@ -90,7 +92,7 @@ public class CodeMetadataAdapter { private void writeAnnotations(DataOutputStream out, Map annotations) throws IOException { out.writeInt(annotations.size()); for (Map.Entry entry : annotations.entrySet()) { - out.writeInt(entry.getKey()); + writeUVInt(out, entry.getKey()); codeAnnotationAdapter.write(out, entry.getValue()); } } @@ -102,7 +104,7 @@ public class CodeMetadataAdapter { } Map map = new HashMap<>(size); for (int i = 0; i < size; i++) { - int pos = in.readInt(); + int pos = readUVInt(in); ICodeAnnotation ann = codeAnnotationAdapter.read(in); if (ann != null) { map.put(pos, ann); diff --git a/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/DiskCodeCache.java b/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/DiskCodeCache.java index 1fb9d0a5..0a8f0608 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/DiskCodeCache.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/DiskCodeCache.java @@ -37,7 +37,7 @@ import jadx.core.utils.files.FileUtils; public class DiskCodeCache implements ICodeCache { private static final Logger LOG = LoggerFactory.getLogger(DiskCodeCache.class); - private static final int DATA_FORMAT_VERSION = 8; + private static final int DATA_FORMAT_VERSION = 9; private final Path srcDir; private final Path metaDir; diff --git a/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/BaseDataAdapter.java b/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/BaseDataAdapter.java deleted file mode 100644 index 75211545..00000000 --- a/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/BaseDataAdapter.java +++ /dev/null @@ -1,26 +0,0 @@ -package jadx.gui.utils.codecache.disk.adapters; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; - -import org.jetbrains.annotations.Nullable; - -public abstract class BaseDataAdapter implements DataAdapter { - - public void writeNullableUTF(DataOutput out, @Nullable String str) throws IOException { - if (str == null) { - out.writeByte(0); - } else { - out.writeByte(1); - out.writeUTF(str); - } - } - - public @Nullable String readNullableUTF(DataInput in) throws IOException { - if (in.readByte() == 0) { - return null; - } - return in.readUTF(); - } -} diff --git a/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/DataAdapterHelper.java b/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/DataAdapterHelper.java new file mode 100644 index 00000000..aab5d55e --- /dev/null +++ b/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/DataAdapterHelper.java @@ -0,0 +1,63 @@ +package jadx.gui.utils.codecache.disk.adapters; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +import org.jetbrains.annotations.Nullable; + +public class DataAdapterHelper { + + public static void writeNullableUTF(DataOutput out, @Nullable String str) throws IOException { + if (str == null) { + out.writeByte(0); + } else { + out.writeByte(1); + out.writeUTF(str); + } + } + + public static @Nullable String readNullableUTF(DataInput in) throws IOException { + if (in.readByte() == 0) { + return null; + } + return in.readUTF(); + } + + /** + * Write unsigned variable length integer (ULEB128 encoding) + */ + public static void writeUVInt(DataOutput out, int val) throws IOException { + if (val < 0) { + throw new IllegalArgumentException("Expect value >= 0, got: " + val); + } + int current = val; + int next = val; + while (true) { + next >>>= 7; + if (next == 0) { + // last byte + out.writeByte(current & 0x7f); + return; + } + out.writeByte((current & 0x7f) | 0x80); + current = next; + } + } + + /** + * Read unsigned variable length integer (ULEB128 encoding) + */ + public static int readUVInt(DataInput in) throws IOException { + int result = 0; + int shift = 0; + while (true) { + byte v = in.readByte(); + result |= (v & (byte) 0x7f) << shift; + shift += 7; + if ((v & 0x80) != 0x80) { + return result; + } + } + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/InsnCodeOffsetAdapter.java b/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/InsnCodeOffsetAdapter.java index e391f413..b317038a 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/InsnCodeOffsetAdapter.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/InsnCodeOffsetAdapter.java @@ -12,11 +12,11 @@ public class InsnCodeOffsetAdapter implements DataAdapter { @Override public void write(DataOutput out, InsnCodeOffset value) throws IOException { - out.writeShort(value.getOffset()); + DataAdapterHelper.writeUVInt(out, value.getOffset()); } @Override public InsnCodeOffset read(DataInput in) throws IOException { - return new InsnCodeOffset(in.readShort()); + return new InsnCodeOffset(DataAdapterHelper.readUVInt(in)); } } diff --git a/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/NodeDeclareRefAdapter.java b/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/NodeDeclareRefAdapter.java index 69bebab4..118315e4 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/NodeDeclareRefAdapter.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/NodeDeclareRefAdapter.java @@ -21,13 +21,13 @@ public class NodeDeclareRefAdapter implements DataAdapter { throw new RuntimeException("Null node in NodeDeclareRef"); } refAdapter.write(out, node); - out.writeShort(value.getDefPos()); + DataAdapterHelper.writeUVInt(out, value.getDefPos()); } @Override public NodeDeclareRef read(DataInput in) throws IOException { ICodeNodeRef ref = (ICodeNodeRef) refAdapter.read(in); - int defPos = in.readShort(); + int defPos = DataAdapterHelper.readUVInt(in); NodeDeclareRef nodeDeclareRef = new NodeDeclareRef(ref); nodeDeclareRef.setDefPos(defPos); // restore def position if loading metadata without actual decompilation diff --git a/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/VarNodeAdapter.java b/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/VarNodeAdapter.java index f2f4a3ba..ee4dd2dc 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/VarNodeAdapter.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/VarNodeAdapter.java @@ -8,7 +8,12 @@ import jadx.api.metadata.annotations.VarNode; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.MethodNode; -public class VarNodeAdapter extends BaseDataAdapter { +import static jadx.gui.utils.codecache.disk.adapters.DataAdapterHelper.readNullableUTF; +import static jadx.gui.utils.codecache.disk.adapters.DataAdapterHelper.readUVInt; +import static jadx.gui.utils.codecache.disk.adapters.DataAdapterHelper.writeNullableUTF; +import static jadx.gui.utils.codecache.disk.adapters.DataAdapterHelper.writeUVInt; + +public class VarNodeAdapter implements DataAdapter { private final MethodNodeAdapter mthAdapter; public VarNodeAdapter(MethodNodeAdapter mthAdapter) { @@ -18,8 +23,8 @@ public class VarNodeAdapter extends BaseDataAdapter { @Override public void write(DataOutput out, VarNode value) throws IOException { mthAdapter.write(out, value.getMth()); - out.writeShort(value.getReg()); - out.writeShort(value.getSsa()); + writeUVInt(out, value.getReg()); + writeUVInt(out, value.getSsa()); ArgTypeAdapter.INSTANCE.write(out, value.getType()); writeNullableUTF(out, value.getName()); } @@ -27,8 +32,8 @@ public class VarNodeAdapter extends BaseDataAdapter { @Override public VarNode read(DataInput in) throws IOException { MethodNode mth = mthAdapter.read(in); - int reg = in.readShort(); - int ssa = in.readShort(); + int reg = readUVInt(in); + int ssa = readUVInt(in); ArgType type = ArgTypeAdapter.INSTANCE.read(in); String name = readNullableUTF(in); return new VarNode(mth, reg, ssa, type, name); diff --git a/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/VarRefAdapter.java b/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/VarRefAdapter.java index e708ec5a..507df257 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/VarRefAdapter.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/codecache/disk/adapters/VarRefAdapter.java @@ -6,21 +6,19 @@ import java.io.IOException; import jadx.api.metadata.annotations.VarRef; -public class VarRefAdapter extends BaseDataAdapter { +public class VarRefAdapter implements DataAdapter { public static final VarRefAdapter INSTANCE = new VarRefAdapter(); @Override public void write(DataOutput out, VarRef value) throws IOException { int refPos = value.getRefPos(); - if (refPos <= 0) { - throw new RuntimeException("Variable refPos is invalid: " + value); - } - out.writeInt(refPos); + DataAdapterHelper.writeUVInt(out, refPos); } @Override public VarRef read(DataInput in) throws IOException { - return VarRef.fromPos(in.readInt()); + int refPos = DataAdapterHelper.readUVInt(in); + return VarRef.fromPos(refPos); } } diff --git a/jadx-gui/src/test/java/jadx/gui/utils/codecache/disk/adapters/DataAdapterHelperTest.java b/jadx-gui/src/test/java/jadx/gui/utils/codecache/disk/adapters/DataAdapterHelperTest.java new file mode 100644 index 00000000..5d8f48ec --- /dev/null +++ b/jadx-gui/src/test/java/jadx/gui/utils/codecache/disk/adapters/DataAdapterHelperTest.java @@ -0,0 +1,40 @@ +package jadx.gui.utils.codecache.disk.adapters; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class DataAdapterHelperTest { + + @Test + void uvInt() throws IOException { + checkUVIntFor(0); + checkUVIntFor(7); + checkUVIntFor(0x7f); + checkUVIntFor(0x80); + checkUVIntFor(0x256); + checkUVIntFor(Byte.MAX_VALUE); + checkUVIntFor(Short.MAX_VALUE); + checkUVIntFor(Integer.MAX_VALUE); + } + + private void checkUVIntFor(int val) throws IOException { + assertThat(writeReadUVInt(val)).isEqualTo(val); + } + + private int writeReadUVInt(int val) throws IOException { + ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(byteOut); + DataAdapterHelper.writeUVInt(out, val); + + DataInput in = new DataInputStream(new ByteArrayInputStream(byteOut.toByteArray())); + return DataAdapterHelper.readUVInt(in); + } +} -- GitLab