Гайд [Nasrultan dump] [1/2] Пересобираем java и получаем защиту как в Shitkrien, Nusrultan, Shitlestial, ReachRekode.

эксперт в майнкрафт апи
Read Only
Статус
Оффлайн
Регистрация
25 Янв 2023
Сообщения
676
Реакции[?]
284
Поинты[?]
22K
Всем привет!

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

Данный метод защиты используют такие читы как Akrien, люди(?нет, не люди.обычные пастеры чей уровень не заходит даже за видосы хаудихо.самые кринжовые клоуны) которые сидят на протекшене от аргентоза.

* Данный метод защиты - полный калл.Он спасает лишь потому, что майнкрафт комьюнити - абсолютно примитивные Java школокодеры.Иметь какие либо большие знания для создания данной защиты не надо.
* Данные защиты абсолютно идентичны.Отличаются лишь версии OpenJDK, pad`ы(= измененные оффсеты) которые добавляют создатели данных "грандиозных" защит.

Ну что ж, теперь к описанию принципа работы данной защиты.

У Java класса есть формат. (
Пожалуйста, авторизуйтесь для просмотра ссылки.
)

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

Начнем со сборки OpenJDK.

Переходим на
Пожалуйста, авторизуйтесь для просмотра ссылки.
.

У них есть building.md/html, но я все же напишу краткий гайд по сборке.

Все что нам нужно :

1) Cygwin (
Пожалуйста, авторизуйтесь для просмотра ссылки.
)
2) BootJDK (да, для сборки JDK, нам понадобится другая JDK) (
Пожалуйста, авторизуйтесь для просмотра ссылки.
)

Итак, мы получили setup-x86_64.exe.
В этой же директории открываем cmd, пишем
setup-x86_64.exe -q -P autoconf -P make -P unzip -P zip
Теперь достанем исходники openjdk на диск C:/
Пожалуйста, авторизуйтесь для просмотра ссылки.
Теперь создадим папку для нашей bootjdk в данной директории и разархивируем туда то что мы качнули чуть выше.
Пожалуйста, авторизуйтесь для просмотра ссылки.
А теперь к сборке.
В cygwin пишем :
bash configure --with-boot-jdk=bootjdk
make images
Это будет очень долго в первый раз.
Пока у меня идет сборка, расскажу вам что мы будем делать.
Формат класса состоит из :
CAFEBABE (первые 4 байта, "magic value" java класс файла).
Является отсылкой.(
Пожалуйста, авторизуйтесь для просмотра ссылки.
)
Minor версия, Major версия класса. (2/2 байта)
Дальше идет парсинг содержимого класса.
1) констант пул.
2) супер класс, интерфейсы.
3) поля класса.
4) методы класса.
5) аттрибуты класса.
Из описанного выше вам может стать не совсем понятным лишь константпул.
Константпул - основа всего класса.В нем содержится самая основная информация - строки, int/float/long/double значения.
Основная задача идет на JVM_CONSTANT_Utf8
Например : спарсив JVM_CONSTANT_Utf8, он может спарсить и JVM_CONSTANT_Class :
// const u2 name_index = cfs->get_u2_fast();
// cp->klass_index_at_put(index, name_index);
Также и с остальными константами.Я надеюсь принцип понятен.
* Байткод методов также ссылается на индексы в константпуле и сам по себе какого либо значения не несет.
* Все что парсится в классе после константпула - лишь его индексы и размер того, что нужно спарсить.
* Для более подробного анализа вы можете скачать DirtyJOE
Все.java сбилжена.Но как нам что либо редактировать в ней?Лезть в notepad?Давайте это исправим.
Пожалуйста, авторизуйтесь для просмотра ссылки.
пишем
make hotspot-ide-project
и ждем.
Все!
C:\openjdk\build\windows-x86_64-server-release\ide\hotspot-visualstudio
Что ж, половина задачи уже выполнена.
Нам нужен classFileParser.cpp.В нем и происходит основная магия - парсинг класса из байтов.
Заходим в метод parse_stream и видим :
C++:
void ClassFileParser::parse_stream(const ClassFileStream* const stream,
                                   TRAPS) {

  assert(stream != NULL, "invariant");
  assert(_class_name != NULL, "invariant");

  // BEGIN STREAM PARSING
  stream->guarantee_more(8, CHECK);  // magic, major, minor
  // Magic value
  const u4 magic = stream->get_u4_fast();
  guarantee_property(magic == JAVA_CLASSFILE_MAGIC,
                     "Incompatible magic value %u in class file %s",
                     magic, CHECK);

  // Version numbers
  _minor_version = stream->get_u2_fast();
  _major_version = stream->get_u2_fast();

  // Check version numbers - we check this even with verifier off
  verify_class_version(_major_version, _minor_version, _class_name, CHECK);

  stream->guarantee_more(3, CHECK); // length, first cp tag
  u2 cp_size = stream->get_u2_fast();

  guarantee_property(
    cp_size >= 1, "Illegal constant pool size %u in class file %s",
    cp_size, CHECK);

  _orig_cp_size = cp_size;
  if (is_hidden()) { // Add a slot for hidden class name.
    cp_size++;
  }

  _cp = ConstantPool::allocate(_loader_data,
                               cp_size,
                               CHECK);

  ConstantPool* const cp = _cp;

  parse_constant_pool(stream, cp, _orig_cp_size, CHECK);

  assert(cp_size == (const u2)cp->length(), "invariant");

  // ACCESS FLAGS
  stream->guarantee_more(8, CHECK);  // flags, this_class, super_class, infs_len

  // Access flags
  jint flags;
  // JVM_ACC_MODULE is defined in JDK-9 and later.
  if (_major_version >= JAVA_9_VERSION) {
    flags = stream->get_u2_fast() & (JVM_RECOGNIZED_CLASS_MODIFIERS | JVM_ACC_MODULE);
  } else {
    flags = stream->get_u2_fast() & JVM_RECOGNIZED_CLASS_MODIFIERS;
  }

  if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) {
    // Set abstract bit for old class files for backward compatibility
    flags |= JVM_ACC_ABSTRACT;
  }

  verify_legal_class_modifiers(flags, CHECK);

  short bad_constant = class_bad_constant_seen();
  if (bad_constant != 0) {
    // Do not throw CFE until after the access_flags are checked because if
    // ACC_MODULE is set in the access flags, then NCDFE must be thrown, not CFE.
    classfile_parse_error("Unknown constant tag %u in class file %s", bad_constant, THREAD);
    return;
  }

  _access_flags.set_flags(flags);

  // This class and superclass
  _this_class_index = stream->get_u2_fast();
  check_property(
    valid_cp_range(_this_class_index, cp_size) &&
      cp->tag_at(_this_class_index).is_unresolved_klass(),
    "Invalid this class index %u in constant pool in class file %s",
    _this_class_index, CHECK);

  Symbol* const class_name_in_cp = cp->klass_name_at(_this_class_index);
  assert(class_name_in_cp != NULL, "class_name can't be null");

  // Don't need to check whether this class name is legal or not.
  // It has been checked when constant pool is parsed.
  // However, make sure it is not an array type.
  if (_need_verify) {
    guarantee_property(class_name_in_cp->char_at(0) != JVM_SIGNATURE_ARRAY,
                       "Bad class name in class file %s",
                       CHECK);
  }

#ifdef ASSERT
  // Basic sanity checks
  if (_is_hidden) {
    assert(_class_name != vmSymbols::unknown_class_name(), "hidden classes should have a special name");
  }
#endif

  // Update the _class_name as needed depending on whether this is a named, un-named, or hidden class.

  if (_is_hidden) {
    assert(_class_name != NULL, "Unexpected null _class_name");
#ifdef ASSERT
    if (_need_verify) {
      verify_legal_class_name(_class_name, CHECK);
    }
#endif

  } else {
    // Check if name in class file matches given name
    if (_class_name != class_name_in_cp) {
      if (_class_name != vmSymbols::unknown_class_name()) {
        ResourceMark rm(THREAD);
        Exceptions::fthrow(THREAD_AND_LOCATION,
                           vmSymbols::java_lang_NoClassDefFoundError(),
                           "%s (wrong name: %s)",
                           _class_name->as_C_string(),
                           class_name_in_cp->as_C_string()
                           );
        return;
      } else {
        // The class name was not known by the caller so we set it from
        // the value in the CP.
        update_class_name(class_name_in_cp);
      }
      // else nothing to do: the expected class name matches what is in the CP
    }
  }

  // Verification prevents us from creating names with dots in them, this
  // asserts that that's the case.
  assert(is_internal_format(_class_name), "external class name format used internally");

  if (!is_internal()) {
    LogTarget(Debug, class, preorder) lt;
    if (lt.is_enabled()){
      ResourceMark rm(THREAD);
      LogStream ls(lt);
      ls.print("%s", _class_name->as_klass_external_name());
      if (stream->source() != NULL) {
        ls.print(" source: %s", stream->source());
      }
      ls.cr();
    }
  }

  // SUPERKLASS
  _super_class_index = stream->get_u2_fast();
  _super_klass = parse_super_class(cp,
                                   _super_class_index,
                                   _need_verify,
                                   CHECK);

  // Interfaces
  _itfs_len = stream->get_u2_fast();
  parse_interfaces(stream,
                   _itfs_len,
                   cp,
                   &_has_nonstatic_concrete_methods,
                   CHECK);

  assert(_local_interfaces != NULL, "invariant");

  // Fields (offsets are filled in later)
  _fac = new FieldAllocationCount();
  parse_fields(stream,
               _access_flags.is_interface(),
               _fac,
               cp,
               cp_size,
               &_java_fields_count,
               CHECK);

  assert(_fields != NULL, "invariant");

  // Methods
  parse_methods(stream,
                _access_flags.is_interface(),
                &_has_localvariable_table,
                &_has_final_method,
                &_declares_nonstatic_concrete_methods,
                CHECK);

  assert(_methods != NULL, "invariant");

  if (_declares_nonstatic_concrete_methods) {
    _has_nonstatic_concrete_methods = true;
  }

  // Additional attributes/annotations
  _parsed_annotations = new ClassAnnotationCollector();
  parse_classfile_attributes(stream, cp, _parsed_annotations, CHECK);

  assert(_inner_classes != NULL, "invariant");

  // Finalize the Annotations metadata object,
  // now that all annotation arrays have been created.
  create_combined_annotations(CHECK);

  // Make sure this is the end of class file stream
  guarantee_property(stream->at_eos(),
                     "Extra bytes at the end of class file %s",
                     CHECK);

  // all bytes in stream read and parsed
}
Но что за u4, что за stream?
u4 - тайпдеф, который означает unsigned 4 byte.Либо же просто unsigned int.
В джаве их 4 вида :
u1, u2, u4, u8 (unsigned byte(C++ char), short, int, long long)
* Довольно смешным фактом является то, что джава сама по себе отрицает значения которые записываются меньше чем в 4 байта.
* Минимальный размер элемента стека - 4 байта.(локали также).wide значения(long,double) вписываются в 2 элемента.
* И в целом byte,boolean,short,char (1-2 byte) реализованы лишь инструкциями взаимодействия с массивами.(ex : SALOAD, SASTORE)(за обработку байтов и булеанок они вообще используют одну инструкцию - BASTORE/BALOAD.Ущемление прав, никак иначе)
stream - обычный класс-ридер потока байтов.имеет на борту методы аля read_u1/2/4/8, и текущий индекс чтения.
Почему все засрано какими то постоянными guarantee_more, assert, check_property?
Если вам тоже интересен этот вопрос как и мне, то ответ хранится в документации openjdk, а именно кодстайлу.
В двух словах, их главным принципом является лучше все проверить, чем поймать рандомный краш вмки.
Для нашей джавы конечно же желательно отказаться от такого дебага(бтв, assert останутся лишь в debug билде).По хорошему их всех удалить, либо покрыть xorstr.
Окей, теперь давайте рассинхронизируем наши задачи, а именно будем параллельно делать парсер, изменять значения, также изменяя их в джаве.
Создадим новый проект в IDE.
Что нам понадобится? Такой же ридер, врайтер.За основу возьмем DataInputStream, DataOutputStream.Он имеет на борту методы read(1 byte), readShort(2 bytes), readInt(4 bytes), readLong(8 bytes), аналогично с write.
В целом такой класс можно и не делать, а юзать DataInputStream, DataOutputStream, но тогда вы теряете возможность добавлять какой либо алгоритм при записи байтов.(ex : xor++ на каждый write изуродует stream при парсинге)
Я сделал для себя 2 класса :
ClassFileStream, ClassFileWriter
Java:
import java.io.*;

public class ClassFileStream {
    private final DataInputStream dataInputStream;

    public ClassFileStream(byte[] bytes) {
        this.dataInputStream = new DataInputStream(new ByteArrayInputStream(bytes));
    }

    /*
    в джава все числовые типы данных signed, unsigned отсутствует.
    поэтому лучше обернуть их в int, если понадобится дебаг
     */
    public int readByte() throws IOException {
        return dataInputStream.read();
    }

    public int readShort() throws IOException {
        return dataInputStream.readShort();
    }

    public int readInt() throws IOException {
        return dataInputStream.readInt();
    }

    public long readLong() throws IOException {
        return dataInputStream.readLong();
    }

    /*
    я не собираюсь переписывать вам полностью ClassFileFormat
    этот метод позволит мне дописать остальной стрим в output
     */
    public void writeToStream(OutputStream outputStream) throws IOException {
        int r;
        while ((r = dataInputStream.read()) >= 0) {
            outputStream.write(r);
        }
    }
}
Java:
import java.io.*;

public class ClassFileWriter {
    private final ByteArrayOutputStream byteArrayOutputStream;
    private final DataOutputStream dataOutputStream;

    public ClassFileWriter() {
        this.dataOutputStream = new DataOutputStream(byteArrayOutputStream = new ByteArrayOutputStream());
    }

    /*
    в джава все числовые типы данных signed, unsigned отсутствует.
    поэтому лучше обернуть их в int, если понадобится дебаг
     */
    public void writeByte(int value) throws IOException {
        dataOutputStream.write(value);
    }

    public void writeShort(int value) throws IOException {
        dataOutputStream.writeShort(value);
    }

    public void writeInt(int value) throws IOException {
        dataOutputStream.writeInt(value);
    }

    public void writeLong(long value) throws IOException {
        dataOutputStream.writeLong(value);
    }

    public ByteArrayOutputStream getByteArrayOutputStream() {
        return this.byteArrayOutputStream;
    }
}
Подготовим парсер jar прямо в main методе :
Java:
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.ProtectionDomain;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class Main {
    private static final byte BYTE_CA = (byte) 0xCA;
    private static final byte BYTE_FE = (byte) 0xFE;
    private static final byte BYTE_BA = (byte) 0xBA;
    private static final byte BYTE_BE = (byte) 0xBE;

    public static void main(String[] args) {
        if (args.length < 2) {
            ProtectionDomain protectionDomain = Main.class.getProtectionDomain();
            throw new IllegalArgumentException(String.format("Пример использования java -jar %s in.jar out.jar", protectionDomain.getCodeSource().getLocation().getFile()));
        }
        try {
            String inputJar = args[0];
            String outputJar = args[1];
            ZipFile inZip = new ZipFile(inputJar);
            Enumeration<? extends ZipEntry> zipEntries = inZip.entries();
            /*
            Создадим мапу path/byte[] для удобного использования ZipFile
             */
            Map<String, byte[]> map = new HashMap<>();
            while (zipEntries.hasMoreElements()) {
                ZipEntry zipEntry = zipEntries.nextElement();
                InputStream is = inZip.getInputStream(zipEntry);
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                int r;
                while ((r = is.read()) >= 0) {
                    byteArrayOutputStream.write(r);
                }
                map.put(zipEntry.getName(), byteArrayOutputStream.toByteArray());
            }
            ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(outputJar));
            for (Map.Entry<String, byte[]> entry : map.entrySet()) {
                byte[] bytes = entry.getValue();
                if (bytes.length > 3 && bytes[0] == BYTE_CA && bytes[1] == BYTE_FE && bytes[2] == BYTE_BA && bytes[3] == BYTE_BE) {
                    ClassFileWriter classFileWriter = new ClassFileWriter();
                    ClassFile classFile = new ClassFile(new ClassFileStream(bytes), classFileWriter);
                    classFile.parseStream();
                    bytes = classFileWriter.getByteArrayOutputStream().toByteArray();
                }
                zipOutputStream.putNextEntry(new ZipEntry(entry.getKey()));
                zipOutputStream.write(bytes);
                zipOutputStream.closeEntry();
            }
            zipOutputStream.close();
            inZip.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Ну и теперь самое страшное...
Создадим класс ClassFile.
Java:
import java.io.IOException;

public class ClassFile {
    private ClassFileStream classFileStream;
    private ClassFileWriter classFileWriter;

    public ClassFile(ClassFileStream classFileStream, ClassFileWriter classFileWriter) {
        this.classFileStream = classFileStream;
        this.classFileWriter = classFileWriter;
    }

    public void parseStream() throws IOException {
        /*
        вот тут расположим наш код
         */
      
        /*
        запись всего "not dirty" стрима чо бы потом все воркало
         */
        classFileStream.writeToStream(classFileWriter.getByteArrayOutputStream());
    }
}
Поехали.
Вспоминаем что я писал выше.
первые четыре байта -> CAFEBABE.Проверим?
Java:
    public void parseStream() throws IOException {
        int magic = classFileStream.readInt();
        System.out.println(String.format("0x%s", Integer.toHexString(magic).toUpperCase()));
        /*
        запись всего "not dirty" стрима чо бы потом все воркало
         */
        classFileStream.writeToStream(classFileWriter.getByteArrayOutputStream());
    }
Код:
0xCAFEBABE

Process finished with exit code 0
Круть!
Теперь перезапишем это, но в другом виде.Это будет маркером в парсере, что класс зашифрован.
Java:
    private static final int CUSTOM_MAGIC_VALUE = 0xDEADBEEF;
    public void parseStream() throws IOException {
        int magic = classFileStream.readInt();
        classFileWriter.writeInt(CUSTOM_MAGIC_VALUE);
        /*
        запись всего "not dirty" стрима чо бы потом все воркало
         */
        classFileStream.writeToStream(classFileWriter.getByteArrayOutputStream());
    }
Откроем hexed.it и посмотрим в out.jar -> Main.class
Пожалуйста, авторизуйтесь для просмотра ссылки.

Продолжение написания "защиты" во втором туторе.Там мы перепишем parse_constant_pool_entries, соберем и протестим данное дерьмо.

Nursultan Dump :
Пожалуйста, авторизуйтесь для просмотра ссылки.
 
Забаненный
Статус
Оффлайн
Регистрация
19 Май 2020
Сообщения
98
Реакции[?]
13
Поинты[?]
6K
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
+реп
 
Забаненный
Статус
Оффлайн
Регистрация
30 Дек 2022
Сообщения
23
Реакции[?]
0
Поинты[?]
0
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
А
Всем привет!

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

Данный метод защиты используют такие читы как Akrien, люди(?нет, не люди.обычные пастеры чей уровень не заходит даже за видосы хаудихо.самые кринжовые клоуны) которые сидят на протекшене от аргентоза.

* Данный метод защиты - полный калл.Он спасает лишь потому, что майнкрафт комьюнити - абсолютно примитивные Java школокодеры.Иметь какие либо большие знания для создания данной защиты не надо.
* Данные защиты абсолютно идентичны.Отличаются лишь версии OpenJDK, pad`ы(= измененные оффсеты) которые добавляют создатели данных "грандиозных" защит.

