PureBasic - форум

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » PureBasic - форум » OpenSource » PurePortable - портабелизация программ


PurePortable - портабелизация программ

Сообщений 1 страница 10 из 10

1

Проект PurePortable был создан для разработки средств для портабелизации программ с использованием прокси-dll.

Портирование программ осуществляется в основном через перехват вызовов WinApi - перехват обращений к реестру позволяет сохранять настройки не в реестре, а в файле рядом с программой, перехват вызовов для определения системных папок позволяет переместить AppData в папку программы и т.п.

Прокси dll, это dll перехватывающая вызовы WinApi от программы и либо изменяющая их поведение либо перенаправляющая вызовы в оригинальную dll без изменения.

Dll различаются способом внедрения:

1. Подмена системной dll в папке с программой. Для использования данного метода необходимо, чтобы программа имела в таблице импорта (прямого, не отложенного!) вызовы данной dll, либо такие вызовы имели другие dll, вызовы которых импортирует программа и находящиеся в одной папке с программой.
Типичный пример это version.dll. Сама version.dll не имеет экспортируемых функций интересных с точки зрения портабелизации, но из неё отлично устанавливаются хуки. Простой пример такой портабелизации - InnoSetup (выкладывал на ru-board).
Другой пример это winmm.dll, а пример портабелизации сам PureBasic. Да, Пурик имеет ключ /portable, но этот ключ надо ещё и не забыть указать, а случайно забыв Пурик тут же ассоциирует себя с файлами pb ломая нужные ассоциации. Кроме того, Пурик в своих prefs хранит абсолютные пути, что мало вяжется с "портабельностью". Dll решает эти проблемы корректирую пути к Tools в tools.prefs, к компиляторам в PureBasic.prefs, по возможности к файлам RecentFiles и некоторые другие пути. Dll я выкладывал на ru-board и в телеграмме.
А ещё winmm используется фреймворком QT. Даже если сама программа не имеет ничего полезного из импорта, если она использует QT то обычно легко портируется.
Таки способом нельзя подменить dll из списка KnownDLLs и в некоторых других случаях.

2. Подмена системной dll путём замены имени dll непосредственно в коде программы (речь о замене не в исходном коде, а непосредственно в exe, например, каким-нибудь hex-редактором).
Пример - портабелизация утилит SysInternals. Там подменяется comdlg32.dll. Выкладывал на ru-board.
Метод не обязательно применять только для dll из списка KnownDLLs, подходит для любых dll. Например, через прокси winmm, переименовании dll и соответствующем изменении имён в exe-файлах, в одной папке на флешке уживаются HWiNFO32 и HWiNFO64 (разной разрядности).
Метод не работает на сжатых файлах (потребуется разжатие), накрытых протектором, проверяющих свою целостность и т.п.
Кроме того, здесь мы становимся на скользкий путь возможного нарушения "авторских прав" и прочей копирайтной хреномудрии. Так что может не всем подойти по "религиозным" мотивам.

3. Добавление своей dll в импорт. Правкой таблицы импорта непосредственно в exe-файле или в одной из используемых им dll можно добавить dll в список. Программа такую dll загрузит, хотя ничего в ней на прямую не использует.
Метод так же, как и предыдущий, не работает на сжатых файлах, накрытых протектором, проверяющих свою целостность и т.п.
Здесь мы так же вмешиваемся непосредственно в код и возможны "копирайтные" ограничения.
Свой алгоритм правки пока не реализован, для внедрения используется утилита setdll из Microsoft (sic!) Detours.

4. Использование внешнего запускателя - создаётся "замороженный" процесс, внедряется dll и процесс размораживается. К сожалению, у меня пока нет рабочих примеров данного метода.
Как пример есть утилита withdll из того же Detours.

В dll используются следующие способы перехвата:

1. Сплайсинг на базе библиотеки MinHook.
2. Перехват IAT.
3. Прямая подмена вызовов. Это вроде даже и не метод, но для полноты должен быть упомянут - при подмене, например, advapi32.dll (метод 2 - правка кода) хуки могут быть вообще не нужны, так как программа напрямую вызывает функции из "подменной" dll.

Ссылки:
Смежная тема тема на ru-board
Википедия - перехват

Правовые моменты.
Сам по себе перехват не является чем-то незаконным и даже используется в самой ОС Windows, например, для обеспечения совместимости приложений с ОС, а слайсинг предусмотрен практически "из коробки".
Тем не менее, в некоторых случаях, например при правке кода для внедрения dll может быт что-нибудь нарушено. Обсуждение в таких "тонких" случаях лучше перенести на ru-board.

Отредактировано Smitis (26.08.2023 20:11:02)

0

2

Исходный код с примерами портированных программ выложу в ближайшее время.
Если что, на ru-board можно взять предыдущую версию (4.08.01)

0

3

Продублирую здесь

useful

useful написал(а):

