- Баллы
- 1
Тут на форуме буржуев, обсуждали "иньекции" своего кода и я решил покопать в разные стороны (накопал себе визуал студию и выучил мануал на сишарп прицепом, но тема не об этом(в сишарп веселее)) и ничего принципиального нового не накопал.
Но выяснил, что в русском сегменте интернета таких "знаний" нет, что и буду исправлять.
Я не буду повторяться многократно, ссылка на класс ниже, по тексту будет только сигнатура метода или указатель на строку:
https://github.com/RawCode/UBT/blob/master/src/rc/ubt/impl/UnsafeImpl.java
Все объекты (еще есть примитивы) создаются и хранятся в куче, даже если единственный хранитель указателя находится на стеке, сам объект всё равно сидит в куче.
Объекты сидящие в куче имеют внутренний недоступный для чтения набор метаданных, это можно выяснить или с сорцов или просто поменяв класс объекта и попытавшись прочитать память других объектов (не прокатит).
В ряде случае подменить сам объект проблематично, например чтобы подменить объект висящий в стеке надо или писать кастом нативку или колдовать с памятью потока или рассчитывать труайди объекта и менять метаданные менеджера кучи.
Так вот, всё это весело но не надо, от того и потому что класс объекта хранится в специальном поле -1 и может быть прочитан или изменён через unsafe.
Если менять классы который объявлены final - придётся или прописывать им magicaccessor или цепочку наследования вручную иначе JVM заметить несоответствие сигнатур в констант пуле и приклеит бороду вам и вашему коду. Но речь не об этом.
Структура объекта:
-8 недоступный для чтения заголовок в два слова
0 сведения о блокировках
4 верхний регистр указателя класса или указатель класса (64\32)
8 нижный регистр указателя класса или данные класса (64\32)
верхний регистр указателя класса всегда равен нулю если применяется compressed OOP
на основании вышеуказанных достаточно простых и понятных свойств объекта написан следующий код лишенный документации по умолчанию:
Расширяем целевой класс, в нашем случае:
public class DedicatedPlayerListImpl extends DedicatedPlayerList
если вы планируете перехватывать класс объявленный final расширять придётся в рантайме, остальное так-же (не предмет данной статьи)
если вы планируете добавить новые поля - плохая новость, менять размер существующих объектов (как и существующих массивов) НЕЛЬЗЯ, вообще нельзя, даже через свои собственные нативки вам потребуется делать новый объект и регенерировать ссылки на него.
если вы добавите новые поля, ждите проблем.
данные хранить в дополнительном классе где всё статик и паблик, так можно.
получаем указатель на целевой класс:
Object o = MinecraftServer.getServer().getPlayerList();
не важно, что и как, только убедитесь чтобы метод не возращал копию или коллекцию иначе будете долго страдать от непонятных проблем.
static методы перехватываются также но целью модицификации памяти будет прототип класса, там 100500 всяких секретных полей и чтобы сделать грамотно придётся читать сорцы JVM, это кстати всё равно потребуется для final методов, но не сейчас.
как уже сказано выше, работа с классами КУДА сложнее чем работа с инстансами объектов, начиная от другого порядка полей в зависимости от версии JVM и заканчивая иным значением полей, который выглядят внешне точно также.
поэтому доставать искомый указатель на класс мы будем из инстанса нужного нам объекта, например так:
x = UnsafeImpl.unsafe.allocateInstance(DedicatedPlayerListImpl.class);
targetref = wpad ? UnsafeImpl.unsafe.getInt(x, 8l) : UnsafeImpl.unsafe.getInt(x, 4l)
ставится в целевой объект точно также, случай с nref не рассматривается, вообще возможно что ктото даёт яве больше 32 гигабайт памяти, но это явно не ведро.
Вот собственно и всё, мы тупо берём и переписываем указатель на класс в инстансе тем самым меняя класс объекта и все методы, оставляя ссылки нетронутыми.
Но выяснил, что в русском сегменте интернета таких "знаний" нет, что и буду исправлять.
Я не буду повторяться многократно, ссылка на класс ниже, по тексту будет только сигнатура метода или указатель на строку:
https://github.com/RawCode/UBT/blob/master/src/rc/ubt/impl/UnsafeImpl.java
Все объекты (еще есть примитивы) создаются и хранятся в куче, даже если единственный хранитель указателя находится на стеке, сам объект всё равно сидит в куче.
Объекты сидящие в куче имеют внутренний недоступный для чтения набор метаданных, это можно выяснить или с сорцов или просто поменяв класс объекта и попытавшись прочитать память других объектов (не прокатит).
В ряде случае подменить сам объект проблематично, например чтобы подменить объект висящий в стеке надо или писать кастом нативку или колдовать с памятью потока или рассчитывать труайди объекта и менять метаданные менеджера кучи.
Так вот, всё это весело но не надо, от того и потому что класс объекта хранится в специальном поле -1 и может быть прочитан или изменён через unsafe.
Если менять классы который объявлены final - придётся или прописывать им magicaccessor или цепочку наследования вручную иначе JVM заметить несоответствие сигнатур в констант пуле и приклеит бороду вам и вашему коду. Но речь не об этом.
Структура объекта:
-8 недоступный для чтения заголовок в два слова
0 сведения о блокировках
4 верхний регистр указателя класса или указатель класса (64\32)
8 нижный регистр указателя класса или данные класса (64\32)
верхний регистр указателя класса всегда равен нулю если применяется compressed OOP
на основании вышеуказанных достаточно простых и понятных свойств объекта написан следующий код лишенный документации по умолчанию:
Код:
Object[] tester = {new Object(),new Object()}; //создаём массив под 2 объекта
if (unsafe.getInt(tester, 0x4) != 0) //если верхний регистр не равен нулю, то у нас однозначно не применяются сжатые заголовки - narrow references
nref = false;
if (unsafe.getInt(tester, 0x8) == 2) //если нижный регистр равен двум - это размер массива то перед нами JVM32 соответственно у нас узкая подложка и размер указателей равен 4l
wpad = false;
Расширяем целевой класс, в нашем случае:
public class DedicatedPlayerListImpl extends DedicatedPlayerList
если вы планируете перехватывать класс объявленный final расширять придётся в рантайме, остальное так-же (не предмет данной статьи)
если вы планируете добавить новые поля - плохая новость, менять размер существующих объектов (как и существующих массивов) НЕЛЬЗЯ, вообще нельзя, даже через свои собственные нативки вам потребуется делать новый объект и регенерировать ссылки на него.
если вы добавите новые поля, ждите проблем.
данные хранить в дополнительном классе где всё статик и паблик, так можно.
получаем указатель на целевой класс:
Object o = MinecraftServer.getServer().getPlayerList();
не важно, что и как, только убедитесь чтобы метод не возращал копию или коллекцию иначе будете долго страдать от непонятных проблем.
static методы перехватываются также но целью модицификации памяти будет прототип класса, там 100500 всяких секретных полей и чтобы сделать грамотно придётся читать сорцы JVM, это кстати всё равно потребуется для final методов, но не сейчас.
как уже сказано выше, работа с классами КУДА сложнее чем работа с инстансами объектов, начиная от другого порядка полей в зависимости от версии JVM и заканчивая иным значением полей, который выглядят внешне точно также.
поэтому доставать искомый указатель на класс мы будем из инстанса нужного нам объекта, например так:
x = UnsafeImpl.unsafe.allocateInstance(DedicatedPlayerListImpl.class);
targetref = wpad ? UnsafeImpl.unsafe.getInt(x, 8l) : UnsafeImpl.unsafe.getInt(x, 4l)
ставится в целевой объект точно также, случай с nref не рассматривается, вообще возможно что ктото даёт яве больше 32 гигабайт памяти, но это явно не ведро.
Вот собственно и всё, мы тупо берём и переписываем указатель на класс в инстансе тем самым меняя класс объекта и все методы, оставляя ссылки нетронутыми.