Ну что ж, теперь к описанию принципа работы данной защиты.

У Java класса есть формат. (
Пожалуйста, авторизуйтесь для просмотра ссылки.
)

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

Начнем со сборки OpenJDK.

Переходим на
Пожалуйста, авторизуйтесь для просмотра ссылки.
.

У них есть building.md/html, но я все же напишу краткий гайд по сборке.

Все что нам нужно :

1) Cygwin (
Пожалуйста, авторизуйтесь для просмотра ссылки.
)
2) BootJDK (да, для сборки JDK, нам понадобится другая JDK) (
Пожалуйста, авторизуйтесь для просмотра ссылки.
)

Итак, мы получили setup-x86_64.exe.
В этой же директории открываем cmd, пишем
setup-x86_64.exe -q -P autoconf -P make -P unzip -P zip
Теперь достанем исходники openjdk на диск C:/
Пожалуйста, авторизуйтесь для просмотра ссылки.
Теперь создадим папку для нашей bootjdk в данной директории и разархивируем туда то что мы качнули чуть выше.
Пожалуйста, авторизуйтесь для просмотра ссылки.
А теперь к сборке.
В cygwin пишем :
bash configure --with-boot-jdk=bootjdk
make images
Это будет очень долго в первый раз.
Пока у меня идет сборка, расскажу вам что мы будем делать.
Формат класса состоит из :
CAFEBABE (первые 4 байта, "magic value" java класс файла).
Является отсылкой.(
Пожалуйста, авторизуйтесь для просмотра ссылки.
)
Minor версия, Major версия класса. (2/2 байта)
Дальше идет парсинг содержимого класса.
1) констант пул.
2) супер класс, интерфейсы.
3) поля класса.
4) методы класса.
5) аттрибуты класса.
Из описанного выше вам может стать не совсем понятным лишь константпул.
Константпул - основа всего класса.В нем содержится самая основная информация - строки, int/float/long/double значения.
Основная задача идет на JVM_CONSTANT_Utf8
Например : спарсив JVM_CONSTANT_Utf8, он может спарсить и JVM_CONSTANT_Class :
// const u2 name_index = cfs->get_u2_fast();
// cp->klass_index_at_put(index, name_index);
Также и с остальными константами.Я надеюсь принцип понятен.
* Байткод методов также ссылается на индексы в константпуле и сам по себе какого либо значения не несет.
* Все что парсится в классе после константпула - лишь его индексы и размер того, что нужно спарсить.
* Для более подробного анализа вы можете скачать DirtyJOE
Все.java сбилжена.Но как нам что либо редактировать в ней?Лезть в notepad?Давайте это исправим.
Пожалуйста, авторизуйтесь для просмотра ссылки.
пишем
make hotspot-ide-project
и ждем.
Все!
C:\openjdk\build\windows-x86_64-server-release\ide\hotspot-visualstudio
Что ж, половина задачи уже выполнена.
Нам нужен classFileParser.cpp.В нем и происходит основная магия - парсинг класса из байтов.
Заходим в метод parse_stream и видим :
C++:
void ClassFileParser::parse_stream(const ClassFileStream* const stream,
                                   TRAPS) {

  assert(stream != NULL, "invariant");
  assert(_class_name != NULL, "invariant");

  // BEGIN STREAM PARSING
  stream->guarantee_more(8, CHECK);  // magic, major, minor
  // Magic value
  const u4 magic = stream->get_u4_fast();
  guarantee_property(magic == JAVA_CLASSFILE_MAGIC,
                     "Incompatible magic value %u in class file %s",
                     magic, CHECK);

  // Version numbers
  _minor_version = stream->get_u2_fast();
  _major_version = stream->get_u2_fast();

  // Check version numbers - we check this even with verifier off
  verify_class_version(_major_version, _minor_version, _class_name, CHECK);

  stream->guarantee_more(3, CHECK); // length, first cp tag
  u2 cp_size = stream->get_u2_fast();

  guarantee_property(
    cp_size >= 1, "Illegal constant pool size %u in class file %s",
    cp_size, CHECK);

  _orig_cp_size = cp_size;
  if (is_hidden()) { // Add a slot for hidden class name.
    cp_size++;
  }

  _cp = ConstantPool::allocate(_loader_data,
                               cp_size,
                               CHECK);

  ConstantPool* const cp = _cp;

  parse_constant_pool(stream, cp, _orig_cp_size, CHECK);

  assert(cp_size == (const u2)cp->length(), "invariant");

  // ACCESS FLAGS
  stream->guarantee_more(8, CHECK);  // flags, this_class, super_class, infs_len

  // Access flags
  jint flags;
  // JVM_ACC_MODULE is defined in JDK-9 and later.
  if (_major_version >= JAVA_9_VERSION) {
    flags = stream->get_u2_fast() & (JVM_RECOGNIZED_CLASS_MODIFIERS | JVM_ACC_MODULE);
  } else {
    flags = stream->get_u2_fast() & JVM_RECOGNIZED_CLASS_MODIFIERS;
  }

  if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) {
    // Set abstract bit for old class files for backward compatibility
    flags |= JVM_ACC_ABSTRACT;
  }

  verify_legal_class_modifiers(flags, CHECK);

  short bad_constant = class_bad_constant_seen();
  if (bad_constant != 0) {
    // Do not throw CFE until after the access_flags are checked because if
    // ACC_MODULE is set in the access flags, then NCDFE must be thrown, not CFE.
    classfile_parse_error("Unknown constant tag %u in class file %s", bad_constant, THREAD);
    return;
  }

  _access_flags.set_flags(flags);

  // This class and superclass
  _this_class_index = stream->get_u2_fast();
  check_property(
    valid_cp_range(_this_class_index, cp_size) &&
      cp->tag_at(_this_class_index).is_unresolved_klass(),
    "Invalid this class index %u in constant pool in class file %s",
    _this_class_index, CHECK);

  Symbol* const class_name_in_cp = cp->klass_name_at(_this_class_index);
  assert(class_name_in_cp != NULL, "class_name can't be null");

  // Don't need to check whether this class name is legal or not.
  // It has been checked when constant pool is parsed.
  // However, make sure it is not an array type.
  if (_need_verify) {
    guarantee_property(class_name_in_cp->char_at(0) != JVM_SIGNATURE_ARRAY,
                       "Bad class name in class file %s",
                       CHECK);
  }

