"Хакерство" на java

Рейтинг: 2Ответов: 1Опубликовано: 02.11.2014

Учу java. Дошёл до внутренних классов. Написал такой код:

package a;

public class A 
{
    private int a;

    public A(int a) { this.a = a; }

    private class Internal
    {
        private void doSomething() { int b = A.this.a; }
    }
}

Таким образом компилятор добавляет в класс A метод:

static int access$0(a A);

(то, что имя метода именно такое, проверил с помощью рефлексии).

Допустим, я злоумышленник и хочу получить доступ к скрытому полю а класса А. Добавляю я в папку a-файл Hacker.java, содержащий следующий код:

package a;

public class Hacker
{
    public static int getA(A a){
        return A.access$0(a);
    }
}

Компилирую его уже не через Eclipse, а через cmd вот так:

D:\6. ФПМИ\Информатика\JavaProjects>javac -classpath Internal\bin Internal\bin\a\Hacker.java

(т.е. пакет a у меня находится в D:\6. ФПМИ\Информатика\JavaProjects\Internal\bin), на что мне компилятор сообщает:

Internal\bin\a\Hacker.java:6: error: cannot find symbol
                return A.access$0(a);
                        ^   symbol:   method access$0(A)   location: class A 1 error

Почему? Почему из меня такой плохой хакер?) В общем, объясните, пожалуйста, как надо. Но только доступно объясните, ибо я ещё просто студентик. )

Ответы

▲ 4Принят

Вероятно, компилятор Java достаточно умён, чтобы не позволить вам напрямую вызывать синтетические методы. А вот уже JVM можно обмануть. Для этого произведём следующие манипуляции:

  • Скомпилируем класс A и в декомпиляторе посмотрим сигнатуру метода, сгенерированного для доступа к приватному полю. Пусть она будет static int access$0(A a).
  • Забэкапим скомпилированные классы A.class и A$Internal.class. В исходнике класса A удалим внутренний класс и добавим метод access$0:
package a;

public class A 
{
    private int a;

    public A(int a) { this.a = a; }

    static int access$0(A a) {
        return a.a;
    }
}
  • Скомпилируем эту версию класса A. Скомпилируем класс Hacker, указав в параметре -classpath компилятора путь к модифицированной версии класса A.
  • Теперь можно заменить модифицированный класс A на оригинальный и запустить программу.

На уровне байт-кода не будет никакой принципиальной разницы между вызовами синтетического метода access$0 и написанного нами метода с таким же именем. JVM не будет задумываться о корректности предоставления доступа к этому методу классу Hacker, она считает, что об этом позаботился компилятор (как видите, он не позволяет вам даже скомпилировать код, содержащий прямой вызов синтетического метода).

Теперь осталось придумать хоть одно практическое применение такого подхода. Ведь всё это можно было сделать без танцев с бубнами при помощи рефлексии.