Блог пользователя qwaker.00

Автор qwaker.00, 13 лет назад, По-русски

Введение

Тема будет интересна всем, но познать её смогут только "счастливые" обладатели компилятора в популярном пакете dev-cpp для windows. На самом деле среду эту я не люблю и не пользуюсь никогда в силу жуткий глюков и тормозов, но как-то она мне попалась на первых парах изучения C и поэтому до сих пор используется мною как средство установки на комп консольных компилятора g++ и отладчика gdb. Быстро, удобно, бесплатно и всегда под рукой в интернете.

Сабдж

Собственно речь сейчас не идёт о прелестях этого старого приложения. Многие слышали о магии С++, многие её отведали, многие сваливают на неё ошибки в своем коде во время контестов. Ниже, представлен один из примеров подобного заклинания, о возможности компиляции которого даже приходится задуматься, не говоря уже о результате выполнения. Внимание! Уберите от экранов слабонервных, детей и паскальщиков:

char s[56] = "\xB8\1\0\0\0+\xD2\xB9\x1E\0\0\0Q\x8B\xC8\3\xC2\x8B\xD1PRh\x32 @\0\xA1"
             "\xD0P@\0\x83\xC0 P\xFF\x15\xE8P@\0\x83\xC4\bZXY\xE2\xDB\xC3%\x75\n";
int main(){
    (((void(*)())(char*)s)());
    return 0;
}

Не думаю, что кто-то из ныне живущих людей на планете сможет с уверенностью сходу сказать, что этот код делает. Код без привычных #include. Состоящий буквально из одной строки кода. Он также врядли сделает что-то полезное на других компиляторах, но вот Dev-cpp и его GNU C++ 3.4.2 вполне согласен со всем написанным и готов его корректно исполнить.

Так что же он делает, задаётся вопросом нетерпеливый читатель? Попробуйте сначала предположить что, а потом проверьте у себя на машине. :)

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

Ссылки

Собственно код http://pastebin.com/w9pyjAtm

Dev-cpp http://www.bloodshed.net/dev/devcpp.html

  • Проголосовать: нравится
  • +19
  • Проголосовать: не нравится

13 лет назад, # |
Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

Ошибочка вышла, сорри.

  • 13 лет назад, # ^ |
    Rev. 3   Проголосовать: нравится +6 Проголосовать: не нравится

    За спойлером подозрение на ответ. Мне подсказывают, что он правильный, сделал не запуская.

13 лет назад, # |
Rev. 3   Проголосовать: нравится +13 Проголосовать: не нравится
В правке дизассемблированный код из массива.

UDP:
(((void(*)())(char*)s)()); эта штука преобразуется в
mov    eax,0x804a040
call   eax

0x804a040 - по этому адресу находится код (в первой правке) который в цикле вызывает какую-то функцию (вроде set_app_type) с параметрами.
  • 13 лет назад, # ^ |
    Rev. 4   Проголосовать: нравится 0 Проголосовать: не нравится

    В прошлой правке раскомментированый код дизассемблера. Осторожно, спойлер.

13 лет назад, # |
Rev. 2   Проголосовать: нравится +6 Проголосовать: не нравится

По поводу паскальщиков это Вы напрасно... :)
На паскале асемблерные вставки также можно делать похожим образом. Даже более того - например, можно напрямую делать дамп памяти, изменять её, оперировать с регистрами, видеопамятью и т.д. и т.п.
Другое дело, что школьникам по большому счёту подобные знания не только излишни, но в большинстве случаев бывают вредны.
Почему - объяснять не буду, как и не буду объяснять, почему не буду объяснять... :) 

  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Знаю одного паскальщика, который ещё KolibriOS поддерживал :)
  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Насчёт школьников это зря. Когда я в школе учился лет 10 назад, помню, в моде был Borland Pascal - и чтобы использовать в досовской программулине мышь было просто необходимо либо ассемблерную вставку писать, либо вызывать прерывания с помощью специально обученных процедур. И то и другое в общем выглядело одинаково дико. ;-)

    Но какой же честолюбивый школьник согласится оставить хорошую программу без мышки?
    • 13 лет назад, # ^ |
      Rev. 2   Проголосовать: нравится +11 Проголосовать: не нравится

      Естественно. И правильно делали, что там использовали. :)
      Мы, например, когда ещё в школе были "Корветы" писали на паскале асемблерные модули и делали програмы-генераторы для подключения к паскалевским библиотекам спрайтов и музыки - какая же своя игрушка без музыки и анимации?.
      Потом научили это чудо говорить - то же самое проделывали и ребята в Каменец-Подольске. И всё это умещали в 48К ...
      Эх, интересное было время... :)

      • 13 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        И всё это умещали в 48К

        Да, программисты были не зажравшиеся... Вот сейчас смотрю на веб-клиент для ICQ - и там снизу требования приписаны... Так меня очень развлекает что для примитивного чата нужно 1 гиг оперативки... ;-)