#ifdef ASSERT
  // Basic sanity checks
  if (_is_hidden) {
    assert(_class_name != vmSymbols::unknown_class_name(), "hidden classes should have a special name");
  }
#endif

  // Update the _class_name as needed depending on whether this is a named, un-named, or hidden class.

  if (_is_hidden) {
    assert(_class_name != NULL, "Unexpected null _class_name");
#ifdef ASSERT
    if (_need_verify) {
      verify_legal_class_name(_class_name, CHECK);
    }
#endif

  } else {
    // Check if name in class file matches given name
    if (_class_name != class_name_in_cp) {
      if (_class_name != vmSymbols::unknown_class_name()) {
        ResourceMark rm(THREAD);
        Exceptions::fthrow(THREAD_AND_LOCATION,
                           vmSymbols::java_lang_NoClassDefFoundError(),
                           "%s (wrong name: %s)",
                           _class_name->as_C_string(),
                           class_name_in_cp->as_C_string()
                           );
        return;
      } else {
        // The class name was not known by the caller so we set it from
        // the value in the CP.
        update_class_name(class_name_in_cp);
      }
      // else nothing to do: the expected class name matches what is in the CP
    }
  }

  // Verification prevents us from creating names with dots in them, this
  // asserts that that's the case.
  assert(is_internal_format(_class_name), "external class name format used internally");

  if (!is_internal()) {
    LogTarget(Debug, class, preorder) lt;
    if (lt.is_enabled()){
      ResourceMark rm(THREAD);
      LogStream ls(lt);
      ls.print("%s", _class_name->as_klass_external_name());
      if (stream->source() != NULL) {
        ls.print(" source: %s", stream->source());
      }
      ls.cr();
    }
  }

  // SUPERKLASS
  _super_class_index = stream->get_u2_fast();
  _super_klass = parse_super_class(cp,
                                   _super_class_index,
                                   _need_verify,
                                   CHECK);

  // Interfaces
  _itfs_len = stream->get_u2_fast();
  parse_interfaces(stream,
                   _itfs_len,
                   cp,
                   &_has_nonstatic_concrete_methods,
                   CHECK);

  assert(_local_interfaces != NULL, "invariant");

  // Fields (offsets are filled in later)
  _fac = new FieldAllocationCount();
  parse_fields(stream,
               _access_flags.is_interface(),
               _fac,
               cp,
               cp_size,
               &_java_fields_count,
               CHECK);

  assert(_fields != NULL, "invariant");

  // Methods
  parse_methods(stream,
                _access_flags.is_interface(),
                &_has_localvariable_table,
                &_has_final_method,
                &_declares_nonstatic_concrete_methods,
                CHECK);

  assert(_methods != NULL, "invariant");

  if (_declares_nonstatic_concrete_methods) {
    _has_nonstatic_concrete_methods = true;
  }

  // Additional attributes/annotations
  _parsed_annotations = new ClassAnnotationCollector();
  parse_classfile_attributes(stream, cp, _parsed_annotations, CHECK);

  assert(_inner_classes != NULL, "invariant");

  // Finalize the Annotations metadata object,
  // now that all annotation arrays have been created.
  create_combined_annotations(CHECK);

  // Make sure this is the end of class file stream
  guarantee_property(stream->at_eos(),
                     "Extra bytes at the end of class file %s",
                     CHECK);

  // all bytes in stream read and parsed
}
Но что за u4, что за stream?
u4 - тайпдеф, который означает unsigned 4 byte.Либо же просто unsigned int.
В джаве их 4 вида :
u1, u2, u4, u8 (unsigned byte(C++ char), short, int, long long)
* Довольно смешным фактом является то, что джава сама по себе отрицает значения которые записываются меньше чем в 4 байта.
* Минимальный размер элемента стека - 4 байта.(локали также).wide значения(long,double) вписываются в 2 элемента.
* И в целом byte,boolean,short,char (1-2 byte) реализованы лишь инструкциями взаимодействия с массивами.(ex : SALOAD, SASTORE)(за обработку байтов и булеанок они вообще используют одну инструкцию - BASTORE/BALOAD.Ущемление прав, никак иначе)
stream - обычный класс-ридер потока байтов.имеет на борту методы аля read_u1/2/4/8, и текущий индекс чтения.
Почему все засрано какими то постоянными guarantee_more, assert, check_property?
Если вам тоже интересен этот вопрос как и мне, то ответ хранится в документации openjdk, а именно кодстайлу.
В двух словах, их главным принципом является лучше все проверить, чем поймать рандомный краш вмки.
Для нашей джавы конечно же желательно отказаться от такого дебага(бтв, assert останутся лишь в debug билде).По хорошему их всех удалить, либо покрыть xorstr.
Окей, теперь давайте рассинхронизируем наши задачи, а именно будем параллельно делать парсер, изменять значения, также изменяя их в джаве.
Создадим новый проект в IDE.
Что нам понадобится? Такой же ридер, врайтер.За основу возьмем DataInputStream, DataOutputStream.Он имеет на борту методы read(1 byte), readShort(2 bytes), readInt(4 bytes), readLong(8 bytes), аналогично с write.
В целом такой класс можно и не делать, а юзать DataInputStream, DataOutputStream, но тогда вы теряете возможность добавлять какой либо алгоритм при записи байтов.(ex : xor++ на каждый write изуродует stream при парсинге)
Я сделал для себя 2 класса :
ClassFileStream, ClassFileWriter
Java:
import java.io.*;

