コード例 #1
0
  /**
   * Auxiliary method to register the compiled version of this class. In any event the class is
   * corrupted or non compliant with java, it is marked as invalid so further parsing for this
   * resource is skipped.
   *
   * @param meta The Meta reference is used to register the found java compiled version on Meta
   *     level (so an overview is present).
   * @param resource The Resource reference is used to register the found java compiled version on
   *     Resource level.
   */
  public void registerCompiledVersion(Meta meta, Resource resource) {
    try {
      // Register on Resource level
      String magicNumber = this.getMagicNumber(resource);
      String javaCompilerVersion = this.lookupJavaVersion(magicNumber);
      resource.setCompiledVersion(javaCompilerVersion);

      // Register on Meta level
      FoundJavaVersions foundJavaVersions = meta.getFoundJavaVersions();
      if (foundJavaVersions == null) {
        foundJavaVersions = new FoundJavaVersions();
        meta.setFoundJavaVersions(foundJavaVersions);
      }
      FoundJavaVersion foundJavaVersion = null;
      for (FoundJavaVersion currentJavaVersion : foundJavaVersions.getFoundJavaVersionList()) {
        if (currentJavaVersion.getFoundJavaVersion().equals(javaCompilerVersion)) {
          foundJavaVersion = currentJavaVersion;
          break;
        }
      }
      if (foundJavaVersion == null) {
        foundJavaVersion = new FoundJavaVersion();
        foundJavaVersion.setFoundJavaVersion(javaCompilerVersion);
        foundJavaVersions.getFoundJavaVersionList().add(foundJavaVersion);
      }
      foundJavaVersion.setTotalClasses(foundJavaVersion.getTotalClasses() + 1);
    } catch (IOException | ArtificerException e) {
      // Empty .class files, or .class files not compliant with java intrinsic magic number are
      // marked
      // as invalid (as further processing is not required/not possible)
      LOGGER.warn(
          "Resource " + resource.getName() + ", is not a valid java class and will be skipped.");
      resource.setValidClass(false);
    }
  }
コード例 #2
0
  /**
   * Auxiliary method to determine the compiler version. Every java class has some 'leading' bytes
   * as part of a file signature and basically represents the magic number and version.
   *
   * <p>Every '.class' file starts off with the following: - Magic Number [4 bytes] - Version
   * Information [4 bytes]
   *
   * <p>javac -target 1.1 ==> CA FE BA BE 00 03 00 2D javac -target 1.2 ==> CA FE BA BE 00 00 00 2E
   * javac -target 1.3 ==> CA FE BA BE 00 00 00 2F javac -target 1.4 ==> CA FE BA BE 00 00 00 30
   * javac -target 1.5 ==> CA FE BA BE 00 00 00 31 javac -target 1.6 ==> CA FE BA BE 00 00 00 32
   * javac -target 1.7 ==> CA FE BA BE 00 00 00 33 javac -target 1.8 ==> CA FE BA BE 00 00 00 34
   *
   * <p>See also:
   * http://stackoverflow.com/questions/698129/how-can-i-find-the-target-java-version-for-a-compiled-class
   *
   * @param resource The resource used to determine the compiler version.
   * @return The magic number associated with the compiler version.
   * @throws IOException When reading bytes from the class file fails.
   * @throws ArtificerException When the class file is not compliant with the standard Java
   *     identification of byte code(i.e. prefixed with CAFEBABE)
   */
  public String getMagicNumber(Resource resource) throws IOException, ArtificerException {
    DataInputStream dataInputStream = new DataInputStream(Files.newInputStream(resource.getPath()));

    // Get first 4 bytes, as that represents the magic number
    if (dataInputStream.readInt() != MAGIC_NUMBER) {
      throw new ArtificerException(resource.getName() + " is not a valid java class!");
    }
    return Integer.toHexString(dataInputStream.readInt());
  }
コード例 #3
0
  /**
   * Auxiliary method to extract the signature associated with the resource. This is fetched from
   * the 'Signature_attribute' structure, which has the following format:
   *
   * <p>[java 8] Signature_attribute { u2 attribute_name_index; u4 attribute_length; u2
   * signature_index; }
   *
   * <p>- signature_index: The value of the 'signature_index' must be a valid index in the
   * 'constant_pool' table. The 'constant_pool' entry at that index must be a 'CONSTANT_Utf8_info'
   * structure representing a class signature if this 'Signature' is an attribute of a 'ClassFile'
   * structure. It is a method signature of this 'Signature' is an attribute of a 'method_info'
   * structure. It is a field signature otherwise.
   *
   * @param dataInputStream The byte stream associated with the resource (aka .class file).
   * @param resource The resource associated with the attribute.
   * @throws IOException When reading bytes from the stream fails.
   */
  public static void extractSignature(
      DataInputStream dataInputStream, String location, Resource resource) throws IOException {
    int signatureIndex = dataInputStream.readUnsignedShort();
    String signature =
        ConstantPoolAnalyser.extractStringValueByConstantPoolIndex(
            resource.getConstantPool(), signatureIndex);
    LOGGER.debug("Class signature: " + signature + " (location: " + location + ")");

    // Add signature (when applicable) to the referenced classes
    SignatureAnalyser.referencedClasses(resource.getReferencedClasses(), signature);
  }
