1 июля 2011 г.

Задачка №11

Объяснить этот снимок экрана.


Здесь идёт пошаговая трассировка программы в CPU отладчике. В текущий момент идёт проход по коду условного оператора if:
if (PLongCall^.MovDL_OpCode = $B2) and
   (PLongCall^.MovDL_Val = $01) and
   (PLongCall^.MovEAX_OpCode = $A1) and
   (PLongCall^.Call_OpCode = $E8) and
   (PLongCall^.MovEAX_ClassType^ = ClassCreatePtr) then
Сейчас мы вычисляем первую скобку в if. Как видно из скриншота, в левой части выражения стоит ровно то значение, с которым происходит сравнение. Тем не менее, после выполнения команды сравнения (cmp), результат оказывается ложным.

Задача: объяснить, как такое может быть.

Дополнительная информация:
  • Я не менял значение ZF отладчиком
  • Программа однопоточна
  • Версия Delphi и Windows значения не имеют
  • Отладчик показывает True, как при вычислении (Evaluate) всего данного выражения в if, так и всех его подвыражений
  • Здесь не участвуют антивирус, антиотладочный код, динамическая генерация кода и другие подобные вещи. Эту ситуацию можно воспроизвести на пустом VCL приложении с добавленным в него только одним этим if-ом и больше ничего лишнего.

Ещё дополнительная информация:
  • Ситуация до выполнения команды была той же самой - $B2 в проверяемом байте. И за миллион команд до этой - та же ситуация.
  • Это поведение, которое вы используете постоянно по время отладки.
  • Об этом не знают многие новички, а те, кто знает - часто забывают (даже опытные программисты). Кажется, сложность решения этой задачи как раз и происходит от этого факта.

Ответ на задачку будет выложен примерно через месяц.

Ответ.