public class ClassFileStream {
    private final DataInputStream dataInputStream;

    public ClassFileStream(byte[] bytes) {
        this.dataInputStream = new DataInputStream(new ByteArrayInputStream(bytes));
    }

    /*
    в джава все числовые типы данных signed, unsigned отсутствует.
    поэтому лучше обернуть их в int, если понадобится дебаг
     */
    public int readByte() throws IOException {
        return dataInputStream.read();
    }

    public int readShort() throws IOException {
        return dataInputStream.readShort();
    }

    public int readInt() throws IOException {
        return dataInputStream.readInt();
    }

    public long readLong() throws IOException {
        return dataInputStream.readLong();
    }

    /*
    я не собираюсь переписывать вам полностью ClassFileFormat
    этот метод позволит мне дописать остальной стрим в output
     */
    public void writeToStream(OutputStream outputStream) throws IOException {
        int r;
        while ((r = dataInputStream.read()) >= 0) {
            outputStream.write(r);
        }
    }
}
Java:
import java.io.*;

public class ClassFileWriter {
    private final ByteArrayOutputStream byteArrayOutputStream;
    private final DataOutputStream dataOutputStream;

    public ClassFileWriter() {
        this.dataOutputStream = new DataOutputStream(byteArrayOutputStream = new ByteArrayOutputStream());
    }