コード例 #4
0
  /**
   * The structure has the following (general) format:
   *
   * <p>[java 8] annotation { u2 type_index; u2 num_element_value_pairs; element_value_pairs
   * element_value_pair[num_element_value_pairs] }
   *
   * <p>- type_index: The value of the 'type_index' item must be a valid index in the
   * 'constant_pool' table. The 'constant_pool' table entry at that index must be a
   * 'CONSTANT_Utf8_info' structure representing a field descriptor. The field descriptor denotes
   * the type of the annotation represented by this 'annotation' structure. -
   * num_element_value_pairs: The value of 'num_element_value_pairs' item gives the number of
   * element-value pairs of the annotation represented by this 'annotation' structure.
   *
   * @param dataInputStream The byte stream associated with the resource (aka .class file).
   * @param index The annotation index, used for precise data logging.
   * @param resource The resource associated with the attribute.
   * @throws IOException When reading bytes from the stream fails.
   */
  protected static void extractAnnotation(
      DataInputStream dataInputStream, int index, Resource resource) throws IOException {
    StringBuilder buffer = new StringBuilder();
    buffer.append("Annotation (index: ").append(index).append(")");

    // Read the type index
    int typeIndex = dataInputStream.readUnsignedShort();
    String descriptor =
        ConstantPoolAnalyser.extractStringValueByConstantPoolIndex(
            resource.getConstantPool(), typeIndex);
    buffer.append(", of type (index: ").append(typeIndex).append("): ").append(descriptor);
    LOGGER.debug(buffer.toString());

    // Add signature (when applicable) to the referenced classes
    SignatureAnalyser.referencedClasses(resource.getReferencedClasses(), descriptor);

    // Read the element value pairs
    int numberOfElementValuePairs = dataInputStream.readUnsignedShort();
    extractElementValuePairs(dataInputStream, numberOfElementValuePairs, resource);
  }
コード例 #5
0
  /**
   * The structure has the following (general) format:
   *
   * <p>[java 8] element_value_pair { u2 element_name_index element_value value }
   *
   * <p>- element_name_index: The value of the 'element_name_index' must be a valid index into the
   * 'constant_pool' table. The 'constant_pool' entry at that index must be a 'CONSTANT_Utf8_info'
   * structure. The 'constant_pool' entry denotes the name of the element of the element-value pair
   * represented by this 'element_value_pairs' entry. In other words, the entry denotes an element
   * of the annotation type specified by 'type_index'. - value: The value of the 'value' item
   * represents the value of the element-value pair represented by this 'element_value_pairs' entry.
   *
   * <p>The 'element_value' structure is a discriminated union representing the value of an
   * element-value pair. It has the following format:
   *
   * <p>element_value { u1 tag union { u2 const_value_index;
   *
   * <p>{ u2 type_name_index; u2 constant_name_index; } enum_const_value;
   *
   * <p>u2 class_info_index;
   *
   * <p>annotation annotation_value;
   *
   * <p>{ u2 num_value; element_value values[num_value] } array_value } value }
   *
   * <p>In other words, the union describes it is one of the following types: - const_value_index -
   * enum_const_value - class_info_index - annotation_value - array_value
   *
   * @param dataInputStream The byte stream associated with the resource (aka .class file).
   * @param index The index of the element value pair.
   * @param resource The resource associated with the attribute.
   * @throws IOException When reading bytes from the stream fails.
   */
  protected static void extractElementValuePair(
      DataInputStream dataInputStream, int index, Resource resource) throws IOException {
    StringBuilder buffer = new StringBuilder();
    buffer.append("Element-value pair (index: ").append(index).append(")");

    // Read the element key
    int elementNameIndex = dataInputStream.readUnsignedShort();
    String element =
        ConstantPoolAnalyser.extractStringValueByConstantPoolIndex(
            resource.getConstantPool(), elementNameIndex);
    buffer.append(", with key (index: ").append(elementNameIndex).append("): ").append(element);

    // Extract the value
    extractElementValue(dataInputStream, buffer, resource);
  }
