отсутствующие классы в постоянном пуле classfiles

я использую анализ байт-кода, чтобы получить все импортированные классы classfile (с BCEL). Теперь, когда я читаю пул констант, не все импортированные классы упоминаются как CONSTANT_Class (см. spec), но только как CONSTANT_Utf8. Теперь мой вопрос: могу ли я полагаться исключительно на записи CONSTANT_Class в постоянном пуле для чтения импортированных файлов? мне действительно нужно смотреть на каждую запись и угадывать, является ли это именем класса? Это также не кажется правильным в каждой ситуации imo. Или мне нужно прочитать весь байт-код? С уважением


person wrm    schedule 04.01.2012    source источник
comment
Эта книга, на которую вы ссылаетесь, была опубликована в 1999 году, поэтому я бы не стал использовать ее в качестве справочника.   -  person Viruzzo    schedule 04.01.2012


Ответы (2)


Нет, неправильно использовать только записи CONSTANT_Class_info для обнаружения зависимостей от других классов/интерфейсов. Если вы анализируете входные файлы, которым доверяете, или допускаете неверную информацию, вы можете обойтись анализом только пула констант, за исключением одного крайнего случая. Чтобы получить точную информацию о произвольном вводе, вам необходимо проанализировать весь файл класса. (Я предполагаю, что под «зависимостями» вы подразумеваете те классы или интерфейсы, без которых загрузка или связывание класса может привести к исключениям, как описано в глава 5 JVMS. Сюда не входят классы, полученные с помощью Class.forName или других средств отражения.)

Рассмотрим следующий класс.

public class Main {
    public static void main(String[] args) {
        identity(null);
    }
    public static Object identity(Foo x) {
        return x;
    }
}

javap -p -v Main.class печатает:

Classfile /C:/Users/jbosboom/Documents/stackoverflow/build/classes/Main.class
  Last modified Jul 2, 2014; size 346 bytes
  MD5 checksum 2237cda2a15a58382b0fb98d6afacc7e
  Compiled from "Main.java"
public class Main
  SourceFile: "Main.java"
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #3.#17         //  java/lang/Object."<init>":()V
   #2 = Class              #18            //  Main
   #3 = Class              #19            //  java/lang/Object
   #4 = Utf8               <init>
   #5 = Utf8               ()V
   #6 = Utf8               Code
   #7 = Utf8               LineNumberTable
   #8 = Utf8               LocalVariableTable
   #9 = Utf8               this
  #10 = Utf8               LMain;
  #11 = Utf8               identity
  #12 = Utf8               (LFoo;)Ljava/lang/Object;
  #13 = Utf8               x
  #14 = Utf8               LAAA;
  #15 = Utf8               SourceFile
  #16 = Utf8               Main.java
  #17 = NameAndType        #4:#5          //  "<init>":()V
  #18 = Utf8               Main
  #19 = Utf8               java/lang/Object
  #20 = Utf8               java/lang/Thread
  #21 = Class              #20            //  java/lang/Thread
  #21 = Utf8               (LBar;)LFakename;
{
  public Main();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 6: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LMain;

  public static java.lang.Object identity(Foo);
    descriptor: (LFoo;)Ljava/lang/Object;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: areturn
      LineNumberTable:
        line 11: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       2     0     x   LAAA;
}

Класс Foo, указанный в качестве параметра метода identity, не отображается в пуле констант как запись CONSTANT_Class_info. Он появляется в дескрипторе метода для identity (запись № 12). Дескрипторы полей также могут ссылаться на классы, не отображаемые как записи CONSTANT_Class_info. Таким образом, чтобы найти все зависимости только из пула констант, вам нужно просмотреть все записи UTF8.

Крайний случай: могут существовать некоторые записи UTF8, на которые могут ссылаться записи CONSTANT_String_info. Повторяющиеся записи UTF8 будут объединены, поэтому одна запись UTF8 может быть дескриптором метода, строковым литералом или и тем, и другим. Если вы анализируете только пул констант, вы должны смириться с этой двусмысленностью (вероятно, из-за чрезмерной аппроксимации и обработки ее как зависимости).

Если вы уверены, что входные данные были созданы компилятором Java с хорошим поведением под вашим контролем, вы можете проанализировать все записи UTF8, помня о угловом регистре строки, и прекратить чтение здесь. Если вам нужно защититься от злоумышленника, передающего созданные вручную файлы классов вашего инструмента (например, вы пишете декомпилятор, а злоумышленник хочет предотвратить декомпиляцию), вам необходимо проанализировать весь файл класса. Вот несколько примеров потенциальных проблем.

  • Запись № 20 называет класс, не используемый Main. JVM может или не может попытаться разрешить эту ссылку (JVMS 5.4 допускает как ленивую, так и нетерпеливую загрузку). Поскольку класс существует, в любом случае ошибка не возникнет, поэтому эта дополнительная запись безвредна, но она обманет инструменты, просматривающие пул констант, и заставят их думать, что Thread является зависимостью.
  • Запись № 21 — это неиспользуемый дескриптор метода, относящийся к двум фиктивным классам. Поскольку этот дескриптор не используется, ошибка не возникнет, но, опять же, инструменты, доверяющие пулу констант, будут его анализировать.
  • Запись №14 — это дескриптор поля, относящийся к вымышленному классу. Эта запись на самом деле используется атрибутом LineNumberTable, но эта отладочная информация не проверяется JVM, поэтому ссылка безвредна, но может обмануть инструменты.
  • У меня нет примера для этого, но атрибут InnerClasses ссылается на записи CONSTANT_Class_info и не проверяется на согласованность с другими файлами классов (согласно JVMS 4.7.6, хотя и в ненормативном примечании). Эти ссылки не препятствуют загрузке или связыванию, но могут запутать инструмент, исследующий пул констант.

Это просто то, что я придумал из головы. Умный взломщик, внимательно изучив JVMS, мог бы, вероятно, найти больше мест для добавления записей в постоянный пул, которые выглядят использованными, но на самом деле ими не являются. Если вам нужна точная информация даже перед лицом злоумышленника, вам нужно проанализировать весь файл класса и понять, как JVM будет его использовать.

person Jeffrey Bosboom    schedule 03.07.2014
comment
Впечатляющий ответ, спасибо, что нашли время написать его. - person Jorge Vicente Cantero; 09.11.2019

См. JVMS 4.2, Внутренняя форма класса FQ и Имена интерфейсов.

В двух словах: структуры классов указывают на записи UTF8.

(Или вместо этого вы говорите, что не все классы, на которые ссылаются, представлены записью класса и имени?)


FWIW, будьте осторожны, полагаясь исключительно на эту информацию для определения зависимостей, поскольку классы могут загружаться динамически и могут вообще не отображаться.

person Dave Newton    schedule 04.01.2012
comment
да, я знаю, что я не могу полагаться только на это... но для моего варианта использования этого достаточно. я хочу сказать, что все ссылки, которые мне нужны, представлены как записи utf8, а не как записи класса. поэтому мне нужно угадать, является ли запись utf8 на самом деле именем класса.. (насколько я понимаю, все загружаемые классы представлены как записи классов, но это не так) - person wrm; 05.01.2012