13 лет назад, # |
  Проголосовать: нравится -20 Проголосовать: не нравится
По-моему, еще надо убедиться, что в системе отключена DEP 
  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Не обязательно. DEP не будет ругаться на простое исполнение сегмента данных. Она будет ругаться, если последовательность выглядит очень плохо.
    • 13 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Если я не ошибаюсь, DEP просто предотвращает исполнение страниц, у которых не выставлен флаг "для исполнения". А у сегмента данных он несомненно не выставлен.

13 лет назад, # |
  Проголосовать: нравится +2 Проголосовать: не нравится
Вот поэтому я больше люблю Java.
  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Я видимо не знаток в Java, но... Там можно заставить работать строку? ;)
    • 13 лет назад, # ^ |
        Проголосовать: нравится +13 Проголосовать: не нравится
      Нет, но такие фокусы больше подходят для Дэвида Блейна, чем для языка программирования. :)
      • 13 лет назад, # ^ |
          Проголосовать: нравится +1 Проголосовать: не нравится
        Меня привлекает сама возможность делать магию. :)
        Когда её нет, чувствуешь себя скованным в неких рамках.
        • 13 лет назад, # ^ |
          Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

          Дык айда к нам на electronix.ru... Обещаю, там намного магичнее... А проблем с отладкой вообще может быть море, т.к. не знаешь, программа ли сбоит, или где-то припаялось плохо, или уже нога процессора отгорела... ;-)

          UPD: Да, по поводу "заставить работать строку на java" - этим пользуются даже промышленно обфускаторы - шифруют код а потом его восстанавливают с помощью рефлексии... Ну ведь и в си всё это тоже в первую очередь для защиты программы от копирования использовалось...

      • 13 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        У таких фокусов спокон веку два серьёзных применения:
        - вредоносное ПО;
        - защита от копирования.
        • 13 лет назад, # ^ |
            Проголосовать: нравится 0 Проголосовать: не нравится
          Ага, используется жуликами и теми кто защищается от жуликов.
          Но вообще на самом деле каждому, кто ещё не задумывался о таком коде было бы полезно это сделать :) Основы всё таки
    • 13 лет назад, # ^ |
        Проголосовать: нравится +8 Проголосовать: не нравится
      Я в 10-м классе тоже был в восторге от таких финтов... Чудесные времена светлой наивности... ;-)

      Когда мне хочется развлечься - у меня под рукой не только ассемблеры для x86, но и для AVR, ARM и прочих чудесных платформ... Операционные системы занимающие по 700 байт в ПЗУ и несколько десятков в оперативке... Ну и т.п.

      Но когда мне надо работать - да здравствует java в которой не приходится сутками искать за всякими ________ (нужное вписать) где же это они умудрились нечаянно "запустить" данные вместо кода или натворить ещё стопиццот удивительных в своём идиотизме вещей.
  • 13 лет назад, # ^ |
      Проголосовать: нравится +1 Проголосовать: не нравится
    А если язык умеет делать больше, то его надо любить меньше?
    • 13 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится
      Java тоже умеет больше... но по-своему... Чего одна рефлексия стоит...

      Так что любить надо всё... Я никогда не стану всерьёз задумываться о написании веб-приложения на Си, или о программировании микроконтроллеров на java-подобных извращениях... ;-)
13 лет назад, # |
  Проголосовать: нравится +1 Проголосовать: не нравится

А это разве С++? По-моему, тут чистый Си. А вот ниже С++. Кто сможет понять, что делает этот код?