コード例 #6
0
 /**
  * Auxiliary method to get a reference of the JavaSpecification associated with the resource.
  *
  * @param resource The resource associated with the JavaSpecification.
  * @return A reference of the JavaSpecification (or null).
  */
 public JavaSpecification getSpecification(Resource resource) {
   if (resource != null) {
     return this.specificationMap.get(resource.getCompiledVersion());
   }
   return null;
 }
コード例 #7
0
  /**
   * Auxiliary method to extract an 'element_value' from the data input stream. Note that this
   * method can be called recursively (in case the value of the element_value is an array with more
   * element_value's).
   *
   * @param dataInputStream The byte stream associated with the resource (aka .class file).
   * @param buffer The buffer with relevant text to support (deep and semantic) logging.
   * @param resource The resource associated with the attribute.
   * @throws IOException When reading bytes from the stream fails.
   */
  public static void extractElementValue(
      DataInputStream dataInputStream, StringBuilder buffer, Resource resource) throws IOException {
    // Read the tag
    int tag = dataInputStream.readUnsignedByte();

    // Read the element value
    switch ((char) tag) {
      case 'B':
        int constantByteValueIndex = dataInputStream.readUnsignedShort();
        ConstantInfo byteValue =
            findConstantByIndex(constantByteValueIndex, resource.getConstantPool());
        buffer
            .append(", with value (index: ")
            .append(constantByteValueIndex)
            .append(", type: byte): ")
            .append(byteValue.getIntValue());
        LOGGER.debug(buffer.toString());
        break;

      case 'C':
        int constantCharValueIndex = dataInputStream.readUnsignedShort();
        ConstantInfo charValue =
            findConstantByIndex(constantCharValueIndex, resource.getConstantPool());
        buffer
            .append(", with value (index: ")
            .append(constantCharValueIndex)
            .append(", type: char): ")
            .append((char) charValue.getIntValue());
        LOGGER.debug(buffer.toString());
        break;

      case 'D':
        int constantDoubleValueIndex = dataInputStream.readUnsignedShort();
        ConstantInfo doubleValue =
            findConstantByIndex(constantDoubleValueIndex, resource.getConstantPool());
        buffer
            .append(", with value (index: ")
            .append(constantDoubleValueIndex)
            .append(", type: double): ")
            .append(doubleValue.getIntValue());
        LOGGER.debug(buffer.toString());
        break;

      case 'F':
        int constantFloatValueIndex = dataInputStream.readUnsignedShort();
        ConstantInfo floatValue =
            findConstantByIndex(constantFloatValueIndex, resource.getConstantPool());
        buffer
            .append(", with value (index: ")
            .append(constantFloatValueIndex)
            .append(", type: float): ")
            .append(floatValue.getIntValue());
        LOGGER.debug(buffer.toString());
        break;

      case 'I':
        // Read the value index
        int constantIntegerValueIndex = dataInputStream.readUnsignedShort();
        ConstantInfo integerValue =
            findConstantByIndex(constantIntegerValueIndex, resource.getConstantPool());
        buffer
            .append(", with value (index: ")
            .append(constantIntegerValueIndex)
            .append(", type: integer): ")
            .append(integerValue.getIntValue());
        LOGGER.debug(buffer.toString());
        break;

      case 'J':
        // Read the value index
        int constantLongValueIndex = dataInputStream.readUnsignedShort();
        ConstantInfo longValue =
            findConstantByIndex(constantLongValueIndex, resource.getConstantPool());
        buffer
            .append(", with value (index: ")
            .append(constantLongValueIndex)
            .append(", type: long): ")
            .append(longValue.getIntValue());
        LOGGER.debug(buffer.toString());
        break;

      case 'S':
        // Read the value index
        int constantShortValueIndex = dataInputStream.readUnsignedShort();
        ConstantInfo shortValue =
            findConstantByIndex(constantShortValueIndex, resource.getConstantPool());
        buffer
            .append(", with value (index: ")
            .append(constantShortValueIndex)
            .append(", type: short): ")
            .append(shortValue.getIntValue());
        LOGGER.debug(buffer.toString());
        break;

      case 'Z':
        // Read the value index
        int constantBooleanValueIndex = dataInputStream.readUnsignedShort();
        ConstantInfo booleanValue =
            findConstantByIndex(constantBooleanValueIndex, resource.getConstantPool());
        buffer
            .append(", with value (index: ")
            .append(constantBooleanValueIndex)
            .append(", type: boolean): ")
            .append(booleanValue.getIntValue());
        LOGGER.debug(buffer.toString());
        break;

      case 's':
        // Read the value index
        int constantStringValueIndex = dataInputStream.readUnsignedShort();
        ConstantInfo stringValue =
            findConstantByIndex(constantStringValueIndex, resource.getConstantPool());
        buffer
            .append(", with value (index: ")
            .append(constantStringValueIndex)
            .append(", type: String): ")
            .append(stringValue.getStringValue());
        LOGGER.debug(buffer.toString());
        break;

      case 'e':
        // Read the enum type index
        int typeNameIndex = dataInputStream.readUnsignedShort();

        // The value of the type_name_index item must be a valid index in the 'constant_pool' table.
        // The 'constant_pool'
        // entry at that index must be a 'CONSTANT_Utf8_info' structure representing a field
        // descriptor.
        String typeNameDescriptor =
            ConstantPoolAnalyser.extractStringValueByConstantPoolIndex(
                resource.getConstantPool(), typeNameIndex);

        // Read the constant name index
        int constantNameIndex = dataInputStream.readUnsignedShort();

        // The value of constant_name_index item must be a valid index in the 'constant_pool' table.
        // The 'constant_pool'
        // entry at that index must be a 'CONSTANT_Utf8_info' structure.
        String enumValue =
            ConstantPoolAnalyser.extractStringValueByConstantPoolIndex(
                resource.getConstantPool(), constantNameIndex);
        buffer
            .append(", with type name (index: ")
            .append(typeNameIndex)
            .append(", type: enum): ")
            .append(typeNameDescriptor);
        buffer
            .append(" and value (index: ")
            .append(constantNameIndex)
            .append("): ")
            .append(enumValue);
        LOGGER.debug(buffer.toString());

        // Add signature (when applicable) to the referenced classes
        SignatureAnalyser.referencedClasses(resource.getReferencedClasses(), typeNameDescriptor);
        break;

      case 'c':
        // Read the class index
        int classInfoIndex = dataInputStream.readUnsignedShort();

        // The class_info_index denotes a class literal as the value of this element-value pair. The
        // class_info_index
        // must be a valid index into the 'constant_pool' table. The 'constant_pool' entry at that
        // index must be a
        // 'CONSTANT_Utf8_info' structure representing a return descriptor. The return descriptor
        // give the type
        // corresponding to the class literal represented by this 'element_value' structure. Types
        // correspond to class
        // literals as follows:
        //
        // - For a class literal C.class, where C is the name of the class, interface, or array
        // type, the corresponding
        //   type is C. The return descriptor in the 'constant_pool' will be an ObjectType or an
        // ArrayType.
        //   Example: the class literal Object.class corresponds to Object, so the 'constant_pool'
        // entry is Ljava/lang/Object;
        // - For a class literal p.class, where p is the name of a primitive type, the corresponding
        // type is p. The
        //   return descriptor in the 'constant_pool' will be a BaseType character.
        //   Example: the literal int.class corresponds to the type int, so the 'constant_pool'
        // entry is I.
        // - For a class literal void.class, the corresponding type is void. The return descriptor
        // in the 'constant_pool'
        //   will be V.
        //   Example: the literal void.class corresponds to void, so the 'constant_pool' entry is V,
        // whereas
        //   the class literal Void.class corresponds to the type Void, so the 'constant_pool' entry
        // is Ljava/lang/Void;
        String descriptor =
            ConstantPoolAnalyser.extractStringValueByConstantPoolIndex(
                resource.getConstantPool(), classInfoIndex);
        buffer
            .append(", with value (index: ")
            .append(classInfoIndex)
            .append(", type: Class) with descriptor: ")
            .append(descriptor);
        LOGGER.debug(buffer.toString());

        // Add signature (when applicable) to the referenced classes
        SignatureAnalyser.referencedClasses(resource.getReferencedClasses(), descriptor);
        break;

      case '@':
        buffer.append(
            ", with type annotation -> delegating to extract annotation (with index: -1)...");
        LOGGER.debug(buffer.toString());

        // Read annotation
        extractAnnotation(dataInputStream, -1, resource);
        break;

      case '[':
        int numberOfValues = dataInputStream.readUnsignedShort();
        buffer
            .append(", with type array (size: ")
            .append(numberOfValues)
            .append(") entering recursion...");
        LOGGER.debug(buffer.toString());

        // Read array members, delegate by recursion
        LOGGER.debug("Number of nested element-value pairs: " + numberOfValues);
        for (int index = 0; index < numberOfValues; index++) {
          StringBuilder nestedBuffer = new StringBuilder();
          nestedBuffer.append("Nested element-value pair (index: ").append(index).append(")");
          extractElementValue(dataInputStream, nestedBuffer, resource);
        }
        break;

      default:
        buffer.append(", but tag is unsupported: ").append(tag);
        LOGGER.warn(buffer.toString());
    }
  }