1
1
package jadx .core .xmlgen ;
2
2
3
3
import java .io .BufferedInputStream ;
4
+ import java .io .ByteArrayInputStream ;
4
5
import java .io .IOException ;
5
6
import java .io .InputStream ;
6
7
import java .util .ArrayList ;
@@ -102,41 +103,52 @@ public ResContainer decodeFiles() {
102
103
void decodeTableChunk () throws IOException {
103
104
is .checkInt16 (RES_TABLE_TYPE , "Not a table chunk" );
104
105
is .checkInt16 (0x000c , "Unexpected table header size" );
105
- /* int size = */
106
- is .readInt32 ();
106
+ int size = is .readInt32 ();
107
107
int pkgCount = is .readInt32 ();
108
108
109
- strings = parseStringPool ();
110
- for (int i = 0 ; i < pkgCount ; i ++) {
111
- parsePackage ();
109
+ int pkgNum = 0 ;
110
+ while (is .getPos () < size ) {
111
+ long chuckStart = is .getPos ();
112
+ int type = is .readInt16 ();
113
+ int headerSize = is .readInt16 ();
114
+ long chunkSize = is .readUInt32 ();
115
+ long chunkEnd = chuckStart + chunkSize ;
116
+ switch (type ) {
117
+ case RES_NULL_TYPE :
118
+ // skip
119
+ break ;
120
+
121
+ case RES_STRING_POOL_TYPE :
122
+ strings = parseStringPoolNoSize (chuckStart , chunkEnd );
123
+ break ;
124
+
125
+ case RES_TABLE_PACKAGE_TYPE :
126
+ parsePackage (chuckStart , headerSize , chunkEnd );
127
+ pkgNum ++;
128
+ break ;
129
+ }
130
+ is .skipToPos (chunkEnd , "Skip to table chunk end" );
131
+ }
132
+ if (pkgNum != pkgCount ) {
133
+ LOG .warn ("Unexpected package chunks, read: {}, expected: {}" , pkgNum , pkgCount );
112
134
}
113
135
}
114
136
115
- private PackageChunk parsePackage () throws IOException {
116
- long start = is .getPos ();
117
- is .checkInt16 (RES_TABLE_PACKAGE_TYPE , "Not a table chunk" );
118
- int headerSize = is .readInt16 ();
137
+ private void parsePackage (long pkgChunkStart , int headerSize , long pkgChunkEnd ) throws IOException {
119
138
if (headerSize < 0x011c ) {
120
139
die ("Package header size too small" );
140
+ return ;
121
141
}
122
- long size = is .readUInt32 ();
123
- long endPos = start + size ;
124
-
125
142
int id = is .readInt32 ();
126
143
String name = is .readString16Fixed (128 );
127
-
128
- long typeStringsOffset = start + is .readInt32 ();
129
- /* int lastPublicType = */
130
- is .readInt32 ();
131
- long keyStringsOffset = start + is .readInt32 ();
132
- /* int lastPublicKey = */
133
- is .readInt32 ();
134
-
144
+ long typeStringsOffset = pkgChunkStart + is .readInt32 ();
145
+ int lastPublicType = is .readInt32 ();
146
+ long keyStringsOffset = pkgChunkStart + is .readInt32 ();
147
+ int lastPublicKey = is .readInt32 ();
135
148
if (headerSize >= 0x0120 ) {
136
- /* int typeIdOffset = */
137
- is .readInt32 ();
149
+ int typeIdOffset = is .readInt32 ();
138
150
}
139
- is .skipToPos (start + headerSize , "package header end" );
151
+ is .skipToPos (pkgChunkStart + headerSize , "package header end" );
140
152
141
153
BinaryXMLStrings typeStrings = null ;
142
154
if (typeStringsOffset != 0 ) {
@@ -152,7 +164,7 @@ private PackageChunk parsePackage() throws IOException {
152
164
PackageChunk pkg = new PackageChunk (id , name , typeStrings , keyStrings );
153
165
resStorage .setAppPackage (name );
154
166
155
- while (is .getPos () < endPos ) {
167
+ while (is .getPos () < pkgChunkEnd ) {
156
168
long chunkStart = is .getPos ();
157
169
int type = is .readInt16 ();
158
170
LOG .trace ("res package chunk start at {} type {}" , chunkStart , type );
@@ -182,7 +194,6 @@ private PackageChunk parsePackage() throws IOException {
182
194
LOG .warn ("Unknown chunk type {} encountered at offset {}" , type , chunkStart );
183
195
}
184
196
}
185
- return pkg ;
186
197
}
187
198
188
199
@ SuppressWarnings ("unused" )
@@ -493,69 +504,59 @@ private RawValue parseValue() throws IOException {
493
504
private EntryConfig parseConfig () throws IOException {
494
505
long start = is .getPos ();
495
506
int size = is .readInt32 ();
496
- if (size < 28 ) {
497
- throw new IOException ("Config size < 28 " );
507
+ if (size < 4 ) {
508
+ throw new IOException ("Config size < 4 " );
498
509
}
499
510
500
- short mcc = (short ) is .readInt16 ();
501
- short mnc = (short ) is .readInt16 ();
511
+ // Android zero fill this structure and only read the data present
512
+ var configData = new byte [Math .max (52 , size - 4 )];
513
+ is .readFully (configData , 0 , size - 4 );
514
+ var configIs = new ParserStream (new ByteArrayInputStream (configData ));
502
515
503
- char [] language = unpackLocaleOrRegion (( byte ) is . readInt8 (), ( byte ) is . readInt8 (), 'a' );
504
- char [] country = unpackLocaleOrRegion (( byte ) is . readInt8 (), ( byte ) is . readInt8 (), '0' );
516
+ short mcc = ( short ) configIs . readInt16 ( );
517
+ short mnc = ( short ) configIs . readInt16 ( );
505
518
506
- byte orientation = (byte ) is .readInt8 ();
507
- byte touchscreen = (byte ) is .readInt8 ();
508
- int density = is .readInt16 ();
519
+ char [] language = unpackLocaleOrRegion ((byte ) configIs .readInt8 (), (byte ) configIs .readInt8 (), 'a' );
520
+ char [] country = unpackLocaleOrRegion ((byte ) configIs .readInt8 (), (byte ) configIs .readInt8 (), '0' );
509
521
510
- byte keyboard = (byte ) is .readInt8 ();
511
- byte navigation = (byte ) is .readInt8 ();
512
- byte inputFlags = (byte ) is .readInt8 ();
513
- byte grammaticalInflection = (byte ) is .readInt8 ();
522
+ byte orientation = (byte ) configIs .readInt8 ();
523
+ byte touchscreen = (byte ) configIs .readInt8 ();
524
+ int density = configIs .readInt16 ();
514
525
515
- short screenWidth = (short ) is .readInt16 ();
516
- short screenHeight = (short ) is .readInt16 ();
526
+ byte keyboard = (byte ) configIs .readInt8 ();
527
+ byte navigation = (byte ) configIs .readInt8 ();
528
+ byte inputFlags = (byte ) configIs .readInt8 ();
529
+ byte grammaticalInflection = (byte ) configIs .readInt8 ();
517
530
518
- short sdkVersion = (short ) is .readInt16 ();
519
- is .readInt16 (); // minorVersion must always be 0
531
+ short screenWidth = (short ) configIs .readInt16 ();
532
+ short screenHeight = ( short ) configIs .readInt16 ();
520
533
521
- byte screenLayout = 0 ;
522
- byte uiMode = 0 ;
523
- short smallestScreenWidthDp = 0 ;
524
- if (size >= 32 ) {
525
- screenLayout = (byte ) is .readInt8 ();
526
- uiMode = (byte ) is .readInt8 ();
527
- smallestScreenWidthDp = (short ) is .readInt16 ();
528
- }
534
+ short sdkVersion = (short ) configIs .readInt16 ();
535
+ configIs .readInt16 (); // minorVersion must always be 0
529
536
530
- short screenWidthDp = 0 ;
531
- short screenHeightDp = 0 ;
532
- if (size >= 36 ) {
533
- screenWidthDp = (short ) is .readInt16 ();
534
- screenHeightDp = (short ) is .readInt16 ();
535
- }
537
+ byte screenLayout = (byte ) configIs .readInt8 ();
538
+ byte uiMode = (byte ) configIs .readInt8 ();
539
+ short smallestScreenWidthDp = (short ) configIs .readInt16 ();
540
+ short screenWidthDp = (short ) configIs .readInt16 ();
541
+ short screenHeightDp = (short ) configIs .readInt16 ();
536
542
537
- char [] localeScript = null ;
538
- char [] localeVariant = null ;
539
- if (size >= 48 ) {
540
- localeScript = readScriptOrVariantChar (4 ).toCharArray ();
541
- localeVariant = readScriptOrVariantChar (8 ).toCharArray ();
542
- }
543
+ char [] localeScript = readScriptOrVariantChar (4 , configIs ).toCharArray ();
544
+ char [] localeVariant = readScriptOrVariantChar (8 , configIs ).toCharArray ();
543
545
544
- byte screenLayout2 = 0 ;
545
- byte colorMode = 0 ;
546
- if (size >= 52 ) {
547
- screenLayout2 = (byte ) is .readInt8 ();
548
- colorMode = (byte ) is .readInt8 ();
549
- is .readInt16 (); // reserved padding
550
- }
546
+ byte screenLayout2 = (byte ) configIs .readInt8 ();
547
+ byte colorMode = (byte ) configIs .readInt8 ();
548
+ configIs .readInt16 (); // reserved padding
551
549
552
- is .skipToPos (start + size , "Config skip trailing bytes" );
550
+ is .checkPos (start + size , "Config skip trailing bytes" );
553
551
554
552
return new EntryConfig (mcc , mnc , language , country ,
555
553
orientation , touchscreen , density , keyboard , navigation ,
556
554
inputFlags , grammaticalInflection , screenWidth , screenHeight , sdkVersion ,
557
555
screenLayout , uiMode , smallestScreenWidthDp , screenWidthDp ,
558
- screenHeightDp , localeScript , localeVariant , screenLayout2 ,
556
+ screenHeightDp ,
557
+ localeScript .length == 0 ? null : localeScript ,
558
+ localeVariant .length == 0 ? null : localeVariant ,
559
+ screenLayout2 ,
559
560
colorMode , false , size );
560
561
}
561
562
@@ -574,16 +575,20 @@ private char[] unpackLocaleOrRegion(byte in0, byte in1, char base) {
574
575
}
575
576
576
577
private String readScriptOrVariantChar (int length ) throws IOException {
577
- long start = is .getPos ();
578
+ return readScriptOrVariantChar (length , is );
579
+ }
580
+
581
+ private static String readScriptOrVariantChar (int length , ParserStream ps ) throws IOException {
582
+ long start = ps .getPos ();
578
583
StringBuilder sb = new StringBuilder (16 );
579
584
for (int i = 0 ; i < length ; i ++) {
580
- short ch = (short ) is .readInt8 ();
585
+ short ch = (short ) ps .readInt8 ();
581
586
if (ch == 0 ) {
582
587
break ;
583
588
}
584
589
sb .append ((char ) ch );
585
590
}
586
- is .skipToPos (start + length , "readScriptOrVariantChar" );
591
+ ps .skipToPos (start + length , "readScriptOrVariantChar" );
587
592
return sb .toString ();
588
593
}
589
594
0 commit comments