Для диалога, если обходной путь всё таки нужен, важно не абстрактное "массивы PureBasic", а описание задачи:

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

Сложный вопрос. Программы то разные. Одни много пишут в реестр, другие почти ничего.

В настоящий момент настройки из реестра хранятся в текстовом файле с определённой структурой записей (правда, встречаются специальные служебные символы и некоторые очень "умные" текстовые редакторы испытывают когнитивный диссонанс).
При старте программы файл читается и в памяти создаются два динамических массива (точнее, массивы обычные, размер меняется через Redim.) - один для хранения путей из реестра, к которым обращается программа и на основе которого потом формируются виртуальные хендлы для функций работы с реестром. Второй массив - непосредственно хранимые в реестре данные.
При завершении программы всё пишется обратно.
Отлично работает для одного процесса. Собственно, попадалась только одна программа (сейчас, к сожалению, сходу не скажу какая), которая создавала ОЧЕНЬ много разделов в реестре (десятки тысяч) и всё это начинало тормозить.

Если же процессов несколько, возникает проблема.
Откуда берётся несколько процессов - некоторые программы запускают сами себя. Зачем, не знаю. Видимо так надо. Например последняя 10-я версия TheBat!.
Некоторые имеют в своём составе несколько исполняемых файлов - целый пакет программ, использующих одни и те разделы в реестре для данных.

Один из вариантов, который сейчас рассматриваю, это использование RegLoadAppKey - там перехватываются запросы к реестру, но данные будут не в памяти (а потом в файле), а в отдельном улье реестра. Но там какая то беда с доступом. Из под админа всё работает, а из под пользователя нет прав. Хотя вроде как права как раз не нужны, это не RegLoadKey. Либо я что-то недопонимаю. Инфы, увы, очень мало.

Другой вариант - приспособить под это дело другую базу данных.

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

Отредактировано Smitis (26.08.2023 15:58:56)

0

4

Т.е. речь идёт о чужих программах.
Как это связано с совместным использованием данных несколькими процессами?
Я естественно предполагал что мы точно заем кто создаёт и какие данные, а также кому их нужно предоставить потому, что всё это сами разрабатываем.

p.s. главный вопрос: цель какая? Кто с кем, что должен  делить?

Отредактировано useful (26.08.2023 16:05:05)

0

5

useful

useful написал(а):

Как это связано с совместным использованием данных несколькими процессами?

Один процесс записал данные в реестр. Например, банальное, какие-нибудь сделанные пользователем настройки. Затем им был запущен другой процесс (другой exe), который эти данные прочитал.
Типичный пример - WinCatalog. Запускает вспомогательные программы. Те тоже лезут в реестр, за теми же самыми данными, которые WinCatalog вот только что сам поменял. Там ещё и dotnet в этих вспомогательных прогах, к дотнету я ещё не подступился, так что с портабелизацией WinCatalog у меня облом (надеюсь, пока). Но я пытаюсь на его примере описать проблему.

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

И да, ещё раз подтверждаю, все процессы чужие. Что они там вытворяют с данными непонятно.

Как вариант ещё рассматривал такой, если другой процесс фоновый, что-то там обработал и завершил работу, а данные ему нужны просто для информации, например, как у WinCatalog чисто путь к каталогу (это не точно, просто предположение), то перехватывать CreateProcess, в этот момент скидывать данные на диск, запущенный процесс подцепит прокси-dll, заново прочитает данные и нормально отработает. Это если он в данных ничего существенного не меняет. Если основной процесс ждёт завершения вспомогательного, тут даже может быть проще, вспомогательный сохраняет, основной потом перечитывает.
В каждом конкретном случае надо разбираться отдельно поэтому хочется простого универсального решения. Ну как с реестром - одна прога пишет, другая это читает и меняет или не меняет и обратно не пишет, не важно, главное что всё имитирует реестр и работает ассинхронно, бесконфликтно, заботу берёт на себя это стороннее решение (пусть даже тот самый sqlite).

Ещё пример, Nitro PDF. Кроме основного exe куча мелких. Подозреваю, по больше части их можно вообще выкинуть, например, прожки, судя по всему просто отправляющие разработчику информацию о сбоях, а другие просто получают команды из командной строки, но фиг его знает. Разбираться муторно.

Ешё пример из таких "многоэкзешных" - Acrobat Reader.

Отредактировано Smitis (26.08.2023 17:34:45)

0

6

Smitis написал(а):

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

Реестр это по сути СУБД

А абсолютно любая многопользовательская СУБД умеет

Smitis написал(а):

... асинхронно, бесконфликтно ...

Я всё равно не понимаю ЦЕЛЬ. Подменить для чужих программ обращения к реестру?

Это совсем не может быть связано с началом обсуждения

Smitis написал(а):

Но расшарить массивы PureBasic между несколькими процессами не получается. Вот и ищу обходной путь.

Для меня это просто ещё одно подтверждение, что проблемы ответов на форумах в самих вопросах.