25 комментариев :

  1. Александр, при всем уважении, я бы Вас на работу не взял. Слишком сложный код дня понимания выдаете. Ну и "магические числа" (с) Макконнелл.

    ОтветитьУдалить
  2. С чего вы взяли, что это мой код?

    ОтветитьУдалить
  3. Ааааа....
    Тогда я Вам такие перлы могу зарелизить в блог, с которыми сталкивался за годы работы, что у Вас кроме конкурсов ни на что времени не останется )))))

    ОтветитьУдалить
  4. Да без проблем - присылайте. Мне иногда подкидывают такие вещи.

    Только это не конкурс - призов-то нет. Это так, для интереса только.

    ОтветитьУдалить
  5. Сравнение с регистром идет словом, а не байтом.
    В этом проблема?

    ОтветитьУдалить
  6. Я комментировать не буду, через энцать дней опубликую ответ.

    ОтветитьУдалить
  7. К сожалению у Вас как всегда беда с контекстом, непонятно что за код и в каких условиях работает, работает ли с оборудованием, на каких процах (AMD | INTEL | ARM и тд). Что до этого было. И тд.

    Мои варианты:
    1. Либо ошибка выравнивания проца.

    2. Либо некогерентность кеша процессора и работы оборудования в режиме DMA mastering, т.е. само оборудование в память писало что то не оповещая кеш процессора, а при выходе из отладки кеш сотни раз обновился т.к. исполнялось нового кода в десятки раз больше чем объём кеша.

    3. Либо кривая компиляция именно этого блока.

    4. Либо включён режим процессора прерываний по не выравненным переменным и эта переменная в стеке неправильно размещена и стек изменился.

    ОтветитьУдалить
  8. Как воспроизвести ошибку?
    Ошибка не воспроизводится.

    ОтветитьУдалить
  9. 2 анонимный от "6 ИЮЛЯ 2011 Г. 17:19":

    Я стараюсь формулировать задачи указанием максимального количества деталей для её решения, чтобы до ответа нужно было сделать шаг или два. Те детали, которые стандартны и/или не имеют отношения к задаче, я опускаю.

    Если я буду перечислять все те вещи, которые не имеют отношения к задаче (это не разгон, это не ошибка в процессоре, это не вспышка на солнце, это не соринка на мониторе, это не маскировка malware, это не перегрев, сюда не вмешивался режим ядра и т.п.), то это займёт ужасно много времени. Не думаю, что это понравится большей части людей.

    Могу дать три дополнительных кусочка к картине, если совсем уж никак - указал в посте. Лично мне кажется, что я уже практически озвучил ответ.

    Вообще, тут не угадаешь - одни говорят: мало данных, другие - да это вообще не задача.

    (Ваши догадки я комментировать не буду)

    2 анонимный от "6 ИЮЛЯ 2011 Г. 17:47":

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

    ОтветитьУдалить
  10. Конкурс по нахождению экстрасенсов и телепатов?

    Вот представтье, что к Вам приходит студент с курсовиком по делфи на десять строк, и спрашивает "почему у меня всё что изображено на форме плавно и медленно вращается по часовой стрелке"?

    Ваши действия, при этом он не хочет показывать исходный код полностью, показывает только
    a^:=b;
    говорит что сдесь ошибка, и обещает миллион долларов если поможите, т.к. сессия а папа депутат?

    ОтветитьУдалить
  11. Когда будет опубликован ответ, у вас будет возможность сказать, что ещё можно было указать, чтобы не открыть решение. Не нужно так расстраиваться от того, что не получается.

    ОтветитьУдалить
  12. Может быть это отладчик заменил команду на int 3, чтобы поставить в этом месте точку останова.

    ОтветитьУдалить
  13. и проверка неполная, по хорошему - надо еще атрибуты страницы памяти проверить...

    ОтветитьУдалить
  14. для чего это? Чтобы показать, какой вы умный? Если специально не извращаться, код работает так, как и от него ожидают.

    ОтветитьУдалить
  15. Не нравится - не читайте. Это простое решение ваших проблем. Может вы не заметили, но это персональный блог.

    ОтветитьУдалить
  16. $B2 = 13 = перенос строки
    Может, в этом проблема? Сравнивается не значение "$B2", а значение "перенос строки"

    ОтветитьУдалить
  17. 1. Команда CMP сравнивает два значения, и в случае их равенства ставит ZF=0. На скриншоте это и видно.
    2. Оптимизация условия AND. Если хотя бы одно из вложенных условий равно False, то остальные проверять не нужно, и сразу идет переход на "False" ветку условия. На скриншоте это команда JNZ - Если значения разные, CMP вернет ZF=1, и произойдет переход, иначе сравниваем остальне части.

    Та же оптимизация делается и с оператором OR

    ОтветитьУдалить
  18. Вы сейчас добиваетесь чтоб мы решили задачу Y тогда как нужно решить задачу Z чтоб получить результат X, при этом Z не предлагать.
    Причём результат вполне практический - чтоб программа работала как задумывалась в любых нормальных условиях.

    Z - чтоб как нибудь выполнялась проверка, её тоже можно осуществить всеми способами, например собрать данные в массив и сравнить какой нибудь библиотечной функцией сравнения массива байт, которая гарантированно работает и десятилетиями проверена. Затрат времени и мозгов минимальный.

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

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

    Т.е. Ваша ошибка в том что вы потеряли контекст и вывели задачу из области практики в какую то другую, и это даже не область искусства.
    Вся разница в том что:
    1. Имея реальную проблему мы знаем предпосылки, т.е. мы знаем что изменилось и что менялось на протяжении всего проекта и можем подумать как это относится к именно этому куску кода.
    2. Мы имеем больше кода, который явно с этим взаимодействует, мы видим картину если не в целом (например если это код DLLки), то явно не через замочную скважину, а через окно.
    3. Мы можем что нибудь предпринять, как нибудь воздействовать, на данном сайте нет.
    4. Мы можем просто поменять и переписать либо большую часть либо в целом. Либо полностью поменять весь алгоритм затрагивающий это место.

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

    ОтветитьУдалить
  19. Боже...

    Вас никогда не тормошил коллега "смотри, какую я штуку нашёл"?

    Если ещё не дошло - это не конкурс, здесь нет никаких призов. Я увидел что-то интересное - я поделился.

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

    Я не знаю, это действительно так сложно понять, что требует отдельного пояснения?

    Я просто рассказываю что-то интересное, что видел сам - ровно как рассказывал бы ваш коллега.

    >>> Имея реальную проблему мы знаем предпосылки, т.е. мы знаем что изменилось и что менялось на протяжении всего проекта и можем подумать как это относится к именно этому куску кода.

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

    Всё, что написано здесь, больше подобно головоломке с одним отсутствующим кусочком, чем "перебору двигателя на полном ходу".

    Вы сильно путаете реальную проблему, которая возникает в процессе разработки/отладки, и специально подготовленную информацию.

    >>> Мы имеем больше кода, который явно с этим взаимодействует, мы видим картину если не в целом (например если это код DLLки), то явно не через замочную скважину, а через окно.

    Как я уже сказал - это не имеет отношения к задаче. Здесь нет никакого окна, я выдернул из проекта всё, что имеет отношение к задаче (кроме самого ответа). Я специально указал, что реальный код ни на что не влияет - ровно это же поведение получается в пустом VCL приложении. Я даже указал, что здесь нет никакого иного кода программы (вроде потоков) или сторонних программ (вроде антивирусов). Я даже сделал наижирнейшие намёки, что вопрос тут в процессе отладки.

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

    Ну, и много ли было бы понятно из такой формулировки? Вот там действительно потребовался бы нехилый телепат. А тут - всё на ладони.

    >>> Мы можем что нибудь предпринять, как нибудь воздействовать, на данном сайте нет.

    Да пожалуйста. Создайте пустое VCL приложение и воздействуйте на него как угодно. Там даже if из примера не обязателен. Вперёд.

    >>> Мы можем просто поменять и переписать либо большую часть либо в целом. Либо полностью поменять весь алгоритм затрагивающий это место.

    По-моему, из снимка экрана вполне очевидно следует, что алгоритм здесь ни при чём. Требуется объяснить, почему показания отладчика не соответствуют реальному положению дел. Задача заключается только в этом. Если бы вы заменили набор условий в if на побайтовое сравнение блоков - ничего бы не изменилось.

    Если речь идёт про исходный проект, откуда это взялось, то могу вас заверить, что единственное изменение в нём, которое влияет на это поведение - это (censored) (вырезано цензурой, как ответ). Потому что в том проекте вам был бы необходим (censored), чтобы (censored) и привело к (censored), где мы и увидели несостыковку. Следовательно, без (censored) не было бы возможности вообще увидеть проблему. Не столько потому, что её не было, а потому что (censored). Иными словами, любое изменение в том проекте, которое вы бы сделали, не повлияло бы на проблему.

    >>> найдётся телепат который сразу поймёт в чём дело

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

    ОтветитьУдалить
  20. Извиняюсь, лишнего навыдумывал и разозлился. Ошибка была в том что я вообще не так понял задачу в принципе от начала до конца.

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

    Я в современных технологиях винды реально не так силён, т.к. пишу для АРМ/Ниос/8битников и свои примитивные процы на ПЛИС. Максимум что для компа пишу это простые отладочные и тестовые программы с парой десятков кнопочек которые обработчики медиапотоков в режиме реального времени.

    ОтветитьУдалить
  21. Дополню
    "Кажется понял, отладчик имеет права доступа по чтению на сегмент кода, а реальная программа может и не иметь, особенно если включены средства защиты от вирусов или переполнения буферов и т.д. "

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

    Просто очень давно ознакомившись с ошибками ещё в 16битном защищенном режиме ещё 3.11 винды и пару раз потратив кучу времени на разбирательства и выяснил причины понял что с сегментом кода лучше не взаимодействовать так как в примере. А если взаимодействовать то настроить права и поэтому предполагал что права настроены как нужно.

    ОтветитьУдалить
  22. Поскольку прошёл почти месяц - опубликовал ответ.

    ОтветитьУдалить
  23. Судя по скриншоту - такого произойти не может (кстати в сторонних отладчиках переход происходит?)
    Поэтому предполагаю что выставлена опция в настройках компилера на полное соответствие булевой последовательности...

    ОтветитьУдалить
  24. хм, ответ-то я и не посмотрел :)
    модификация памяти сторонним ПО (в данном случае отладчиком) не очевидна, хотя смотря какой отладчик и какие опции отладки выставлены. В той-же OllyDebug можно выставить соответствующую опцию и смотреть на реальное состояние памяти :)

    ОтветитьУдалить

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и (опционально) ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

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

Примечание. Отправлять комментарии могут только участники этого блога.