Как я могу десериализовать объект, если он был перемещен в другой пакет или переименован? - программирование
Подтвердить что ты не робот

Как я могу десериализовать объект, если он был перемещен в другой пакет или переименован?

Рассмотрим следующую ситуацию:

Существует файл сериализации, созданный старой версией приложения. К сожалению, пакет изменился для класса, который был сериализован. И теперь мне нужно загрузить информацию из этого файла в тот же класс, но находится в другом пакете. Этот класс имеет serialVersionUID, определенный и не измененный (т.е. Совместимый).

Вопрос: Можно ли загружать новые экземпляры класса из этого файла с помощью любых трюков (кроме тривиального копирования класса в старый пакет, а затем с использованием логики оболочки десериализации)? Можно ли использовать readResolve() для восстановления после перемещения/переименования класса? Если нет, объясните, почему.

4b9b3361

Ответ 1

Вопрос: Можно ли загрузить новые экземпляры класса из этого файла используя любые трюки (кроме тривиальных копирование класса в старый пакет и затем с использованием десериализационной обертки логика)?

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

Можно использовать readResolve() для восстановить или переименовать класс? Если нет, объясните, почему.

Нет, поскольку механизм десериализации будет работать намного раньше, на том этапе, где он пытается найти класс, который десериализуется, - он не знает, что класс в другом пакете имеет метод readResolve(), который он должен использовать.

Ответ 2

Возможно:

class HackedObjectInputStream extends ObjectInputStream {

    public HackedObjectInputStream(InputStream in) throws IOException {
        super(in);
    }

    @Override
    protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
        ObjectStreamClass resultClassDescriptor = super.readClassDescriptor();

        if (resultClassDescriptor.getName().equals("oldpackage.Clazz"))
            resultClassDescriptor = ObjectStreamClass.lookup(newpackage.Clazz.class);

        return resultClassDescriptor;
    }
}

Это также позволяет игнорировать несоответствие serialVersionUID или даже десериализовать класс, если его структура поля была изменена.

Ответ 3

Вероятно, лучший способ - воссоздать старый класс (имя, пакет и серийный идентификатор), прочитать в сериализованной форме, затем скопировать данные в экземпляр нового объекта и повторно выполнить его.

Если у вас много таких сериализованных объектов, возможно, вы могли бы написать небольшой script, чтобы сделать это "изменение схемы" за один раз.

Другой вариант - воскресить старый класс и реализовать его метод readResolve, чтобы вернуть экземпляр нового класса (возможно, объявив конструктор копирования). Лично я думаю, что я бы пошел на изменение схемы script, а затем удалил старый класс навсегда.

Ответ 4

Если вы используете редактор Cygnus Hex Editor, вы можете вручную изменить имя пакета/класса.

Если новое имя (всегда включая пакет) имеет тот же размер, вы можете просто заменить старое имя на новое имя, но если размер изменился, вам нужно обновить первые 2 символа перед именем с новой новой длиной.

Щелкните правой кнопкой мыши стандартные типы данных и измените на Big Endian.

Длина - это подписанное слово.

Например:

00 0E 70 61 63 6B 61 67 65 2E 53 61 6D 70 6C 65
.  .  p   a  c  k  a  g  e  .  S  a  m  p  l  e

как пакет. Сэмпл написан. 00 0E означает 14, число символов "package.Sample" имеет.

Если мы хотим изменить на newpackage.Sample, мы заменим эту строку на:

00 12 6E 65 77 70 61 63 6B 61 67 65 2E 53 61 6D 70 6C 65
.  .  n  e  w  p   a  c  k  a  g  e  .  S  a  m  p  l  e

00 12 означает 18, количество символов "newpackage.Sample" имеет.

И, конечно же, вы можете сделать патчер, чтобы обновить его автоматически.

Ответ 5

Я не думаю, что можно делать то, что вы хотите.

Формат файла сериализации содержит имена классов. В деталях он имеет следующую структуру:

AC ED

номер версии протокола

данные объекта

описание класса объекта

Описание класса имеет следующий формат:

полное имя класса

уникальный идентификатор серийной версии (SHA1 от поля и методы)

параметры сериализации

дескрипторы полей

При попытке десериализации механизма сериализации объектов сначала сравниваются имена классов (и вы не проходите этот шаг), тогда он сравнивает serialVersionUID и только после прохождения этих двух шагов десериализует объект.

Ответ 6

Дополнение к шестнадцатеричному редактированию.

Это сработало для меня, и было проще заменить прежнее имя пакета на новые, вместо того чтобы выполнять замены классов, переопределяя ObjectInputStream. Тем более, что были анонимные классы.

Вот сценарий, который заменяет старый путь класса новым путём класса в двоичном формате.

Вот содержание моего скрипта hexreplace.sh:

#!/bin/bash
set -xue

OLD_STR=$(echo -n $1 | hexdump -ve '1/1 "%.2X"')
NEW_STR=$(echo -n $2 | hexdump -ve '1/1 "%.2X"')
SRC_FILE=$3
DST_FILE=$4

TMP_FILE=$(mktemp /tmp/bin.patched.XXXXXXXXXX)

[ -f $SRC_FILE ]

hexdump -ve '1/1 "%.2X"' "$SRC_FILE" | sed "s/$OLD_STR/$NEW_STR/g" | xxd -r -p > "$TMP_FILE"

mv "$TMP_FILE" "$DST_FILE"

Бежать

hexreplace.sh old.class.path new.class.path source_file destination_file

Скрипт работает правильно, когда исходные и целевые файлы одинаковы.