Показаны сообщения с ярлыком dirty hack. Показать все сообщения
Показаны сообщения с ярлыком dirty hack. Показать все сообщения

четверг, 22 декабря 2011 г.

Загрузка двух нативных библиотек через JNI под линуксом


Недавно по работе появилась редкая для меня задача, излагаю. Скажу сразу, решения в лоб пока не нашел, а workaround-ы мне известны, но для них требуется помощь от поставщика одной из сторонних библиотек, а потому задача более чем актуальна. 
Знатоки JVM И юникса - VERY welcome..
Итак, задача. Есть две нативные либы, А  и Б, или, в нейминга линукса, это libA.so и libB.so. А зависит от Б (должна вызывать функции из нее). А - написана мной, Б - предоставлена сторонними разработчиками в виде одного жалкого .so файла, доступа к исходникам нет.
Мне нужно загрузить эти две либы в JVM через JNI и вызвать функцию из библиотеки А, которая, в свою очередь, использует функцию из Б.
Следующий код:
static {
    System.loadLibrary(B);
    System.loadLibrary(A);
}
Отлично работает в Windows (для .dll файлов, соотетственно), при условии, что обе библиотеки лежат в java.library.path (я тут кстати раньше в блоге писал, как можно менять java.library.path в рантайме).
Как это работает в Windows - загружается B.dll, дальше A.dll пробует загрузиться, видит, что надо бы загрузить B.dll, от которой она зависит, видит, что B.dll в память процесса уже загружена, и не пытается найти и загрузить B самостоятельно, ища ее в PATH.
Теперь, что, по видимому, происходит в линуксе.
Дополнительный логгинг показывает, что libB.so корректно находится в java.library.path и загружается. Затем, рантайм-линкер пытается загрузить libA.so, и замечает, что ВНЕЗАПНО, libB.so в LD_LIBRARY_PATH нет! А менять LD_LIBRARY_PATH я не хочу, не то что неспортивно, но хочется локально решать проблемы. Чтобы уточнить - обе библиотеки находятся в директории, которая входит в java.library.path, но НЕ входит в LD_LIBRARY_PATH).
У кого-нибудь есть какие-нибудь идеи?
Перепробовал немало хаков уже, включая линковку libA.so с уничтожением таблицы символов, загрузку libB.so динамически из libA.so, получение указателя на нужную функцию (манглированную) и вызов ее - не работает ничего из этого так, как мне надо.
Еще раз уточню условия - я не хочу модифицировать LD_LIBRARY_PATH, я не хочу класть свои либы в "общеизвестные" места типа /usr/lib.
Вариант добыть вместо libB.so статически либу (libB.a) и влинковать ее жестко в libA.so мне вариант кажется хорошим, но исходников нет.
Сконвертировать libB.so -> libB.a? Теоритически наверное, можно.

суббота, 5 марта 2011 г.

sun.misc.Unsafe - немного магии вокруг JVM

Когда-то, выбирая декомпилятор, натолкнулся я на несколько изумительных статей на wasm.ru. Статьи они кажется потерли, или по крайней мере изменили адреса, но сохранили их в виде вордовского файла, который можно загрузить отсюда:



Описывают, что такое Unsafe api, предоставляющее программисту возможность работать с классами, методами и т.п. ниже того уровня , который позволяют штатные средства платформы Java - на фактически на уровне тех самых С-структур, которые используются внутри JVM для предоставления сущностей класс, инстанс класса, метод и т.п.

Что дает возможность делать многие вещи, которые в программировании на Java  "традиционно" считаются невыполнимыми - функция sizeOf, (возвращающая точный размер объекта в памяти , взятый напрямую из поля  структуры, которая представляет этот объект внутри JVM), наследования от final-класса (делается двумя шагами по сути -  в список предков класса добавляется нужный нам класс, и из таблицы модификаторов доступа для этого суперкласса во время выполнения программы удаляется final - и

всего делов ;)).



Ну и уже более изощренные и хакерские штучки - самомодицирующиеся во время выполнения методы..и т.п. Очень рекомендую эти статьи к прочтению :).

Да, конечно -- смещения полей в структурах высчитываются на пальцах, класс sun.mics.Unsafe недокументирован (но присутствует в Java с самого ее начала, и очень навряд ли будет из нее удален - я очень сильно подозреваю, что предоставляемые им низкоуровневые возможности используются инструментами типа отладчиков и т.п.), и в продакшне ни один Project Manager такое использоватьне позволит -- да и не требуется в обычных приложения никогда столь низкоуровневый доступ к JVM. Но знать, как ява-машины работает с классами внутри себя -- полезно и интересно, а знать что из обычной программы на чистой яве можно получать доступ такого уровня -- еще интересней.

И напоследок два слова по поводу того, что это gap в ява-машине, который будет закрыт в следующем же релизе и т.п.

Я не думаю, что это дыра в системе безопасности Java. Собственно, суммирую и повторю описанное в первой из статей.
Итак. Есть две точки входа для получения инстанса класса Unsafe, который позволит нам творить черную магию:

1) Получить его можно через Unsage.getUnsafe() - НО! только в том случае, если вызывающий класс был загружен первичным класслоадером (www.tedneward.com/files/Papers/BootClasspath /BootClasspath.pdf - тут можно прочитать подробней про иерархию класслоадеров). Это сделать несложно -- всего-то добавить ключ -Xbootclasspath в список стартовых опций ява-машины. Но для этого надо иметь доступ  к среде выполнения.

2) Можно просто взять private переменную theUnsafe - которая хранит инстанс класса Unsafe внутри него. Но если есть Security Manager и установлена для него соответствующая политика запрещения опасной рефлексии , получить значение этой закрытой переменной не удастся.

Соответственно, мой вывод -- это API не является уязвимостью в JVM, вовсе нет.
Потому что точно так же рефлексией (если она не запрещена в политиках безопасности) можно творить безобразия внутри приложения, но и польза от нее может быть большая-- надо просто разумно выставлять политики безопасности в каждом конкретном случае.

Да - Unsafe API дает беспрецедентный уровень доступа к среде выполнения Java из программы. Но чтобы получить доступ к этому апи -- надо либо иметь соотв. права на той машине, где выполняется приложение (например, возможность задавать параметры запуска ява-машины), либо должен быть соответственно сконфигурирован (без учета этой опасности) Security Manager.