EXE
Если вам необходимо просто открыть файл EXE, то вы можете воспользоваться программами из списка по ссылке: Чем открыть файл EXE? Если же вам необходимо изменить (отредактировать) файл, мы подготовили соответствующий список специальных программ для работы с данным типом файлов.
Самый популярный EXE редактор - это CodeWeavers CrossOver (для ОС Mac). Ниже мы составили список программ для операционной системы Mac, Linux, Windows. Вы можете скачать редактор бесплатно, кликнув по названию программы для вашей операционной системы - каждая из них гарантированно позволит осуществить редактирование нужного файла.
Исполняемый файл EXE является приложением Windows либо DOS систем. Такой тип файла может включать в себя самораспаковывающиеся архивы. Имеют невероятно широкое распространение.
Узнать подробнее что такое формат EXE
Знаете другие программы, которые могут редактировать файлы .EXE? Не смогли разобраться как отредактировать файл? Пишите в комментариях - мы дополним статью и ответим на ваши вопросы.
Программа для редактирования exe файлов, которая поставляется с поддержкой создания резервных копий и пакетного экспорта. Она позволяет просмотр, замену, добавление, удаление и извлечение ресурсов, расположенных в 32-разрядных и 64-разрядных исполняемых файлах Windows и файлах ресурсов (*.res).
Исполняемые файлы Windows (или, более конкретно, файлы Win32 PE) часто содержат небольшую базу данных ресурсов: графические файлы, текстовые строки, значки и другие программные атрибуты. Часто может оказаться полезным изменить эти ресурсы, например, изменить внешний вид программы или исправить неправильный перевод в таблице строк. Anolis Resourcer предназначена для изменения ресурсов исполняемых файлов.
Поддерживает просмотр растровых изображений, значков, курсоров, различных форматов изображений (GIF, JPEG, PNG и т.д.), AVI анимации. Можно просматривать и анализировать меню, диалоги, информацию о версии и таблицы строк.
Для большинства форматов данных о ресурсах exe файлов поддерживается вид миниатюр, позволяющий просмотреть их все сразу, без необходимости открывать их по отдельности. Доступны три размера миниатюр: 16x16, 32x32 и 96x96.
Все ресурсы могут быть сохранены на диск в виде файла необработанных данных (*.bin), кроме того, большинство известных типов данных, таких как изображения, значки и курсоры, могут быть сохранены в соответствующие им форматы файлов.
Также можно пакетно извлечь все ресурсы из одного файла или каталога, полного исполняемых файлов, всего за пару щелчков мыши. Достаточно выбрать пункт меню «Batch Export» в меню «Tools».
Программа позволяет заменить большинство ресурсов их эквивалентами на основе файлов (т.е. заменить ресурс Bitmap файлом *.bmp или ресурс Icon Directory на файл *.ico). Для некоторых типов доступны дополнительные параметры, например, значки.
Некоторые типы ресурсов не имеют специального формата файла (например, информация о версии или таблицы строк), поэтому к замене данной информации следует подходить с осторожностью.
Ресурсы значков хранятся в двух местах: каталог, содержащий ссылки на изображения и фактические изображения, составляющие значок.
Большинство редакторов ресурсов удаляют старые изображения прежде чем добавить новые и перезаписать каталог. Вместо этого у данного редактора файлов формата exe есть возможность объединить значки, в результате чего, получается объединение исходного значка и значка нового файла.
Resourcer работает на платформе NET Framework, поэтому он будет работать как программа x64 в системах x64 (и, следовательно, не будет зависеть от файловой системы WOW64 x64 и изолированной программной среды API).
Но что еще более важно, Resourcer может выполнять одни и те же операции и с 32-разрядными исполняемыми файлами, и с 64-разрядными, обеспечивая прямую совместимость для современных вычислительных систем.
Портативное ПО - не требует установки, достаточно извлечь и запустить.
Поддерживаются аргументы командной строки, что предоставляет возможность автоматизации задач редактирования ресурсов exe файлов из среды пакетных / командных сценариев.
Программа позволяет открывать исполняемые файлы Win32 в таких форматах, как EXE, DLL, CPL, OCX, SCR и MUI, а также скомпилированные сценарии ресурсов (RES, RCT).
Кроме того, следует знать, что можно импортировать данные ресурсов с жесткого диска, используя такие расширения файлов, как BMP, GIF, JPG, PNG, AVI, ICO, CUR, HTML, XML, DIB и RLE.
Интерфейс программы на английском, но он очень прост, поэтому разобраться с ним не составит труда. Для открытия exe файла на редактирование нужно нажать кнопку «Open» и указать файл, который необходимо отредактировать. Также можно открыть через меню «File» и далее пункт «Open», или воспользовавшись комбинацией клавиш CTRL + O.
Язык: Английский
Лицензия: GNU GPL v2Протестировано на ОС: Windows 7 x64, Windows 10 x64
Заявлена совместимость с ОС: любая ОС, для которой существует NET Framework 2.0 (например, Windows 98 и более поздние версии)
Официальный сайт: www.anol.is/resourcer (веб-архив)
Резервная копия на Яндекс Диске, версия 0.9.3531.38736
Вот здесь я уже приводил пример излишней заботы о нашей безопасности. Лично меня такая забота очень сильно бесит. Если выбирать между безопасностью и свободой, то я всё-таки предпочёл бы свободу.
Но человечество развивается так, как развивается. Даже если большинство людей, как и я, предпочитают свободу, условия жизни в обществе нам диктует меньшинство. И это меньшинство решает, что нам можно, а что нельзя.
В частности, владельцы почтовых сервисов предоставляют нам бесплатную почту, но платой за эту бесплатность является безоговорочное принятие их правил. Например, если вы захотите отправить в письме по электронной почте какой-нибудь файл, то это может оказаться весьма затруднительным или даже невозможным.
Поскольку все разработчики и владельцы почтовых программ считают нас беспомощными недееспособными недоумками, которые не могут сами позаботиться о своей безопасности (к сожалению, часто так оно и есть), то они пытаются оградить нас от всех возможных опасностей, подстерегающих нас в Интернете.
В итоге полностью и всех оградить всё-равно не получается, а дееспособные и мыслящие (вроде меня))) люди получают дополнительные искусственные препятствия, которые очень сильно мешают в работе. В итоге, чтобы выполнить простейшее действие (например, отправить файл по почте), приходится тратить намного больше времени и сил, чем хотелось бы.
В чём, собственно, сложность? И вообще зачем я написал так много “букаф”, а к сути так и не подобрался?
Ну, во-первых, я люблю писать. А во-вторых, надо было сделать какое-то вступление, чтобы люди, далёкие от этого вопроса, узнали не только его решение, но и причины возникновения.
Итак, ближе к делу…
При отправке файлов по электронной почте в большинстве случаев неприятностей не возникает (когда вы отправляете фотографии или текстовые документы).
Но вот если вы решите отправить по почте программу (EXE-файл), то здесь вы можете столкнуться с неожиданностями, которые многих вгонят в недоумение или даже бешенство.
Например, при попытке отправить письмо с вложением ЕХЕ-файла через почтовый ящик от Гугла, вы увидите следующее:
То есть ваш вложенный файл ГУГЛ заблокировал и не загрузил, потому что посчитал его небезопасным. Сразу скажу, что здесь я пытаюсь отправить программу собственной разработки, которая ГАРАНТИРОВАННО не содержит вирусов.
Но суть в том, что почтовики все EXE-файлы считают потенциально опасными. Так что у вас не получится доказать, что вы не верблюд.
Если же, невзирая на предупреждение, вы нажмёте кнопку ОТПРАВИТЬ, то увидите другое предупреждение:
Да, Гугл так и быть, отправит ваше письмо, но без вложения! Однако смыслом отправки для вас является как раз таки отправка приложения (то есть вложения).
Так что же делать? Копировать вашу программу на флешку и по старинке отправлять в точку назначения с курьером? Но мы же, блин, живём в 21-м цифровом веке!
Решения, конечно, есть. Не все они одинаково хорошо применимы для разных почтовых систем. Но всё таки “не мытьём, так катаньем” вы сможете отправить EXE-файл по почте. Далее я расскажу о нескольких способах.
Итак, вот основные способы:
Итак, самый простой способ - это запаковать вашу программу в архив, и попытаться отправить по почте этот архив. Раньше это часто срабатывало. Однако почтовики постоянно совершенствуются (иногда не в ту сторону), и сегодня почти все почтовые сервисы умеют распаковывать или проверять архивы. Следовательно, они понимают, что в архиве находится EXE-файл и точно также блокируют отправку. Либо просто считают архив потенциально опасным, как и ЕХЕ-файл.
Поэтому лучше сразу попробовать второй способ - упаковать вашу программу в архив и установить пароль на архив. У разных архиваторов это делается по разному.
Для примера приведу последовательность действий в архиваторе 7-Zip и для операционной системы Windows XP.
Итак, в Проводнике через контекстное меню выбираем
7-ZIP - ДОБАВИТЬ К АРХИВУ
Откроется окно, где вы можете задать разные параметры архива, в том числе и пароль. Для установки пароля в группе ШИФРОВАНИЕ в поле ВВЕДИТЕ ПАРОЛЬ задайте любой пароль и нажмите кнопку ОК (см. рис.).
После создания архива у вас в той же папке, где был ваш ЕХЕ-файл, появится файл имя_файла.zip
. Вот его то теперь вы можете попробовать отправить по почте. Только не забудьте сообщить получателю письма пароль, который вы установили на архив, иначе он не сможет его открыть.
Большинство почтовых сервисов отправят такое письмо. Но не Гугл. Гугл по уровню маразма в области безопасности пошёл дальше остальных. Поэтому вы увидите всё то же сообщение о блокировке (см. рис. выше в начале статьи).
Если у вас есть ящик на другом сервисе, например, на Мэйле или Яндексе, то лучше не заморачиваться и отправить письмо с архивом через эти сервисы. Но если вы хотите отправить письмо именно через почту Гугла (например, это ваш рабочий ящик, который знают все ваши клиенты и вы не хотите использовать другую почту), то остаётся третий способ.
Третий способ заключается в загрузке архива (лучше именно архива с паролем, а не просто файла) на какой-либо файлообменник и отправке ссылки на этот архив по почте.
В качестве файлообменника может быть, например, Яндекс.Диск, Облако от Мэйла, Гугл.Диск или что-то иное.
Поскольку не все пользователи могут пользоваться Мэйлом и Яндексом (например, на Украине эти сервисы заблокированы), то я приведу пример с Гугл.Диском.
Кратко расскажу, как создать Гугл.Диск.
Если у вас есть почта на Гугле, то наверняка есть и Гугл.Диск. Если нет, просто переходите по ссылке https://drive.google.com и создайте Гугл.Диск (вы должны быть авторизованы в почте Гугл).
Сначала вы увидите такую картину:
Здесь выбираете “Для личных целей” и нажимаете “Перейти к Google Диску”. После этого откроется страница, где нужно
ввести адрес вашего почтового ящика (ящик у вас уже должен быть, и это должен быть ящик на Гугле, то есть адрес должен
иметь вид: ваша_почта@gmail.com
).
Нажимаете кнопку ДАЛЕЕ и при необходимости следуете инструкциям (возможно, придётся ввести пароль от вашего ящика).
Когда вы войдёте в Гугл-Диск, то вам надо будет загрузить ваш файл. Для этого надо щёлкнуть по меню СОЗДАТЬ в левом верхнем углу:
В открывшемся меню надо выбрать ЗАГРУЗИТЬ ФАЙЛЫ:
Откроется стандартное диалоговое окно Windows для выбора файла. Выбираете в этом окне нужный файл (в нашем примере это veslog.zip), и нажимаете кнопку ОТКРЫТЬ.
Когда файл загрузится, то в правом нижнем углу появится небольшое окно с надписью “Загрузка файла завершена”. Теперь ваш файл на Гугл.Диске, но он доступен только вам. Чтобы сделать его доступным для других, надо открыть доступ.
Для этого щёлкаем правой кнопкой мыши по имени файла, и в открывшемся меню выбираем “Открыть доступ”:
Появится окно, где вы можете назначить людей, для которых будет доступен этот файл, а также установить некоторые другие настройки доступа. Но нам ничего этого не надо - мы просто нажмём кнопку ГОТОВО:
В этом же окне можно было сразу скопировать ссылку на файл. Но для случая, когда у вашего файла уже открыт совместный доступ, расскажу, как получить ссылку на файл.
Для этого щёлкаем правой кнопкой мыши по имени файла, и в открывшемся меню выбираем “Копировать ссылку общего доступа”:
Теперь для вашего файла открыт доступ по ссылке и ссылка скопирована в буфер обмена.
Остаётся только создать письмо и вставить туда ссылку (комбинацией клавиш CTRL + V или через контекстное меню - ПРАВАЯ КНОПКА МЫШИ - ВСТАВИТЬ).
Ну что же, на этом всё. Надеюсь, эта статья поможет новичкам. Если для вас это было полезным - не забывайте делиться с друзьями в социальных сетях, а также пишите комментарии (благодарности приветствуются))).
И не забывайте подписываться на новости, чтобы не пропустить новые полезные статьи.
Если что-то ещё осталось непонятно - посмотрите видео:
ПРИМЕЧАНИЕ
Есть ещё один способ отправки “неотправляемых” файлов по почте. Но в Гугле он также не будет работать.
Этот способ заключается в изменении расширения файла. Например, можно заменить ЕХЕ на ВМР.
А при получении файла снова изменить расширение на ЕХЕ. Если для вас такие понятия как “расширение” неведомы,
то узнать об этом можно здесь.
Проблема конвертирования файлов с форматом exe в формат iso довольно распространенная. Первое, что необходимо отметить — такой конвертации не существует, так как это два сильно отличающихся по смыслу объекта. Переделать группу файлов, сконвертированную архиватором в формате exe (но не исполняемый файл/программу) в образ iso действительно можно, ниже рассказано, как это сделать.
Любая программа — готовый набор файлов, временно заархивированный, чтобы тратилось меньше памяти на жестких дисках. Получая файл в формате exe, пользователь получает именно архив, который, во время установки, распаковывается.
Чтобы превратить файл exe в iso, необходимо распаковать архив и записать его на флешку. Этот вариант подходит для некоторых версий Windows.
Важно! Обратите внимание, что исполняемый файл exe, если он устанавливается в формате программы не может быть переделан в iso. Превратить в образ для записи можно только те файлы с расширением exe, которые являются самораспаковывающимся архивом.
Автор считает, что эти материалы могут вам помочь:
Скачать эту утилиту можно по этой ссылке http://www.softportal.com/software-2464-ultraiso.html. Установка простая, следуйте подсказкам, появляющимся на экране, ничего особенного для нее не требуется. После, воспользуйтесь такой инструкцией:
Откройте программу UltraISO.
Файлы, которые вы получили из самораспаковывающегося архива перетяните в правое верхнее окно. Если простым перетягиванием переместить файлы не получается, то воспользуйтесь таким путем: Файл => Открыть => Выберите папку, в которую распаковали exe => В правом нижнем углу, где стоит "iso файлы", выберите "Все файлы". Выделите все объекты из архива exe и нажмите открыть.
Выберите "Инструменты" => Создать образ CD.
Выбрав стандартные (установлены по умолчанию) или нужные вам параметры нажмите “Сделать”.
Через несколько минут образ будет создан и помещен в ту папку, которую вы указали.
Если вам нужно записать образ на флешку, читайте нашу инструкцию.
Почитайте внимательней мат.часть на предмет развития и появления .NET. Эта технология развилась благодаря COM-технологии. А все эти Ко-Сервера, Ко-Клиенты ничто иное как исполнимые файлы прописанные в реестре.Не связывался с COM, но хорошо представляю, как загружаются .net приложения. Система вместе с исполняемым файлом загружает mscoree.dll передаёт управление _CorExeMain, а не нативной точке входа в exe файле. На системах до Windows XP, где загрузчик не подозревает о существовании .net, исполняемый файл загружается обычным образом, и для того, чтобы .net приложения могли загружаться и в старых системах, в каждой сборке есть небольшой stub для Windows, состоящий из одной команды:
jmp _CorExeMain
Это должен разработчик заботится. При написании инсталяционного скрипта для продукта он должен обнаружить нужное и выдать сообщение чего не хватает.Да, так говорит Microsoft. Но пользователю никто не запрещает запустить уже установленную программу после переустановки операционной системы. Писали бы в ошибке тогда «Переустановите программу».
Сейчас он способен собрать Hello World, но в этой статье я хочу рассказать не про парсинг и внутреннее устройство компилятора, а про такую важную часть как побайтовая сборка exe файла.
Обычно работа с exe файлами заключается в изучении или модификации их структуры. Сами же исполняемые файлы при этом формируют компиляторы, и этот процесс кажется немного магическим для разработчиков.
Но сейчас мы с вами попробуем это исправить!
Для сборки нашей программы нам потребуется любой HEX редактор (лично я использовал HxD).
Для старта возьмем псевдокод:
Исходный кодfunc MessageBoxA(u32 handle, PChar text, PChar caption, u32 type) i32 ['user32.dll'] func ExitProcess(u32 code) ['kernel32.dll'] func main() { MessageBoxA(0, 'Hello World!', 'MyApp', 64) ExitProcess(0) }
Более-менее важные поля я отметил, остальные заполнены нулями.
Стуктура IMAGE_DOS_HEADERStruct IMAGE_DOS_HEADER { u16 e_magic // 0x5A4D "MZ" u16 e_cblp // 0x0080 128 u16 e_cp // 0x0001 1 u16 e_crlc u16 e_cparhdr // 0x0004 4 u16 e_minalloc // 0x0010 16 u16 e_maxalloc // 0xFFFF 65535 u16 e_ss u16 e_sp // 0x0140 320 u16 e_csum u16 e_ip u16 e_cs u16 e_lfarlc // 0x0040 64 u16 e_ovno u16[4] e_res u16 e_oemid u16 e_oeminfo u16[10] e_res2 u32 e_lfanew // 0x0080 128 }
Отлично, теперь, когда нам известна структура заголовка DOS Header запишем ее в наш файл.
(1) RAW DOS Header (Offset 0x00000000)4D 5A 80 00 01 00 00 00 04 00 10 00 FF FF 00 00 40 01 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00
УточнениеГотово, первые 64 байта записали. Теперь нужно добавить еще 64, это так называемый DOS Stub (Заглушка). Во время запуска из-под DOS, она должна уведомить пользователя что программа не предназначена для работы в этом режиме.Сначала я использовал левую колонку как на скриншоте для указания смещения внутри файла, но тогда неудобно копировать исходный текст, приходится обрезать каждую строку.
Поэтому для удобства в первой скобке каждого блока указан порядок добавления в файл, а в последней смещение в файле (Offset) по которому должен располагаться данный блок.
Например, первый блок мы вставляем по смещению 0x00000000, и он займет 64 байта (0x40 в 16-ричной системе), следующий блок мы будем вставлять уже по этому смещению 0x00000040 и т.д.
Но в целом, это маленькая программа под DOS которая выводит строку и выходит из программы.
Запишем наш Stub в файл и рассмотрим его детальнее.
0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F 74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20 6D 6F 64 65 2E 0D 0A 24 00 00 00 00 00 00 00 00
0000 push cs ; Запоминаем Code Segment(CS) (где мы находимся в памяти) 0001 pop ds ; Указываем что Data Segment(DS) = CS 0002 mov dx, 0x0E ; Указываем адрес начала строки DS+DX, которая будет выводиться до символа $(Конец строки) 0005 mov ah, 0x09 ; Номер инструкции (Вывод строки) 0007 int 0x21 ; Вызов системного прерывания 0x21 0009 mov ax, 0x4C01 ; Номер инструкции 0x4C (Выход из программы) ; Код выхода из программы 0x01 (Неудача) 000c int 0x21 ; Вызов системного прерывания 0x21 000e "This program cannot be run in DOS mode.\x0D\x0A$" ; Выводимая строка
Код заглушки может немного отличатся (от компилятора к компилятору) я сравнивал gcc и delphi, но общий смысл одинаковый.
А еще забавно, что строка заглушки заканчивается как \x0D\x0D\x0A$. Скорее всего причина такого поведения в том, что c++ по умолчанию открывает файл в текстовом режиме. В результате символ \x0A заменяется на последовательность \x0D\x0A. В результате получаем 3 байта: 2 байта возврата каретки Carriage Return (0x0D) что бессмысленно, и 1 на перевод строки Line Feed (0x0A). В бинарном режиме записи (std::ios::binary) такой подмены не происходит.
Для проверки корректности записи значений я буду использовать Far с плагином ImpEx:
Struct IMAGE_NT_HEADERS64 { u32 Signature // 0x4550 "PE" Struct IMAGE_FILE_HEADER { u16 Machine // 0x8664 архитектура x86-64 u16 NumberOfSections // 0x03 Количество секций в файле u32 TimeDateStamp // Дата создания файла u32 PointerToSymbolTable u32 NumberOfSymbols u16 SizeOfOptionalHeader // Размер IMAGE_OPTIONAL_HEADER64 (Ниже) u16 Characteristics // 0x2F } Struct IMAGE_OPTIONAL_HEADER64 { u16 Magic // 0x020B Указывает что наш заголовок для PE64 u8 MajorLinkerVersion u8 MinorLinkerVersion u32 SizeOfCode u32 SizeOfInitializedData u32 SizeOfUninitializedData u32 AddressOfEntryPoint // 0x1000 u32 BaseOfCode // 0x1000 u64 ImageBase // 0x400000 u32 SectionAlignment // 0x1000 (4096 байт) u32 FileAlignment // 0x200 u16 MajorOperatingSystemVersion // 0x05 Windows XP u16 MinorOperatingSystemVersion // 0x02 Windows XP u16 MajorImageVersion u16 MinorImageVersion u16 MajorSubsystemVersion // 0x05 Windows XP u16 MinorSubsystemVersion // 0x02 Windows XP u32 Win32VersionValue u32 SizeOfImage // 0x4000 u32 SizeOfHeaders // 0x200 (512 байт) u32 CheckSum u16 Subsystem // 0x02 (GUI) или 0x03 (Console) u16 DllCharacteristics u64 SizeOfStackReserve // 0x100000 u64 SizeOfStackCommit // 0x1000 u64 SizeOfHeapReserve // 0x100000 u64 SizeOfHeapCommit // 0x1000 u32 LoaderFlags u32 NumberOfRvaAndSizes // 0x16 Struct IMAGE_DATA_DIRECTORY [16] { u32 VirtualAddress u32 Size } } }
Далее идет заголовок IMAGE_FILE_HEADER общий для архитектур x86 и x64.
Machine — Указывает для какой архитектуры предназначен код в нашем случае для x64
NumberOfSections — Количество секции в файле (О секциях чуть ниже)
TimeDateStamp — Дата создания файла
SizeOfOptionalHeader — Указывает размер следующего заголовка IMAGE_OPTIONAL_HEADER64, ведь он может быть заголовком IMAGE_OPTIONAL_HEADER32.
Characteristics — Здесь мы указываем некоторые атрибуты нашего приложения, например, что оно является исполняемым (EXECUTABLE_IMAGE) и может работать более чем с 2 Гб RAM (LARGE_ADDRESS_AWARE), а также что некоторая информация была удалена (на самом деле даже не была добавлена) в файл (RELOCS_STRIPPED | LINE_NUMS_STRIPPED | LOCAL_SYMS_STRIPPED).
SizeOfCode — Размер исполняемого кода в байтах (секция .text)
SizeOfInitializedData — Размер инициализированных данных (секция .rodata)
SizeOfUninitializedData — Размер не инициализированных данных (секция .bss)
BaseOfCode — указывает на начало секции кода блок
SectionAlignment — Размер по которому нужно выровнять секции в памяти
FileAlignment — Размер по которому нужно выровнять секции внутри файла
SizeOfImage — Размер всех секций программы
SizeOfHeaders — Размер всех заголовков вместе (IMAGE_DOS_HEADER, DOS Stub, IMAGE_NT_HEADERS64, IMAGE_SECTION_HEADER[IMAGE_FILE_HEADER.NumberOfSections]) выровненный по FileAlignment
Subsystem — Указывает тип нашей программы GUI или Console
MajorOperatingSystemVersion, MinorOperatingSystemVersion, MajorSubsystemVersion, MinorSubsystemVersion — Говорят о том на какой системе можно запускать данный exe, и что он может поддерживать. В нашем случае мы берем значение 5.2 от Windows XP (x64).
SizeOfStackReserve — Указывает сколько приложению нужно зарезервировать памяти под стек. Этот параметр по умолчанию составляет 1 Мб, максимально можно указать 1Гб. Вроде как умные программы на Rust умеют считать необходимый размер стека, в отличии от программ на C++ где этот размер нужно править вручную.
SizeOfStackCommit — Размер по умолчанию составляет 4 Кб. Как должен работать данный параметр пока не разобрался.
SizeOfHeapReserve — Указывает сколько резервировать памяти под кучу. Равен 1 Мб по умолчанию.
SizeOfHeapCommit — Размер по умолчанию равен 4 Кб. Подозреваю что работает аналогично SizeOfStackCommit, то есть пока неизвестно как.
IMAGE_DATA_DIRECTORY — массив записей о каталогах. В теории его можно уменьшить, сэкономив пару байт, но вроде как все описывают все 16 полей даже если они не нужны. А теперь чуть подробнее.
У каждого каталога есть свой номер, который описывает, где хранится его содержимое. Пример:
Export(0) — Содержит ссылку на сегмент который хранит экспортируемые функции. Для нас это было бы актуально если бы мы создавали DLL. Как это примерно должно работать можно посмотреть на примере следующего каталога.
Import(1) — Этот каталог указывает на сегмент с импортируемыми функциями из других DLL. В нашем случае значения VirtualAddress = 0x3000 и Size = 0xB8. Это единственный каталог, который мы опишем.
Resource(2) — Каталог с ресурсами программы (Изображения, Текст, Файлы и т.д.)
Значения других каталогов можно посмотреть в документации.
50 45 00 00 64 86 03 00 F4 70 E8 5E 00 00 00 00 00 00 00 00 F0 00 2F 00 0B 02 00 00 3D 00 00 00 13 00 00 00 00 00 00 00 00 10 00 00 00 10 00 00 00 00 40 00 00 00 00 00 00 10 00 00 00 02 00 00 05 00 02 00 00 00 00 00 05 00 02 00 00 00 00 00 00 40 00 00 00 02 00 00 00 00 00 00 02 00 00 00 00 00 10 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 30 00 00 B8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Далее описываем все секции нашего приложения согласно структуре IMAGE_SECTION_HEADER
Структура IMAGE_SECTION_HEADERStruct IMAGE_SECTION_HEADER { i8[8] Name u32 VirtualSize u32 VirtualAddress u32 SizeOfRawData u32 PointerToRawData u32 PointerToRelocations u32 PointerToLinenumbers u16 NumberOfRelocations u16 NumberOfLinenumbers u32 Characteristics }
Name — имя секции из 8 байт, может быть любым
VirtualSize — сколько байт копировать из файла в память
VirtualAddress — адрес секции в памяти выровненный по SectionAlignment
SizeOfRawData — размер сырых данных выровненных по FileAlignment
PointerToRawData — адрес секции в файле выровненный по FileAlignment
Characteristics — Указывает какие данные хранит секция (Код, инициализированные или нет данные, для чтения, для записи, для исполнения и др.)
Почему Virtual Address (VA) начинается с 1000, а не с нуля я не знаю, но так делают все компиляторы, которые я рассматривал. В результате 1000 + 3 секции * 1000 (SectionAlignment) = 4000 что мы и записали в SizeOfImage. Это полный размер нашей программы в виртуальной памяти. Вероятно, используется для выделения места под программу в памяти.
Name | RAW Addr | RAW Size | VA | VA Size | Attr --------+---------------+---------------+-------+---------+-------- .text | 200 | 200 | 1000 | 3D | CER .rdata | 400 | 200 | 2000 | 13 | I R .idata | 600 | 200 | 3000 | B8 | I R
I — Initialized data, инициализированные данные
U — Uninitialized data, не инициализированные данные
C — Code, содержит исполняемый код
E — Execute, позволяет исполнять код
R — Read, позволяет читать данные из секции
W — Write, позволяет записывать данные в секцию
.text (.code) — хранит в себе исполняемый код (саму программу), атрибуты CE
.rdata (.rodata) — хранит в себе данные только для чтения, например константы, строки и т.п., атрибуты IR
.data — хранит данные которые можно читать и записывать, такие как статические или глобальные переменные. Атрибуты IRW
.bss — хранит не инициализированные данные, такие как статические или глобальные переменные. Кроме того, данная секция обычно имеет нулевой RAW размер и ненулевой VA Size, благодаря чему не занимает места в файле. Атрибуты URW
.idata — секция содержащая в себе импортируемые из других библиотек функции. Атрибуты IR
Важный момент, секции должны следовать друг за другом. При чем как в файле, так и в памяти. По крайней мере когда я менял их порядок произвольно программа переставала запускаться.
Теперь, когда нам известно какие секции будет содержать наша программа запишем их в наш файл. Тут смещение оканчивается на 8 и запись будет начинаться с середины файла.
(4) RAW Sections (Offset 0x00000188) 2E 74 65 78 74 00 00 00 3D 00 00 00 00 10 00 00 00 02 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 60 2E 72 64 61 74 61 00 00 13 00 00 00 00 20 00 00 00 02 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 40 2E 69 64 61 74 61 00 00 B8 00 00 00 00 30 00 00 00 02 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 00 40
Взглянем как выглядит блок секций в Far:
Далее мы запишем в наш файл сами секции, но тут есть один нюанс.
Как вы могли заметить на примере SizeOfHeaders, мы не можем просто записать заголовок и перейти к записи следующего раздела. Так как что бы записать заголовок мы должны знать сколько займут все заголовки вместе. В результате нам нужно либо посчитать заранее сколько понадобиться места, либо записать пустые (нулевые) значения, а после записи всех заголовков вернуться и записать уже их реальный размер.
Поэтому программы компилируются в несколько проходов. Например секция .rdata идет после секции .text, при этом мы не можем узнать виртуальный адрес переменной в .rdata, ведь если секция .text разрастется больше чем на 0x1000 (SectionAlignment) байт, она займет адреса 0x2000 диапазона. И соответственно секция .rdata будет находиться уже не в адресе 0x2000, а в адресе 0x3000. И нам будет необходимо вернуться и пересчитать адреса всех переменных в секции .text которая идет перед .rdata.
Но в данном случае я уже все рассчитал, поэтому будем сразу записывать блоки кода.
0000 push rbp 0001 mov rbp, rsp 0004 sub rsp, 0x20 0008 mov rcx, 0x0 000F mov rdx, 0x402000 0016 mov r8, 0x40200D 001D mov r9, 0x40 0024 call QWORD PTR [rip + 0x203E] 002A mov rcx, 0x0 0031 call QWORD PTR [rip + 0x2061] 0037 add rsp, 0x20 003B pop rbp 003C ret
Но скажем так, если бы это была не функция main, а подфункция следовало бы сделать именно так.
А вот первые 3 в данном случае хоть и не обязательны, но желательны. Например, если бы мы использовали не MessageBoxA, а printf то без этих строк получили бы ошибку.
Согласно соглашению о вызовах для 64-разрядных систем MSDN, первые 4 параметра передаются в регистрах RCX, RDX, R8, R9. Если они туда помещаются и не являются, например числом с плавающей точкой. А остальные передаются через стек.
По идее если мы передаем 2 аргумента функции, то должны передать их через регистры и зарезервировать под них два места в стеке, что бы при необходимости функция могла скинуть регистры в стек. Так же мы не должны рассчитывать, что нам вернут эти регистры в исходном состоянии.
Так вот проблема функции printf заключается в том, что, если мы передаем ей всего 1 аргумент, она все равно перезапишет все 4 места в стеке, хотя вроде бы должна перезаписать только одно, по количеству аргументов.
Поэтому если не хотите, чтобы программа себя странно вела, всегда резервируйте как минимум 8 байт * 4 аргумента = 32(0x20) байт, если передаете функции хотя бы 1 аргумент.
Рассмотрим блок кода с вызовами функций
MessageBoxA(0, 'Hello World!', 'MyApp', 64) ExitProcess(0)
rcx = 0
rdx = абсолютный адрес строки в памяти ImageBase + Sections[".rdata"].VirtualAddress + Смещение строки от начала секции, строка читается до нулевого байта
r8 = аналогично предыдущему
r9 = 64(0x40) MB_ICONINFORMATION, значок информации
А далее идет вызов функции MessageBoxA, с которым не все так просто. Дело в том, что компиляторы стараются использовать как можно более короткие команды. Чем меньше размер команды, тем больше таких команд влезет в кэш процессора, соответственно, будет меньше промахов кэша, подзагрузок и выше скорость работы программы. Для более подробной информации по командам и внутренней работе процессора можно обратиться к документации Intel 64 and IA-32 Architectures Software Developer’s Manuals.
Мы могли бы вызвать функцию по полному адресу, но это заняло бы как минимум (1 опкод + 8 адрес = 9 байт), а с относительным адресом команда call занимает всего 6 байт.
Давайте взглянем на эту магию поближе: rip + 0x203E, это ни что иное, как вызов функции по адресу, указанному нашим смещением.
Я подсмотрел немного вперед и узнал адреса нужных нам смещений. Для MessageBoxA это 0x3068, а для ExitProcess это 0x3098.
Пора превратить магию в науку. Каждый раз, когда опкод попадает в процессор, он высчитывает его длину и прибавляет к текущему адресу инструкции (RIP). Поэтому, когда мы используем RIP внутри инструкции, этот адрес указывает на конец текущей инструкции / начало следующей.
Для первого call смещение будет указывать на конец команды call это 002A не забываем что в памяти этот адрес будет по смещению Sections[".text"].VirtualAddress, т.е. 0x1000. Следовательно, RIP для нашего call будет равен 102A. Нужный нам адрес для MessageBoxA находится по адресу 0x3068. Считаем 0x3068 — 0x102A = 0x203E. Для второго адреса все аналогично 0x1000 + 0x0037 = 0x1037, 0x3098 — 0x1037 = 0x2061.
Именно эти смещения мы и видели в командах ассемблера.
0024 call QWORD PTR [rip + 0x203E] 002A mov rcx, 0x0 0031 call QWORD PTR [rip + 0x2061] 0037 add rsp, 0x20
55 48 89 E5 48 83 EC 20 48 C7 C1 00 00 00 00 48 C7 C2 00 20 40 00 49 C7 C0 0D 20 40 00 49 C7 C1 40 00 00 00 FF 15 3E 20 00 00 48 C7 C1 00 00 00 00 FF 15 61 20 00 00 48 83 C4 20 5D C3 00 00 00 ........ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Хочется отметить что всего лишь 4 строки реального кода содержат весь наш код на ассемблере. А все остальное нули что бы набрать FileAlignment. Последней строкой заполненной нулями будет 0x000003F0, после идет 0x00000400, но это будет уже следующий блок. Итого в файле уже 1024 байта, наша программа весит уже целый Килобайт! Осталось совсем немного и ее можно будет запустить.
0400 "Hello World!\0" 040D "MyApp\0"
48 65 6C 6C 6F 20 57 6F 72 6C 64 21 00 4D 79 41 70 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Первое что нас ждет новая структура IMAGE_IMPORT_DESCRIPTOR
Структура IMAGE_IMPORT_DESCRIPTORStruct IMAGE_IMPORT_DESCRIPTOR { u32 OriginalFirstThunk (INT) u32 TimeDateStamp u32 ForwarderChain u32 Name u32 FirstThunk (IAT) }
OriginalFirstThunk — Адрес указывает на список имен импортируемых функций, он же Import Name Table (INT)
Name — Адрес, указывающий на название библиотеки
FirstThunk — Адрес указывает на список адресов импортируемых функций, он же Import Address Table (IAT)
func MessageBoxA(u32 handle, PChar text, PChar caption, u32 type) i32 ['user32.dll'] func ExitProcess(u32 code) ['kernel32.dll']
58 30 00 00 00 00 00 00 00 00 00 00 3C 30 00 00 68 30 00 00 88 30 00 00 00 00 00 00 00 00 00 00 48 30 00 00 98 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
INT | Time | Forward | Name | IAT --------+--------+----------+--------+-------- 0x3058 | 0x0 | 0x0 | 0x303C | 0x3068 0x3088 | 0x0 | 0x0 | 0x3048 | 0x3098 0x0000 | 0x0 | 0x0 | 0x0000 | 0x0000
063С "user32.dll\0" 0648 "kernel32.dll\0"
75 73 65 72 33 32 2E 64 6C 6C 00 00 6B 65 72 6E 65 6C 33 32 2E 64 6C 6C 00 00 00 00
78 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 78 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4D 65 73 73 61 67 65 42 6F 78 41 00 00 00
Подсказка, вспомните что секция .idata соответствует смещению в файле 0x0600, а в памяти 0x3000. Для первой библиотеки INT равен 3058, значит в файле это будет смещение 0x0658. По этому адресу видим запись 0x3078 и вторую нулевую. Означающую конец списка. 3078 ссылается на 0x0678 это RAW-строка
«00 00 4D 65 73 73 61 67 65 42 6F 78 41 00 00 00»
Первые 2 байта нас не интересуют и равны нулю. А вот дальше идет строка с названием функции, заканчивающаяся нулем. То есть мы можем представить её как "\0\0MessageBoxA\0".
При этом IAT ссылается на аналогичную таблице IAT структуру, но только в нее при запуске программы будут загружены адреса функций. Например, для первой записи 0x3068 в памяти будет значение отличное от значения 0x0668 в файле. Там будет адрес функции MessageBoxA загруженный системой к которому мы и будем обращаться через вызов call в коде программы.
И последний кусочек пазла, библиотека kernel32. И не забываем добить нулями до SectionAlignment.
(10) RAW kernel32.dll (Offset 0x00000688-0x00000800) A8 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A8 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 45 78 69 74 50 72 6F 63 65 73 73 00 00 00 00 00 00 00 00 00 00 00 ........ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Отлично! Все нормально определилось, значит теперь наш файл готов к запуску.
Барабанная дробь…
Поздравляю, мы справились!
Файл занимает 2 Кб = Заголовки 512 байт + 3 секции по 512 байт.
Число 512(0x200) ни что иное, как FileAlignment, который мы указали в заголовке нашей программы.
Дополнительно:
Если хочется вникнуть чуть глубже, можно заменить надпись «Hello World!» на что-нибудь другое, только не забудьте изменить адрес строки в коде программы (секция .text). Адрес в памяти 0x00402000, но в файле будет обратный порядок байт 00 20 40 00.
Или квест чуть сложнее. Добавить в код вызов ещё одного MessageBox. Для этого придется скопировать предыдущий вызов, и пересчитать в нем относительный адрес (0x3068 — RIP).
Если кто-то собрал свой exe значит мой труд был не напрасен.
Думаю в скором времени создать ELF файл похожим образом, интересна ли будет такая статья?)
Ссылки:
Руслана Якушева
Модуль перезаписи URL - это расширение IIS, которое доступно для загрузки для автономного сервера IIS, а также предварительно установлено на любом веб-сайте на веб-сайтах Windows Azure (WAWS) и доступно для вашего использования.В этом пошаговом руководстве вы узнаете, как создать и протестировать набор правил перезаписи для модуля перезаписи URL.
Для этого пошагового руководства требуются следующие предварительные условия:
Чтобы продемонстрировать, как работает модуль перезаписи URL, мы будем использовать простой тестовый ASP.NET-страницу. Эта страница считывает переменные веб-сервера и выводит их значения в браузер.
Скопируйте следующий код ASP.NET и поместите его в папку% SystemDrive % \ inetpub \ wwwroot \ в файле с именем article.aspx :
<% @ Page Language = "C #"%> Тест модуля перезаписи URL Тестовая страница модуля перезаписи URL
<таблица> Переменная сервера Значение Исходный URL: <% = Запрос.ServerVariables ["HTTP_X_ORIGINAL_URL"]%> Конечный URL: <% = Request.ServerVariables ["SCRIPT_NAME"] + "?" + Request.ServerVariables ["QUERY_STRING"]%>
После копирования этого файла перейдите к http: //localhost/article.aspx
и проверьте, правильно ли отображается страница в браузере.
Мы создадим простое правило перезаписи, которое перезапишет URL-адреса в следующем формате:
http: // localhost / article / 342 / some-article-title
по адресу:
http: //localhost/article.aspx? Id = 342 & title = some-article-title
.
Мы создадим правило перезаписи с помощью пользовательского интерфейса перезаписи URL в диспетчере IIS. Для этого выполните следующие действия:
Теперь вы должны определить фактическое правило перезаписи. В модуле перезаписи URL-адреса правило перезаписи определяется указанием четырех необходимых частей информации:
В текстовом поле Имя введите имя, которое будет однозначно определять правило, например: «Переписать в article.aspx».
В текстовом поле Pattern введите следующую строку:
^ article / ([0-9] +) / ([_ 0-9a-z -] +)
Эта строка является регулярным выражением, которое указывает, что шаблон будет соответствовать любой строке URL-адреса, удовлетворяющей следующим условиям:
Обратите внимание, что некоторые части регулярного выражения заключены в круглые скобки. Эти круглые скобки создают группы захвата, на которые позже можно будет ссылаться в правиле с помощью обратных ссылок.
Поскольку создаваемое нами правило должно перезаписывать URL-адрес, выберите тип действия Rewrite , который указан в поле группы Action .В текстовом поле Rewrite URL: введите следующую строку:
article.aspx? Id = {R: 1} & title = {R: 2}
Эта строка определяет новое значение, на которое нужно переписать входной URL. Обратите внимание, что для значений параметров строки запроса мы использовали {R: 1} и {R: 2}, которые являются обратными ссылками на группы захвата, которые были определены в шаблоне правила с помощью круглых скобок.
Оставьте значения по умолчанию для всех остальных настроек. Страница свойств Edit Inbound Rule должна выглядеть следующим образом:
Сохраните правило, нажав Применить справа.
Правила перезаписи хранятся либо в файле ApplicationHost.config, либо в файлах Web.config. Чтобы проверить конфигурацию только что созданного правила, откройте файл Web.config, расположенный в% SystemDrive % \ inetpub \ wwwroot. В этом файле вы должны увидеть раздел
, содержащий это определение правила:
<перезапись> <правила>
Приведенный выше синтаксис также применяется к настройке перезаписи URL-адресов в Web.config на веб-сайтах Windows Azure (WAWS).
Чтобы проверить правильность перезаписи URL-адресов правилом, откройте веб-браузер и запросите следующий URL-адрес:
http: // localhost / article / 234 / some-title
Вы должны увидеть, что правило перезаписи на вашем веб-сервере изменило исходный URL-адрес на Article.aspx и передал "234" и "some-title" в качестве значений параметров строки запроса.
Теперь мы создадим правило перенаправления, которое будет перенаправлять все URL-адреса в следующем формате:
http: // localhost / blog / some-other-title / 543
в следующий формат:
http: // localhost / article / 543 / some-other-title
Правило перенаправления позволяет нескольким URL-адресам указывать на одну веб-страницу.
Для этого откройте пользовательский интерфейс просмотра функции перезаписи URL в диспетчере IIS.blog / ([_ 0-9a-z -] +) / ([0-9] +) (Этот шаблон будет соответствовать строке URL-адреса, которая начинается с "blog" и захватывает второй и третий сегменты URL-адреса в обратном направлении. ссылки.)
Введите имя, шаблон и действие, как показано ниже:
Введите URL-адрес перенаправления, как показано ниже:
Оставьте значения по умолчанию для всех остальных настроек. Сохраните правило, нажав Применить справа.
Чтобы проверить правильность перенаправления запросов правилом, откройте веб-браузер и запросите следующий URL-адрес:
http: // localhost / blog / some-other-title / 323
Вы должны увидеть, что браузер был перенаправлен на http: // localhost / article / 323 / some-other-title
в результате выполнения правила перенаправления, а затем запрос был переписан в соответствии с созданным вами правилом перезаписи. ранее.
Третье правило, которое мы создадим, используется для блокировки всех запросов к веб-сайту, если для этих запросов не задан заголовок хоста. Этот тип правила полезен, когда вы хотите предотвратить попытки взлома, которые выполняются путем отправки HTTP-запросов на IP-адрес сервера вместо использования имени хоста.
Мы создадим это правило без использования диспетчера IIS. Откройте файл Web.config в папке % SystemDrive% \ inetpub \ wwwroot \
, которую вы использовали для статьи.aspx тестовый файл в начале этой статьи. Найдите раздел
. Вставьте следующее правило в коллекцию
<условия>
Раздел
должен выглядеть следующим образом:
<перезапись> <правила>
Давайте проанализируем правило, чтобы понять, что оно делает.
Указанный выше элемент говорит, что правило будет соответствовать любой строке URL.
Указанный выше элемент добавляет условие к правилу, которое извлекает значение заголовка хоста путем чтения переменной сервера HTTP_HOST, сопоставляет его с шаблоном «localhost» и затем отменяет результат сопоставления.Другими словами, условие проверяет, что заголовок хоста не соответствует "localhost".
Указанный выше элемент сообщает модулю перезаписи URL-адреса завершить HTTP-запрос.
Чтобы проверить это правило, откройте веб-браузер и отправьте запрос по адресу http://127.0.0.1/article/234/some-title
. Вы должны увидеть браузер, который не получает ответа от сервера. Однако, если вы запросите http: // localhost / article / 234 / some-title
, тогда веб-сервер ответит успешно.
Неудачное отображение будет следующим:
Успешное отображение будет следующим:
В этом пошаговом руководстве вы узнали, как настроить правила перезаписи URL-адресов с помощью диспетчера IIS или вручную отредактировав файлы Web.config. Правила, созданные в этом пошаговом руководстве, продемонстрировали некоторые важные функции модуля перезаписи URL, такие как поддержка регулярных выражений и возможность использования заголовков HTTP и переменных сервера для принятия решений о перезаписи.
.