Останусь ка я "капитаном очевидность"

Отредактировано useful (26.08.2023 18:32:16)

0

7

Если речь о том, что фактически одновременно один и тот же код вызывается разными процессами, то тут нужно внимательно смотреть в сторону реентерабельности.
Т.е. код должен понимать, что все данные с которыми он работает рождаются при вызове и перестают существовать при выходе.
В самом коде никаких констант и переменных. Должна быть область памяти указатель(привязанный например к ID процесса) на которую оживает при обращении, через этот указатель на память накладывается структура и вся работа с переменными только в ней.
Сейчас это конечно автоматом пытаются обеспечить параметром threadsafe, но я ещё с времён когда машины были большие а памяти у них мало, для многопользовательской работы стараюсь в ручную это обеспечивать.

0

8

useful написал(а):

Я всё равно не понимаю ЦЕЛЬ. Подменить для чужих программ обращения к реестру?

Да. Цель - в заголовке темы - портабелизация программ.
Один из аспектов портабелизации - перехват обращение к реестру и сохранение данных, которые программа хранит в реестре, в папке программы, в том или ином виде.

useful написал(а):

Реестр это по сути СУБД
А абсолютно любая многопользовательская СУБД умеет

Вот. Имитация реестра через СУБД как вариант. Пока не знаю, как приспособить. Надо разбираться. Надеюсь, штатная в PureBasic sqlite подойдёт.
И мне кажется, тут главное скорость работы, чтобы не было задержек. Собствено, sql тут и не нужен. Тут даже дерева не нужно, хватило бы табличной СУБД.
Отсюда и был вопрос после упоминания minim.

Это совсем не может быть связано с началом обсуждения

В другой теме обсуждались массивы. У меня сейчас "СУБД" это два массива в памяти. Размеры массивов динамически меняются - нельзя заранее узнать, сколько данных решит хранить программа.
Для многих случаев это достаточно. Но не всегда.

Отредактировано Smitis (26.08.2023 19:31:18)

0

9

Исходный код 4.08.02 https://disk.yandex.ru/d/rVH3x1dJw_rZHA

Структура папок:

.lib - основная библиотека, в том числе и MinHook и основной включаемый файл PurePortable.pbi
Здесь же шаблон _PurePortableTemplate.pb для создания новых прокси-dll.
С точки я начинаю имена папок с библиотечными файлами. Здесь такая папка одна, это последняя версия, в реальности к меня папок больше - старые версии, промежуточные экспериментальные...

_All - Здесь готовые PB-файлы для компиляции прокси-dll для некоторых уже портированных программ. Не всех, многие программы не интересны широкой общественности и не выкладываются.
_Games - Здесь для некоторых игрушек.
С символа подчёркивания я начинаю общие папки где лежат исходники по принципу один файл - одна программа или группа сходных программ одной фирмы.

Некоторым программам отведено по целой папке, здесь таких всего две (FAR и PureBasic), в каждой по одному исходнику, достаточному для компиляции dll (всё ненужное исключил).

Исходники рассчитаны на использование PureBasic Pre/Post Processor (PurePProcessor) поэтому там в начале каждого файла есть для него директивы. Некоторые dll без него
не сделать работоспособными, например, msimg32.dll из-за конфликта имён с PureBasic (функция AlphaBlend), но для компиляции большинства можно обойтись и без.

Большинство моментов стандартизировано и не требует особых изменений. Берётся шаблон _PurePortableTemplate.pb и правится под конкретную программу. Полного описания, естественно, нет. Руки не доходят :(
Но на вопросы готов ответить.

Отредактировано Smitis (26.08.2023 20:46:46)

0

10

В догонку
Версия 4.08.03 https://disk.yandex.ru/d/dG0sQWhvo8UXuA
Пара важных исправлений:
- Возвращены функции iif и iifs
- Исправлена константа в модуле EnvironmentVariables
Ошибки чисто синтаксические (компилятор будет ругаться).
Ну и изменения, которые успели появиться:
- Убраны макросы-заглушки InitRegistryHooks, InitCBTHook и прочие подобные.
- Имена некоторых констант изменены, приведены "к общему знаменателю".
  #DBGX_FILEOPERATIONS -> #DBGX_FILE_OPERATIONS
  #DBGX_LOADLIBRARY -> #DBGX_LOAD_LIBRARY
- Добавлена трассировка для функций WinApi, работающими с ini. Включается через константу #DBGX_PROFILE_STRINGS
- Исправления в модуле Исправления в DBGX_FileOperations. Добавлены функции CreateFile.
- В модуле MinHook кое-где убрана ненужная условная компиляция.
- Добавлена глобальная переменная WinDir.
- Инициализация CfgFile перенесена в модуль Registry.
- Доделан модуль ProfileStrings (но ещё не протестирован).

0


Вы здесь » PureBasic - форум » OpenSource » PurePortable - портабелизация программ