Тут повыше уже разобрали, что пример не самый удачный. Идея в том, что берется код функции подсчета факториала для конкретной константы. Чтобы считать для любого числа код следует сделать универсальным - как минимум добавить параметр (число, для которого считается факториал), передаваймый через стек или через регистр. В примере предлагается не передавать параметр, а прямо "вписать" его в код функции (на место той константы, с чего все начиналось). Технически в коде:
B8 00 00 00 00 Тут последние 4 байта - константа 0.
Однако выполнение первой инструкции в коде выше запишет на растоянии 2-х байтов от метки factorial константу 5 (4 байта). Т.к. размер инструкции push ebp один байт, то запись произойдет поверх 4-х нулей и последняя инструкция примет вид:
Ну тут ответ вроде в тексте ясно прописан. MOV потому, что программа "на лету" меняет константу в инструкции. Для простого обнуления XOR, конечно оптимальнее и короче.
Ну, то есть, я понял с кем имею дело. Напомню, я спросил " прочитайте мой первый коментарий. К нему еще вопросы у вас есть?" Не вижу ни одного вопроса. Вы - чистый "grammar nazi" в отношении документации по компиляторам. "Не смей писать отсебятину - пиши строго по стандарту!" - вот ваш дивиз. Ок. К сожалению вы задели меня и придется быть таким же занудой, как вы. Откину вежливость и только факты.
Вывод о путанице между компилятором и линкером делается не из этого, а уже из другого: из того факта, что во всех ваших (в смысле у вас двоих, а не лично Ваших) постах ни разу не фигурировало слово «линкер», а везде фигурирова «компилятор».
Вранье. Откройте глаза и почитайте внимательно.
Но вы оба продолжили, причём спорили...
Мы не спорили, а дискутировали какого года EXE файлы имели таблицу relocation, а когда нет. Этот диалог, надеюсь пошел нам обоим во благо. Но нет, некоторые решили всех поставить на место. Спасибо. Жду от вас детальной статьи по поводу компиляторов и линкеров, где я обязательно найду пару опечаток и прокомментирую.
что вам 53 и вы собаку съели...
Вы просто провидец. )
но как будто бы в вашей картине мира других линкеров нет.
Они есть. Больше того, у меня есть свой собственный линкер и компилятор (о чудо, у меня это 2 в одном и называю я его компилятор). Я обязательно приведу "другие" (не мои) линкеры, когда вы напишите соответствующую статью на habr.
...но NASM — не компоновщик.
Нормально. Человек видит, что я не знаю что такое NASM и обвиняет меня, что я не знаю, что он, оказывается, не компановщик. Не знаю что тут комментировать.
...то есть автор собирает бинарник под линукс. А вы ему пишите о проблемах загрузки такого исполняемого файла под Win64.
Если вы реально читали мои комментарии, то там написано (во втором), что " Это не спор с кем-то, а дополнение текста статьи для тех, кто захочет воспользоваться модификацией кода в runtime." Ну ведь верно, как я не догадался, факториал только под линукс же считать можно...
Вот эти все ваши мелкие оговорки ...
Ну да, у вас ведь ни одной "оговорки" (я написал об одной опечатке, при чем не по сути, а по форме) нет. Вы же не писали потом коммент о ваших собственных оговорках. Браво! Отличная придирка. )
Опять таки, решать проблему с ASLR вы предлагаете установкой флага IMAGE_FILE_RELOCS_STRIPPED, хотя логично было бы ассемберный код подправить так, чтобы вшитые в нём адресные константы попали в таблицу релокаций, либо же код самомодификации написать так, чтобы он определял реальную базу, вычислял дельту релокации на основании разницы между фактической базой и предпочитаемой базой и приплюсовывал эту дельту релокации всюду, где он в генерируемый машинный код вплетает адресные константы, заложенные в код на стадии написания ассемблерного исходника.
Я написал самое простое решение. Когда началась дискуссия, я написал и остальные. Ровно то, что вы тут с пафосом излагаете (по мне так очевидные вещи). Вы этого не увидели? Да вы точно не читали всей дискуссии.
Спасибо еще раз за приведение общеизвестной информации. Хотелось бы понять по какому именно пункту вы считаете, что я ошибаюсь кроме следующего? Когда я писал: " Решение — указать в свойствах секции кода не только бит "разрешение записи", но и бит "грузить только по указанной базе"." я действительно описался - он не в свойствах секции (что очевидно). Это никак не меняет сути. Такой бит есть, лишь в другом месте. Делать из этого вывод " оба не в состоянии разделить компилятор и линкер ", мне кажется немного преждевременным. Вы не учитель в школе? Мне кажется неправильным писать слишком объемные комментарии, приводя уже давно хорошо задокументированную информацию. Я не про вас, про себя. Поэтому, когда я пытаюсь выразить лишь суть, мой комментарий может выглядеть "неполным", а для кого-то "неверным". Я не против. Если кто-то считает, что я "вообще ничего не понимаю", я тоже не против. Если кто-то спрашивает - я уточняю. И на счет спора - прочитайте мой первый коментарий. К нему еще вопросы у вас есть?
Не понял что за "большой комент" (ссылка верная?) Интересно чем это противоречит тому, что я написал? Начиная с появления 32-х битного защищенного режима (386+) и реализации его в Win32 (начиная с WinNT и затем в Win2000+) появилась возможность использовать виртуальное личное 4Гб адресное про-во. Поэтому считалось, что каждый EXE сможет грузиться по тому адресу по которому желает. Как и @Angeldзаметил, в DLL она все равно была нужна, т.к. они делил общее пространство. Так зачем по вашему ее исключал из образа линкер у EXE? И вы за все линкеры сразу говорите? Я лично знаю (помню) несколько компиляторов Си, которые вполне себе таблицу relocation оставляли. И я бы воздержался от подобного "у обоих в голове каша" (лично мне вот видится наоборот). Хотя бы прочтите с чего именно начался диалог (мой первый коммент).
Выскажу предположение по поводу ebp. Возможно программа была написана на Си (или др.) и далее адаптировался ассемблерный листинг компилятора. Автор просто решил "не удалять" сохранение ebp по "только ему известным причинам". Ну или так больше похоже на "настоящую" функцию (автор показал, что знаком с прологом и эпилогом). Кстати, остальные лишние и не оптимальные конструкции тоже могут объясняться источником ассемблерного кода.
Ну мое подтверждение касалось лишь отсутствия необходимости лишней инструкции. Думаю, что временные параметры могут зависеть от поколения процессоров. Кстати, не удивлюсь, если современные процессоры способны просто игнорировать вторую (ненужную) инструкцию, а это выигрывает время.
Мне 53 года, я хорошо знаю формат исполняемых файлов со времен DOS (вообще CPM). В EXE файлах DOS и Win16, в которых используется хотя бы одна глобальная переменная, вообще не возможно без таблицы relocation (хотя формат заголовка там, конечно не как в Win32+). Среди игр под Win32 до 2000 года некоторая часть была без таблицы, т.к. ценился размер файла и лишний килобайт экономили. Только странно сейчас говорить о том, что было 20 лет назад. Только как история, возможно. Могу лишь повторить, что наличие таблицы relocation наоборот дает лишнюю информацию взломщику. Приведите хоть один пример зачем нужна таблица relocation для защиты. Для "перевода" (серьезно?) своего кода в собственную VM используются собственные механизмы. Посмотрите, например DENUVO, как работал StarFoce.
Вот прямо сейчас посмотрел EXE в Windows (в том числе в SysWOW64). Все с таблицей relocation. В "Program Files (x86)" из всех произвольно выбранных - у всех таблица есть. А там полно известных утилит известных фирм. А как у вас с файлами в этих каталогах? Можете назвать какое-нибудь известное ПО (чтобы я мог посмотреть), в котором нет этой таблицы? Если вы программист, вы какой компилятор используете? И вы не первый раз не внимательно читаете, что я пишу. Я сказал лишь, что таблица relocation может быть и не в секции ".reloc", т.к. загрузчику все равно в секции она или нет. Где вы увидели, что "ее компиляторы куда-то прячут в EXE"? Они создают ее ровно так же как и в DLL. А вот различные инструменты могут и перемещать ее. И еще. Упаковщики EXE (уверен, знаете что это такое) могут не иметь этой таблицы - их задача сжать образ. Но в сжатом образе эта таблица все равно есть. Для защиты relocation как раз не нужна - она раскрывает детали работы программы.
В большинстве именно, что есть relocation. И нет, нет тех EXE где компилятор размещает "константы, расчитанные на фиксированный адрес". Такие вещи может сделать только сам программист. И это его головная боль как это решать. Об этом я изначально и писал. Впрочем это выглядит как лекбез. Я лучше ссылку дам на общедоступный хэлп, например по опциям линкека в VS22: https://docs.microsoft.com/en-us/cpp/build/reference/dynamicbase-use-address-space-layout-randomization?view=msvc-170#remarks Достаточно прочитать 10 строк в разделе Remarks. Ответ на ваш вопрос очевиден - никак (т.е. придется грузить по тому адресу, который указан как желательный или вообще не грузить). Именно поэтому компиляторы производят таблицу relocation для всех EXE. Это было раньше, это есть сейчас. Это установилось как обязательный раздел еще в Win16, т.к. там программы разделяют общее адресное пространство, как DLL сейчас. Поэтому это не новшество никак. Простите, когда вы пишите "в огромном количестве старых exe этого нет", вы реально это проверяли. Лично мой опыт (25+ лет занятия защитой прикладного ПО) показывает, что практически в 100% EXE есть таблица relocation. Да, и таблица relocation не обязательно формируется в секцию ".reloc". Она может быть вообще вне секции. Доступ к ней загрузчик получает не так. Посмотрите формат PE заголовка.
так релокейшн не для ручных констант, а для тех которые компилятор в код вставляет, о том что твою константу тоже надо релоцировать компилятор не знает
Хорошо, что вы со мной согласны.
он генерится компиляторами для обычного кода чтобы ОС могла загрузить приложение по любому адресу
Вы реально решили мне объяснить это? Спасибо. :)
и по умолчанию все exe идут без релокейшн с одинаковым адресом 0x40000...
Нет. Relocation по умолчанию генерируется для всех EXE. 0x40000 - лишь "желаемый" адрес загрузки. Похоже лонгрид напрашивается. Множество уязвимостей в серверах (типа Apache) эксплуатируются, исходя из того, что грузится он по одному адресу и дома у хакера и на сервере. Дома отлаживается эксплойт, потом однократно "передается" на сервер и там работает. Поэтому (и не только) в Win64 добавили рандомизатор загрузки. Ему плевать куда вы хотите грузиться - он выбирает случайно один из 256 выравненных адресов и туда грузит EXE. Каждый раз - разный. Очевидно, что без таблицы relocation он этого сделать не сможет. Поэтому они есть во всех EXE, если вы ее сами не "отрубите". Для "чувствительных" к адресу загрузки приложений и есть бит "грузить по желаемому адресу, иначе вообще не грузить".
Насколько я знаю, по умолчанию (честно, не знаю как в NASM, не пользовался) как раз адрес не фиксированный. Только если сами установите. Таблица relocation работает только если вы указываете адрес переменной, используя ее имя. Если вы используете константу (т.е посмотрели адрес и просто указали число, как в моем комментарии и написано), то ни в какие relocation это не попадет, а значит при загрузке по другой базе будет большая проблема. Узнать адрес "где ты находишься" можно и, в данном случае нужно. Именно на то, что проблема есть, и ее надо решать, я и пытался указать. Это не спор с кем-то, а дополнение текста статьи для тех, кто захочет воспользоваться модификацией кода в runtime.
Стоит отметить, что под Win64 используется рандомизатор загрузки образа, т.е. при каждом запуске программы стартовый адрес может (и будет) не одинаковым в том числе и для программ написанных под Win32. В следствии возникает проблема при записи в код констант, означающих адреса (например, адрес переменной) в виде константы. Решение - указать в свойствах секции кода не только бит "разрешение записи", но и бит "грузить только по указанной базе".
Тогда локальные диски нужны лишь чтобы заиметь точную копию мастера перед началом работы и после окончания. Цель, я так понял, восстановить ошибочно удаленный файл. Но это не сработает, если файл был и создан и удален после синхронизации. Какое-то не идеальное решение. Я бы просто раз в день делал полную копию без синхронизации на какой-нибудь бэкап. Тоже не очень, но проще.
Не заметил сразу. Там выше упоминается.
Наверное тут можно упомянуть "Маршалинг": https://docs.microsoft.com/ru-ru/dotnet/standard/native-interop/type-marshaling
Тут повыше уже разобрали, что пример не самый удачный. Идея в том, что берется код функции подсчета факториала для конкретной константы. Чтобы считать для любого числа код следует сделать универсальным - как минимум добавить параметр (число, для которого считается факториал), передаваймый через стек или через регистр. В примере предлагается не передавать параметр, а прямо "вписать" его в код функции (на место той константы, с чего все начиналось).
Технически в коде:
последняя инструкция выглядит так:
B8 00 00 00 00
Тут последние 4 байта - константа 0.
Однако выполнение первой инструкции в коде выше запишет на растоянии 2-х байтов от метки factorial константу 5 (4 байта). Т.к. размер инструкции push ebp один байт, то запись произойдет поверх 4-х нулей и последняя инструкция примет вид:
B8 05 00 00 00
А это уже инструкция:
Ну тут ответ вроде в тексте ясно прописан. MOV потому, что программа "на лету" меняет константу в инструкции. Для простого обнуления XOR, конечно оптимальнее и короче.
Ну, то есть, я понял с кем имею дело. Напомню, я спросил " прочитайте мой первый коментарий. К нему еще вопросы у вас есть?" Не вижу ни одного вопроса.
Вы - чистый "grammar nazi" в отношении документации по компиляторам. "Не смей писать отсебятину - пиши строго по стандарту!" - вот ваш дивиз. Ок.
К сожалению вы задели меня и придется быть таким же занудой, как вы. Откину вежливость и только факты.
Вранье. Откройте глаза и почитайте внимательно.
Мы не спорили, а дискутировали какого года EXE файлы имели таблицу relocation, а когда нет. Этот диалог, надеюсь пошел нам обоим во благо. Но нет, некоторые решили всех поставить на место. Спасибо. Жду от вас детальной статьи по поводу компиляторов и линкеров, где я обязательно найду пару опечаток и прокомментирую.
Вы просто провидец. )
Они есть. Больше того, у меня есть свой собственный линкер и компилятор (о чудо, у меня это 2 в одном и называю я его компилятор). Я обязательно приведу "другие" (не мои) линкеры, когда вы напишите соответствующую статью на habr.
Нормально. Человек видит, что я не знаю что такое NASM и обвиняет меня, что я не знаю, что он, оказывается, не компановщик. Не знаю что тут комментировать.
Если вы реально читали мои комментарии, то там написано (во втором), что " Это не спор с кем-то, а дополнение текста статьи для тех, кто захочет воспользоваться модификацией кода в runtime." Ну ведь верно, как я не догадался, факториал только под линукс же считать можно...
Ну да, у вас ведь ни одной "оговорки" (я написал об одной опечатке, при чем не по сути, а по форме) нет. Вы же не писали потом коммент о ваших собственных оговорках. Браво! Отличная придирка. )
Я написал самое простое решение. Когда началась дискуссия, я написал и остальные. Ровно то, что вы тут с пафосом излагаете (по мне так очевидные вещи). Вы этого не увидели? Да вы точно не читали всей дискуссии.
Адью.
Хорошо. Я не буду с вами спорить.
Спасибо еще раз за приведение общеизвестной информации. Хотелось бы понять по какому именно пункту вы считаете, что я ошибаюсь кроме следующего?
Когда я писал: " Решение — указать в свойствах секции кода не только бит "разрешение записи", но и бит "грузить только по указанной базе"." я действительно описался - он не в свойствах секции (что очевидно). Это никак не меняет сути. Такой бит есть, лишь в другом месте. Делать из этого вывод " оба не в состоянии разделить компилятор и линкер ", мне кажется немного преждевременным. Вы не учитель в школе?
Мне кажется неправильным писать слишком объемные комментарии, приводя уже давно хорошо задокументированную информацию. Я не про вас, про себя. Поэтому, когда я пытаюсь выразить лишь суть, мой комментарий может выглядеть "неполным", а для кого-то "неверным". Я не против. Если кто-то считает, что я "вообще ничего не понимаю", я тоже не против. Если кто-то спрашивает - я уточняю.
И на счет спора - прочитайте мой первый коментарий. К нему еще вопросы у вас есть?
Не понял что за "большой комент" (ссылка верная?)
Интересно чем это противоречит тому, что я написал?
Начиная с появления 32-х битного защищенного режима (386+) и реализации его в Win32 (начиная с WinNT и затем в Win2000+) появилась возможность использовать виртуальное личное 4Гб адресное про-во. Поэтому считалось, что каждый EXE сможет грузиться по тому адресу по которому желает. Как и @Angeldзаметил, в DLL она все равно была нужна, т.к. они делил общее пространство. Так зачем по вашему ее исключал из образа линкер у EXE?
И вы за все линкеры сразу говорите? Я лично знаю (помню) несколько компиляторов Си, которые вполне себе таблицу relocation оставляли.
И я бы воздержался от подобного "у обоих в голове каша" (лично мне вот видится наоборот). Хотя бы прочтите с чего именно начался диалог (мой первый коммент).
Выскажу предположение по поводу ebp. Возможно программа была написана на Си (или др.) и далее адаптировался ассемблерный листинг компилятора. Автор просто решил "не удалять" сохранение ebp по "только ему известным причинам". Ну или так больше похоже на "настоящую" функцию (автор показал, что знаком с прологом и эпилогом).
Кстати, остальные лишние и не оптимальные конструкции тоже могут объясняться источником ассемблерного кода.
Ну мое подтверждение касалось лишь отсутствия необходимости лишней инструкции. Думаю, что временные параметры могут зависеть от поколения процессоров. Кстати, не удивлюсь, если современные процессоры способны просто игнорировать вторую (ненужную) инструкцию, а это выигрывает время.
Мне 53 года, я хорошо знаю формат исполняемых файлов со времен DOS (вообще CPM). В EXE файлах DOS и Win16, в которых используется хотя бы одна глобальная переменная, вообще не возможно без таблицы relocation (хотя формат заголовка там, конечно не как в Win32+).
Среди игр под Win32 до 2000 года некоторая часть была без таблицы, т.к. ценился размер файла и лишний килобайт экономили.
Только странно сейчас говорить о том, что было 20 лет назад. Только как история, возможно.
Могу лишь повторить, что наличие таблицы relocation наоборот дает лишнюю информацию взломщику. Приведите хоть один пример зачем нужна таблица relocation для защиты.
Для "перевода" (серьезно?) своего кода в собственную VM используются собственные механизмы. Посмотрите, например DENUVO, как работал StarFoce.
Вот прямо сейчас посмотрел EXE в Windows (в том числе в SysWOW64). Все с таблицей relocation. В "Program Files (x86)" из всех произвольно выбранных - у всех таблица есть. А там полно известных утилит известных фирм. А как у вас с файлами в этих каталогах?
Можете назвать какое-нибудь известное ПО (чтобы я мог посмотреть), в котором нет этой таблицы?
Если вы программист, вы какой компилятор используете?
И вы не первый раз не внимательно читаете, что я пишу. Я сказал лишь, что таблица relocation может быть и не в секции ".reloc", т.к. загрузчику все равно в секции она или нет. Где вы увидели, что "ее компиляторы куда-то прячут в EXE"? Они создают ее ровно так же как и в DLL. А вот различные инструменты могут и перемещать ее.
И еще. Упаковщики EXE (уверен, знаете что это такое) могут не иметь этой таблицы - их задача сжать образ. Но в сжатом образе эта таблица все равно есть.
Для защиты relocation как раз не нужна - она раскрывает детали работы программы.
В большинстве именно, что есть relocation. И нет, нет тех EXE где компилятор размещает "константы, расчитанные на фиксированный адрес". Такие вещи может сделать только сам программист. И это его головная боль как это решать. Об этом я изначально и писал.
Впрочем это выглядит как лекбез. Я лучше ссылку дам на общедоступный хэлп, например по опциям линкека в VS22: https://docs.microsoft.com/en-us/cpp/build/reference/dynamicbase-use-address-space-layout-randomization?view=msvc-170#remarks
Достаточно прочитать 10 строк в разделе Remarks.
Ответ на ваш вопрос очевиден - никак (т.е. придется грузить по тому адресу, который указан как желательный или вообще не грузить). Именно поэтому компиляторы производят таблицу relocation для всех EXE. Это было раньше, это есть сейчас. Это установилось как обязательный раздел еще в Win16, т.к. там программы разделяют общее адресное пространство, как DLL сейчас. Поэтому это не новшество никак.
Простите, когда вы пишите "в огромном количестве старых exe этого нет", вы реально это проверяли. Лично мой опыт (25+ лет занятия защитой прикладного ПО) показывает, что практически в 100% EXE есть таблица relocation.
Да, и таблица relocation не обязательно формируется в секцию ".reloc". Она может быть вообще вне секции. Доступ к ней загрузчик получает не так. Посмотрите формат PE заголовка.
Хорошо, что вы со мной согласны.
Вы реально решили мне объяснить это? Спасибо. :)
Нет. Relocation по умолчанию генерируется для всех EXE. 0x40000 - лишь "желаемый" адрес загрузки. Похоже лонгрид напрашивается.
Множество уязвимостей в серверах (типа Apache) эксплуатируются, исходя из того, что грузится он по одному адресу и дома у хакера и на сервере. Дома отлаживается эксплойт, потом однократно "передается" на сервер и там работает. Поэтому (и не только) в Win64 добавили рандомизатор загрузки. Ему плевать куда вы хотите грузиться - он выбирает случайно один из 256 выравненных адресов и туда грузит EXE. Каждый раз - разный. Очевидно, что без таблицы relocation он этого сделать не сможет. Поэтому они есть во всех EXE, если вы ее сами не "отрубите". Для "чувствительных" к адресу загрузки приложений и есть бит "грузить по желаемому адресу, иначе вообще не грузить".
Точно. Не нужна. Тем более, что в своей сути cmp работает точно как sub, только результат никуда не записывает, а, как раз, устанавливает флаги.
Насколько я знаю, по умолчанию (честно, не знаю как в NASM, не пользовался) как раз адрес не фиксированный. Только если сами установите.
Таблица relocation работает только если вы указываете адрес переменной, используя ее имя. Если вы используете константу (т.е посмотрели адрес и просто указали число, как в моем комментарии и написано), то ни в какие relocation это не попадет, а значит при загрузке по другой базе будет большая проблема.
Узнать адрес "где ты находишься" можно и, в данном случае нужно. Именно на то, что проблема есть, и ее надо решать, я и пытался указать.
Это не спор с кем-то, а дополнение текста статьи для тех, кто захочет воспользоваться модификацией кода в runtime.
Стоит отметить, что под Win64 используется рандомизатор загрузки образа, т.е. при каждом запуске программы стартовый адрес может (и будет) не одинаковым в том числе и для программ написанных под Win32. В следствии возникает проблема при записи в код констант, означающих адреса (например, адрес переменной) в виде константы.
Решение - указать в свойствах секции кода не только бит "разрешение записи", но и бит "грузить только по указанной базе".
А свой operator new в классе уже запретили объявлять?
Тогда локальные диски нужны лишь чтобы заиметь точную копию мастера перед началом работы и после окончания. Цель, я так понял, восстановить ошибочно удаленный файл. Но это не сработает, если файл был и создан и удален после синхронизации. Какое-то не идеальное решение. Я бы просто раз в день делал полную копию без синхронизации на какой-нибудь бэкап. Тоже не очень, но проще.
Сам "старпер", поэтому понимаю.
Однако:
"По окончании работ происходит повторная синхронизация мастер диска на локальный диск рабочего места."
Все же наоборот.