6 August 2015

О сколько нам открытий чудных готовит Office Microsoft

AssemblerReverse engineering


По сообщениям в комментариях к статье про блокнот, во всех версиях Microsoft Excel, начиная по крайней мере с '97 и до самых новых, в имени листа не всегда можно ввести большую букву Ж. Данная проблема обсуждается в сети уже давно, например на этом форуме забавно наблюдать, как некоторые утверждают, что у них проблемы нет, а у других есть, но не всегда, и никто не понимает, почему так. На первый взгляд можно подумать, что это просто недоработка программистов: они хотели не дать пользователю ввести символ ':', и просто не подумали о том, что Ж находится на той же кнопке.

На деле оказалось всё гораздо хуже. Описать нормальными словами то, что происходит в excel, когда вы просто нажимаете кнопку 'Ж', практически невозможно. Поэтому я попытаюсь обрисовать в целом процесс исследования, сократив его где возможно, и не слишком перегружая статью ассемблерным кодом. В итоге мы узнаем, почему получается так, что не любые символы можно ввести, и как это можно исправить.

С чего начать? Поэкспериментируем немного. Оказывается, что иногда ввести Ж в название листа всё-таки можно, причём если уж один раз это сработало, то её можно будет вводить сколько угодно и где угодно, пока не закроешь Excel. А если не получилось, то как ни старайся, ввести эту букву уже не получится никак. Выяснить, почему так происходит, пока не удаётся. Известно одно: скопипастить её можно всегда.



Ну хорошо. Раз ошибка имеется где-то в коде проверки символов, попробуем найти её через действительно запрещённый символ ':'. Копипастим его в название листа, нажимаем Enter, и получаем сообщение, что такие символы не допускаются. Прервём на этом месте выполнение программы. Мы попадаем куда-то в дебри системных вызовов, во главе с user32.dll, общая вложенность 22 в глубину стека. Начинаем искать, как мы сюда попали. Поднявшись примерно на 15 уровней вверх, обнаруживаем следующий код:



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



Пока всё идёт неплохо. При нажатии проверяются те же самые символы. Код немного отличается, ну уж так видно сработал компилятор. Пора выяснить, почему же не вводится буква 'Ж'. Нажимаем её, и тут нас ждёт сюрприз: точка останова не срабатывает! Как же так? Опять приходится возвращаться выше по стеку вызовов. В эту подпрограмму мы попадаем отсюда:



To есть вызывается подпрограмма по адресу, который содержится в регистре rdi плюс 8. Вот только оказывается, что при нажатии большинства клавиш там будет адрес той самой «правильной» подпрограммы, которую мы видели выше. А если мы нажимаем Ж с шифтом (чтобы она была большая), в этом месте оказывается совсем другая подпрограмма. Вот она:



Это подпрограмма типа «давай, до свидания!» То есть она сразу завершается с кодом 1, ничего не анализируется, и нажатая клавиша никуда не сохраняется.

Мы обнаружили классический вызов по таблице. rdi — индексный регистр, его содержимое указывает на таблицу адресов, и в зависимости от его содержимого, вызывается та или иная подпрограмма. Вот эта таблица:



Анализируя код, который с ней работает, удалось выяснить следующее: в начале таблицы — число строк (0Eh = 14), правда почему-то 2 раза. Каждая строка — описание комбинации клавиш. Сначала диапазон скан-кодов (выделен зеленым), затем допустимые состояния shift, alt и Ctrl (биты 4,8,20 — синим), маска для них (красным), и в конце 64-битный адрес подпрограммы (желтым), которая выполняется при совпадении условий. Большая Ж в этой таблице находится в 6-й строчке.



Скан-код 0BAh (VK_OEM_1), при нажатом шифте (4) приводит к вызову 000000013FB0A454 = «до свидания». Если же ни одна из комбинаций не проходит, то в последней строчке срабатывает код от 0 до FF, и вызывается п/п 000000014039530С, часть которой мы видели выше, где всё идёт по обычному плану, и символ попадает в имя листа.

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

