001/* 002 * Copyright (C) 2009 The Android Open Source Project 003 * Copyright (C) 2015-2017 Keepsafe Software 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package com.android.dexdeps; 019 020import java.io.IOException; 021import java.io.RandomAccessFile; 022import java.nio.charset.StandardCharsets; 023import java.util.Arrays; 024 025/** 026 * Data extracted from a DEX file. 027 */ 028public class DexData { 029 private RandomAccessFile mDexFile; 030 private HeaderItem mHeaderItem; 031 private String[] mStrings; // strings from string_data_* 032 private TypeIdItem[] mTypeIds; 033 private ProtoIdItem[] mProtoIds; 034 private FieldIdItem[] mFieldIds; 035 private MethodIdItem[] mMethodIds; 036 private ClassDefItem[] mClassDefs; 037 038 private byte tmpBuf[] = new byte[4]; 039 private boolean isBigEndian = false; 040 041 /** 042 * Constructs a new DexData for this file. 043 */ 044 public DexData(RandomAccessFile raf) { 045 mDexFile = raf; 046 } 047 048 /** 049 * Loads the contents of the DEX file into our data structures. 050 * 051 * @throws IOException if we encounter a problem while reading 052 * @throws DexDataException if the DEX contents look bad 053 */ 054 public void load() throws IOException { 055 parseHeaderItem(); 056 057 loadStrings(); 058 loadTypeIds(); 059 loadProtoIds(); 060 loadFieldIds(); 061 loadMethodIds(); 062 loadClassDefs(); 063 064 markInternalClasses(); 065 } 066 067 /** 068 * Verifies the given magic number. 069 */ 070 private static boolean verifyMagic(byte[] magic) { 071 return Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v035) || 072 Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v037) || 073 Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v038); 074 } 075 076 /** 077 * Parses the interesting bits out of the header. 078 */ 079 void parseHeaderItem() throws IOException { 080 mHeaderItem = new HeaderItem(); 081 082 seek(0); 083 084 byte[] magic = new byte[8]; 085 readBytes(magic); 086 if (!verifyMagic(magic)) { 087 System.err.println("Magic number is wrong -- are you sure " + 088 "this is a DEX file?"); 089 throw new DexDataException(); 090 } 091 092 /* 093 * Read the endian tag, so we properly swap things as we read 094 * them from here on. 095 */ 096 seek(8+4+20+4+4); 097 mHeaderItem.endianTag = readInt(); 098 if (mHeaderItem.endianTag == HeaderItem.ENDIAN_CONSTANT) { 099 /* do nothing */ 100 } else if (mHeaderItem.endianTag == HeaderItem.REVERSE_ENDIAN_CONSTANT){ 101 /* file is big-endian (!), reverse future reads */ 102 isBigEndian = true; 103 } else { 104 System.err.println("Endian constant has unexpected value " + 105 Integer.toHexString(mHeaderItem.endianTag)); 106 throw new DexDataException(); 107 } 108 109 seek(8+4+20); // magic, checksum, signature 110 mHeaderItem.fileSize = readInt(); 111 mHeaderItem.headerSize = readInt(); 112 /*mHeaderItem.endianTag =*/ readInt(); 113 /*mHeaderItem.linkSize =*/ readInt(); 114 /*mHeaderItem.linkOff =*/ readInt(); 115 /*mHeaderItem.mapOff =*/ readInt(); 116 mHeaderItem.stringIdsSize = readInt(); 117 mHeaderItem.stringIdsOff = readInt(); 118 mHeaderItem.typeIdsSize = readInt(); 119 mHeaderItem.typeIdsOff = readInt(); 120 mHeaderItem.protoIdsSize = readInt(); 121 mHeaderItem.protoIdsOff = readInt(); 122 mHeaderItem.fieldIdsSize = readInt(); 123 mHeaderItem.fieldIdsOff = readInt(); 124 mHeaderItem.methodIdsSize = readInt(); 125 mHeaderItem.methodIdsOff = readInt(); 126 mHeaderItem.classDefsSize = readInt(); 127 mHeaderItem.classDefsOff = readInt(); 128 /*mHeaderItem.dataSize =*/ readInt(); 129 /*mHeaderItem.dataOff =*/ readInt(); 130 } 131 132 /** 133 * Loads the string table out of the DEX. 134 * 135 * First we read all of the string_id_items, then we read all of the 136 * string_data_item. Doing it this way should allow us to avoid 137 * seeking around in the file. 138 */ 139 void loadStrings() throws IOException { 140 int count = mHeaderItem.stringIdsSize; 141 int stringOffsets[] = new int[count]; 142 143 //System.out.println("reading " + count + " strings"); 144 145 seek(mHeaderItem.stringIdsOff); 146 for (int i = 0; i < count; i++) { 147 stringOffsets[i] = readInt(); 148 } 149 150 mStrings = new String[count]; 151 152 seek(stringOffsets[0]); 153 for (int i = 0; i < count; i++) { 154 seek(stringOffsets[i]); // should be a no-op 155 mStrings[i] = readString(); 156 //System.out.println("STR: " + i + ": " + mStrings[i]); 157 } 158 } 159 160 /** 161 * Loads the type ID list. 162 */ 163 void loadTypeIds() throws IOException { 164 int count = mHeaderItem.typeIdsSize; 165 mTypeIds = new TypeIdItem[count]; 166 167 //System.out.println("reading " + count + " typeIds"); 168 seek(mHeaderItem.typeIdsOff); 169 for (int i = 0; i < count; i++) { 170 mTypeIds[i] = new TypeIdItem(); 171 mTypeIds[i].descriptorIdx = readInt(); 172 173 //System.out.println(i + ": " + mTypeIds[i].descriptorIdx + 174 // " " + mStrings[mTypeIds[i].descriptorIdx]); 175 } 176 } 177 178 /** 179 * Loads the proto ID list. 180 */ 181 void loadProtoIds() throws IOException { 182 int count = mHeaderItem.protoIdsSize; 183 mProtoIds = new ProtoIdItem[count]; 184 185 //System.out.println("reading " + count + " protoIds"); 186 seek(mHeaderItem.protoIdsOff); 187 188 /* 189 * Read the proto ID items. 190 */ 191 for (int i = 0; i < count; i++) { 192 mProtoIds[i] = new ProtoIdItem(); 193 mProtoIds[i].shortyIdx = readInt(); 194 mProtoIds[i].returnTypeIdx = readInt(); 195 mProtoIds[i].parametersOff = readInt(); 196 197 //System.out.println(i + ": " + mProtoIds[i].shortyIdx + 198 // " " + mStrings[mProtoIds[i].shortyIdx]); 199 } 200 201 /* 202 * Go back through and read the type lists. 203 */ 204 for (int i = 0; i < count; i++) { 205 ProtoIdItem protoId = mProtoIds[i]; 206 207 int offset = protoId.parametersOff; 208 209 if (offset == 0) { 210 protoId.types = new int[0]; 211 continue; 212 } else { 213 seek(offset); 214 int size = readInt(); // #of entries in list 215 protoId.types = new int[size]; 216 217 for (int j = 0; j < size; j++) { 218 protoId.types[j] = readShort() & 0xffff; 219 } 220 } 221 } 222 } 223 224 /** 225 * Loads the field ID list. 226 */ 227 void loadFieldIds() throws IOException { 228 int count = mHeaderItem.fieldIdsSize; 229 mFieldIds = new FieldIdItem[count]; 230 231 //System.out.println("reading " + count + " fieldIds"); 232 seek(mHeaderItem.fieldIdsOff); 233 for (int i = 0; i < count; i++) { 234 mFieldIds[i] = new FieldIdItem(); 235 mFieldIds[i].classIdx = readShort() & 0xffff; 236 mFieldIds[i].typeIdx = readShort() & 0xffff; 237 mFieldIds[i].nameIdx = readInt(); 238 239 //System.out.println(i + ": " + mFieldIds[i].nameIdx + 240 // " " + mStrings[mFieldIds[i].nameIdx]); 241 } 242 } 243 244 /** 245 * Loads the method ID list. 246 */ 247 void loadMethodIds() throws IOException { 248 int count = mHeaderItem.methodIdsSize; 249 mMethodIds = new MethodIdItem[count]; 250 251 //System.out.println("reading " + count + " methodIds"); 252 seek(mHeaderItem.methodIdsOff); 253 for (int i = 0; i < count; i++) { 254 mMethodIds[i] = new MethodIdItem(); 255 mMethodIds[i].classIdx = readShort() & 0xffff; 256 mMethodIds[i].protoIdx = readShort() & 0xffff; 257 mMethodIds[i].nameIdx = readInt(); 258 259 //System.out.println(i + ": " + mMethodIds[i].nameIdx + 260 // " " + mStrings[mMethodIds[i].nameIdx]); 261 } 262 } 263 264 /** 265 * Loads the class defs list. 266 */ 267 void loadClassDefs() throws IOException { 268 int count = mHeaderItem.classDefsSize; 269 mClassDefs = new ClassDefItem[count]; 270 271 //System.out.println("reading " + count + " classDefs"); 272 seek(mHeaderItem.classDefsOff); 273 for (int i = 0; i < count; i++) { 274 mClassDefs[i] = new ClassDefItem(); 275 mClassDefs[i].classIdx = readInt(); 276 277 /* access_flags = */ readInt(); 278 /* superclass_idx = */ readInt(); 279 /* interfaces_off = */ readInt(); 280 /* source_file_idx = */ readInt(); 281 /* annotations_off = */ readInt(); 282 /* class_data_off = */ readInt(); 283 /* static_values_off = */ readInt(); 284 285 //System.out.println(i + ": " + mClassDefs[i].classIdx + " " + 286 // mStrings[mTypeIds[mClassDefs[i].classIdx].descriptorIdx]); 287 } 288 } 289 290 /** 291 * Sets the "internal" flag on type IDs which are defined in the 292 * DEX file or within the VM (e.g. primitive classes and arrays). 293 */ 294 void markInternalClasses() { 295 for (int i = mClassDefs.length -1; i >= 0; i--) { 296 mTypeIds[mClassDefs[i].classIdx].internal = true; 297 } 298 299 for (int i = 0; i < mTypeIds.length; i++) { 300 String className = mStrings[mTypeIds[i].descriptorIdx]; 301 302 if (className.length() == 1) { 303 // primitive class 304 mTypeIds[i].internal = true; 305 } else if (className.charAt(0) == '[') { 306 mTypeIds[i].internal = true; 307 } 308 309 //System.out.println(i + " " + 310 // (mTypeIds[i].internal ? "INTERNAL" : "external") + " - " + 311 // mStrings[mTypeIds[i].descriptorIdx]); 312 } 313 } 314 315 316 /* 317 * ======================================================================= 318 * Queries 319 * ======================================================================= 320 */ 321 322 /** 323 * Returns the class name, given an index into the type_ids table. 324 */ 325 private String classNameFromTypeIndex(int idx) { 326 return mStrings[mTypeIds[idx].descriptorIdx]; 327 } 328 329 /** 330 * Returns an array of method argument type strings, given an index 331 * into the proto_ids table. 332 */ 333 private String[] argArrayFromProtoIndex(int idx) { 334 ProtoIdItem protoId = mProtoIds[idx]; 335 String[] result = new String[protoId.types.length]; 336 337 for (int i = 0; i < protoId.types.length; i++) { 338 result[i] = mStrings[mTypeIds[protoId.types[i]].descriptorIdx]; 339 } 340 341 return result; 342 } 343 344 /** 345 * Returns a string representing the method's return type, given an 346 * index into the proto_ids table. 347 */ 348 private String returnTypeFromProtoIndex(int idx) { 349 ProtoIdItem protoId = mProtoIds[idx]; 350 return mStrings[mTypeIds[protoId.returnTypeIdx].descriptorIdx]; 351 } 352 353 /** 354 * Returns an array with all of the class references that don't 355 * correspond to classes in the DEX file. Each class reference has 356 * a list of the referenced fields and methods associated with 357 * that class. 358 */ 359 public ClassRef[] getExternalReferences() { 360 // create a sparse array of ClassRef that parallels mTypeIds 361 ClassRef[] sparseRefs = new ClassRef[mTypeIds.length]; 362 363 // create entries for all externally-referenced classes 364 int count = 0; 365 for (int i = 0; i < mTypeIds.length; i++) { 366 if (!mTypeIds[i].internal) { 367 sparseRefs[i] = 368 new ClassRef(mStrings[mTypeIds[i].descriptorIdx]); 369 count++; 370 } 371 } 372 373 // add fields and methods to the appropriate class entry 374 addExternalFieldReferences(sparseRefs); 375 addExternalMethodReferences(sparseRefs); 376 377 // crunch out the sparseness 378 ClassRef[] classRefs = new ClassRef[count]; 379 int idx = 0; 380 for (int i = 0; i < mTypeIds.length; i++) { 381 if (sparseRefs[i] != null) 382 classRefs[idx++] = sparseRefs[i]; 383 } 384 385 assert idx == count; 386 387 return classRefs; 388 } 389 390 /** 391 * Runs through the list of field references, inserting external 392 * references into the appropriate ClassRef. 393 */ 394 private void addExternalFieldReferences(ClassRef[] sparseRefs) { 395 for (int i = 0; i < mFieldIds.length; i++) { 396 if (!mTypeIds[mFieldIds[i].classIdx].internal) { 397 FieldIdItem fieldId = mFieldIds[i]; 398 FieldRef newFieldRef = new FieldRef( 399 classNameFromTypeIndex(fieldId.classIdx), 400 classNameFromTypeIndex(fieldId.typeIdx), 401 mStrings[fieldId.nameIdx]); 402 sparseRefs[mFieldIds[i].classIdx].addField(newFieldRef); 403 } 404 } 405 } 406 407 /** 408 * Runs through the list of method references, inserting external 409 * references into the appropriate ClassRef. 410 */ 411 private void addExternalMethodReferences(ClassRef[] sparseRefs) { 412 for (int i = 0; i < mMethodIds.length; i++) { 413 if (!mTypeIds[mMethodIds[i].classIdx].internal) { 414 MethodIdItem methodId = mMethodIds[i]; 415 MethodRef newMethodRef = new MethodRef( 416 classNameFromTypeIndex(methodId.classIdx), 417 argArrayFromProtoIndex(methodId.protoIdx), 418 returnTypeFromProtoIndex(methodId.protoIdx), 419 mStrings[methodId.nameIdx]); 420 sparseRefs[mMethodIds[i].classIdx].addMethod(newMethodRef); 421 } 422 } 423 } 424 425 /* 426 * BEGIN MODIFIED SECTION 427 */ 428 429 /** 430 * Returns the list of all method references. 431 * @return method refs 432 */ 433 public MethodRef[] getMethodRefs() { 434 MethodRef[] methodRefs = new MethodRef[mMethodIds.length]; 435 for (int i = 0; i < mMethodIds.length; i++) { 436 MethodIdItem methodId = mMethodIds[i]; 437 methodRefs[i] = new MethodRef( 438 classNameFromTypeIndex(methodId.classIdx), 439 argArrayFromProtoIndex(methodId.protoIdx), 440 returnTypeFromProtoIndex(methodId.protoIdx), 441 mStrings[methodId.nameIdx]); 442 } 443 return methodRefs; 444 } 445 446 public FieldRef[] getFieldRefs() { 447 FieldRef[] fieldRefs = new FieldRef[mFieldIds.length]; 448 for (int i = 0; i < mFieldIds.length; i++) { 449 FieldIdItem fieldId = mFieldIds[i]; 450 fieldRefs[i] = new FieldRef( 451 classNameFromTypeIndex(fieldId.classIdx), 452 classNameFromTypeIndex(fieldId.typeIdx), 453 mStrings[fieldId.nameIdx]); 454 } 455 return fieldRefs; 456 } 457 458 /* 459 * END MODIFIED SECTION 460 */ 461 462 463 464 /* 465 * ======================================================================= 466 * Basic I/O functions 467 * ======================================================================= 468 */ 469 470 /** 471 * Seeks the DEX file to the specified absolute position. 472 */ 473 void seek(int position) throws IOException { 474 mDexFile.seek(position); 475 } 476 477 /** 478 * Fills the buffer by reading bytes from the DEX file. 479 */ 480 void readBytes(byte[] buffer) throws IOException { 481 mDexFile.readFully(buffer); 482 } 483 484 /** 485 * Reads a single signed byte value. 486 */ 487 byte readByte() throws IOException { 488 mDexFile.readFully(tmpBuf, 0, 1); 489 return tmpBuf[0]; 490 } 491 492 /** 493 * Reads a signed 16-bit integer, byte-swapping if necessary. 494 */ 495 short readShort() throws IOException { 496 mDexFile.readFully(tmpBuf, 0, 2); 497 if (isBigEndian) { 498 return (short) ((tmpBuf[1] & 0xff) | ((tmpBuf[0] & 0xff) << 8)); 499 } else { 500 return (short) ((tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8)); 501 } 502 } 503 504 /** 505 * Reads a signed 32-bit integer, byte-swapping if necessary. 506 */ 507 int readInt() throws IOException { 508 mDexFile.readFully(tmpBuf, 0, 4); 509 510 if (isBigEndian) { 511 return (tmpBuf[3] & 0xff) | ((tmpBuf[2] & 0xff) << 8) | 512 ((tmpBuf[1] & 0xff) << 16) | ((tmpBuf[0] & 0xff) << 24); 513 } else { 514 return (tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8) | 515 ((tmpBuf[2] & 0xff) << 16) | ((tmpBuf[3] & 0xff) << 24); 516 } 517 } 518 519 /** 520 * Reads a variable-length unsigned LEB128 value. Does not attempt to 521 * verify that the value is valid. 522 * 523 * @throws java.io.EOFException if we run off the end of the file 524 */ 525 int readUnsignedLeb128() throws IOException { 526 int result = 0; 527 byte val; 528 529 do { 530 val = readByte(); 531 result = (result << 7) | (val & 0x7f); 532 } while (val < 0); 533 534 return result; 535 } 536 537 /** 538 * Reads a UTF-8 string. 539 * 540 * We don't know how long the UTF-8 string is, so we have to read one 541 * byte at a time. We could make an educated guess based on the 542 * utf16_size and seek back if we get it wrong, but seeking backward 543 * may cause the underlying implementation to reload I/O buffers. 544 */ 545 String readString() throws IOException { 546 int utf16len = readUnsignedLeb128(); 547 byte inBuf[] = new byte[utf16len * 3]; // worst case 548 int idx; 549 550 for (idx = 0; idx < inBuf.length; idx++) { 551 byte val = readByte(); 552 if (val == 0) 553 break; 554 inBuf[idx] = val; 555 } 556 557 return new String(inBuf, 0, idx, "UTF-8"); 558 } 559 560 561 /* 562 * ======================================================================= 563 * Internal "structure" declarations 564 * ======================================================================= 565 */ 566 567 /** 568 * Holds the contents of a header_item. 569 */ 570 static class HeaderItem { 571 public int fileSize; 572 public int headerSize; 573 public int endianTag; 574 public int stringIdsSize, stringIdsOff; 575 public int typeIdsSize, typeIdsOff; 576 public int protoIdsSize, protoIdsOff; 577 public int fieldIdsSize, fieldIdsOff; 578 public int methodIdsSize, methodIdsOff; 579 public int classDefsSize, classDefsOff; 580 581 /* expected magic values */ 582 public static final byte[] DEX_FILE_MAGIC_v035 = 583 "dex\n035\0".getBytes(StandardCharsets.US_ASCII); 584 585 // Dex version 036 skipped because of an old dalvik bug on some versions 586 // of android where dex files with that version number would erroneously 587 // be accepted and run. See: art/runtime/dex_file.cc 588 589 // V037 was introduced in API LEVEL 24 590 public static final byte[] DEX_FILE_MAGIC_v037 = 591 "dex\n037\0".getBytes(StandardCharsets.US_ASCII); 592 593 // V038 was introduced in API LEVEL 26 594 public static final byte[] DEX_FILE_MAGIC_v038 = 595 "dex\n038\0".getBytes(StandardCharsets.US_ASCII); 596 597 public static final int ENDIAN_CONSTANT = 0x12345678; 598 public static final int REVERSE_ENDIAN_CONSTANT = 0x78563412; 599 } 600 601 /** 602 * Holds the contents of a type_id_item. 603 * 604 * This is chiefly a list of indices into the string table. We need 605 * some additional bits of data, such as whether or not the type ID 606 * represents a class defined in this DEX, so we use an object for 607 * each instead of a simple integer. (Could use a parallel array, but 608 * since this is a desktop app it's not essential.) 609 */ 610 static class TypeIdItem { 611 public int descriptorIdx; // index into string_ids 612 613 public boolean internal; // defined within this DEX file? 614 } 615 616 /** 617 * Holds the contents of a proto_id_item. 618 */ 619 static class ProtoIdItem { 620 public int shortyIdx; // index into string_ids 621 public int returnTypeIdx; // index into type_ids 622 public int parametersOff; // file offset to a type_list 623 624 public int types[]; // contents of type list 625 } 626 627 /** 628 * Holds the contents of a field_id_item. 629 */ 630 static class FieldIdItem { 631 public int classIdx; // index into type_ids (defining class) 632 public int typeIdx; // index into type_ids (field type) 633 public int nameIdx; // index into string_ids 634 } 635 636 /** 637 * Holds the contents of a method_id_item. 638 */ 639 static class MethodIdItem { 640 public int classIdx; // index into type_ids 641 public int protoIdx; // index into proto_ids 642 public int nameIdx; // index into string_ids 643 } 644 645 /** 646 * Holds the contents of a class_def_item. 647 * 648 * We don't really need a class for this, but there's some stuff in 649 * the class_def_item that we might want later. 650 */ 651 static class ClassDefItem { 652 public int classIdx; // index into type_ids 653 } 654}