Logo

5 марта 2017 г.

Обрезка программ при помощи PE Tool

Когда Windows только освоила 32-битную платформу, все программы в ней грузились по одному виртуальному адресу. В Windows 2000 появилась возможность грузить программу по любому адресу, но он по-прежнему оставался фиксированным. А вот в ядре Windows Vista была реализована новая фишка — формирование адресного пространства программы случайным образом (ASLR), для реализации которой система научилась перемещать и программы тоже.

С этого момента большинство разработчиков собирает программы с поддержкой ASLR, что приводит к добавлению секции перемещаемых символов в exe-файлы. Когда-то давно такое поведение компоновщиков считалось за недостаток, но появление ASLR поменяло правила игры. Предполагается, что ASLR затруднит работу некоторых вирусов, но так ли это на самом деле, сказать трудно.

Перемещаемые символы

Перемещаемые символы занимают 5-15% от общего размера exe-файла. Если вы не озабочены проникновением вирусов или хотите эксплуатировать современные программы на старых ОС, можно сэкономить. Windows XP не имеет поддержки ASLR и потому никак не использует перемещаемые символы, так что в программах для нее они лишь тратят место впустую.

Заметьте, что удаление перемещаемых символов — не сжатие программы, оно не мешает работе страничной адресации и не дает накладных расходов, а на системах с ASLR невозможность перемещения программы даже сделает ее загрузку чуточку быстрее, вот только микросекундную разницу вы вряд ли заметите.

PE Tool и сторонние утилиты strip

В дистрибутиве PE Tool есть пакетный файл stip.bat, в котором прописаны ключи, необходимые для оптимизации. Имя strip может конфликтовать с одноименными утилитами в составе некоторых компиляторов. Если же у вас таких нет, смело помещайте pet.exe и strip.bat в каталог, доступный через переменную среды path, либо добавляйте в path каталог с распакованной PE Tool.

Использование PE Tool

Проводить свои эксперименты мы будем на дистрибутиве Far Manager 3-й ветки — ночной сборке, актуальной на момент написания статьи. Судя по новостям с форума Far, он по-прежнему работоспособен на XP, а если что-то ломается, после жалоб пользователей совместимость быстро восстанавливают. Если вы используете Far Manager на XP, информация ниже имеет для вас прикладной смысл.

Что вырежется?

Если не передавать ключ -into, по умолчанию программа прикидывает, что можно удалить из файлов:
T:\Far>strip *.exe *.dll

        /\_/\
    .__/ o o \    PE Tool 0.6 revision 767
   /~__;==t==/    Copyright © 2013-2017 Vladislav Javadov
  (_____)_m_m)

Estimating  Far.exe                 -197734 bytes   -5.0%
Estimating  lpeg.dll
Chained data found, safe stripping stopped
Estimating  lua5.1.dll                 -772 bytes   -8.4%
Estimating  lua51.dll
Chained data found, safe stripping stopped
Estimating  luafar3.dll                -254 bytes   -0.1%

5 files, -198760 bytes (-4.2%)
В дистрибутиве Far некоторые библиотеки собраны компоновщиком GNU, нарушающим стандарт. Обрезать их нельзя, о чем выводится предупреждение. Если хочется поэкспериментировать, ключ -unsafe позволит обрезать любой файл на свой страх и риск.

Сохранение результатов

Чтобы сохранить измененные файлы, нужно указать ключ -into. В качестве аргумента ему передается имя файла или каталога. При обработке нескольких файлов сохранение возможно только в каталог.

Если указать ключ -into без аргументов, программа перепишет исходные файлы:
T:\Far>strip *.exe *.dll -into

        /\_/\
    .__/ o o \    PE Tool 0.6 revision 767
   /~__;==t==/    Copyright © 2013-2017 Vladislav Javadov
  (_____)_m_m)