    /*
    в джава все числовые типы данных signed, unsigned отсутствует.
    поэтому лучше обернуть их в int, если понадобится дебаг
     */
    public void writeByte(int value) throws IOException {
        dataOutputStream.write(value);
    }

    public void writeShort(int value) throws IOException {
        dataOutputStream.writeShort(value);
    }

    public void writeInt(int value) throws IOException {
        dataOutputStream.writeInt(value);
    }

    public void writeLong(long value) throws IOException {
        dataOutputStream.writeLong(value);
    }

    public ByteArrayOutputStream getByteArrayOutputStream() {
        return this.byteArrayOutputStream;
    }
}
Подготовим парсер jar прямо в main методе :
Java:
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.ProtectionDomain;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class Main {
    private static final byte BYTE_CA = (byte) 0xCA;
    private static final byte BYTE_FE = (byte) 0xFE;
    private static final byte BYTE_BA = (byte) 0xBA;
    private static final byte BYTE_BE = (byte) 0xBE;

    public static void main(String[] args) {
        if (args.length < 2) {
            ProtectionDomain protectionDomain = Main.class.getProtectionDomain();
            throw new IllegalArgumentException(String.format("Пример использования java -jar %s in.jar out.jar", protectionDomain.getCodeSource().getLocation().getFile()));
        }
        try {
            String inputJar = args[0];
            String outputJar = args[1];
            ZipFile inZip = new ZipFile(inputJar);
            Enumeration<? extends ZipEntry> zipEntries = inZip.entries();
            /*
            Создадим мапу path/byte[] для удобного использования ZipFile
             */
            Map<String, byte[]> map = new HashMap<>();
            while (zipEntries.hasMoreElements()) {
                ZipEntry zipEntry = zipEntries.nextElement();
                InputStream is = inZip.getInputStream(zipEntry);
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                int r;
                while ((r = is.read()) >= 0) {
                    byteArrayOutputStream.write(r);
                }
                map.put(zipEntry.getName(), byteArrayOutputStream.toByteArray());
            }
            ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(outputJar));
            for (Map.Entry<String, byte[]> entry : map.entrySet()) {
                byte[] bytes = entry.getValue();
                if (bytes.length > 3 && bytes[0] == BYTE_CA && bytes[1] == BYTE_FE && bytes[2] == BYTE_BA && bytes[3] == BYTE_BE) {
                    ClassFileWriter classFileWriter = new ClassFileWriter();
                    ClassFile classFile = new ClassFile(new ClassFileStream(bytes), classFileWriter);
                    classFile.parseStream();
                    bytes = classFileWriter.getByteArrayOutputStream().toByteArray();
                }
                zipOutputStream.putNextEntry(new ZipEntry(entry.getKey()));
                zipOutputStream.write(bytes);
                zipOutputStream.closeEntry();
            }
            zipOutputStream.close();
            inZip.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Ну и теперь самое страшное...
Создадим класс ClassFile.
Java:
import java.io.IOException;

public class ClassFile {
    private ClassFileStream classFileStream;
    private ClassFileWriter classFileWriter;

    public ClassFile(ClassFileStream classFileStream, ClassFileWriter classFileWriter) {
        this.classFileStream = classFileStream;
        this.classFileWriter = classFileWriter;
    }

    public void parseStream() throws IOException {
        /*
        вот тут расположим наш код
         */
     
        /*
        запись всего "not dirty" стрима чо бы потом все воркало
         */
        classFileStream.writeToStream(classFileWriter.getByteArrayOutputStream());
    }
}
Поехали.
Вспоминаем что я писал выше.
первые четыре байта -> CAFEBABE.Проверим?
Java:
    public void parseStream() throws IOException {
        int magic = classFileStream.readInt();
        System.out.println(String.format("0x%s", Integer.toHexString(magic).toUpperCase()));
        /*
        запись всего "not dirty" стрима чо бы потом все воркало
         */
        classFileStream.writeToStream(classFileWriter.getByteArrayOutputStream());
    }
Код:
0xCAFEBABE

Process finished with exit code 0
Круть!
Теперь перезапишем это, но в другом виде.Это будет маркером в парсере, что класс зашифрован.
Java:
    private static final int CUSTOM_MAGIC_VALUE = 0xDEADBEEF;
    public void parseStream() throws IOException {
        int magic = classFileStream.readInt();
        classFileWriter.writeInt(CUSTOM_MAGIC_VALUE);
        /*
        запись всего "not dirty" стрима чо бы потом все воркало
         */
        classFileStream.writeToStream(classFileWriter.getByteArrayOutputStream());
    }
Откроем hexed.it и посмотрим в out.jar -> Main.class
Пожалуйста, авторизуйтесь для просмотра ссылки.

Продолжение написания "защиты" во втором туторе.Там мы перепишем parse_constant_pool_entries, соберем и протестим данное дерьмо.

Nursultan Dump :
Пожалуйста, авторизуйтесь для просмотра ссылки.
А кряк нурлсултана или акриена сделаеш? типо как их крякать
 
Пользователь
Статус
Оффлайн
Регистрация
20 Дек 2022
Сообщения
195
Реакции[?]
65
Поинты[?]
36K
гайд +- нормальный, но ты лучше скажи ебучим пастерам что им от части то что написано в этом гайде не поможет, тк любой человек сможет аттачнутся к их жвмке без проблем и переписать даже обоссаный паблик дампер с UC под кастомную джаву(ну если ты конечно не расскажешь им что надо пропатчить jni методы))))))
 
Забаненный
Статус
Оффлайн
Регистрация
19 Май 2020
Сообщения
98
Реакции[?]
13
Поинты[?]
6K
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
гайд +- нормальный, но ты лучше скажи ебучим пастерам что им от части то что написано в этом гайде не поможет, тк любой человек сможет аттачнутся к их жвмке без проблем и переписать даже обоссаный паблик дампер с UC под кастомную джаву(ну если ты конечно не расскажешь им что надо пропатчить jni методы))))))
не хочешь работать в джессе хаке
 
Начинающий
Статус
Оффлайн
Регистрация
31 Мар 2022
Сообщения
29
Реакции[?]
0
Поинты[?]
0
Всем привет!

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

Данный метод защиты используют такие читы как Akrien, люди(?нет, не люди.обычные пастеры чей уровень не заходит даже за видосы хаудихо.самые кринжовые клоуны) которые сидят на протекшене от аргентоза.

