From 2c2af082690edbe219b22ee48f9af849bed70b49 Mon Sep 17 00:00:00 2001 From: bigConifer <64743731+bigConifer@users.noreply.github.com> Date: Mon, 24 Jan 2022 21:51:45 +0100 Subject: [PATCH] For #5: Fix AXMLPrinter2 bug ArrayIndexOutOfBoundsException Ref https://github.com/WindySha/Xpatch/commit/a5680fd --- .../java/android/content/res/IntReader.java | 5 + .../java/android/content/res/StringBlock.java | 91 +++++++++++++++---- 2 files changed, 79 insertions(+), 17 deletions(-) mode change 100644 => 100755 src/main/java/android/content/res/IntReader.java mode change 100644 => 100755 src/main/java/android/content/res/StringBlock.java diff --git a/src/main/java/android/content/res/IntReader.java b/src/main/java/android/content/res/IntReader.java old mode 100644 new mode 100755 index 3f0f183..f6be233 --- a/src/main/java/android/content/res/IntReader.java +++ b/src/main/java/android/content/res/IntReader.java @@ -15,6 +15,7 @@ */ package android.content.res; +import java.io.DataInputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; @@ -75,6 +76,10 @@ public final int readInt() throws IOException { return readInt(4); } + public final void readFully(byte[] b) throws IOException { + new DataInputStream(m_stream).readFully(b); + } + public final int readInt(int length) throws IOException { if (length<0 || length>4) { throw new IllegalArgumentException(); diff --git a/src/main/java/android/content/res/StringBlock.java b/src/main/java/android/content/res/StringBlock.java old mode 100644 new mode 100755 index 3d5be35..0b4f594 --- a/src/main/java/android/content/res/StringBlock.java +++ b/src/main/java/android/content/res/StringBlock.java @@ -16,6 +16,10 @@ package android.content.res; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; /** * @author Dmitry Skiba @@ -28,6 +32,17 @@ */ public class StringBlock { + private int[] m_stringOffsets; + private byte[] m_strings; + private int[] m_styleOffsets; + private int[] m_styles; + private boolean m_isUTF8; + private static final int CHUNK_TYPE = 0x001C0001; + private static final int UTF8_FLAG = 0x00000100; + + private final CharsetDecoder UTF8_DECODER = Charset.forName("UTF-8").newDecoder(); + private final CharsetDecoder UTF16LE_DECODER = Charset.forName("UTF-16LE").newDecoder(); + /** * Reads whole (including chunk type) string block from stream. * Stream must be at the chunk type. @@ -37,21 +52,20 @@ public static StringBlock read(IntReader reader) throws IOException { int chunkSize=reader.readInt(); int stringCount=reader.readInt(); int styleOffsetCount=reader.readInt(); - /*?*/reader.readInt(); + int flags = reader.readInt(); int stringsOffset=reader.readInt(); int stylesOffset=reader.readInt(); StringBlock block=new StringBlock(); + block.m_isUTF8 = (flags & UTF8_FLAG) != 0; block.m_stringOffsets=reader.readIntArray(stringCount); if (styleOffsetCount!=0) { block.m_styleOffsets=reader.readIntArray(styleOffsetCount); } { int size=((stylesOffset==0)?chunkSize:stylesOffset)-stringsOffset; - if ((size%4)!=0) { - throw new IOException("String data size is not multiple of 4 ("+size+")."); - } - block.m_strings=reader.readIntArray(size/4); + block.m_strings = new byte[size]; + reader.readFully(block.m_strings); } if (stylesOffset!=0) { int size=(chunkSize-stylesOffset); @@ -84,15 +98,64 @@ public String getString(int index) { return null; } int offset=m_stringOffsets[index]; - int length=getShort(m_strings,offset); - StringBuilder result=new StringBuilder(length); - for (;length!=0;length-=1) { - offset+=2; - result.append((char)getShort(m_strings,offset)); + int length; + if (m_isUTF8) { + int[] val = getUtf8(m_strings, offset); + offset = val[0]; + length = val[1]; + } else { + int[] val = getUtf16(m_strings, offset); + offset += val[0]; + length = val[1]; } - return result.toString(); + return decodeString(offset, length); } + private String decodeString(int offset, int length) { + try { + return (m_isUTF8 ? UTF8_DECODER : UTF16LE_DECODER).decode( + ByteBuffer.wrap(m_strings, offset, length)).toString(); + } catch (CharacterCodingException e) { + return null; + } + } + + private static final int getShort(byte[] array, int offset) { + return (array[offset + 1] & 0xff) << 8 | array[offset] & 0xff; + } + + private static final int[] getUtf8(byte[] array, int offset) { + int val = array[offset]; + int length; + if ((val & 0x80) != 0) { + offset += 2; + } else { + offset += 1; + } + val = array[offset]; + if ((val & 0x80) != 0) { + offset += 2; + } else { + offset += 1; + } + length = 0; + while (array[offset + length] != 0) { + length++; + } + return new int[]{offset, length}; + } + + private static final int[] getUtf16(byte[] array, int offset) { + int val = (array[offset + 1] & 0xff) << 8 | array[offset] & 0xff; + if (val == 0x8000) { + int heigh = (array[offset + 3] & 0xFF) << 8; + int low = (array[offset + 2] & 0xFF); + return new int[]{4, (heigh + low) * 2}; + } + return new int[]{2, val * 2}; + } + + /** * Not yet implemented. * @@ -236,10 +299,4 @@ private static final int getShort(int[] array,int offset) { } } - private int[] m_stringOffsets; - private int[] m_strings; - private int[] m_styleOffsets; - private int[] m_styles; - - private static final int CHUNK_TYPE=0x001C0001; }