Stripping  Far.exe                 -197734 bytes   -5.0%
Stripping  lpeg.dll
Chained data found, safe stripping stopped
Stripping  lua5.1.dll                 -772 bytes   -8.4%
Stripping  lua51.dll
Chained data found, safe stripping stopped
Stripping  luafar3.dll                -254 bytes   -0.1%

5 files, -198760 bytes (-4.2%)
По сравнению с предыдущим примером действие Estimating поменялось Stripping, и обработанные файлы заменились. Запускаем Far и убеждаемся в его работоспособности после операции.

Краткая и подробная статистика

PE Tool умеет выводить краткую или подробную статистику обработки файлов. По умолчанию при обработке одного файла протоколируются все действия, а при обработке нескольких — только итоги.

Вывод действий для одного файла выглядит так:
T:\Far>strip lpeg.dll -unsafe

        /\_/\
    .__/ o o \    PE Tool 0.6 revision 767
   /~__;==t==/    Copyright © 2013-2017 Vladislav Javadov
  (_____)_m_m)

       Loading  lpeg.dll                33792 bytes  100.0%
    Image data                          35328 bytes  104.5%
  Chained data                            -14 bytes   -0.0%
Replacing stub  C:\Tools\Pet.exe        -1472 bytes  -95.8%
     Stripping                          -2952 bytes   -8.4%
         Total                          -1430 bytes   -4.2%
Ключ -log actions включает подробный вывод при обработке нескольких файлов.

Включение ASLR в программах, собранных компиляторами Borland

Если вы до сих пор пишете на Delphi версий от 2 до 2006, этот раздел для вас. Как упоминалось ранее, компиляторы Borland Delphi и C++ Builder всегда включали перемещаемые символы в exe-файлы, и до появления ASLR это считалось их недостатком. Сейчас же с их программами сложилась парадоксальная ситуация: перемещаемые символы в образе есть, но флаг ASLR не установлен, в результате чего и место расходуется, и ASLR не работает.

PE Tool может устранить этот недостаток, причем в любую сторону:
  • Вырезать перемещаемые символы, уменьшив размер программы.
  • Сохранить перемещаемые символы и включить ASLR.
Для старых ОС рекомендуется второй способ, для новых — по вашему выбору. ASLR включается одноименным флагом в командной строке:
T:\Far>strip -aslr -into Far.exe

        /\_/\
    .__/ o o \    PE Tool 0.6 revision 767
   /~__;==t==/    Copyright © 2013-2017 Vladislav Javadov
  (_____)_m_m)

            Loading  Far.exe                 3895914 bytes  100.0%
         Image data                          3896320 bytes  100.0%
     Replacing stub  C:\Tools\Pet.exe              0 bytes    0.0%
          Stripping                             -406 bytes   -0.0%
Keeping relocations                           131730 bytes    3.4%
             Saving  Far.exe                 3895914 bytes  100.0%
              Total                                0 bytes    0.0%
Обратите внимание, что ASLR можно включить только для тех программ, перемещаемые символы из которых не были вырезаны ранее, иначе будет выдано сообщение об ошибке:
T:\Far>strip -aslr -into Far.exe

        /\_/\
    .__/ o o \    PE Tool 0.6 revision 767
   /~__;==t==/    Copyright © 2013-2017 Vladislav Javadov
  (_____)_m_m)

            Loading  Far.exe                 3764122 bytes  100.0%
         Image data                          3764224 bytes  100.0%
     Replacing stub  C:\Tools\Pet.exe              0 bytes    0.0%
          Stripping                             -102 bytes   -0.0%
Dynamic image base feature (ASLR) is not available because relocations were
stripped from this file: Far.exe

Установка требуемой версии Windows

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

После прекращения поддержки Windows XP всё большее число программ собирается с версией 6.0 в заголовке, требуя минимум Windows Vista. Поскольку проверка версии технически отделена от привязки к новым функциям, в теории возможна ситуация, когда программа требует новую версию ОС просто так.