template <typename T1, typename T2>
class superClass
{
	template <typename T>
	static char (&check(const volatile T1&, T))[1];
	static char (&check(const volatile T2&, int))[2];
	
	struct Temp
	{
		operator const volatile T2&() const;
		operator const volatile T1&();
	};
public:
	enum { value = sizeof(check(Temp(), 0)) == 1 };
};

//bla-bla-bla
if(superClass<T1, T2>::value)
{
	//Когда это выполняется?
}

  • 13 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится
    Если я не ошибаюсь, то шаблонная функция будет матчена в последнюю очередь. Это означает, что если вторая сигнатура будет корректна с точки зрения преобразования типов, она будет применена. Похоже, что ответ "никогда" :)
    • 13 лет назад, # ^ |
      Rev. 3   Проголосовать: нравится 0 Проголосовать: не нравится

      Отлично, второй эпик слив за этот тред. Пошел читать стандарт по ключевым словам "перегрузка функций", "приоритет выбора между константными и неконстантыми операторами преобразования типа", а также "приоритет выбора при шаблонной подстановке".

      Что делает - понятно, что написано - понятно, но почему это ведет себя именно так?!

      Upd. Похоже, поведение MS VS 2005 на данном примере не соответствует, по крайней мере, описанию преобразования типов, данному у Страуструпа  (R.13.2).

      Действительно.

      struct super{}; struct sub:super{};
      superClass<sub, super>::value

      сопоставляются

          check(const volatile super&, int);
          check(const volatile sub&, T);

      Оба требуют пользовательские преобразования и попадают под пункт 4. Оба содержат только пользовательские и тривиальные преобразования. Выбрать вызываемую функцию невозможно, так как наличие лишнего тривиального преобразования Temp -> const Temp само по себе не является основанием для более высокого приоритета (там же в 13.2).

      • 13 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится
        >>  но почему это ведет себя именно так
        Я не уверен, но по-моему, дело обстоит так. Компилятор, пытаясь найти наилучшее соответствие для функции check, действует согласно главе 13.3.3 Стандарта. По ней, проверка соответствия функции шаблону или специализации производится после проверки на последовательность стандартных преобразований. Часть 13.3.3.2 и  определяет, что во-первых, предпочтительней вызов неконстантной конвертации, а во-вторых, вызов функции с параметром - базовым классом. Если "во-первых" и "во-вторых" совпадут (а это возможно, только если T1 производный от T2), то все ясно, и вызываем первый check. В противном же случае возникает неоднозначность выбора, для разрешения которой и нужен второй параметр: в этом случае однозначно определяется, что мы вызываем второй check.
        >> Upd. Похоже, поведение MS VS 2005 на данном примере не соответствует, по крайней мере, описанию преобразования типов, данному у Страуструпа  (R.13.2).
        Страуструп конечно хорошо, но ИМХО, в таких ситуациях Стандарт лучше :)
13 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Это ты не после лекций по С++ искать такое начал? =)
13 лет назад, # |
  Проголосовать: нравится +3 Проголосовать: не нравится
Для всех, кто очень интересуется подобными вопросами, рекомендую поискать книгу "MS-DOS для профессионалов. Пособие по программированию на IBM-PC", Харьков, 1991 год.
Не смотрите что старая - это самое полное, даже исчерпывающие технические факты, разделы и структуры данных, касающиеся наиболее часто необходимой технической информации, содержащейся в документах DOS Technical Reference и PC/XT/AT Hardware Technical Reference.
Не знаю, или подобный раритет есть в Интернете (во всяком случае мне не попадался) - у меня подарочный бумажный вариант ещё с тех времён. Но от этого ценность данной книги не уменьшается. Для профессионалов вещь очень и очень не лишняя в хозяйстве.
Если кому-то известны другие подобные источники и особенно в электронном виде - буду благодарен за ссылки.
13 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится
Ну и еще пара приколов вдогонку. О первом, пожалуй, знают все, кто хоть немного знаком с новым стандартом: вполне разрешен такой оператор [](){}();
Второй прикол интересней:
enum flag {novalid, valid};
struct
{
	flag value : 1;
} c;
c.value = valid;
assert(c.value == valid);