* Данный метод защиты - полный калл.Он спасает лишь потому, что майнкрафт комьюнити - абсолютно примитивные Java школокодеры.Иметь какие либо большие знания для создания данной защиты не надо.
* Данные защиты абсолютно идентичны.Отличаются лишь версии OpenJDK, pad`ы(= измененные оффсеты) которые добавляют создатели данных "грандиозных" защит.

Ну что ж, теперь к описанию принципа работы данной защиты.

У Java класса есть формат. (
Пожалуйста, авторизуйтесь для просмотра ссылки.
)

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

Начнем со сборки OpenJDK.

Переходим на
Пожалуйста, авторизуйтесь для просмотра ссылки.
.

У них есть building.md/html, но я все же напишу краткий гайд по сборке.

Все что нам нужно :

1) Cygwin (
Пожалуйста, авторизуйтесь для просмотра ссылки.
)
2) BootJDK (да, для сборки JDK, нам понадобится другая JDK) (
Пожалуйста, авторизуйтесь для просмотра ссылки.
)

Итак, мы получили setup-x86_64.exe.
В этой же директории открываем cmd, пишем
setup-x86_64.exe -q -P autoconf -P make -P unzip -P zip
Теперь достанем исходники openjdk на диск C:/
Пожалуйста, авторизуйтесь для просмотра ссылки.
Теперь создадим папку для нашей bootjdk в данной директории и разархивируем туда то что мы качнули чуть выше.
Пожалуйста, авторизуйтесь для просмотра ссылки.
А теперь к сборке.
В cygwin пишем :
bash configure --with-boot-jdk=bootjdk
make images
Это будет очень долго в первый раз.
Пока у меня идет сборка, расскажу вам что мы будем делать.
Формат класса состоит из :
CAFEBABE (первые 4 байта, "magic value" java класс файла).
Является отсылкой.(
Пожалуйста, авторизуйтесь для просмотра ссылки.
)
Minor версия, Major версия класса. (2/2 байта)
Дальше идет парсинг содержимого класса.
1) констант пул.
2) супер класс, интерфейсы.
3) поля класса.
4) методы класса.
5) аттрибуты класса.
Из описанного выше вам может стать не совсем понятным лишь константпул.
Константпул - основа всего класса.В нем содержится самая основная информация - строки, int/float/long/double значения.
Основная задача идет на JVM_CONSTANT_Utf8
Например : спарсив JVM_CONSTANT_Utf8, он может спарсить и JVM_CONSTANT_Class :
// const u2 name_index = cfs->get_u2_fast();
// cp->klass_index_at_put(index, name_index);
Также и с остальными константами.Я надеюсь принцип понятен.
* Байткод методов также ссылается на индексы в константпуле и сам по себе какого либо значения не несет.
* Все что парсится в классе после константпула - лишь его индексы и размер того, что нужно спарсить.
* Для более подробного анализа вы можете скачать DirtyJOE
Все.java сбилжена.Но как нам что либо редактировать в ней?Лезть в notepad?Давайте это исправим.
Пожалуйста, авторизуйтесь для просмотра ссылки.
пишем
make hotspot-ide-project
и ждем.
Все!
C:\openjdk\build\windows-x86_64-server-release\ide\hotspot-visualstudio
Что ж, половина задачи уже выполнена.
Нам нужен classFileParser.cpp.В нем и происходит основная магия - парсинг класса из байтов.
Заходим в метод parse_stream и видим :
C++:
void ClassFileParser::parse_stream(const ClassFileStream* const stream,
                                   TRAPS) {

  assert(stream != NULL, "invariant");
  assert(_class_name != NULL, "invariant");

  // BEGIN STREAM PARSING
  stream->guarantee_more(8, CHECK);  // magic, major, minor
  // Magic value
  const u4 magic = stream->get_u4_fast();
  guarantee_property(magic == JAVA_CLASSFILE_MAGIC,
                     "Incompatible magic value %u in class file %s",
                     magic, CHECK);

  // Version numbers
  _minor_version = stream->get_u2_fast();
  _major_version = stream->get_u2_fast();

  // Check version numbers - we check this even with verifier off
  verify_class_version(_major_version, _minor_version, _class_name, CHECK);

  stream->guarantee_more(3, CHECK); // length, first cp tag
  u2 cp_size = stream->get_u2_fast();

  guarantee_property(
    cp_size >= 1, "Illegal constant pool size %u in class file %s",
    cp_size, CHECK);

  _orig_cp_size = cp_size;
  if (is_hidden()) { // Add a slot for hidden class name.
    cp_size++;
  }

  _cp = ConstantPool::allocate(_loader_data,
                               cp_size,
                               CHECK);

  ConstantPool* const cp = _cp;

  parse_constant_pool(stream, cp, _orig_cp_size, CHECK);

  assert(cp_size == (const u2)cp->length(), "invariant");

  // ACCESS FLAGS
  stream->guarantee_more(8, CHECK);  // flags, this_class, super_class, infs_len

  // Access flags
  jint flags;
  // JVM_ACC_MODULE is defined in JDK-9 and later.
  if (_major_version >= JAVA_9_VERSION) {
    flags = stream->get_u2_fast() & (JVM_RECOGNIZED_CLASS_MODIFIERS | JVM_ACC_MODULE);
  } else {
    flags = stream->get_u2_fast() & JVM_RECOGNIZED_CLASS_MODIFIERS;
  }

  if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) {
    // Set abstract bit for old class files for backward compatibility
    flags |= JVM_ACC_ABSTRACT;
  }

  verify_legal_class_modifiers(flags, CHECK);

  short bad_constant = class_bad_constant_seen();
  if (bad_constant != 0) {
    // Do not throw CFE until after the access_flags are checked because if
    // ACC_MODULE is set in the access flags, then NCDFE must be thrown, not CFE.
    classfile_parse_error("Unknown constant tag %u in class file %s", bad_constant, THREAD);
    return;
  }

  _access_flags.set_flags(flags);

  // This class and superclass
  _this_class_index = stream->get_u2_fast();
  check_property(
    valid_cp_range(_this_class_index, cp_size) &&
      cp->tag_at(_this_class_index).is_unresolved_klass(),
    "Invalid this class index %u in constant pool in class file %s",
    _this_class_index, CHECK);

  Symbol* const class_name_in_cp = cp->klass_name_at(_this_class_index);
  assert(class_name_in_cp != NULL, "class_name can't be null");

  // Don't need to check whether this class name is legal or not.
  // It has been checked when constant pool is parsed.
  // However, make sure it is not an array type.
  if (_need_verify) {
    guarantee_property(class_name_in_cp->char_at(0) != JVM_SIGNATURE_ARRAY,
                       "Bad class name in class file %s",
                       CHECK);
  }

#ifdef ASSERT
  // Basic sanity checks
  if (_is_hidden) {
    assert(_class_name != vmSymbols::unknown_class_name(), "hidden classes should have a special name");
  }
#endif

  // Update the _class_name as needed depending on whether this is a named, un-named, or hidden class.

  if (_is_hidden) {
    assert(_class_name != NULL, "Unexpected null _class_name");
#ifdef ASSERT
    if (_need_verify) {
      verify_legal_class_name(_class_name, CHECK);
    }
#endif

  } else {
    // Check if name in class file matches given name
    if (_class_name != class_name_in_cp) {
      if (_class_name != vmSymbols::unknown_class_name()) {
        ResourceMark rm(THREAD);
        Exceptions::fthrow(THREAD_AND_LOCATION,
                           vmSymbols::java_lang_NoClassDefFoundError(),
                           "%s (wrong name: %s)",
                           _class_name->as_C_string(),
                           class_name_in_cp->as_C_string()
                           );
        return;
      } else {
        // The class name was not known by the caller so we set it from
        // the value in the CP.
        update_class_name(class_name_in_cp);
      }
      // else nothing to do: the expected class name matches what is in the CP
    }
  }

  // Verification prevents us from creating names with dots in them, this
  // asserts that that's the case.
  assert(is_internal_format(_class_name), "external class name format used internally");

  if (!is_internal()) {
    LogTarget(Debug, class, preorder) lt;
    if (lt.is_enabled()){
      ResourceMark rm(THREAD);
      LogStream ls(lt);
      ls.print("%s", _class_name->as_klass_external_name());
      if (stream->source() != NULL) {
        ls.print(" source: %s", stream->source());
      }
      ls.cr();
    }
  }

  // SUPERKLASS
  _super_class_index = stream->get_u2_fast();
  _super_klass = parse_super_class(cp,
                                   _super_class_index,
                                   _need_verify,
                                   CHECK);

  // Interfaces
  _itfs_len = stream->get_u2_fast();
  parse_interfaces(stream,
                   _itfs_len,
                   cp,
                   &_has_nonstatic_concrete_methods,
                   CHECK);

  assert(_local_interfaces != NULL, "invariant");

  // Fields (offsets are filled in later)
  _fac = new FieldAllocationCount();
  parse_fields(stream,
               _access_flags.is_interface(),
               _fac,
               cp,
               cp_size,
               &_java_fields_count,
               CHECK);

  assert(_fields != NULL, "invariant");

  // Methods
  parse_methods(stream,
                _access_flags.is_interface(),
                &_has_localvariable_table,
                &_has_final_method,
                &_declares_nonstatic_concrete_methods,
                CHECK);

  assert(_methods != NULL, "invariant");

  if (_declares_nonstatic_concrete_methods) {
    _has_nonstatic_concrete_methods = true;
  }

  // Additional attributes/annotations
  _parsed_annotations = new ClassAnnotationCollector();
  parse_classfile_attributes(stream, cp, _parsed_annotations, CHECK);

  assert(_inner_classes != NULL, "invariant");

  // Finalize the Annotations metadata object,
  // now that all annotation arrays have been created.
  create_combined_annotations(CHECK);

  // Make sure this is the end of class file stream
  guarantee_property(stream->at_eos(),
                     "Extra bytes at the end of class file %s",
                     CHECK);

  // all bytes in stream read and parsed
}
Но что за u4, что за stream?
u4 - тайпдеф, который означает unsigned 4 byte.Либо же просто unsigned int.
В джаве их 4 вида :
u1, u2, u4, u8 (unsigned byte(C++ char), short, int, long long)
* Довольно смешным фактом является то, что джава сама по себе отрицает значения которые записываются меньше чем в 4 байта.
* Минимальный размер элемента стека - 4 байта.(локали также).wide значения(long,double) вписываются в 2 элемента.
* И в целом byte,boolean,short,char (1-2 byte) реализованы лишь инструкциями взаимодействия с массивами.(ex : SALOAD, SASTORE)(за обработку байтов и булеанок они вообще используют одну инструкцию - BASTORE/BALOAD.Ущемление прав, никак иначе)
stream - обычный класс-ридер потока байтов.имеет на борту методы аля read_u1/2/4/8, и текущий индекс чтения.
Почему все засрано какими то постоянными guarantee_more, assert, check_property?
Если вам тоже интересен этот вопрос как и мне, то ответ хранится в документации openjdk, а именно кодстайлу.
В двух словах, их главным принципом является лучше все проверить, чем поймать рандомный краш вмки.
Для нашей джавы конечно же желательно отказаться от такого дебага(бтв, assert останутся лишь в debug билде).По хорошему их всех удалить, либо покрыть xorstr.
Окей, теперь давайте рассинхронизируем наши задачи, а именно будем параллельно делать парсер, изменять значения, также изменяя их в джаве.
Создадим новый проект в IDE.
Что нам понадобится? Такой же ридер, врайтер.За основу возьмем DataInputStream, DataOutputStream.Он имеет на борту методы read(1 byte), readShort(2 bytes), readInt(4 bytes), readLong(8 bytes), аналогично с write.
В целом такой класс можно и не делать, а юзать DataInputStream, DataOutputStream, но тогда вы теряете возможность добавлять какой либо алгоритм при записи байтов.(ex : xor++ на каждый write изуродует stream при парсинге)
Я сделал для себя 2 класса :
ClassFileStream, ClassFileWriter
Java:
import java.io.*;

public class ClassFileStream {
    private final DataInputStream dataInputStream;

    public ClassFileStream(byte[] bytes) {
        this.dataInputStream = new DataInputStream(new ByteArrayInputStream(bytes));
    }

    /*
    в джава все числовые типы данных signed, unsigned отсутствует.
    поэтому лучше обернуть их в int, если понадобится дебаг
     */
    public int readByte() throws IOException {
        return dataInputStream.read();
    }

    public int readShort() throws IOException {
        return dataInputStream.readShort();
    }

    public int readInt() throws IOException {
        return dataInputStream.readInt();
    }

    public long readLong() throws IOException {
        return dataInputStream.readLong();
    }

    /*
    я не собираюсь переписывать вам полностью ClassFileFormat
    этот метод позволит мне дописать остальной стрим в output
     */
    public void writeToStream(OutputStream outputStream) throws IOException {
        int r;
        while ((r = dataInputStream.read()) >= 0) {
            outputStream.write(r);
        }
    }
}
Java:
import java.io.*;

public class ClassFileWriter {
    private final ByteArrayOutputStream byteArrayOutputStream;
    private final DataOutputStream dataOutputStream;

    public ClassFileWriter() {
        this.dataOutputStream = new DataOutputStream(byteArrayOutputStream = new ByteArrayOutputStream());
    }

    /*
    в джава все числовые типы данных signed, unsigned отсутствует.
    поэтому лучше обернуть их в int, если понадобится дебаг
     */
    public void writeByte(int value) throws IOException {
        dataOutputStream.write(value);
    }

    public void writeShort(int value) throws IOException {
        dataOutputStream.writeShort(value);
    }

    public void writeInt(int value) throws IOException {
        dataOutputStream.writeInt(value);
    }

    public void writeLong(long value) throws IOException {
        dataOutputStream.writeLong(value);
    }

    public ByteArrayOutputStream getByteArrayOutputStream() {
        return this.byteArrayOutputStream;
    }
}
Подготовим парсер jar прямо в main методе :
Java:
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.ProtectionDomain;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class Main {
    private static final byte BYTE_CA = (byte) 0xCA;
    private static final byte BYTE_FE = (byte) 0xFE;
    private static final byte BYTE_BA = (byte) 0xBA;
    private static final byte BYTE_BE = (byte) 0xBE;

    public static void main(String[] args) {
        if (args.length < 2) {
            ProtectionDomain protectionDomain = Main.class.getProtectionDomain();
            throw new IllegalArgumentException(String.format("Пример использования java -jar %s in.jar out.jar", protectionDomain.getCodeSource().getLocation().getFile()));
        }
        try {
            String inputJar = args[0];
            String outputJar = args[1];
            ZipFile inZip = new ZipFile(inputJar);
            Enumeration<? extends ZipEntry> zipEntries = inZip.entries();
            /*
            Создадим мапу path/byte[] для удобного использования ZipFile
             */
            Map<String, byte[]> map = new HashMap<>();
            while (zipEntries.hasMoreElements()) {
                ZipEntry zipEntry = zipEntries.nextElement();
                InputStream is = inZip.getInputStream(zipEntry);
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                int r;
                while ((r = is.read()) >= 0) {
                    byteArrayOutputStream.write(r);
                }
                map.put(zipEntry.getName(), byteArrayOutputStream.toByteArray());
            }
            ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(outputJar));
            for (Map.Entry<String, byte[]> entry : map.entrySet()) {
                byte[] bytes = entry.getValue();
                if (bytes.length > 3 && bytes[0] == BYTE_CA && bytes[1] == BYTE_FE && bytes[2] == BYTE_BA && bytes[3] == BYTE_BE) {
                    ClassFileWriter classFileWriter = new ClassFileWriter();
                    ClassFile classFile = new ClassFile(new ClassFileStream(bytes), classFileWriter);
                    classFile.parseStream();
                    bytes = classFileWriter.getByteArrayOutputStream().toByteArray();
                }
                zipOutputStream.putNextEntry(new ZipEntry(entry.getKey()));
                zipOutputStream.write(bytes);
                zipOutputStream.closeEntry();
            }
            zipOutputStream.close();
            inZip.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Ну и теперь самое страшное...
Создадим класс ClassFile.
Java:
import java.io.IOException;

public class ClassFile {
    private ClassFileStream classFileStream;
    private ClassFileWriter classFileWriter;

    public ClassFile(ClassFileStream classFileStream, ClassFileWriter classFileWriter) {
        this.classFileStream = classFileStream;
        this.classFileWriter = classFileWriter;
    }

    public void parseStream() throws IOException {
        /*
        вот тут расположим наш код
         */
     
        /*
        запись всего "not dirty" стрима чо бы потом все воркало
         */
        classFileStream.writeToStream(classFileWriter.getByteArrayOutputStream());
    }
}
Поехали.
Вспоминаем что я писал выше.
первые четыре байта -> CAFEBABE.Проверим?
Java:
    public void parseStream() throws IOException {
        int magic = classFileStream.readInt();
        System.out.println(String.format("0x%s", Integer.toHexString(magic).toUpperCase()));
        /*
        запись всего "not dirty" стрима чо бы потом все воркало
         */
        classFileStream.writeToStream(classFileWriter.getByteArrayOutputStream());
    }
Код:
0xCAFEBABE

Process finished with exit code 0
Круть!
Теперь перезапишем это, но в другом виде.Это будет маркером в парсере, что класс зашифрован.
Java:
    private static final int CUSTOM_MAGIC_VALUE = 0xDEADBEEF;
    public void parseStream() throws IOException {
        int magic = classFileStream.readInt();
        classFileWriter.writeInt(CUSTOM_MAGIC_VALUE);
        /*
        запись всего "not dirty" стрима чо бы потом все воркало
         */
        classFileStream.writeToStream(classFileWriter.getByteArrayOutputStream());
    }
Откроем hexed.it и посмотрим в out.jar -> Main.class
Пожалуйста, авторизуйтесь для просмотра ссылки.

Продолжение написания "защиты" во втором туторе.Там мы перепишем parse_constant_pool_entries, соберем и протестим данное дерьмо.

Nursultan Dump :
Пожалуйста, авторизуйтесь для просмотра ссылки.
написано понятно и просто, читать интересно да и сам гайд интересный, но ты хотяб скажи ренеймерам рича что их лучшие клиенты это не спасет
 
эксперт в майнкрафт апи
Read Only
Статус
Оффлайн
Регистрация
25 Янв 2023
Сообщения
676
Реакции[?]
284
Поинты[?]
22K
гайд +- нормальный, но ты лучше скажи ебучим пастерам что им от части то что написано в этом гайде не поможет, тк любой человек сможет аттачнутся к их жвмке без проблем и переписать даже обоссаный паблик дампер с UC под кастомную джаву(ну если ты конечно не расскажешь им что надо пропатчить jni методы))))))
Назвать всех пастерами и не понять в чем смысл защиты = база.

Ну, "не пастер" ты наш, отвечай тогда на вопросик :
Каким образом тебе поможет какой либо эвент/хук(я не знаю на чем основан UC dumper) расположенный до parse_stream(да в целом похуй где, хоть хук parse_stream), если байтстрим изначально изуродован и переписан механизм парса байтов из него.
 
Пользователь
Статус
Оффлайн
Регистрация
20 Дек 2022
Сообщения
195
Реакции[?]
65
Поинты[?]
36K
Назвать всех пастерами и не понять в чем смысл защиты = база.

Ну, "не пастер" ты наш, отвечай тогда на вопросик :
Каким образом тебе поможет какой либо эвент/хук(я не знаю на чем основан UC dumper) расположенный до parse_stream(да в целом похуй где, хоть хук parse_stream), если байтстрим изначально изуродован и переписан механизм парса байтов из него.
мужик ты видимо не понял суть моего сообщения)
я тебе написал про то что банально хук на DefineClass повесят и все классы любезно выдампят
и не пиши мне хуйню по типу то что ты можешь пропатчить в жмвке этот метод или вырезать к хуям GetEnv
и если че я написал только про ДАМП КЛАССОВ а то что у них будут сломанные опкоды или еще какая нить хуйня мне похуй!
 
Начинающий
Статус
Оффлайн
Регистрация
16 Апр 2022
Сообщения
7
Реакции[?]
0
Поинты[?]
0
мужик ты видимо не понял суть моего сообщения)
я тебе написал про то что банально хук на DefineClass повесят и все классы любезно выдампят
и не пиши мне хуйню по типу то что ты можешь пропатчить в жмвке этот метод или вырезать к хуям GetEnv
и если че я написал только про ДАМП КЛАССОВ а то что у них будут сломанные опкоды или еще какая нить хуйня мне похуй!
тя чет расплавило братишка
 
эксперт в майнкрафт апи
Read Only
Статус
Оффлайн
Регистрация
25 Янв 2023
Сообщения
676
Реакции[?]
284
Поинты[?]
22K
мужик ты видимо не понял суть моего сообщения)
я тебе написал про то что банально хук на DefineClass повесят и все классы любезно выдампят
и не пиши мне хуйню по типу то что ты можешь пропатчить в жмвке этот метод или вырезать к хуям GetEnv
и если че я написал только про ДАМП КЛАССОВ а то что у них будут сломанные опкоды или еще какая нить хуйня мне похуй!
Нахуя ты отвечаешь если твои познания равны 0?
Хули толку от того что ты повесишь хуки на дефайн класс?
Механизм обработки спаршенных байтов другой, ты получишь невалидные классы.
Что ты несешь вообще?
Про последнюю строку которую ты выдал я вообще молчу.Какие нахуй сломанные опкоды?
Я хуею
 
Пользователь
Статус
Оффлайн
Регистрация
20 Дек 2022
Сообщения
195
Реакции[?]
65
Поинты[?]
36K
эксперт в майнкрафт апи
Read Only
Статус
Оффлайн
Регистрация
25 Янв 2023
Сообщения
676
Реакции[?]
284
Поинты[?]
22K
да мне похуй что ты мне пишешь, ты видимо в глаза долбишься или я не пойму)
Посмотреть вложение 236470
:kappa:
а теперь ответь на вопрос:
нахуя тебе сломанные классы которые запускаются лишь по правилам пересобранной джавы?
к чему твои сообщения выше вообще?))
таки я не понял причем тут вообще байткод методов?))))
 
Пользователь
Статус
Оффлайн
Регистрация
20 Дек 2022
Сообщения
195
Реакции[?]
65
Поинты[?]
36K
:kappa:
а теперь ответь на вопрос:
нахуя тебе сломанные классы которые запускаются лишь по правилам пересобранной джавы?
к чему твои сообщения выше вообще?))
таки я не понял причем тут вообще байткод методов?))))
я просто написал что каждый школьник с минимальными знаниями может сдампить классы через тот же DefineClass
я не пойму тебя, ты сидишь с пеной у рта мне что то пытаешься доказать бля)
 
review
Начинающий
Статус
Оффлайн
Регистрация
16 Июл 2021
Сообщения
4
Реакции[?]
1
Поинты[?]
0
я просто написал что каждый школьник с минимальными знаниями может сдампить классы через тот же DefineClass
я не пойму тебя, ты сидишь с пеной у рта мне что то пытаешься доказать бля)
А ты не пиши просто, по всей видимости, ты и есть тот школьник =)
 
эксперт в майнкрафт апи
Read Only
Статус
Оффлайн
Регистрация
25 Янв 2023
Сообщения
676
Реакции[?]
284
Поинты[?]
22K
я просто написал что каждый школьник с минимальными знаниями может сдампить классы через тот же DefineClass
я не пойму тебя, ты сидишь с пеной у рта мне что то пытаешься доказать бля)
Может.Дальше что?Как это относится к данной защите?
 
Сверху Снизу