Ключом -osver можно прописать любую версию ОС в заголовок:
T:\Far>strip -into Far.exe -osver 4
Если понизить версию в программе, действительно использующей новые функции, при ее запуске будет выдана ошибка об отсутствии функции в одной из системных библиотек, чаще всего это kernel32.dll или user32.dll:
Точка входа в процедуру SetFilePointerEx не найдена в библиотеке DLL KERNEL32.dll.
Версию -osver можно задавать одним числом или двумя, через точку. Если второе число опущено, берется значение 0.

Таблица соответствия версий ядра Windows

  • Windows 95, Windows NT 4  — 4.0.
  • Windows 98 — 4.10.
  • Windows ME — 4.99.
  • Windows 2000 — 5.0.
  • Windows XP — 5.1.
  • Windows Server 2003, Windows XP x64 — 5.2.
  • Windows Vista, Windows Server 2008 — 6.0.
  • Windows 7, Windows Server 2008 R2 — 6.1.
  • Windows 8, Windows Server 2012 — 6.2.
  • Windows 8.1, Windows Server 2012 R2 — 6.3.
  • Windows 10 — 10.0.

Пользовательские заглушки

В дистрибутиве PE Tool есть каталог CustomStubs, где файлы с пользовательскими DOS-заглушками имеют номера версий Windows в названиях. Если вы меняете версию в заголовке, при помощи ключа -stub можно подменить заглушку, чтобы при запуске из-под DOS программа выдала сообщение, какая именно версия Windows ей требуется.

Учтите, что заглушки размером более 60 байт добавляют минимум 512 байт к программе — секции exe-файлов должны быть выровнены на границу 512 байт, меньше нельзя:
T:\Far>strip -into Far.exe -stub T:\Pet\CustomStubs\Win5.exe

        /\_/\
    .__/ o o \    PE Tool 0.6 revision 767
   /~__;==t==/    Copyright © 2013-2017 Vladislav Javadov
  (_____)_m_m)

       Loading  Far.exe                     3764122 bytes  100.0%
    Image data                              3764224 bytes  100.0%
Replacing stub  CustomStubs\Win5.exe            448 bytes  700.0%
     Stripping                                 -102 bytes   -0.0%
        Saving  Far.exe                     3764634 bytes  100.0%
         Total                                  512 bytes    0.0%
На момент написания статьи PE Tool выводит приращение размеров без знака, отчего не сразу удается понять, что размер файла увеличился, а не уменьшился. В будущих версиях эта недоработка будет устранена — прирост размера будет выводиться с плюсом в начале.

Unicode в окне консоли

PE Tool использует Unicode для внутреннего представления строк, что позволяет обрабатывать файлы с любыми символами в именах, поддерживаемыми Windows: кириллицей, латиницей с диакритикой, каной, иероглифами и пр. Для корректного отображения всех символов окну консоли должен быть назначен шрифт TrueType или OpenType, чаще всего это Lucida Console или Consolas.

Если консоль использует растровый шрифт, некоторые символы будут отображаться с искажениями. Это может доставить неудобство, но на работоспособность PE Tool или содержимое изменяемых файлов никак не повлияет.

Искажение выводимых символов легко определить по значку авторских прав в шапке программы. Если всё в порядке, он имеет вид ©, иначе на его месте будут присутствовать два искаженных символа (в примере ниже один из символов не виден, поскольку это неразрывный пробел U+00A0):
        /\_/\
    .__/ o o \    PE Tool 0.6 revision 767
   /~__;==t==/    Copyright  ┌ 2013-2017 Vladislav Javadov
  (_____)_m_m) 

PE Tool и старые ОС

PE Tool использует Unicode-версии системных функций и консоль с поддержкой UTF-8, из-за чего требует ядро типа NT и системные функции Windows 2000, — в заголовке pet.exe прописана версия 5.0. На версиях младше Windows 2000 PE Tool можно запустить только при помощи KernelEx.