И вот, в очередной раз остановившись где-то в недрах mso.dll, я с удивлением вижу, что в этой таблице какие-то совсем другие числа! Так я наконец узнал страшную тайну microsoft excel.



Как видим, здесь теперь не 14, а 13 строк, и в середине другие скан-коды (выделены зеленым). А именно, в шестой строке, там где раньше была буква Ж, теперь shift-6. То есть то же самое двоеточие, только из русской раскладки. То же самое и с остальными клавишами. Вот теперь действительно стало всё понятно. Довольно быстро выяснилось, что таких таблиц тут не одна, а целых 43 штуки (для каждой области экрана, отдельно для основного поля, поля ввода формул и т.д.). И все они заполняются один раз, при первом нажатии клавиши, а заполнение зависит от выбранной именно в этот момент раскладки и больше никогда не меняется.

То есть если после запуска excel при первом нажатии любой клавиши, пусть даже стрелки вниз, выбрана английская раскладка, буквы 'Ж' вы больше не увидите. И наоборот, если раскладка была русская, больших 'Ж' будет сколько угодно, а запрещённым «назначается» shift-б, и, кстати, shift-7 тоже. И после этого назвать лист, скажем, «рога & копыта» уже не получится, хотя символ '&' вроде как разрешён.

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

Тем не менее, исправить эту ошибку можно, и даже проще, чем ожидалось. Оказалось, что исходным материалом для заполнения всех этих таблиц служит текстовая (!) таблица комбинаций клавиш, расположенная в файле XLINTL32.DLL, лежащего в одной из папок офиса. Его часть как раз изображена на КДПВ.

Вот так выглядит фрагмент, касающийся названия листа:

~Sh+~Alt+~Ctrl+Return~Sh+~Alt+~Ctrl+Execute~Sh+~Alt+~Ctrl+Escape~Sh+~Alt+~Ctrl+Cancel
~Sh+~Alt+~Ctrl+Tab:*?Ctrl+;Ctrl+:Ctrl+'Ctrl+"F2Default

Что же делает excel? Он разбирает части этой строки и делает из неё ту самую таблицу, подбирая такие скан-коды, которые приводили бы к вводу нужных символов, с учётом раскладки. Представляете? Он анализирует текстовое представление, чтобы сделать из него таблицу скан-кодов, чтобы потом, при нажатии клавиш, сравнивать полученный код с каждой строкой и вызывать соответствующую процедуру. Заполнять все 43 таблицы при каждом нажатии естественно не годится. Поэтому это делается один раз. Так что программисты не забыли про раскладки, а провели с ними огромную работу. Только одно они не учли — во время работы excel их можно переключать.

Кстати, теперь ясно, почему в русском варианте не 14 строк, а 13. Одна из запрещённых комбинаций Ctrl+' невозможна в русской раскладке, потому что апострофа в ней в принципе нет, поэтому и скан-кода для него не находится.

Вернёмся однако к ошибке. В середине текста видны подряд те самые 3 символа :*? для запрещения. Чтобы всё исправить, достаточно в файле XLINTL32.DLL заменить эти 3 символа :*? на 3 звёздочки, потому что звёздочка на обоих раскладках в одном месте. Это можно сделать с помощью любого двоичного редактора или даже FAR, т.к. он позволяет менять текстовую часть двоичного файла по F4, при этом не испортив его.

После этого excel перестанет отбрасывать нужные скан-коды, и можно будет при любой раскладке вводить большую Ж, '&', '^' и запятую, при этом действительно запрещённые ':' и '?' всё равно не пройдут 2 проверки, которые описаны в начале статьи. Короче всё будет хорошо. И в следующий раз, когда будете набирать какой-нибудь текст в ворде или другом приложении офиса, постарайтесь не думать о том, что там происходит внутри.
Tags:реверсингexcelдревние баги
Hubs: Assembler Reverse engineering
+225
112.6k 338
Comments 89
Top of the last 24 hours