From a5680fd61ce8574b63c9adc75aa560fc28ee534f Mon Sep 17 00:00:00 2001 From: Windy <819170366@qq.com> Date: Sun, 26 May 2019 15:17:15 +0800 Subject: [PATCH] Fix AXMLPrinter2 bug: java.lang.ArrayIndexOutOfBoundsException in StringBlock.getShort --- .../wind/android/content/res/IntReader.java | 315 +++++----- .../wind/android/content/res/StringBlock.java | 546 ++++++++++-------- 2 files changed, 457 insertions(+), 404 deletions(-) mode change 100644 => 100755 axmlprinter/src/main/java/wind/android/content/res/IntReader.java mode change 100644 => 100755 axmlprinter/src/main/java/wind/android/content/res/StringBlock.java diff --git a/axmlprinter/src/main/java/wind/android/content/res/IntReader.java b/axmlprinter/src/main/java/wind/android/content/res/IntReader.java old mode 100644 new mode 100755 index 0a006d1..fc0cd27 --- a/axmlprinter/src/main/java/wind/android/content/res/IntReader.java +++ b/axmlprinter/src/main/java/wind/android/content/res/IntReader.java @@ -1,156 +1,159 @@ -/* - * Copyright 2008 Android4ME - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package wind.android.content.res; - -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; - -/** - * @author Dmitry Skiba - * - * Simple helper class that allows reading of integers. - * - * TODO: - * * implement buffering - * - */ -public final class IntReader { - - public IntReader() { - } - public IntReader(InputStream stream,boolean bigEndian) { - reset(stream,bigEndian); - } - - public final void reset(InputStream stream,boolean bigEndian) { - m_stream=stream; - m_bigEndian=bigEndian; - m_position=0; - } - - public final void close() { - if (m_stream==null) { - return; - } - try { - m_stream.close(); - } - catch (IOException e) { - } - reset(null,false); - } - - public final InputStream getStream() { - return m_stream; - } - - public final boolean isBigEndian() { - return m_bigEndian; - } - public final void setBigEndian(boolean bigEndian) { - m_bigEndian=bigEndian; - } - - public final int readByte() throws IOException { - return readInt(1); - } - public final int readShort() throws IOException { - return readInt(2); - } - public final int readInt() throws IOException { - return readInt(4); - } - - public final int readInt(int length) throws IOException { - if (length<0 || length>4) { - throw new IllegalArgumentException(); - } - int result=0; - if (m_bigEndian) { - for (int i=(length-1)*8;i>=0;i-=8) { - int b=m_stream.read(); - if (b==-1) { - throw new EOFException(); - } - m_position+=1; - result|=(b<0;length-=1) { - array[offset++]=readInt(); - } - } - - public final byte[] readByteArray(int length) throws IOException { - byte[] array=new byte[length]; - int read=m_stream.read(array); - m_position+=read; - if (read!=length) { - throw new EOFException(); - } - return array; - } - - public final void skip(int bytes) throws IOException { - if (bytes<=0) { - return; - } - long skipped=m_stream.skip(bytes); - m_position+=skipped; - if (skipped!=bytes) { - throw new EOFException(); - } - } - - public final void skipInt() throws IOException { - skip(4); - } - - public final int available() throws IOException { - return m_stream.available(); - } - - public final int getPosition() { - return m_position; - } - - /////////////////////////////////// data - - private InputStream m_stream; - private boolean m_bigEndian; - private int m_position; -} +/* + * Copyright 2008 Android4ME + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package wind.android.content.res; + +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +/** + * @author Dmitry Skiba + * + * Simple helper class that allows reading of integers. + * + * TODO: + * * implement buffering + * + */ +public final class IntReader { + + public IntReader() { + } + public IntReader(InputStream stream,boolean bigEndian) { + reset(stream,bigEndian); + } + + public final void reset(InputStream stream,boolean bigEndian) { + m_stream=stream; + m_bigEndian=bigEndian; + m_position=0; + } + + public final void close() { + if (m_stream==null) { + return; + } + try { + m_stream.close(); + } + catch (IOException e) { + } + reset(null,false); + } + + public final InputStream getStream() { + return m_stream; + } + + public final boolean isBigEndian() { + return m_bigEndian; + } + public final void setBigEndian(boolean bigEndian) { + m_bigEndian=bigEndian; + } + + public final int readByte() throws IOException { + return readInt(1); + } + public final int readShort() throws IOException { + return readInt(2); + } + 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(); + } + int result=0; + if (m_bigEndian) { + for (int i=(length-1)*8;i>=0;i-=8) { + int b=m_stream.read(); + if (b==-1) { + throw new EOFException(); + } + m_position+=1; + result|=(b<0;length-=1) { + array[offset++]=readInt(); + } + } + + public final byte[] readByteArray(int length) throws IOException { + byte[] array=new byte[length]; + int read=m_stream.read(array); + m_position+=read; + if (read!=length) { + throw new EOFException(); + } + return array; + } + + public final void skip(int bytes) throws IOException { + if (bytes<=0) { + return; + } + long skipped=m_stream.skip(bytes); + m_position+=skipped; + if (skipped!=bytes) { + throw new EOFException(); + } + } + + public final void skipInt() throws IOException { + skip(4); + } + + public final int available() throws IOException { + return m_stream.available(); + } + + public final int getPosition() { + return m_position; + } + + /////////////////////////////////// data + + private InputStream m_stream; + private boolean m_bigEndian; + private int m_position; +} diff --git a/axmlprinter/src/main/java/wind/android/content/res/StringBlock.java b/axmlprinter/src/main/java/wind/android/content/res/StringBlock.java old mode 100644 new mode 100755 index dc6dee4..ad24891 --- a/axmlprinter/src/main/java/wind/android/content/res/StringBlock.java +++ b/axmlprinter/src/main/java/wind/android/content/res/StringBlock.java @@ -1,248 +1,298 @@ -/* - * Copyright 2008 Android4ME - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package wind.android.content.res; - -import wind.android.content.res.ChunkUtil; -import wind.android.content.res.IntReader; - -import java.io.IOException; - -/** - * @author Dmitry Skiba - * - * Block of strings, used in binary xml and arsc. - * - * TODO: - * - implement get() - * - */ -public class StringBlock { - - /** - * Reads whole (including chunk type) string block from stream. - * Stream must be at the chunk type. - */ - public static StringBlock read(IntReader reader) throws IOException { - wind.android.content.res.ChunkUtil.readCheckType(reader,CHUNK_TYPE); - int chunkSize=reader.readInt(); - int stringCount=reader.readInt(); - int styleOffsetCount=reader.readInt(); - /*?*/reader.readInt(); - int stringsOffset=reader.readInt(); - int stylesOffset=reader.readInt(); - - StringBlock block=new StringBlock(); - 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); - } - if (stylesOffset!=0) { - int size=(chunkSize-stylesOffset); - if ((size%4)!=0) { - throw new IOException("Style data size is not multiple of 4 ("+size+")."); - } - block.m_styles=reader.readIntArray(size/4); - } - - return block; - } - - /** - * Returns number of strings in block. - */ - public int getCount() { - return m_stringOffsets!=null? - m_stringOffsets.length: - 0; - } - - /** - * Returns raw string (without any styling information) at specified index. - */ - public String getString(int index) { - if (index<0 || - m_stringOffsets==null || - index>=m_stringOffsets.length) - { - 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)); - } - return result.toString(); - } - - /** - * Not yet implemented. - * - * Returns string with style information (if any). - */ - public CharSequence get(int index) { - return getString(index); - } - - /** - * Returns string with style tags (html-like). - */ - public String getHTML(int index) { - String raw=getString(index); - if (raw==null) { - return raw; - } - int[] style=getStyle(index); - if (style==null) { - return raw; - } - StringBuilder html=new StringBuilder(raw.length()+32); - int offset=0; - while (true) { - int i=-1; - for (int j=0;j!=style.length;j+=3) { - if (style[j+1]==-1) { - continue; - } - if (i==-1 || style[i+1]>style[j+1]) { - i=j; - } - } - int start=((i!=-1)?style[i+1]:raw.length()); - for (int j=0;j!=style.length;j+=3) { - int end=style[j+2]; - if (end==-1 || end>=start) { - continue; - } - if (offset<=end) { - html.append(raw,offset,end+1); - offset=end+1; - } - style[j+2]=-1; - html.append('<'); - html.append('/'); - html.append(getString(style[j])); - html.append('>'); - } - if (offset'); - style[i+1]=-1; - } - return html.toString(); - } - - /** - * Finds index of the string. - * Returns -1 if the string was not found. - */ - public int find(String string) { - if (string==null) { - return -1; - } - for (int i=0;i!=m_stringOffsets.length;++i) { - int offset=m_stringOffsets[i]; - int length=getShort(m_strings,offset); - if (length!=string.length()) { - continue; - } - int j=0; - for (;j!=length;++j) { - offset+=2; - if (string.charAt(j)!=getShort(m_strings,offset)) { - break; - } - } - if (j==length) { - return i; - } - } - return -1; - } - - ///////////////////////////////////////////// implementation - - private StringBlock() { - } - - /** - * Returns style information - array of int triplets, - * where in each triplet: - * * first int is index of tag name ('b','i', etc.) - * * second int is tag start index in string - * * third int is tag end index in string - */ - private int[] getStyle(int index) { - if (m_styleOffsets==null || m_styles==null || - index>=m_styleOffsets.length) - { - return null; - } - int offset=m_styleOffsets[index]/4; - int style[]; - { - int count=0; - for (int i=offset;i>> 16); - } - } - - private int[] m_stringOffsets; - private int[] m_strings; - private int[] m_styleOffsets; - private int[] m_styles; - - private static final int CHUNK_TYPE=0x001C0001; -} +/* + * Copyright 2008 Android4ME + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package wind.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 + * + * Block of strings, used in binary xml and arsc. + * + * TODO: + * - implement get() + * + */ +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. + */ + public static StringBlock read(IntReader reader) throws IOException { + ChunkUtil.readCheckType(reader, CHUNK_TYPE); + int chunkSize = reader.readInt(); + int stringCount = reader.readInt(); + int styleOffsetCount = 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; + block.m_strings = new byte[size]; + reader.readFully(block.m_strings); + } + if (stylesOffset != 0) { + int size = (chunkSize - stylesOffset); + if ((size % 4) != 0) { + throw new IOException("Style data size is not multiple of 4 (" + size + ")."); + } + block.m_styles = reader.readIntArray(size / 4); + } + + return block; + } + + /** + * Returns number of strings in block. + */ + public int getCount() { + return m_stringOffsets != null ? + m_stringOffsets.length : + 0; + } + + + public String getString(int index) { + if (index < 0 || m_stringOffsets == null || index >= m_stringOffsets.length) { + return null; + } + int offset = m_stringOffsets[index]; + 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 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. + *

+ * Returns string with style information (if any). + */ + public CharSequence get(int index) { + return getString(index); + } + + /** + * Returns string with style tags (html-like). + */ + public String getHTML(int index) { + String raw=getString(index); + if (raw==null) { + return raw; + } + int[] style=getStyle(index); + if (style==null) { + return raw; + } + StringBuilder html=new StringBuilder(raw.length()+32); + int offset=0; + while (true) { + int i=-1; + for (int j=0;j!=style.length;j+=3) { + if (style[j+1]==-1) { + continue; + } + if (i==-1 || style[i+1]>style[j+1]) { + i=j; + } + } + int start=((i!=-1)?style[i+1]:raw.length()); + for (int j=0;j!=style.length;j+=3) { + int end=style[j+2]; + if (end==-1 || end>=start) { + continue; + } + if (offset<=end) { + html.append(raw,offset,end+1); + offset=end+1; + } + style[j+2]=-1; + html.append('<'); + html.append('/'); + html.append(getString(style[j])); + html.append('>'); + } + if (offset'); + style[i+1]=-1; + } + return html.toString(); + } + + /** + * Finds index of the string. + * Returns -1 if the string was not found. + */ + public int find(String string) { + if (string==null) { + return -1; + } + for (int i=0;i!=m_stringOffsets.length;++i) { + int offset=m_stringOffsets[i]; + int length=getShort(m_strings,offset); + if (length!=string.length()) { + continue; + } + int j=0; + for (;j!=length;++j) { + offset+=2; + if (string.charAt(j)!=getShort(m_strings,offset)) { + break; + } + } + if (j==length) { + return i; + } + } + return -1; + } + + ///////////////////////////////////////////// implementation + + private StringBlock() { + } + + /** + * Returns style information - array of int triplets, + * where in each triplet: + * * first int is index of tag name ('b','i', etc.) + * * second int is tag start index in string + * * third int is tag end index in string + */ + private int[] getStyle(int index) { + if (m_styleOffsets==null || m_styles==null || + index>=m_styleOffsets.length) + { + return null; + } + int offset=m_styleOffsets[index]/4; + int style[]; + { + int count=0; + for (int i=offset;i>> 16); + } + } +}