Для экспериментов с программами под старые ОС лучше брать PE Tool 0.6 или выше, поскольку именно в 6-й ветке были улучшены диагностические сообщения о неподдерживаемых форматах файлов.

На момент написания статьи выпущена только альфа PE Tool 0.6, но она рассматривается как альфа лишь с точки зрения развития программы: изменений по сравнению с версией 0.5.1 пока мало, а версию 0.5.2 решено не выпускать, поскольку 5-я ветка закрыта.

Удачных экспериментов!

Ссылки

22 апреля 2016 г.

Этюд 10. Логические и битовые операции

На сайте compiler.su есть статья: «Так ли нужны операции «&&», «||» и «^^»?». Затронутая в ней тема очень интересна, особенно для функционального языка с выводом типов.

На данный момент многие вещи, которые в традиционных языках решаются усложнением синтаксиса и/или конечного кода, в Канторе решены перегрузкой функций. Нельзя ли решить перегрузкой проблему разнотипности операций? В чем вообще проблема?

В других языках

В Delphi для логических и битовых операций используются одни и те же ключевые слова — not, and, or и xor, поэтому в выражениях нужно использовать скобки:
MaybeUTF8 := (FirstByte and $С0 = $80) and (NextByte and $С0 = $80);
В SQL проблемы нет, но в нем нет ни битовых операций, ни логического типа (зачастую). Например, в Oracle есть только встроенная функция bitand и нет логического типа:
select nvl((select 1 from dual where bitand(first_byte, 192) = 128 and
  bitand(next_byte, 192) = 128), 0) maybe_utf8 from dual;
Для получения требуемого результата пришлось использовать числа 0 и 1, играющие роль логических значений, и встроенную функцию nvl, чтобы превратить null в 0. В целом код не слишком очевиден, но из него видно, что логические и битовые операции в SQL различаются, как и в Си. Напоминаю, что именно SQL является примером для подражания Кантора.

В Канторе

Поскольку в данный момент принято, что битовые и логические операции в Канторе различны, наш пример записывается без скобок:
maybeUTF8 = firstByte & $С0 = $80 && nextByte & $С0 = $80;
В сравнении вместо = можно писать ==, а вместо символьного && — ключевое слово and, то есть можно и так:
maybeUTF8 = firstByte & $С0 == $80 and nextByte & $С0 == $80;
Навскидку не могу сказать, какая запись наглядней и чище.

Что поменяется, если мы вдруг воспользуемся перегрузкой функций по типу и захотим использовать одни и те же символы/слова для записи обоих видов операций? Чисто внешне поменяется вроде бы мало:
maybeUTF8 = firstByte and $С0 == $80 and nextByte and $С0 == $80;
А вот с точки зрения синтаксического разбора мы придем к ситуации в Delphi, поскольку у предложенной записи возможны несколько интерпретаций, в том числе и такая:
maybeUTF8 = (firstByte and $С0) == ($80 and nextByte) and ($С0 == $80);
Сразу понятно, зачем в Delphi нужны скобки. Можно попытаться решить задачу установкой приоритетов, но они всегда будут неочевидны для рядового программиста, что превратит код в ребусы, а программирование на Канторе — в составление и разгадывание ребусов... По части ребусов как-то не хочется отбирать пальму первенства у Си, перед Кантором стоит противоположная задача.

Справедливости ради нужно отметить, что разделение битовых и логических операций в Си — одно из немногих различий в синтаксисе, сделанных в Си лучше Паскаля.

Нашим последним спасением может стать вывод типов, имеющийся в Канторе как и в любом другом функциональном языке. Если из-за множественной перегрузки вывод неоднозначен, можно использовать оператор-подсказку is:
maybeUTF8 = (firstByte and $С0 == $80) is Core:Boolean and
  (nextByte and $С0 == $80) is Core:Boolean;
Однако, увы, результат не соответствует ожиданиям. Поскольку оператор is имеет наивысший приоритет, для применения его к части выражения выводимое подвыражение надо заключать в скобки, что сводит на нет весь смысл, — со скобками и без is всё понятно. Стало быть, нововведение не состоялось: в борьбе за бесскобочный синтаксис битовые и логические операции должны быть различны.

Напоминаю, что естественная запись операций в Канторе является лишь синтаксическим сахаром к вызовам функций, а в объектном коде всё хранится «как в Лиспе»:
new from firstByte.BitAnd($С0).Equals($80).And(
  nextByte.BitAnd($С0).Equals($80)
) as maybeUTF8;

9 апреля 2016 г.

Форматирование строк

Форматирование строк в Канторе будет реализовано как предметно-ориентированный язык (DSL). Символ традиционный — знак процента:
s = {%Строка {%} из {%}.%}.AsString[{index, count}];
Если свойству AsString будет сопоставлена операция *, вызов можно записать и так:
s = {%Строка {%} из {%}.%} * {index, count};
Формат {%} подразумевает, что тип сопоставляемого выражения будет передаваться через RTTI, как это сделано в Delphi. Соответствующая реализация в CoreLite пока находится в разработке (отложена), но повлияла на Кантор.

На Delphi

Штатная функция SysUtils.Format:
s = Format('Строка %d из %d.', [Index, Count]);
Удивительно, но формируемые компилятором Delphi сведения о типе передаваемых параметров лишь ограниченно используются в стандартных функциях RTL/VCL — только для генерации исключений о несоответствии типа. В известных мне форматтерах строк Delphi нет символа, соответствующего понятию «тип из параметра».

Поскольку необходимая информация передается, соответствующая реализация возможна. Формат «тип из параметра» будет реализован в CoreLite и в дальнейшем будет использоваться в Канторе.

Цели

Сейчас все борятся за безопасность кода и статический анализ, а в Канторе его можно сразу встроить в язык, благо общий механизм для этого есть — расширения в виде предметно-ориентированных языков.

14 мая 2015 г.

Фрактальная модель: теория объектно-ориентированной ОС

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

Данная статья основана на материалах 2012 года, публиковавшихся на предыдущем сайте проекта и даже упомянутом в качестве литературы к одной из статей конференции «Объектные системы — 2013» (см. ссылки в конце статьи).

Сейчас текст адаптирован под текущую ситуацию в проекте и публикуется как предварительный материал к описанию фрактальной модели как ключевой концепции. Изначально планировалось разбить его на несколько частей, но поскольку тема не развивалась с 2013 года, решено опубликовать всё в одной статье, закрыв технический долг по теориям ООП.

21 марта 2015 г.

Множественное наследование и взаимная абстрактность

Этот пост по-быстрому адаптирован из моего ответа на «Тостере» ради краткого изложения некоторых принципов ООП в Канторе.

Принципы SOLID покрывают общий случай наследования в ООП, когда классы могут наследоваться друг от друга без посредничества интерфейсов. Следование SOLID должно способствовать нормализации классов, чтобы получилась сбалансированная ОО-система. При этом в самих SOLID явного правила нормализации нет, оно появляется в конкретной реализации:
Множественное наследование допустимо только от взаимно-абстрактных классов, — то есть классов, не имеющих реализации одинаковых методов. Одинаковость методов в языке определяется совместимостью по присваиванию с учетом ООП.
В языках с интерфейсами интерфейсы всегда взаимно-абстрактны реализующим их классам, поэтому классы могут реализовывать любое количество интерфейсов, принципы SOLID от этого не пострадают. Собственно, интерфейсы были введены в языки программирования ради упрощения модели наследования.

В Канторе интерфейсов нет, поэтому модель наследования — общий случай, регулируемый SOLID. Взаимная абстрактность в Канторе — отношение между классами, вычисляемое в момент компиляции. Класс-наследник не может иметь реализации свойств с одинаковыми сигнатурами, полученных от предков. Сигнатура — это комбинация имени и параметров свойства, ее уникальность определяется правилами перегрузки (overload).

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