Перейти к содержанию
Old Phone Forum
  • Вход

    Вы сейчас не залогинены на форуме.

    Для возможности комментариев, загрузки файлов, подписок на ответы - вам надо войти.

Обучение патчам.


Рекомендуемые сообщения

Итак, для начала с предисловия.

Самое первое, что хочу сказать - это правила этой темы. В данной теме - ТОЛЬКО УРОКИ, никаких вопросов и прочего, потому что это тема создана для систематизирования данных по обучению патчам. Все вопросы - пожалуйста, задавайте в теме по вопросам. Тема будет постоянно иметь статус закрытой, и открываться только для добавления новых уроков. Также хочу сказать - я не идеальный патчмейкер, к тому считаю, что я вообще довольно очень слабо шарю в этом деле, и тем более - я человек, и могу ошибаться даже в простых вещах. Если Вы заметили какой-то недочет, ошибку - отписывайтесь в лс, это приветствуется.

Подразумевается, что Вы научились понимать системы счисления основные, которые могут использоваться при разработке патчей. А именно - двоичная, десятичная и шестнадцатеричная, прочитали минимум - статью по основам патчеписания от Позитрона. В этой теме я постараюсь, по мере своих сил, попытаться научить желающих писать патчи - исполнить их это желание. Я постараюсь подробно рассматривать аспекты патчеписания, буду рассказывать, как стоит мыслить при разработке патчей, какие ошибки я допускал, а заодно временами - постараюсь конкретно разбирать некоторые патчи (не только свои, надеюсь авторы их будут не против), будут рассмотрены основные тонкости патчеписания, и надеюсь, если Вы будете полностью идти по моей методологии - то у Вас получится писать полноценные патчи. Только что хочу сказать - я буду стараться подробно рассказывать, и поэтому придется много читать. Итак, потихоньку, начнем :)

==================================================================

Урок №1 - Начало.

Итак, расскажу, как я учился патчам.. Сначала - я не знал о патчах ровным счетом ничего.. Потом узнал, что пишутся они программой BinEdit. Скачал ее, распаковал, запустил.. Начал рассуждать, изучать программу (скажу сразу - по-началу, она мне показалось довольно таки большой, я много чего не понимал, но чисто интуитивно начал по немножку копать). Итак, первые мои патчи были - это перенос патчиков с D900 на G600. (Следовательно, нужно открыть в BinEdit прошивки этих двух телефонов. D900 - D900XEFK2, G600 - G600XEGL1). После этого шага я выбрал патч наугад. Им оказался "Легкие секретные коды".

Итак, открыл я данный патч в WordPad'e, начал изучать структуру.. Это особого труда не составило, а азы английского языка мне и помогли разобраться конкректней. Итак, взял я первую строку патча на анализ:

<nord offset="0x0177972F" from="353336393633" to="363636363636" />

offset - это адрес. Насчет следующих двух тегов я попытался догадаться, что тег from - это то, что было в прошивке, тег to - то, что изменяется. Я не понял поначалу смысла от тега from, и поэтому решил пойти пока что дальше. Ввожу в BinEdit адрес 0177972F, и меня скидывает на начало прошивки, а то есть - на адрес 0x0. Я не понял, в чем проблема. Тут логически немного подумав, попробовал поискать в прошивке "353336393633".. и о чудо! Нашелся один адрес - 1177972F. Тут я начал думать, почему в offset написано 0177972F, а не 1177972F. Тогда я не понял этого.. Но решил не углубляться в это дело, понял систему - нужно первый нолик заменять на единичку, и все будет окей. Итак, вернемся к нашему патчу. Начал я пытаться дальше разбираться.. Смотрю, а что такое это "353336393633".. Хорошо, что первая вкладка в BinEdit (далее - BE) - это HEX. Я начал анализировать... Посередине были указаны байты все, т.е. сама прошивка. А справа - писался текст, который выглядил следующим образом (536963.#*53696.#). Тут мне бросилась в глаза закономерность - в HEX'e байт 35, а текст - 5; байт 33, а текст 3. Тогда я не умел компилировать патчи, но обнаружения этой закономерности мне позволило портануть патч, и далее - его самому дорабатывать. Итак, понял я немного систему преобразования адресов, нужно было рассматривать блок to. Он был следующим - "363636363636". Я преобразовал в уме это в цифры (что это будет? Правильно - 666666), затем поглядел опции патча - и действительно:

<check title="Заменить #*536963# Последовательная загрузка Java на #*666666#" offset="0x0177972F" type="data" on="363636363636" off="353336393633" />

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

Следующие адреса труда не составили, только что хочу отметить - что, увидел, что можно укорачивать код, просто забив до конца текста - нулями. Не понятно? Объясню.

Следующая строчка патча:

<nord offset="0x0177B9F8" from="383939392A3833373823" to="30312300000000000000" />

Преобразовал адрес, получил - 1177B9F8, открываю, вижу текст "8999*8378#". Смотрю опции патча:

<check title="Заменить *#8999*8378# - Инженерное меню на *#01#" offset="0x0177B9F8" type="data" on="30312300000000000000" off="383939392A3833373823" />

Итак, он заменяется на 303123 (а это 01, а байт "23" - это как я выяснил - знак "#"), и все байты, что ранее были по символ "#" просто заменяются на "00". Тут все оказалось до конца понятным, я начал портировать данный патч на G600. По началу портировал именно так патч, как он есть, изменив только адреса. Затем мне уже начали подавать идеи, что у них немного коды отличаются, и есть такие, которые часто используются, но довольно трудные. При портировании я еще столкнулся со следующим - базовый адрес.

Базовый адрес - это такой адрес памяти телефона, на котором располагается сама прошивка. В D900 он был - 0x10000000 (или как я пишу - 0x1), а в G600 - 0x20000000 (0x2). Как я это выяснил? Для начала я не обращал внимания на вопросы, задаваемые BinEdit при загрузке прошивки.. Но выяснить это удалось другим способом - поиском нужного мне кода. Я увидел различие в базах, и догадался, что в G600 к адресу, указанному в патче нужно прибавлять в начало не 1, а 2. Далее ничего интересного, я просто модифицировал патч.

Итак, рассмотрим некоторые аспекты.

 

1.1 Преобразование адресов. На самом деле - файл начинается с адреса 0x0. Представление, где лежат на самом деле данные (т.е. реальный адрес в памяти телефона) - это фишка BE. Итак, постараюсь объяснить преобразование адресов. Берем адрес, указанный в патчем. Он может состоять из любой длины (хоть 0x1), но длина адреса памяти в телефоне обязательно должна быть равна 8. Ни больше, ни меньше. Возьмем к примеру адрес - 0x01234567. Отсекаем 0x, получаем 01234567. Длина оказалась равна 8. Слева нолик, а как мы знаем из математики - количество нулей в начале выражения - роли не играет никакой. Отсекаем нолик, получаем 1234567, длина - 7. До нужной длины не хватает одной цифры, проблемы нет - прибавляем нужную цифру базового адреса. В D900 база какая я сказал? 0x1 -> 1, прибавляем просто 1, получаем адрес 11234567. Вуаля, все верно. А какая база в G600? 0x2 -> 2, прибавляем просто 2, получаем адрес 21234567. Все верно.

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

Итак, расширим наши знания по прибавлению адреса. А если оказалось так, что в патче указан адрес 0x123456. Что мы делаем? Отсекаем 0x, получаем 123456, длина - 6. До нужной длины не хватает двух символов, что мы сделаем? Надеюсь Вы подумали, что цифры базового адреса. Т.е. для D900 - мы бы прибавили 11, и получили бы в итоге - 11123456. Я рад буду, если Вы подумали хотя бы так. Но я попытаюсь Вас исправить. Базовый адрес мы прибавляем только как первый символ, все остальное - должны быть нули. Т.е. правильный адрес окажется - 10123456. В G600 бы оказался - 20123456.

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

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

 

1.2 Числа в HEX-преобразовании. Закономерность, что символ "0" в HEX'e выглядит как '30' имеет пару особенностей. Все числа, от 0 до 9 подходят под эту закономерность. Но - здесь нет чисел других, только от нуля до девяти. Число 11 - будет выгдядеть как 1 и 1. Т.е. в HEX'e - 3131.

 

Если Вы прочитали, попытались проделать со мной то, что я Вам рассказал, и у Вас это получилось - то могу поздравить Вас, вы портировали первый патч! Значит я не зря старался, и у Вас есть перспективы на будущее.

В следующем уроке я расскажу основные концепции патчеписательства, которые необходимо соблюдать, если Вы действительно хотите начать писать полноценные патчи, а также начнем изучать МСС-скрипты.

  • Like 39
Ссылка на комментарий
Поделиться на другие сайты

Урок №2 - МСС-скрипты.

Итак, приступим дальше к нашему подъеме в сфере патчеписательства.

Начну немного не совсем по прошивке. Что хочу сказать. Создание патчей - это довольно кропотливое занятие, времени улетает на это уйма, сам не замечаешь, как быстро оно убегает.. Также, когда что-то не получается, очень часто хочется бросить это пагубное дело.. Но потом с новыми силами все равно возвращаешься :) Итак, что хочу казать по поводу концепций патчеписания. Для начала, Вы наверняка хотите сразу же делать из телефона бомбу, чуть ли не ставить операционную систему на телефон =) Но хочу Вас возможно немного огорчить - всему свое время. Если Вы хотите писать хорошие патчи ПОТОМ - то придется постараться хорошо изучить азы СЕЙЧАС. Мой опыт в сфере "обучения патчам" показывает печальный опыт.. Не соблюдают мои советы, пытаются сразу портировать трудный патч, не имея представлений об ассемблере.. На вопрос "а почему ты сделал здесь именно так" ответ приходит "не знаю...".. Да, не спорю - мне тоже хотелось сразу же писать фокусные меню, и прочее прочее. Но поверьте, этого не будет, если Вы не захотите изучить азы. Да, Вы возможно портируете пару трудных патчей, предварительно немало заколебав патчмейкера, который в итоге Вам же добрую половину и сделает.. Первое, что Вам нужно сделать - это

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

Далее - второе правило. Не нужно пытаться, как можно скорее выложить патч.. Делайте это не ради людей-форумчан.. делайте ради себя (как бы это гнустно не звучало, но опыт показывает, что много людей, качающих патчи, просто бессовестно к этому относятся). Делайте даже не патчи ради себя, а пытайтесь понять патч, понять, почему сделано "тут" именно "так". Скажу сразу - смысл всего патча Вы поймете вряд ли, потому что некоторые патчи, которые я портировал спустя полгода своей патчеписательской "карьеры", смысл их только понял через два года.. Может чуток меньше, но смысл надеюсь понятен. Еще пару пожеланий. Насчет всех званий/медалей... Не кричите об этом, не просите - и тогда награда найдет своих героев. Опыт показывает, пока Вы хотите получить медаль, пытаетесь делать все ради них, тем самым показывая корыстную сторону - это омрачевает Ваш дух (так сказать), и Вы уже не герой =) А если будете бескорыстно делать патчи, даже просто ради того, чтобы именно научиться им - то в скором времени

и в Ваш дом придет удача :59:

Также, хочу отметить небольшую вещь. Будучи более опытными, Вы наверняка будете обмениваться различной информацией с патчмейкеры, например по поводу использования различных функций, просто помощи и т.д. Убедительно Вас прошу запомнить такую вещь - программисты не очень и любят, когда другие забывают об их помощи (в частности, я такой). Когда поможешь человеку например в ICQ, он доделывает патч и выставляет на форум, ни слова не замолвив о тебе - становится жутко неприятно, и просто появляется отвращение помогать в следующий раз. Я думаю, раз не плату берем, то иметь элементарную человеческую совесть - можно..

Итак, подведем итог. Цель - обучиться патчам, а не как бы поскорее их выложить и нарубить спасибок за них. (Кстати, опять таки из личного опыта. Все помнят поговорку - "поспешишь - людей насмешишь"? Я ее не любил никогда, и она была не про меня... пока я не начал делать патчи.. Вот в этом деле поговорка точно срабатывает. Чем быстрее хочешь выложить патч - тем больше раз ты его еще перезальешь, исправляя ошибки). Итак - обучиться патчам, чтобы в ближайшем будущем можно было писать трудные патчи с нуля. Соблюдая эту цель - я Вам обещаю, Вы добьетесь этого при хорошем уровне заинтересованности в своем деле, умения (или хотя бы на первое время - попытки) идти до конца.

Итак, теперь приступим к разбору прошивки.

Так вот, львиная доля прошивки телефона построена на MCC-скриптах. Скрипт представляет собой 16 байт, в котором есть идентификатор скрипта, показывающей, что это за МСС вообще, и остальное - возможные параметры скрипта. Дальше, углубляться не будем, и начнем изучать окошки. "Окошки" - это информационные окна, создаваемые телефоном. Примерами таких окон являются "Клавиатура разблокирована", "Удалено", "Скопировано" и т.д. Есть чуть ли не сборник патчи, "убивающих" эти окошки. Теперь остановимся на этом деле.. Есть несколько МСС-скриптов, отвечающих именно за вывод окошек:

1) MCC_REPORT. Выводит обычное информационное окно типа "Информация". Примеры как раз именно таких окон я привел выше. Разберемся, как использовать этот скрипт. Параметры известны некоторые - это звук окна, тип окна (соответственно и иконка), время, а также - текстовый индекс окна (более детально об этом - ниже):

Итак, пример скрипта:

.mcc mcc_report 0x1 0x2 0xA 0x701

Первый параметр - это звук. Здесь он хранится так - просто номер звука. От 0 до вроде как 0x10. Возможно некоторые звуки будут повторяться, но это уже так телефон устроен..

Второй параметр - это время появления окна. Измеряется в секундах. Т.е. параметр 0x2 - означает, что время появления окна это две секунды.

Третий параметр - не используется в телефоне почему-то.

Четвертый параметр - текстовый индекс. На нем поподробнее остановимся. В телефоне при использовании текста в 99% все проделывается через текстовый индекс. Индекс - это номер текстового ресурса. Для чего это сделано.. Ну поглядим, если мы будем указывать явный адрес текста... Разве это будет хорошо? Вот мы живем в России.. и будем создавать свое окно со своим текстом.. Поймут надпись кто? Только русские.. (точнее - те, кто знают русский язык).. А ведь среди нас есть еще и украинцы, англичане, французы и т.д. Так вот, есть в телефоне функция lk_get_text (о функциях подробней будет в следующих уроках, сейчас расскажу только суть). Она по индексу текстового ресурса выдает адрес на нужный текст нужного языка. Как узнать, какой текст заложен на определенном языке определенного текста? Или наоборот, как узнать индекс нужного текста? Все довольно таки просто. Если прошивка загружена в BE, то в большинстве случаев, таблица текстовых ресурсов автоматически найдена программой, и доступна нам, простым смертным, к просмотру :). В правой части программы открываем вкладку "Языковые ресурсы", выделяем там нужный нам язык (будем рассматривать - русский), нажимаем кнопку листика с двумя зелеными стрелочками, и вуаля - текста загрузились в нижнюю таблицу. Пролистываем начальные неважные текстовые ресурсы, и далее можем увидеть, что есть текстовый ресурс, его индекс в прошивке, и его конкретный адрес. Нас интересует именно индекс. Посмотрим на примере выше, какой текст выведется на русском языке в данном мсс-скрипте? Надеюсь, это будет Вам не трудно.. Итак, какой текст выведется? Правильно, текст таков: "Сохранено". Если у Вас это удачно получилось, то могу Вас снова поздравить - Ваш опыт повысился, и мы можем идти дальше.

Пойду немного обширнее.. Итак, видов информационных окон есть всего 7:

а) Информация (пример - "Клавиатура разблокирована", "Сохранено"). Несет в себе просто информационных характер, часто о выполнении какого-либо действия, имеет ограниченное время на появление ;

б) Ошибка (пример - "Батарея разряжена"). По сути - является тем же самым окном, что и выше, только имеет другой звук и иконку, также имеет ограниченное время на появление ;

в) Блокировка (пример - "Введите PIN"). Данное окно уже сложнее, имеет текстовое поле внутри себя, другую иконку, не имеет звука, и также имеет неограниченное время на появление ;

г) Будильник. Ну тут пример только один, будильник и разные события огранайзера. Ну думаю тут объяснять особо не надо - мелодия отдельная, выбирается в телефоне и т.д. ;

д) Подтверждение (пример - "Удалите часть элементов с телефона"). Не имеет звука, имеет неограниченное время появления ;

е) Прогресс-бар (пример - отправка файла по Bluetooth). Довольно тоже трудноватая структура его создания, объяснять особого смысла нет ;

ж) Вопрос. В прицнипе, отличается от подтверждения только иконкой.

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

Рассмотрим, самые простые окна. Большинство простых окон рисуется через mcc_report. Информационные окна с чуть более сложной структурой (например, когда текст является динамическим. Т.е. он не имеет своего конкретного индекса, и формируется уже перед выводом окна. Например - после удаления смс-ки, появляется окно с количеством свободного места смс в телефоне (ведь посудите, зачем хранить сотни комбинаций готовых текстовых ресурсов, когда можно сформировать динамический? ну более подробно, про динамический

 

текст - уже в последующих уроках), выводится через mcc_report_more. Но до данного скрипта нужно сформировать самостоятельно текст, а это уже достаточно сложно для текущего уровня знаний, поэтому это в следующих уроках.

Итак, будем разбирать сейчас "убийство" нужных окошек. Для начала рассмотрим "убийство" обычных окон типа "Информация". Есть патчи на данную тематику (например, "Убрать сообщение `Необходим ответ на сообщение`", "Убрать окна о переадресации", "Убрать сообщение `Клавиатура разблокирована` и т.д.). Способов создания окон довольно много, а способов их "уничтожения" - море и маленький океан :) Ну попробуем рассмотреть, самые простые.

 

Способ 1. Окошко ничего не в прицнипе не сделает, имеет статус просто оповещения о начале или завершении какого-либо действия. Например - окно "Клавиатура разблокирована". При создании данного окна сначала выполняется разблокировка телефона, затем создание данного окна, а затем выход. Т.е. какой можно вывод отсюда сделать? Окошко вообще не нужно, главное - не тронуть сам процесс разблокировки телефона. Итак, научимся искать нужные нам окна. После появления патча "CGSN" проблем с этим нет, можно найти почти любое окно, снять его адрес и проблем нет. НО - это банально просто. До появления данного патча, великие умы находили такое, чего мы с CGSN'ом не сделаем. Низкий им поклон за это, и попробуем для начала поискать вручную.

Итак, как будем искать. Нужно вбить в поиск МСС-скрипт. Итак, текст "Клавиатура разблокирована" не динамический, и хранится в прошивке. Поищем его текстовый индекс. Он равен - 0x440. Узнали текстовый индекс, запомним его. Для начала поиска скрипта нужно указать его тип. (Помните, я говорил, что первый байт мсс-скрипта - это его идентификатор, тип скрипта?). Так вот, нас интересует только mcc_report. Открываем в правой стороне BE вкладку "МСС", и ищем там "MCC_REPORT". Нашли, смотрим его код - 47. Это и есть его идентификатор (Внимание! В разных телефонах разные иденфикаторы мсс-скриптов!). Итак, первый байт мы узнали. Далее параметры в виде звука, времени и ненужного параметра можно опустить. Как сделать это в данном случае? Нужно заменить неизвестные байты скрипта знаком '?'. Тогда BE при виде данного символа будет использовать перебор, или просто не обращать внимания на байт, которому соответствует этот знак.

Так вот, рассмотрим длину MCC-скрипта (любого):

5C 00 1111 2222 3333 44444444 5555 6666

Размер скрипта, как я говорил - 16 байт.

Первый байт - иденфикатор. Размер, как я уже сказал - 1 байт.

Далее идут параметры. Первый параметр (00) - имеет размер также один байт.

Второй параметр (1111) уже побольше, имеет размер - два байта.

Третий параметр (2222) - два байта.

Четвертый параметр (3333) - два байта.

Пятый параметр (44444444) - уже четыре байта.

Шестой (5555) и седьмой (6666) параметры - по два байта.

Итак, что нам известно о скрипте вывода сообщения "Клавиатуры разблокирована"? Это первый байт, его идентификатор - 47, и текстовый индекс - 0x440. А в каком параметре у mcc_report хранится текстовый индекс? Правильно, в пятом.

Итак, что нам нужно вбить в поиск:

1) 47 (идентификатор mcc_report) ;

2) ?? (неизвестный первый параметр) ;

3) ???? (неизвестный второй параметр) ;

4) ???? (неизвестный третий параметр) ;

5) 4004 (номер текстового индекса в формате LittleEndian. О нем - ниже).

Итак, вот, что нам нужно ввести в поиск:

47??????????4004, нажимаем Enter - и вуаля, найден адрес 113BBA6C. Выбираем его, и смотрим на вкладку "МСС" в левой части BE:

113BBA4C  5C 00 0000 0000 0000 00000000 0000 0000  Подготовка окна

113BBA5C  17 0A 9C00 0000 0000 00000000 0000 0000  Выполнить 0xA x2=156 x3=0 x4=0 x5=0 x6=0

113BBA6C  47 0F 0200 0A00 4004 00000000 0000 0000  Сообщение "Клавиатура разблокирована"

113BBA7C  38 00 0000 0000 0000 00000000 0000 0000  Завершение MCC-скрипта.

Все прекрасно выполнено. Но что же такое "формат LittleEndian" (спасибо за исправление - FRAER)? Второе название ему - это переворачивание байт. Суть в том, что запись идет по байтно с конца в начало.

Допустим, есть у нас число - 11223344. Его мы его перевернем (или преобразуем в формат LittleEndian) - то у нас получится 44332211. Это переворачивание четырехбайтного числа. Двухбайтное (коим является текстовый индекс) выполняется также. Пример:

1234 -> 3412.

Рассмотрим переворачивание нашего текстового индекса. В "нашем" (людском) формате - это 0440. Первый байт - 04, второй байт - 40. Мы его записываем с конца в начало, т.е. сначала идет второй байт (40), потом первый (04). Поэтому и получилось число - 4004.

 

Теперь, вернемся к прошивке. Этот блок разблокирует клавиатуру.

Первый скрипт ("Подготовка окна") очищает экран от артефактов возможных, инициализирует фон меню и т.д.

Далее - "Выполнить XXXXX". Это называется таск (task -eng, задание). Просто выполняет конкретный кусок кода. В данном случае - он разблокирует клавиатуру. Далее выводится сообщение, интересующее нас, и завершение мсс-скрипта. Последний - возвращает телефон в то место, откуда блок скриптов был вызван.

Итак, какие на данном этапе могли появится вопросы? Ну например, откуда я узнал, что таск с адреса 113BBA5C разблокирует клавиатуру.. Ну вообще, потому что уже раскапывал это дело. На данном этапе знаний, нам еще рано рассматривать thumb-код (а именно он разблокирует клавиатуру) на языке ассемблер.

Вернемся, к прошивке.

 

Так вот, способ первый убийства окошка - это с места его рисования перенаправить на следующий мсс-скрипт.

Как нам быть? Для начала, нужно открыть ARM-компилятор. Как это делается:

В BE в главном меню открываем "Инструменты -> Компилятор". В уже компиляторе в главном меню нажимаем "Файл -> Новый файл". Открылось чистое поле.

Первая изученная нами директива компилятора будет директива ".start". Она создает новый блок патча с заданным директивы этим адресом.

С какого адреса мы будем изменять прошивку (другими словами, создавать наш патч). Это адрес самого окошка, а следовательно - 113BBA6C. Но в компиляторе уже нужно различать разные системы счисления. Число в двоичной системе записывается как 0b10101010 (к примеру). В десятичной - как мы и привыкли, допустим число "10". А в шестнадцатеричной - это 0x12345678.

Так вот, число "113BBA6C" - это в шестнадцатеричной системе счисления (т.к. присутствуют буквы).

 

Поэтому что нужно добавить, чтобы компилятор понял систему счисления? Правильно, 0x.

Итак, в компиляторе пишем такое:

.start 0x113BBA6C.

Все, мы указали адрес изменения прошивки.

Но как нам быть, как нам убрать данное окно? Итак, способ первый, рассматриваем нами, это просто переход с одного скрипта на другой. Но опять же, все завязано на скриптах, и мы также будем использовать скрипт. Нас интересует скрипт под названием MCC_TRANSF. Он позволяет телефону перейти с одного адреса выполнения на другой.

Если мы хорошенько подумаем, то куда нам следует перейти с адреса 113BBA6C, чтобы и не увидеть окошко, но и не "сломать" прошивку? Правильно, на следующий адрес, т.е. на адрес 113BBA7C. У MCC_TRANSF есть только один нужный параметр, это адрес перехода. Номер параметра - 5.

 

Остальные нужно оставить нулями.

Т.е. вот наш весь исходный код компилятора:

.start 0x113BBA6C

.mcc mcc_transf 0 0 0 0 0x113BBA7C

Теперь объясню. Стандартно, все параметры скрипта BE приравнивают нулю. Вот почему мы написали только пять параметров, а не семь. Потому что:

.mcc mcc_transf 0 0 0 0 0x113BBA7C 0 0

будем также компилироваться, как и:

.mcc mcc_transf 0 0 0 0 0x113BBA7C

Но тогда наверняка спросите Вы - а почему тогда нули до адреса мы оставили?

Да потому что, если мы напишем:

.mcc mcc_transf 0x113BBA7C - то это будет считаться первым параметром скрипта!

 

Также, есть второй способ, который подходит в данном случае. Это команда "ничего не делания". Скрипт называется - mcc_zero. У него никаких параметров нет :) В прицнипе, забегая вперед, скажу, что это полный аналог команды `nop` ассемблера, просто переход на следующую команду. Вот, можем компилировать наш данный патч, и радоваться изменениям телефона. Вот только, насколько я помню, это окно необходимо, поскольку оно включает подсветку (в коде прорисовки информационных окон есть вызов функции включения подсветки дисплея), и поэтому данный патч всегда считался полуглюченным.

 

Давайте теперь рассмотрим "убийство" вопросов/подтверждений. Примером является включение громкой связи

(и соответственно, есть патч "Громкая связь без подтверждения". Итак, сообщение "Включить динамик?" является вопросом. Вопрос - это мсс-скрипт - MCC_QUESTION. (Какой у него идентификатор? Правильно, 49).

Итак, нам требуется найти данный скрипт по той же аналогии, что мы и искали сообщение об клавиатурые. Здесь расскажу о параметрах данного скрипта - первый и второй параметры - текстовый индексы софт-клавиш (причем здесь уже отдельные текстовые индексы, адрес ресурса получают здесь не функцией lk_get_text, а - lk_get_sofk). Сами индексы и ресурсы можно посмотреть в программе Resource Manager при соответствующей карте прошивки. А вот третий параметр - это индекс текстового ресурса самого вопроса (в формате LittleEndian). Запомним, что в наших телефонах ВСЕ числа хранятся в таком формате).

Предположим, что мы не знаем индексы софт-клавиш. Но нам нужно найти индекс нужного нам вопроса. Попробуйте найдите текстовый индекс вопроса "Включить динамик?". Если Вы вспомните, как это делается, и попробуете это сделать, то должны получить число - 0x0AC5. (Обратите внимание, что нам нужно именно "Включить динамик?", а не просто "Включить динамик).

Итак, что нам нужно ввести в поиск:

1) 49 (идентификатор мсс-скрипта MCC_QUESTION) ;

2) ?? (неизвестный параметр) ;

3) ???? (неизвестный параметр) ;

4) C50A (текстовый индекс ресурса "Включить динамик?").

Итак, в поле поиска вводим: 49??????C50A, нажимаем Enter, и вуаля! Нашелся один адрес 10F04694. Посмотрим, что идет дальше:

10F04694  49 58 2C00 C50A 0000 00000000 0E03 0000  Вопрос "Включить динамик?"

10F046A4  18 09 0100 0E00 0100 C4EAB010 0000 0000  Если нажать кнопку "левая софт" то переход на 0x10B0EAC4

10F046B4  19 09 0100 0F00 0100 C41B2410 0000 0000  Или если нажать на кнопку "правая софт" то переход на 0x10241BC4

10F046C4  38 00 0000 0000 0000 00000000 0000 0000  Завершение МСС-скрипта

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

Давайте попробуем его убрать посредствам скрипта mcc_transf.

Итак, откуда надо начинать директиву .start?

Если хорошенько подумать, то должен быть такой код:

.start 0x10F04694

Итак, еще раз хорошенько подумаем, а на какой адрес мы будем переводить телефон? (Замечу, что здесь не скрипт mcc_zero не подходит, потому что следующим скриптом после вопроса идет обработка клавиш. А он бы подошел только в случае, если включение громкой связи шло следующим скриптом). Итак, хорошо подумали, на какой адрес будем переводить телефон? Правильно, на 0x10B0EAC4.

Итак, значит вот весь исходный код компилятора:

.start 0x10F04694

.mcc mcc_transf 0 0 0 0 0x10B0EAC4

Компилируем, прошиваемся, проверяем - и о чудо, патч работает :)

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

  • Like 30
Ссылка на комментарий
Поделиться на другие сайты

Урок №3 - МСС-скрипты. Продолжение

Итак, в прошлом уроке мы совсем чуточку затронули мсс-скрипты, но уже хоть что-то должно быть понятно. Так вот, пойдем далее. Будем разбирать вкратце основные мсс-скрипты. В этом уроке мы начнем с того, что попытаемся "побегать" по телефону.

Начало пути, по которому следует идти - это главное меню телефона (далее - ГМ). А как его найти? Расскажу. Есть мсс-скрипт, выделяющий пункт меню. Т.е. устанавливает его текущим (по его номеру) по умолчанию. Когда мы первый раз заходим в гм, какой выделен пункт? Правильно, "Сообщения". А какой это по номеру пункт? Пятый. Так вот, при входе в гм есть мсс-скрипт, устанавливающий как раз таки выделенным пунктом - "Сообщения". Скрипт называется - mcc_set_curmenu. Что нам нужно - нужно попытаться через уже знакомый поиск найти этот скрипт. Какие параметры у данного скрипта? Всего лишь 1 - это номер пункта меню для установки. Запомните - в телефоне отсчет начинается с нуля! Т.е. если допустим нам нужно установить первый пункт меню, то параметром для скрипта будет 0, а не 1. Номер данного параметра в скрипте - второй, а не первый. Вспоминаем, что нам нужно для поиска:

1) Идентификатор нужного скрипта (какой он у mcc_set_curmenu? Правильно, 78) ;

2) Его параметры.

Итак, составляем сигнатуру скрипта для поиска:

1) 78 (идентификатор mcc_set_curmenu) ;

2) 00 (нулевой первый параметр [всегда равен нулю в данном мсс-скрипте] ;

3) 0400 (Номер пункта меню для установки в формате LittleEndian (надеюсь понятно, почему 0400, а не 0500? Если нет, то напомню еще раз - отсчет ведется с нуля, т.е. 0 - первый пункт меню, 1 - второй, 2 - третий и т.д.)

Итак, мы составили сигнатуру для поиска, вводим в поиск "78000400", нажимаем Enter, смотрим..

Ой, а что это так много выдало адресов? Давайте посмотрим. Первый найденный адрес - это 0x1002F05B... Первое, что бросается в глаза - последний "символ" адреса - 0xB (16-ричная) -> 11 (десятичная). Так как размер скриптов всегда статичен, и равен (если Вы не забылы) 16 байтам, то скрипты должны всегда располагаться по адресу, кратному четырем!. А именно: 0, 4, 8, 0xC. Если в поиске мсс-скрипта мы нашли адрес, последним символ которого не кратен четырем - можете сразу его отбрасывать, это так называемый - мусор. Как так получилось, что нашелся мусор? Самое простое объяснение - это просто совпадение. В прошивке оказались данные (в данном случае под словом "данные" я здесь подразумеваю - содержание прошивки) в таком же виде, как Вы ввели в поиск. Как же нам быть? Ну например так. Т.к. я сказал, что в скрипте все остальные параметры не нужны, значит они следовательно - нулевые. Т.е. для большей точности - давайте нашу сигнатуру поиска забьем нулями до полного скрипта (т.е. до размера, равного шестнадцати байтам). Итак, получаем: 78000400000000000000000000000000 (16 байт в текстовом виде - это 32 символа). Итак, вставляем это в поиск, нажимаем Enter... и вуаля :evil: Нашлось всего три адреса. Рассмотрим их, чтобы научиться отсеивать ненужные адреса от адреса гм в данном поиске.

Первый адрес - 0x107DCCA0. Смотрим его структуру:

107DCCA0  78 00 0400 0000 0000 00000000 0000 0000  Пункт меню 5 будет выбран по умолчанию

107DCCB0  75 00 0100 0141 0000 00000000 3000 0000  Начало меню, ширина 48

107DCCC0  29 00 0100 0000 0080 00000000 0000 0000  MCC_FLAG

107DCCD0  1B 00 0000 0000 0000 6075FF10 0000 0000  Вызов подпрограммы 0x10FF7560

107DCCE0  7C 0F 0100 0400 0200 00000000 0000 0000  Меню 0xF из 4 пунктов

107DCCF0  76 0F 0100 0000 0202 00000000 1000 0000  Отобразить меню 0xF с параметром 202

107DCD00  79 00 0000 0000 0000 9065FF10 0000 0000  По пункту меню 1 переход на адрес 0x10FF6590

107DCD10  79 00 0100 0000 0000 6C0B4111 0000 0000  По пункту меню 2 переход на адрес 0x11410B6C

107DCD20  79 00 0200 0000 0000 90EB8711 0000 0000  По пункту меню 3 переход на адрес 0x1187EB90

107DCD30  79 00 0300 0000 0000 20F32E10 0000 0000  По пункту меню 4 переход на адрес 0x102EF320

107DCD40  79 00 0400 0000 0000 90CD7D10 0000 0000  По пункту меню 5 переход на адрес 0x107DCD90

107DCD50  1B 00 0000 0000 0000 E417BC10 0000 0000  Вызов подпрограммы 0x10BC17E4

107DCD60  18 09 0100 0F00 0000 A00B3511 0000 0000  Если нажать кнопку "правая софт" то переход на 0x11350BA0

107DCD70  19 09 0100 0200 0000 A00B3511 0000 0000  Или если нажать кнопку "С кратко" то переход на 0x11350BA0

107DCD80  38 00 0000 0000 0000 00000000 0000 0000  Завершение МСС скрипта

Главная интересующая строка, что мы здесь видим - это Меню 0xF из 4 пунктов. Разве в гм 4 пункта? Нет, следовательно уже выбрасываем этот адрес.

 

Второй адрес - 0x10EF58A4. На данном этапе трудновато объяснить, почему он не подходит.. Пока пропустим это, потом уже смогу объяснить, когда Вы начнете понимать ассемблер как минимум.

 

Третий адрес - 0x11350BD0.

11350BD0  78 00 0400 0000 0000 00000000 0000 0000  Пункт меню 5 будет выбран по умолчанию

11350BE0  75 00 0100 0041 0000 00000000 F000 0000  Начало меню, ширина 240

11350BF0  17 13 2700 0000 0000 00000000 0000 0000  Выполнить 0x13 x2=39 x3=0 x4=0 x5=0 x6=0

11350C00  76 27 0000 0000 0202 00000000 1000 0000  Отобразить меню 0x27 с параметром 202

11350C10  79 00 0000 0000 0000 8450EF10 0000 0000  По пункту меню 1 переход на адрес 0x10EF5084

11350C20  79 00 0100 0000 0000 14AE3311 0000 0000  По пункту меню 2 переход на адрес 0x1133AE14

11350C30  79 00 0200 0000 0000 D8E37711 0000 0000  По пункту меню 3 переход на адрес 0x1177E3D8

11350C40  79 00 0300 0000 0000 6063FF10 0000 0000  По пункту меню 4 переход на адрес 0x10FF6360

11350C50  79 00 0400 0000 0000 2C364111 0000 0000  По пункту меню 5 переход на адрес 0x1141362C

11350C60  79 00 0500 0000 0000 D02A8B11 0000 0000  По пункту меню 6 переход на адрес 0x118B2AD0

11350C70  79 00 0600 0000 0000 88088811 0000 0000  По пункту меню 7 переход на адрес 0x11880888

11350C80  79 00 0700 0000 0000 08E8E710 0000 0000  По пункту меню 8 переход на адрес 0x10E7E808

11350C90  79 00 0800 0000 0000 ECFC2A10 0000 0000  По пункту меню 9 переход на адрес 0x102AFCEC

11350CA0  79 00 0A00 0000 0000 24E6AF10 0000 0000  По пункту меню B переход на адрес 0x10AFE624

11350CB0  18 09 0100 0F00 0000 50F17210 0000 0000  Если нажать кнопку "правая софт" то переход на 0x1072F150

11350CC0  19 09 0100 0200 0000 50F17210 0000 0000  Или если нажать кнопку "С кратко" то переход на 0x1072F150

11350CD0  38 00 0000 0000 0000 00000000 0000 0000  Завершение МСС скрипта

Итак, рассмотрим повнимательней. ГМ отличается от обычных менюшек телефона, думаю это ни для кого не секрет. Неудивительно, что и структура его создания будет тоже отличаться. На что нам тут надо обратить внимание - скрипт mcc_menu_select (или в описании - По пункту X переход на адрес 0xY). Так вот, видим, что тут описано адреса переходов по 9 пунктам, и еще по одному.. (Не беспокойтесь о том, что много необъясненной информации, сейчас все по порядку пойдет). Маленький секрет. Помним меню Look Test? Так вот, разработчики зачем-то добавили возможность переходить из главного меню (во всяком случае, это отражают мсс-скрипты прошивки, на самом же деле думаю ни у кого из Вас этого не удавалось). Так вот, для тех, кто разрабатывает патчи с ГМ, состоящих из 9 пунктов - у вас должна быть такая структура ГМ. Заодно также и приведу пример для тех, кто разрабатывает патчи с ГМ, состоящих их 12 пунктов на примере G600. Проделываем все тоже самое (напомню еще раз - статьи будут в основном по прошивкам телефонов D900 (D900XEFK2) и G600 (G600XEGL1):

207AA144  78 00 0400 0000 0000 00000000 0000 0000  Пункт меню 5 будет выбран по умолчанию

207AA154  75 00 0100 0041 0000 00000000 F000 0000  Начало меню, ширина 240

207AA164  17 23 5C00 0000 0000 00000000 0000 0000  Выполнить 0x23 x2=92 x3=0 x4=0 x5=0 x6=0

207AA174  18 1B 2F00 0100 0100 50834521 0000 0000  Если x1=27 x2=47 x3=1 то переход на 0x21458350

207AA184  1B 00 0000 0000 0000 A4DB0321 0000 0000  Вызов подпрограммы 0x2103DBA4

207AA194  17 13 2F00 0000 0000 00000000 0000 0000  Выполнить 0x13 x2=47 x3=0 x4=0 x5=0 x6=0

207AA1A4  76 2E 0000 0000 0202 00000000 1000 0000  Отобразить меню 0x2E с параметром 202

207AA1B4  18 09 0100 0E00 0100 E81ECB20 0000 0000  Если нажать кнопку "левая софт" то переход на 0x20CB1EE8

207AA1C4  19 09 0100 0D00 0100 E81ECB20 0000 0000  Или если нажать кнопку "i/ok" то переход на 0x20CB1EE8

207AA1D4  19 09 0100 0300 0100 E81ECB20 0000 0000  Или если нажать кнопку "3" то переход на 0x20CB1EE8

207AA1E4  79 00 0200 0000 0000 80FD4521 0000 0000  По пункту меню 3 переход на адрес 0x2145FD80

207AA1F4  79 00 0800 0000 0000 68F04B21 0000 0000  По пункту меню 9 переход на адрес 0x214BF068

207AA204  79 00 0900 0000 0000 F8527E20 0000 0000  По пункту меню A переход на адрес 0x207E52F8

207AA214  79 00 0000 0000 0000 80899521 0000 0000  По пункту меню 1 переход на адрес 0x21958980

207AA224  79 00 0100 0000 0000 F8659921 0000 0000  По пункту меню 2 переход на адрес 0x219965F8

207AA234  79 00 0A00 0000 0000 C4B74F21 0000 0000  По пункту меню B переход на адрес 0x214FB7C4

207AA244  79 00 0300 0000 0000 C8477D20 0000 0000  По пункту меню 4 переход на адрес 0x207D47C8

207AA254  79 00 0400 0000 0000 54A87E20 0000 0000  По пункту меню 5 переход на адрес 0x207EA854

207AA264  79 00 0500 0000 0000 2CDCCD20 0000 0000  По пункту меню 6 переход на адрес 0x20CDDC2C

207AA274  79 00 0600 0000 0000 58197E20 0000 0000  По пункту меню 7 переход на адрес 0x207E1958

207AA284  79 00 0700 0000 0000 802AFD20 0000 0000  По пункту меню 8 переход на адрес 0x20FD2A80

207AA294  79 00 0B00 0000 0000 146C0921 0000 0000  По пункту меню C переход на адрес 0x21096C14

207AA2A4  79 00 0A00 0000 0000 48477E20 0000 0000  По пункту меню B переход на адрес 0x207E4748

207AA2B4  18 09 0100 0F00 0100 70220C21 0000 0000  Если нажать кнопку "правый софт" то переход на 0x210C2270

207AA2C4  18 09 0100 0F00 0000 94769621 0000 0000  Если нажать кнопку "правый софт" то переход на 0x21967694

207AA2D4  19 09 0100 0200 0000 94769621 0000 0000  Или если нажать кнопку "С кратко" то переход на 0x21967694

207AA2E4  38 00 0000 0000 0000 00000000 0000 0000  Завершение МСС скрипта

 

Вот примерно такая структура. В 12-пунктовых меню некрасиво сделано тем, что описание переходов по пунктам меню сделано довольно хаотично, как видим: сначала переход по номеру 3, потом 9, потом A (0xA (16) -> 10 (10)) и т.д.

Так вот, возвратимся к D900.

 

Вот перед нами главное меню телефона на мсс-скриптах. Допустим, мы захотели посмотреть мсс-скрипты пункта "Сообщения". Что нам нужно сделать? Смотрим мсс-скрипт: По пункту меню 5 переход на адрес 0x1141362C. Значит что? Думаю не трудно догадаться, что нужно перейти по адресу 0x1141362C. Итак, смотрим:

1141362C  1B 00 0000 0000 0000 68B12D10 0000 0000  Вызов подпрограммы 0x102DB168

1141363C  5C 00 0000 0000 0000 00000000 0100 0000  Подготовка окна

1141364C  78 00 0000 0000 0000 00000000 0000 0000  Пункт меню 1 будет выбран по умолчанию

1141365C  75 00 0100 0041 0000 00000000 3000 0000  Начало меню, ширина 48

1141366C  6D 03 0000 0000 0000 00000000 0000 0000  MCC_SET_KEYBOARD

1141367C  7C 22 0000 0700 0200 00000000 0000 0000  Меню 0x22 из 7 пунктов

1141368C  76 22 0000 0000 C202 00000000 1000 0000  Отобразить меню 0x22 с параметром 2C2

1141369C  79 00 0000 0000 0000 68AB2D10 0000 0000  По пункту меню 1 переход на адрес 0x102DAB68

114136AC  79 00 0100 0000 0000 6C374111 0000 0000  По пункту меню 2 переход на адрес 0x1141376C

114136BC  79 00 0200 0000 0000 7830BC10 0000 0000  По пункту меню 3 переход на адрес 0x10BC3078

114136CC  79 00 0300 0000 0000 9C3C4111 0000 0000  По пункту меню 4 переход на адрес 0x11413C9C

114136DC  79 00 0400 0000 0000 C095FF10 0000 0000  По пункту меню 5 переход на адрес 0x10FF95C0

114136EC  79 00 0500 0000 0000 E44B3311 0000 0000  По пункту меню 6 переход на адрес 0x11334BE4

114136FC  79 00 0600 0000 0000 3CA53B11 0000 0000  По пункту меню 7 переход на адрес 0x113BA53C

1141370C  18 09 0100 0F00 0000 50F17210 0000 0000  Если нажать кнопку "правая софт" то переход на 0x1072F150

1141371C  19 09 0100 0200 0000 50F17210 0000 0000  Или если нажать кнопку "С кратко" то переход на 0x1072F150

1141372C  19 09 0100 0900 0000 A00B3511 0000 0000  Или если нажать кнопку "вкл/выкл (красная)" то переход на 0x11350BA0

1141373C  19 09 0100 0600 0000 6063FF10 0000 0000  Или если нажать кнопку "влево" то переход на 0x10FF6360

1141374C  19 09 0100 0700 0000 D02A8B11 0000 0000  Или если нажать кнопку "вправо" то переход на 0x118B2AD0

1141375C  38 00 0000 0000 0000 00000000 0000 0000  Завершение МСС скрипта

 

Вот здесь мы и познакомимся ближе с основными мсс-скриптами.

Итак, давайте пока что "Вызов подпрограммы" опустим ниже, и начнем со строения самого меню.

Итак, первый - mcc_init_restore (Подготовка окна). Думаю здесь особо проблем не должно возникнуть, довольно справедливое его описание. В этом скрипте подготавливается фон меню, очищается некоторая часть экрана от возможных артефактов с предыдующих меню, включается подсветка дисплея и т.д. Параметры у данного скрипта есть, их всего два - второй и шестой. Могут быть или нулем, или единицей, других вариантов быть не может. Первый параметр (а по расположению - второй) непонятно что делает, предназначение специфическое. А второй (по расположению - шестой) устанавливает допустимые нажатий клавиш). По скажу напрямую - я не видел специального использования данных параметров, чаще всего и пишем так:

.mcc mcc_init_restore

т.е. все параметры равны нулю.

 

Далее, следующий скрипт - mcc_set_curmenu. Ну его мы уже разобрали выше.

Далее - mcc_menu_start (Начало меню, ширина 48). Всего три параметра у него. Второй, третий и шестой. Первый и второй параметры (по расположению которые второй и третий) - отвечают за маску допустимых клавиш в меню. Подробней о маске допустимых клавиш - это обозначение тех клавиш, на которое телефон будет реагировать в данном меню. Структура непростая, используются логическое побитные операции для понимания телефоном, какую мы маску написали, но об этом - намного дальше. За что отвечает третий (шестой) параметр - честно скажу, не знаю. Описание у него несовсем корректное приведено, потому что "48" это не ширина меню.

Далее - mcc_set_keyboard. Параметр всего лишь один, он как раз идет первый. Служит также для допустих клавиш, особо заострять на нем внимания не будет, скажу - я его специально ни разу не использовал в своих патчах.

Далее - mcc_menu_config (Меню 0x22 из 7 пунктов). А вот это очень довольно-таки важный скрипт. В кратце - в данном скрипте строится структура пунктов меню, т.е. внутри самого скрипта создается меню, задается количество его пунктов, какие это пункты, текст их, и т.д. В разных меню в прицнипе по разному, но смысл несет один и тот же - описание самих пунктов меню. Параметры мало чего сейчас Вам скажут.. но на всякий случай напишу их: первый - номер глобальный для mcc_menu_config, второй - кейс функции, описывающей меню, третий - количество пунктов в данном меню, четвертый - тип меню. Теперь поподробнее. Зачем этот "глобальный номер"? Разработчиками в принципе сделан для разделения функций под разные меню. Еще подробней. Есть главный скрипт - mcc_menu_config. В нем есть таблица функций для разных меню, обобщенная по какому-нибудь признаку. Например - функция, которая строит все меню, связанный с сообщениями. Или же - с "моими файлами", "настройками" и т.д. Далее - кейс. Что такое кейс? А он уже служит для идентификации конкретного меню из всей функции. Т.е. разберем на примере данное меню. (mcc_menu_config 0x22 0 0x7 0x2, данное представление Вы можете увидеть, если откроете вкладку "Код" в левой часте BE, но не всегда BE корректно интерпретирует мсс-скрипты на данной вкладке, так что не гарантируется, что Вы всегда увидете нужное). Глобальный номер у данного меню - 0x22, следовательно - 0x22 - это номер функции, строящей все менюшки, связанные с сообщениями. Кейс равен нулю, т.е. самое первое меню. Если бы кейс был равен допустим единице, то уже это означает другое меню. Далее - третий параметр 0x7 - из семи пунктов состоит данное меню. Если Вы помните, то я говорил, что отсчет в телефоне идет с нуля, то у Вас должен появится вопрос - а почему здесь семь пунктов меню, и указано число 7, а не 6? Если у Вас действительно он появился, то это очень хорошо. Объясняю. В функции создания меню специально сделано так, чтобы было возможно создавать меню без пунктов, т.е. пустое. Примером является - пустая папка. Мы заходим в нее, и видим там текст "Пусто". Вот такое произойдет, если будет в данном меню - ноль пунктов. Следующий параметр - 0x2 - тип меню. На этом особо зацикливаться не стоит, но вкратце расскажу. Есть разные типы меню. Блоковые однострочные (0x2) - самые распространенные, примером является любой пункт в ГМ (в моделях, отличных от D900 - не все пункты меню, но большинство). Блоковое двустрочное (0x3) - примером является папка "Другие файлы" (именно список файлов, без возможности передвигаться клавишами влево-вправо). Колесное однострочное (0x4) - примером допустим является менюшка, как раз где можно двигаться клавишами влево-вправо. Например - журнал последних звонков на зеленой трубке, которое вызывается с режима ожидания (рабочего стола). Клавишами влево-вправо можно переключаться между последними звонками, исходящими, входящими и т.д. Колесное двустрочное (0x5) - честно говоря, не могу вспомнить такого меню в телефоне, но представить их легко - все тоже самое, как и в однострочном, только высота курсора больше, и можно поместить текст во вторую строку, располагающуся на курсоре. Обычное всплывающее меню (0x6) - обычно используется для опций. Например - опции при вводе смс, опции в журнале звонков и т.д. На этом закончим типы меню.

Далее - mcc_display_menu (Отобразить меню 0x22 с параметром 2C2). В данном мсс-скрипте происходит его рисование на дисплей по заданной структуре в mcc_menu_config. Какие параметры: первый и второй полностью совпадают с теми, что мы ввели в mcc_menu_config выше, третий, четвертый и пятый - я не знаю, для чего используются (обычно беру те, которые указаны в прошивке при создании такого же типа меню, что и я создаю), а шестой - отвечает за то, что будет отображаться слева от названий пунктов меню и будет ли отображаться вообще. Возможны следующие значения - не отображаются вообще. Т.е. рисуется только названия пунктов меню (такое делается, когда слева от названия пункта меню рисуют какую-либо иконку). Возможно рисовование номеров пунктов меню (как это чаще всего и встречается). А также возможно, чтобы рисовались радиокнопки вместо номеров пунктов меню (используется например в активации Bluetooth). На данном мсс-скрипте телефон предоставляет возможность пользователю бегать по менюшке, т.е. когда мы бегаем по менюшке, выполняется mcc_display_menu и на этом телефон останавливается. Почему я это говорю? Потому что выполнение мсс-скприптов выполняется последовательно,по одному, поэтому в mcc_display_menu используется функция, чтобы телефон "не убегал" дальше по скриптам, а предоставил возможность побегать по менюшке.

Далее - mcc_menu_select (По пункту XX переход на адрес YY). Данный скрипт думаю особого объяснения не требует. Он устанавливает по какому адресу нужно перейти в случае, если мы "заходим" в пункт меню. Параметры - второй - это номер пункта меню, и пятый - это адрес перехода. Пару примечаний. Бывает такое, что вместо номера пункта меню - написано "0xFFFF" - это означает, что адрес перехода является одинаковых для всех пунктов меню, т.е. при выборе хоть первого пункта меню, хоть второго - будем переходить в указанный в скрипте адрес. Также хочу отметить такую вещь, что бывает адрес переходов по некоторым пунктам меню не описаны, например - описаны адреса переходов по первому пункту меню, второму и четвертому. А что делать телефону, в случае, если выбрали третий пункт меню? Такое обычно происходит в случае, если адреса переходов сохранены еще в самом формировании меню, а именно - mcc_menu_config.

Далее - mcc_if и mcc_elseif (Если XX, то переход на YY). Различий в прицнипе между ними особо никаких нет, но они отличаются.. А чем конкретно - я могу только догадываться, поэтому не буду врать. Это аналог в прицнипе конструкции if .. then.. else.. (если - тогда, иначе). В данном меню есть mcc_if и mcc_elseif нажатий кнопок. Параметры данных скриптов: первый - номер глобальный для mcc_if (аналог номеру mcc_menu_config, т.е. mcc_if (и соответственно и mcc_elseif) также подразделяются на условия для какой-то определенной сферы), второй - кейс конкретного условия, третий и четвертый и шестой - динамические, могут использоваться, а могут и не использоваться. А пятый параметр - это адрес, куда телефону нужно перейти в случае, если условие будет истинно.

Ну и далее, последний скрипт - mcc_exit (Завершение скрипта) - если при нажатии на какую-нить клавишу, которая не описана в условиях опроса кнопок - то выполняется данный скрипт, и чаще всего - телефон просто выходит в режим ожидания (рабочий стол). Наверняка появится вопрос - а почему описано так мало кнопок? Ведь телефон при нажатии на качельку, цифры, навигацию не выходит же на рабочий стол. Объясню - часть кнопок блокируется, как я описывал выше, причем блокируется до выполнения скриптов условий нажатий клавиш, поэтому на них не реагирует телефон, клавиши навигации "регистрируются" при формировании меню, а также еще могут быть случаи опроса клавиш уже в адресе, которой указан в "По пункту XX переход на 0xYY".

 

На данном этапе мы разобрали основные мсс-скрипты, используемые при формировании меню. Если хорошо это поймете, то успех уже на Вашей стороне :( Конечно, не спорю, довольно много труднопереваримой информации, но как-то же патчмейкеры это все понимают.. все приходит со временем.. В завершении урока, давайте разберем маленький патчик. Им будет - замена функции перехода с одного пункта на другой, а точнее - "Поменять местами пункты в SMS меню". Вот вроде бы и просто с текущим багажом знаний, но чего-то не хватает, не так ли? Итак, будем пробовать поменять местами адреса переходов. С чего начнем? Правильно, с открытия ГМ. Открываем адрес гм - надеюсь, помним как его найти? Далее - нас интересует пункт №5 "Сообщения". Итак, мы на адресе 0x1141362C. Какова наша задача? Поменять местами адреса функций в мсс. Ну тут, надеюсь проблем не возникнет. Итак, подумали, выбрали. Первый действием у нас - замена функций со второй пункта меню на первый. Итак, подумали... Берем адрес скрипта перехода по первому пункту меню, это - 0x1141369C. Это первый адрес изменения прошивки. Открываем компилятор (напомню - Инструменты -> Компилятор), затем в компиляторе - Файл -> Новый файл. Пишем следующее:

.start 0x1141369C

Итак, нам нужно поменять адрес перехода по пункту меню. Это пятый параметр. Взглянем на скрипт:

79 00 0000 0000 0000 68AB2D10 0000 0000.

Вот фрагмент 68AB2D10 - ничего не напоминает? Попробуйте преобразовать из формата LittleEndian в человеческий.. ЧТо получится? Правильно, 102DAB68, что и является адресом перехода по пункту меню.

Эту часть скрипта мы и будем менять. Рассмотрим также такое понятие, как смещение. Простым языком - это сколько нужно прибавить байт к одному адресу, чтобы получить конечный.

Подробней объясняю. Нам нужно получить адрес скрипта фактический, на котором расположен адрес перехода по пункту меню. Тут довольно просто, посчитаем:

79 00 0000 0000 0000 68AB2D10 0000 0000.

0_1__2_3__4 5__6_7_8_9_1011 1213 1415

Вот данные циферки и являются численным смещением по данному мсс-скрипту. Возможно, встанет у Вас вопрос - а почему цифра идет не по каждому символу скрипта. А потому, что 2 символа в таком текстовом виде - являются одним байтом. Помните, я говорил идентификатор мсс-скприпта размером в один байт? Вот как раз, 79 - и является одним байтом. Итак, адрес функции перехода у нас лежит по смещению 8, поэтому в компиляторе пишем следующее:

.start 0x1141369C+8

Итак, мы установили адрес изменения прошивки, теперь нам нужно записать туда новые данные. Размер для изменения, если подумать - 4 байта, потому что размер адреса - это четыре байта, поэтому, чтобы записать новый адрес - нам требуется изменить всего лишь - 4 байта. Итак, для сохранения адреса есть директива компилятора - .word. Данная директива автоматически переворачивает адрес, если это необходима. В нашем случае, что мы копаем свифты, переворачивание необходимо. Итак, а какой нам нужно адрес записать? Правильно, адрес функции, что стоит по переходу по второму пункту меню. Итак, на данный момент в компиляторе у вас должно быть следующее:

.start 0x1141369C+8

.word 0x1141376C

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

.start 0x114136AC+8

.word 0x102DAB68

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

 

А пока, как уже по традиции - попытайтесь Вы, мои дорогие начинающие патчмейкеры, переварить данную информацию. Успехов Вам!

  • Like 25
Ссылка на комментарий
Поделиться на другие сайты

Урок №4 - Первые МСС-врезки.

В прошлом уроке мы познакомились с мсс-скриптами, отвечающими за строение меню. Да и вообще - были в прицнипе разобраны самые частовстречающиеся скрипты.

В данном уроке мы поставим выше планку - мы будем пробовать портировать патч уже с использованием свободного места прошивки! Что хочу сказать.. Пока Вы находитесь на довольно слабых стадиях умения создавать/портировать патчи - забудьте вообще о таком понятии, как "портировать по SMP". Единственная Ваша возможность портануть патч - это взять его исходник. О других способах можете забыть.

Какие на данном этапе бывали ошибки.

Ошибка 1. Часто начинающие патчмейкеры думают, что все в этой жизни просто, при портировании патча нужно только изменить адреса в теге "nord offset" и все. Хочу сказать следующее. Портирование патча - это довольно сложный процесс, никогда не знаешь, какие трудности он принесет. Бывали случаи, когда действительно, чтобы перенести патч с одного телефона даже на другой - нужно было просто изменить адрес изменения прошивки - это такие патчи бывали только в 0.0001% случаях, поэтому о нем забудьте.

Ошибка 2. Вам удалось найти исходник, Вы даже сделали больше - Вы нашли нужные эквиваленты врезок, и т.д. НО! Этого недостаточно. Первое, что необходимо при портировании - пытаться разобраться в патче. Необязательно необходимо досканально изучить его прицнип действия, нет. Так изучить патч Вы еще довольно долгое время не сможете (я имею ввиду довольно трудоемкие патчи). Но Вам нужно пробовать понять - а почему здесь написан именно данный код, именно данный скрипт - откуда он вообще взят и т.д.

Итак, расскажу, каков вообще прицнип действия патча с использованием свободного места.

Свободное место используют для расширения стандартного кода прошивки, например - для дополнительных проверок.

План построения таких патчей таков:

1) Ищется адрес врезки ;

2) В этом адресе переадресуем телефон на свободное место ;

3) Выполняем дополнительный код ;

4) В большинстве случаев, особо важной частью является - восстановление затертого кода ;

5) Возвращаемся в место работы телефона, чуть дальшее места врезки.

Разбирать будем простой патч - запароливание какого-либо пункта меню. На самом деле здесь все довольно легко. (За данный урок хочу выразить огромную благодарность Дмитрию (mdemonv) - именно он научил меня этому делу, и я хочу рассмотреть патч, который он объяснял мне). Паролить будем пункт - избранное видео.

Итак, по шагам выполняем наши действия.

Шаг №1. Поиск адреса врезки. (Так как пока мы работаем только с мсс-частью прошивки, то значит и врезка у нас будет мсс). Начнем :) Избранное видео.. Смотрим, как к нему добраться? Главное меню-Мои файлы (пункт 6)-Видеоклипы (пункт 2) и третьим пунктом у нас и будут избранные видеозаписи. Значит, первое что нам нужно - это открыть главное меню. (Надеюсь, помните как? Если нет, значит перечитывайте урок №3 (часть, отвечающую за поиск адреса главного меню) и возвращайтесь сюда). Нас интересует адрес перехода по пункту 6 - это 0x118B2AD0. Сразу ищем блок, отвечающий за адреса переходов по пунктам данного меню:

118B2BC0  79 00 0000 0000 0000 340B0711 0000 0000  По пункту меню 1 переход на адрес 0x11070B34

118B2BD0  79 00 0100 0000 0000 F4160711 0000 0000  По пункту меню 2 переход на адрес 0x110716F4

118B2BE0  79 00 0200 0000 0000 A4190711 0000 0000  По пункту меню 3 переход на адрес 0x110719A4

118B2BF0  79 00 0300 0000 0000 341D0711 0000 0000  По пункту меню 4 переход на адрес 0x11071D34

118B2C00  79 00 0400 0000 0000 84200711 0000 0000  По пункту меню 5 переход на адрес 0x11072084

Как я уже сказал, далее нам нужно пройти во второй пункт, т.е. в видеоклипы. Переходим по этому пункту. Дальше - откроем избранные видеозаписи (это пункт №3). Итак, на каком адресе мы в итоге должны быть? Если у Вас все получилось верно, то адрес должен быть - 0x102EF3D0.

Вот он адрес инициализации меню избранных видеозаписей, это и будет нашим адресом врезки, запомним его.

Следующий шаг (его не было в плане) - там нужен адрес свободного места прошивки. Тут маленький совет дам - если Вы будете портировать данный патч, да или вообще, заниматься патчами, использующие свободное место в прошивке, и у данного телефона есть текущий патчмейкер - то Вам бы желательно обратиться к нему с просьбой выделить кусочек свободного места в прошивке, чтобы впоследствии не оказалось конфликта патчей. Если же нет патчмейкера, то все в ваших руках. Как найти свободное место в прошивке? Ну для начала, это может быть любая неиспользуемая картинка в телефоне. Нашли такую, взяли ее адрес, и пошли патчить. Если с этим обстоит проблема, то есть конечно вариант похуже, взять какой-нить ненужный Вам язык в прошивке, и изменять его. Конечно это убирает работоспособность прошивки на данном языке, но будучи начинающими патчмейкерами, мне кажется, что это не должно особо волновать. Получив опыт и знания, позже Вы перенесете патчи в нормальное место. Как взять какой-нибудь ненужный язык, и на нем располагать патчи? Все довольно просто. Открываем вкладку "Языковые ресурсы" в правой части BE, выделяем язык ненужный (например, им оказался - Eesti). Затем видим, что первые индексы идут помеченными фрагментом (s). Пролистываем дальше, итак, начались индексы 0x0000, 0x0001 и т.д. Но так как они пустые, пожалуй пропустим их, и выберем первый тот, в котором окажется, хоть какой-нибудь текст. Им оказался текстовый ресурс с индексом 0x0007. Дважды щелкаем по нему, и видим, как адрес прошивки установился на 0x10BE51D7. Вот и нашли свободный адрес.

Шаг №2. Адресование выполнения кода в свой адрес.. В данном случае, для врезки будем использовать скрипт перехода с одного адреса мсс на другой - mcc_transf. Для этого пишем в компилятор следующее:

.start 0x102EF3D0

.mcc mcc_transf 0 0 0 0 my_code

 

.start 0x10BE51D7

my_code:

А теперь, по порядку. Напомню (потому что явно не указал) - директива компилятора .mcc дает компилятору понять, что нужно скомпилировать мсс-скрипт, что и мы делаем.

Также, рассмотрим новое понятие, как метка. Метка сделана для человека, для упрощения создания/понимание кода. Объявление метки происходит следующим образом - пишем в нужном месте ее название, а затем сразу же знак :.

Вот смотрите, можно было написать так:

.start 0x102EF3D0

.mcc mcc_transf 0 0 0 0 0x10BE51D7

или:

.start 0x102EF3D0

.mcc mcc_transf 0 0 0 0 my_code

 

.start 0x10BE51D7

my_code:

Что удобней? Возможно, Вам сейчас мало покажется различий, но вообще - в более обширных патчах, это будет явно заметно.

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

На данном этапе, если Вы хорошо воспринимали предыдующие уроки, Вы должны заметить ошибку. Мы не можем писать скрипты на адресе, не кратному 4, кем 0x10BE51D7 и является. Увеличим адрес до кратного 4, для этого в данном случае особых проблем не нужно, просто прибавим 1 -> 0x10BE51D8.

Следовательно, в компиляторе должно быть записано следующее:

.start 0x102EF3D0

.mcc mcc_transf 0 0 0 0 my_code

 

.start 0x10BE51D8

my_code:

Все, на данном этапе мы нашли место врезке в телефоне, адресовали в наш свободный адрес. Что нам нужно дальше?

Шаг №3. Выполнение своего дополнительного кода. Что нам нужно сделать здесь? Нужно организовать проверку пароля, и если он верен, то продолжить вход в избранные видеозаписи. На самом деле вручную делать это очень трудно, но на помощь к нам приходят встроенные функции прошивки. Нам не нужно писать никаких условий, для этого есть отдельная функция. Нам достаточно ее просто вызвать, все проверки хранятся уже в ней самой. Вызов функции на уровне мсс (и соответственно, функция должна быть описана на мсс) выполняется скриптом - mcc_call. При "натыкании" на такой скрипт, с телефоном происходит следующее: он входит в адрес вызова функции, выполняет тот код, и возвращается обратно, и выполняет уже скрипты, следующие после mcc_call. Какова структура вызова функций? Довольно проста.

Пойдем по порядку:

mcc_call. Параметры идентичны скрипту mcc_transf. Т.е. нужно только указать пятый параметр, адрес функции.

Пример:

.start 0x12345678

.mcc mcc_task 1 2

.mcc mcc_call 0 0 0 0 my_function

.mcc mcc_transf 0 0 0 0 0x12346000

 

.start свободный_адрес

my_function:

;тут можно указать любые необходимые нам скрипты, Например:

.mcc mcc_init_restore

.mcc mcc_set_curmenu

.mcc mcc_return

Мы увидели новый неизвестный нам скрипт - mcc_return. Параметров у данного скрипта нет, для чего он тогда предназначен? Он показывает, что наша функция закончилась, и нам нужно возвратиться в место вызова функции, и продолжить работу телефона. Итак, показываю, как происходит в данном случае работа телефона:

Мы находимся на адресе 0x12345678. Как выполняются скрипты:

1) mcc_task 1 2 ;

2) mcc_call нашей функции ;

3) mcc_init_restore ;

4) mcc_set_curmenu ;

5) mcc_return ;

6) и только уже сейчас выполняется mcc_transf 0 0 0 0 0x12346000.

Возможно, довольно трудно объяснил. Объясню попроще. Аналогия mcc_call в жизни. Допустим, Вы сидите играете в видеоигру - это mcc_task 1 2. Вы вспомнили, что Вам нужно сходить в магазин. Поход в магазин - это mcc_call 0 0 0 0 my_function. Вы ставите на паузу игру, тем самым уходя в магазин, или выполняя содержимое функции my_function. Сам путь к магазину - это mcc_init_restore, покупки - это mcc_set_curmenu, а возврат домой - это mcc_return. Придя домой, Вы отжали паузу игры, и продолжили играть - это mcc_transf 0 0 0 0 0x12346000. Теперь надеюсь полегче понятно? Также, забегая вперед, скажу - что mcc_call - это аналог команды bl в ассемблере.

Вернемся к прошивке. Нам нужно вызвать функцию проверки пароля - на D900 она хранится в 0x102AF5EC. Как ее найти? Да, довольно просто. Изначально ведь можно заблокировать список последних вызовов. Вот и отлично, будем рассматривать это дело. Открываем главное меню, вспоминаем - список последних звонков, это Меню-1-1. Открываем адрес перехода по первому пункту меню, далее - опять по первому пункту меню, и смотрим:

10B1A148  17 0B 1800 0000 0000 00000000 0000 0000  Выполнить 0xB x2=24 x3=0 x4=0 x5=0 x6=0

10B1A158  18 05 A300 0100 0100 14617B11 0000 0000  Если x1=5 x2=163 x3=1 то переход на mcc_dir_call_log_privacy -> 0x117B6114

10B1A168  17 05 4400 0000 0000 00000000 0000 0000  Выполнить 0x5 x2=68 x3=0 x4=0 x5=0 x6=0

10B1A178  17 05 F200 0000 0000 00000000 0000 0000  Выполнить 0x5 x2=242 x3=0 x4=0 x5=0 x6=0

10B1A188  1C 00 0000 0000 0000 F89FB110 0000 0000  Переход на адрес mcc_dir_make_all_call_log_list_new -> 0x10B19FF8

Нас интересует строка:

10B1A158  18 05 A300 0100 0100 14617B11 0000 0000  Если x1=5 x2=163 x3=1 то переход на mcc_dir_call_log_privacy -> 0x117B6114

Вот в данном условии проверяется - включена ли блокировка последних звонков, и если условие истинно - то переход на адрес перехода в скрипт, вызывающий запрос пароля. Открываем адрес условия - 0x117B6114. А там увидем:

117B6114  17 05 1401 0100 0100 00000000 0000 0000  Выполнить 0x5 x2=276 x3=1 x4=1 x5=0 x6=0

117B6124  1B 00 0000 0000 0000 ECF52A10 0000 0000  Вызов подпрограммы mcc_sec_app_privacy_enter_pw -> 0x102AF5EC

117B6134  37 00 0000 0000 0000 00000000 0000 0000  Возврат из подпрограмы

Нас интересует вызов подпрограммы в данном блоке скриптов. Здесь как раз таки и вызывается функция проверки пароля. Вот аналогично Вы можете найти эквивалент данной функции и в своей прошивке.

Итак, узнали адрес блокировки, это 0x102AF5EC. Вернемся к плану - на свободном месте нам нужно выполнить дополнительный код, восстановить затертый код скриптом-врезкой mcc_transf, затем вернутся в дальнейший код выполнения телефоном.

Так вот, пишем в компилятор такое:

.start 0x102EF3D0

.mcc mcc_transf 0 0 0 0 my_code ;врезаемся по адресу 0x102EF3D0 и адресуем на свободное место

 

.start 0x10BE51D8

my_code: ;описываем необходимый для выполнения код на свободном месте

.mcc mcc_call 0 0 0 0 0x102AF5EC ;вызываем процедуру проверки пароля

.mcc mcc_task 0x21 0x10 0x3A ;восстанавливаем затертый код

.mcc mcc_transf 0 0 0 0 0x102EF3E0 ;возвращаемся на следующий скрипт

Вот и все, весь патч паролирования избранных видеозаписей. Но я думаю, довольно много неясностей. Ну чтож, подробней опишу.

Итак, в адресе 0x102EF3D0 мы переходим на свободное место прошивки для выполнения своего кода, затирая скрипт, записанный в телефоне - на свой: mcc_transf 0 0 0 0 my_code. Но ведь на данном адресе уже был скрипт! И чтобы телефон не ушел в ступор, нам нужно будет его восстановить, что мы и сделаем после выполнения проверки пароля.

На свободном адресе мы вызвали функцию проверки пароля (все уже проверки на корректность пароля содержатся в данной функции, поэтому нам заботиться об этом не стоит). Затем, следующий скрипт - mcc_task 0x21 0x10 0x3A. Этот скрипт и лежал по адресу 0x102EF3D0, поэтому вы восстанавливаем его (напомню, как можно поглядеть данный вид интерпретации мсс-скриптов - нужно открыть вкладку "Код" в левой части BE), затем нужно вернуться на следующий скрипт, который идет после врезки. Откроем адрес 0x102EF3D0, посмотрим - адрес следующего скрипта - это 0x102EF3E0, поэтому в него мы и возвращаемся. Вот и все.

Но - можем немного оптимизировать наш код :)

Давайте исключим из нашего кода все скрипты с mcc_transf. Зачем нам вычислять адреса следующего скрипта и т.д. Ведь у нас есть хороший скрипт - mcc_call. Подумайте хорошо, как бы нам переписать код. А затем сверьте с моим:

 

.start 0x102EF3D0

.mcc mcc_call 0 0 0 0 my_code

 

.start 0x10BE51D8

my_code:

.mcc mcc_call 0 0 0 0 0x102AF5EC

.mcc mcc_task 0x21 0x10 0x3A

.mcc mcc_return

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

Такие врезки можно организовывать на любом мсс-скрипте, главное - корректно восстановить затертый код. На форуме хотели запаролить разблокировку телефона? Все в Ваших руках, если хорошо почитаете уроки. Наводку (именно наводку, а не на водку :128:) я Вам даю:

1) Ищете адрес окна "Клавиатура разблокирована" ;

2) Врезаетесь в него ;

3) Переходите на свободный адрес, организовываете вызов проверки пароля, восстанавливаете затертый код, и переходите на следующий скрипт, следующий после окна "Клавиатура разблокирована".

 

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

 

Хочу еще раз выразить благодарность Дмитрию aka mdemonv за обучение данному патчу меня ;)

  • Like 23
Ссылка на комментарий
Поделиться на другие сайты

  • 5 месяцев спустя...

Урок №5 - Исследование кода МСС-скрипта.

Ну чтож.. прошло немало времени с тех пор, как я писал последний урок.. Попробуем возобновить работу.

Даже и не знаю, что рассматривать конкретно.. Но думаю - будем уже пытаться портировать патчики..

Для примера возьмем патч `Экономный режим в яркости дисплея`, в котором мы уже будем смотреть, а что же творится в этих самых мсс-скриптах?

Итак, открываем данный патч, как обычно - на D900XEFK2, будем пытаться его портировать на G600XEGL1, тем самым научимся исследовать две прошивки одновременно, а заодно и думать об мсс. Начнем.

Первым делом - открываем в BinEdit - D900XEFK2. Затем в главном меню выбираем: Действия - Связанный bin - Открыть новый редактор, в котором мы выберем вторую прошивку для исследования. Это будет - G600XEGL1. В итоге, у нас открыто два BinEdit'a с нужными прошивками.

Следующий шаг, что нам нужно сделать - это выключить синхронизацию между прошивками. Я это делаю, т.к. при исследовании кода на одной прошивке сбивается адрес на второй, поэтому делается все это - ручками, и только ими. Для этого проводим следующее действие в обоих окнах BinEdit (есстно не одновременно, а по очереди): Действия - Связанный bin - Синхронизация. Щелкаем, тем самым сняв галочку.

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

<nord offset="0x0124C6C6" from="01" to="00" />

Вспоминаем первый наш урок.. Какой натуральный адрес изменяется? 1121C6C6, т.к. база в D900 - 0x1. А в G600 как бы этот адрес выглядел? Вспоминаем.. Т.к. база 0x2, значит адрес будет - 2121C6C6.

Первым шагом нам надо - открыть главное меню телефона (есстно на мсс-уровне). Я начинаю чаще всего - с этого. Вспоминаем урок №3 - там я показывал, как найти адрес главного меню телефона. Напомню - в D900 это 11350BB0, а в G600 - 207AA114.

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

Вспоминаем, как добраться до яркости дисплея в D900? Меню-Настройки-Дисплей-Яркость (Меню-9-3-5). А в G600 это - Меню-Настройки-Подсветка-Яркость-Настройка яркости (Меню-12-2-2-2). В принципе, в данном случае поочередность действий нам не требуется, так что открываем яркость дисплея в D900.

Итак это - 11350BB0->102AFCEC, 102AFCEC->102B0DBC, 102B0DBC->113BAFAC. МСС-скрипты яркости дисплея на D900 открыты (ее адрес 0x113BAFAC).. Далее - открываем ее же нa G600: 207AA114->21096C14, 21096C14->21098E50, 21098E50->2030F4C4, 2030F4C4->2197F20C, 2197F20C->21097CD4. Теперь и на G600 яркость дисплея открыта, ее адрес - 0x21097CD4.

Хочу заострить внимание на переходе G600: 21096C14->21098E50.

Вы видите, что начиная с адреса 21096C24 идет серый блок? (у кого нет серого блока, проверяем следующее: последняя ли версия BinEdit, и присутствует ли файл mask.col в папке с программой). Это означает, что с данного адреса начинается новый блок мсс, не имеющий никакого отношения к скриптам, идущим до 21096C24. Поэтому блок, состоящий всего лишь из одного скрипта (на адресе 21096C14) является завершенным.

Итак, мы получили мсс-адреса описания яркости дисплея на мсс-уровне (D900: 113BAFAC, G600: 21097CD4).

Начинаем исследовать, что же происходит у нас при входе в данный пункт?

Сначала идет подготовка окна, затем происходит переход на другой адрес (в D900 это переход на 107990FC). Выполняем поочередные действия - сначала в D900 переходим по адресу 0x107990FC, затем в G600 переходим по адресу 207BC48C). Итак, что мы видим дальше? Здесь выполняется какой-то таск. В D900 это: mcc_task 0xA 0x36, в G600 это: mcc_task 0xA 0x37. Так что же все-таки происходит в данном таске? Открываем BinEdit [D900XEFK2] (т.е. тот BinEdit, в котором у нас открыта прошивка D900XEFK2), теперь нас интересует - вкладка "МСС" в правой части программы. Открываем ее. Нас интересует скрипт - MCC_TASK, с номер 0xA и кейсом 0x36 (вспоминаем прошлые уроки, где я рассказывал про номера и кейсы).

Идем по порядку. Для начала - забыли про кейсы, нас пока интересует скрипт MCC_TASK с номером 0xA. Ищем в правой часте программы на вкладке МСС - MCC_TASK 0A (надеюсь понятно, почему именно данную надпись? Если бы у нас шел номер таска не 0xA, а допустим 0x13, тогда что бы мы искали на вкладке МСС? MCC_TASK 13 и т.д.). Нашли - это запись:

1121AB24|170A|MCC_TASK 0A.

Отлично. Дважды щелкаем по записи (т.е. по адресу), открылась в левой части вкладка "Код" - в данной вкладке BinEdit уже в основном предназначен для распознавания ассемблера (хотя также в ряде случаев здесь интерпретируются и мсс-скрипты, и данные). Что мы видем? В столбик идут всякие разные команды, в которых

мы ничего не понимаем :bk: Немного объясню - это THUMB-код ассемблера (Если Вы читали другие статьи про режимы нашего процессора, то помним, что THUMB-команды это 2 байта, и только лишь bl/blx вызовы имеют размер - 4 байта). Сейчас разбирать каждую команду не имеет смысла, так как 90% тех читателей, кто хочет именно обучиться - ничего не поймут. Да и в прицнипе - и я не смотрю на данный код, т.к. он нужен только в редких случаев, когда не имеем возможности нормально подобраться к нужному кейсу, или нужный кейс не соответствует тем данным, что мы предполагаем получить. Это вряд ли Вы поймете, поэтому пропускаем мимо ушей, в нужный момент вернемся к этому.

Итак, продолжаем. Мы открыли содержание MCC_TASK 0A, теперь нас интересует - какой кейс у нас был? А кейс у нас был (вспоминаем) - 0x36. Что же делать дальше с этим кейсом? Да все просто, пролистываем код вниз, пока не наткнемся на записи вида:

1124AC18:	F205	DCD	0x05F2;B loc_1124B7F4;При 0x0000;Переход на адрес 0x1124B7F4
1124AC1A:	E905	DCD	0x05E9;B loc_1124B7E2;При 0x0001;Переход на адрес 0x1124B7E2
1124AC1C:	AC07	DCD	0x07AC;B loc_1124BB68;При 0x0002;Переход на адрес 0x1124BB68
1124AC1E:	AF07	DCD	0x07AF;B loc_1124BB6E;При 0x0003;Переход на адрес 0x1124BB6E
1124AC20:	EC08	DCD	0x08EC;B loc_1124BDE8;При 0x0004;Переход на адрес 0x1124BDE8
1124AC22:	2F0C	DCD	0x0C2F;B loc_1124C46E;При 0x0005;Переход на адрес 0x1124C46E
1124AC24:	490C	DCD	0x0C49;B loc_1124C4A2;При 0x0006;Переход на адрес 0x1124C4A2
1124AC26:	1B32	DCD	0x321B;B loc_11251046;При 0x0007;Переход на адрес 0x11251046
1124AC28:	860A	DCD	0x0A86;B loc_1124C11C;При 0x0008;Переход на адрес 0x1124C11C
1124AC2A:	8E0A	DCD	0x0A8E;B loc_1124C12C;При 0x0009;Переход на адрес 0x1124C12C

Вот это и есть описания кейсов. Каждая запись означает, на какой адрес ведет какой кейс. У нас кейс 0x36, значит пролистываем ниже, то того, пока не увидим запись вида ";При 0x0036 ;Переход на адрес ..". Итак, эта запись находится на адресе 1124AC84:

1124AC84: 900C DCD 0x0C90 ;B loc_1124C530 ;При 0x0036 ;Переход на адрес 0x1124C530

Отсюда мы узнаем, что по кейсу 0x36 выполняется код по адресу 0x1124C530.

На данный момент кейсы таска 0xA в прошивке G600XEGL1 интепретируются BinEdit'ом неправильно, точнее - вообще не интерпретируются. Поэтому здесь придется немного схалявить.. Но - хотя бы попробуйте открыть MCC_TASK 0A в G600. Если у Вас это получится, то попробуйте сравнить с моим адресом. MCC_TASK 0A находится на адресе: 20B3FF24. Ну вот в прицнипе, Вы уже знаете, как примерно открывать содержимое мсс-скрипта. Возможно у Вас встанет вопросы - ну с таском все понятно, все довольно ясно. А что, если нам выпадает другой скрипт? Еще проще :) Смотрим на скрипт, допустим это:

207BC49C  66 00 0000 0000 0000 00000000 0000 0000  Начало вызова пользовательских событий

Вспоминаем про идентификатор скрипта (см. Урок №3), смотрим - ид данного скрипта - 66. Открываем в правой части BinEdit вкладку MCC, и ищем там в графе "КОД - 66. Для упрощения, можно нажать на саму вкладочку "Код" для упорядочивания записей по возрастанию/убывания данного кода. Вообщем, поискав - мы найдем запись:

2016CC00|66|MCC_USER_EVENT_START

Надеюсь, это понятно. Вернемся, к нашим баранам. Так что же в итоге выполняется в этом 0x36 кейсе? Открываем адрес 1124C530 (надеюсь, еще не забыли, откуда он взялся), и смотрим его код (пока каждая команда нас не интересует, буду вкратце рассказывать поблочно).

Выделю первый блок:

LDR	R0, =0x00000A19
BL	lk_get_text
LDR	R1, =gv_ImageTitleIconStart
MOV	R5, #0x0
LDRH	R2, [R1, #0]
MOV	R1, #0x0
MOV	R3, R5
BL	_Reg_Draw_Title

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

Следующий блок:

MOV	R0, #0x8
BL	_lk_get_sofk
MOV	R4, R0
MOV	R0, #0x46
BL	_lk_get_sofk
MOV	R2, #0x0
MOV	R1, #0x0
STR	R2, [SP, #0x8]
MOV	R2, R4
MOV	R3, R5
STR	R1, [SP]
STR	R1, [SP, #0x4]
BL	_Reg_Draw_Softkey

В данном блоке прорисовываются софт-клавиши.

Далее идет код некоторых проверок, затем блок рисования картинки:

MOV	R2, #0x20
MOV	R1, #0x0
STR	R2, [SP, #0x8]
MOV	R2, #0x1
MOV	R3, R7
MOV	R0, #0x0
STR	R1, [SP]
STR	R1, [SP, #0x4]
LDR	R6, =LcdPattern
BL	_lk_DisplayImage

Затем еще раз и т.д. Но нам что конкретно там - уже не важно, т.к. здесь от меня требовалось лишь показать, как узнать, что происходит в самом скрипте.

Вернемся к портированию патча. Что от нас требуется? Убрать для начала ограничение. Если подумать, ведь при нажатии на кнопку влево (уменьшение яркости) - должна быть проверка, типа - если подсветка на уровне 1, то прекратить выполнение действия, и оставить значение подсветки - 1.

Поглядим это, вернувшись на мсс-описание яркости дисплея.

Приглядимся:


1079911C  69 06 0700 0000 0000 2C102B10 0000 0000  При нажатии "вправо" переход на 0x102B102C
1079912C  69 06 1000 0000 0000 2C102B10 0000 0000  При нажатии "боковая вверх" переход на 0x102B102C
1079913C  69 06 0600 0000 0000 CC917910 0000 0000  При нажатии "влево" переход на 0x107991CC
1079914C  69 06 1300 0000 0000 CC917910 0000 0000  При нажатии "боковая вниз" переход на 0x107991CC
1079915C  69 06 0E00 0000 0000 CCAF3B11 0000 0000  При нажатии "левая софт" переход на 0x113BAFCC
1079916C  69 06 0D00 0000 0000 CCAF3B11 0000 0000  При нажатии "i/ok" переход на 0x113BAFCC
1079917C  69 06 0900 0000 0000 98D08111 0000 0000  При нажатии "вкл/выкл (красная)" переход на 0x1181D098
1079918C  69 06 0F00 0000 0000 D0E0F810 0000 0000  При нажатии "правая софт" переход на 0x10F8E0D0

Вот наши обработчики нажатий клавиш. Интересует нас - выполнение функции по нажатию на кнопку `влево`. Значит переходим по адресу 107991CC. Поглядев скрипты по этому адресу можно понять, что все действие разворачивается в одном таске (mcc_task 0xA 0x34), а затем телефон возвращается в описание яркости дисплея. На G600 - выполняется другой таск (mcc_task 0xA 0x35), но прицнип тот же. Открываем сначала на D900 - MCC_TASK 0A (1124AB24), затем кейс 0x34 (1124C6BC), затем в G600 - MCC_TASK 0A (20B3FF24), ну а тут я подскажу пока - адрес перехода по кейсу 0x35 - 20B4229A.

Итак, на данный момент должно быть у вас открыто: [D900XEFK2] - адрес 1124C6BC, [G600XEGL1] - адрес 20B4229A. Открываем наш патч на D900, смотрим - первый адрес для изменения это 1124C6C6. Глядим данный адрес на D900:

1124C6C6: 0128 CMP R0, #0x1

Видим, что данная строчка в патче - это изменение именно проверки в таске по кнопке влево.

Также видим аналогичный код в G600:

20B422A4: 0129 CMP R1, #0x1

Вот уже хорошо, первый адрес для изменения в прошивке G600 - мы нашли. Открываем компилятор в G600 и пишем такое:

.start 0x20B422A4

.byte 0

Таким образом мы добьемся в патче следующего:

<nord offset="0xB422A4" from="01" to="00" />

Т.е. того, что нам нужно. Операнда .byte записывает 1 байт по нужному адресу. Хотя возможно указывание и нескольких байт, только нужно их писать через запятую. Т.е. операнда записывает побайтно нужные данные.

Идем дальше, с первой строкой мы разобрались. Теперь вторая строчка изменения прошивки:

<nord offset="0x0124C6F4" from="013804" to="C04605" />

Мы видим, что даже не пролистывая прошиву - мы видим в D900 уже этот адрес для изменения.

Глянем в G600 - видим примерно тоже.

Но так как не показываются нам кейсы в G600, для достоверности покажу один из способов узнавания - а точно ли мы нашли эквивалент? Для этого нам потребуется sym-файл прошивки, для D900 у меня он есть, объясняю.

Видим блок:

LDR	R1, =0x10B73920
ADD	R1, #0x34
MOV	R0, #0x1B
BL	_tra1_3trace

Это блок вызова трассировки, такие ф-ии понапиханы везде в прошивку, по ним и можно ориентироваться. Скажу сразу - функций трассировки в телефоне достаточно разных, поэтому не обязательно ориентироваться только на эту. Объясню, что такое трассировка в телефоне - это запись в специальный трассировочный буфер определенной строки для отладки прошивки. В строках бывают разные параметры, но нам не важно. Главное - сама строка. Отлаживают ее сами корейцы, поэтому это не для нас. Но - мы возьмем это как козырь :)

Смотрим в D900, какая строка передается в данную функцию. Строка передается в регистр R1, т.е. нас интересует код:

LDR R1, =0x10B73920

ADD R1, #0x34

Команда ldr - загружает адрес на данный в конкретный регистр. В данном случае, в регистр R1 помещается значение 0x10B73920. Команда add является аналогом арифметической операции сложение, т.е. попросту - плюс. В данном случае, здесь "написано", что к адресу, помещенному в регистр R1, прибавить значение 0x34.

Посчитаем вручную, чтоже это все получается? Открываем стандартный виндовский калькулятор, выбираем режим "Инженерный", затем выбираем радиокнопку - hex, и вводим:

10B73920, затем плюс и 34. Получилось число 10B73954.

А теперь проще. Приглядимся на комментарий к данной команде (add). Это справа зеленая надпись, отделенная точкой с запятой:

;R1 = R1 + 52 "4" = 0x10B73954 (280443220)

Поясню откуда всякие непонятные цифры:

a) 52. Число "52" взялось оттуда, что число 0x34 (а это шестнадцатеричная система счисления) переведено в десятичную, отсюда и 52.

б) "4". Здесь замечу - не цифра 4, а символ. А написано 4, потому что байт 0x34 это номер символа "4" в ANSI-кодировке (я это кстати рассматривал уже в уроке №1).

в) 280443220. Аналог к пункту "а". Просто итоговое полученное число (0x10B73954) из шестнадцатеричной системы счисления преобразовали в десятичную.

Ну в прицнипе хватит разжовывать, надеюсь все это - Вы уже поняли.

[D900XEFK2]: Открываем на вкладке HEX адрес 10B73954, и видим там такую строку:

TASK_SET_GET_CONTRAST_LEVEL

В G600 немного проще - там сразу же передается адрес на строку в функцию трассировки (это просто исключение в данном месте), т.е. без всяких арифметических действий. А передается туда адрес - 2197B478. Открываем его на вкладке HEX, смотрим текст:

TASK_SET_GET_CONTRAST_LEVEL

Совпадает с D900? Совпадает, значит мы можем быть уверены, что эквивалент найден правильно. (Но - бывают и случаи, что даже при совпадении строки трассировки - эквиваленты не совпадают, т.к. одна и та же строка может передаваться в разных местах телефона!)

Возвращаемся обратно на вкладку код, к нашему именно коду (у кого непонятно что там появилось - это сейчас пытается BinEdit интерпретировать текст как код. Поэтому - нажмите стрелку влево, рядом с полем ввода адреса). Смотрим на второй адрес патча - 1124C6F4, ищем аналог в G600 - это 20B422D0.

Открываем уже открытый компилятор и пишем следующее:

.start 0x20B422D0

nop

.byte 5

Команда nop - ничего не делает, таким образом мы затерли команду по адресу 20B422D0, т.е. теперь команда sub r0,1 не выполнится. А на следующем адресе запишется байт 5.

Ну вот и теперь разобрались со вторым адресом патча.

Смотрим следующий - 1124C74A. Прокрутив дальше код [D900XEFK2] - мы вновь увидим адрес для изменения, и прокрутив код [G600XEGL1] - увидим его аналог - 20B422E2. Следовательно, что мы добавляем в компилятор?

.start 0x20B422E2

nop

.byte 5

Ну и остался последний адрес [D900XEFK2] - 1124C770, также прокрутив код, найдем и ему аналог [G600XEGL1] - 20B42308.

Теперь добавляем четвертый адрес в компилятор, и компилируем наш патч. Ура, мы его портировали! Можем свериться с уже готовым патчем в G600.

У вас может возникнуть вопрос - а почему различия такие?

В нашем скомпилированном патче со второй строки идет измененные данные - C04605, а в G600 - C0460528. Все просто. Сравните последний байт (два символа) в поле from и в поле to - есть разница между ними? Что там 28, что там - тоже 28. Просто в компилятор было записано там не .byte 5, а cmp r0,5. Вот и все.

Подведя итог рассматривания данного патча, я хочу сказать вот что. Так как в G600 не интерпретируются кейсы в MCC_TASK 0A, я Вам советую попробовать портировать данный патч на любую другую старенькую модель телефона. Желательно, конечно, на ту, где данный патч уже есть, чтоб было с чем сравнивать правильность портирования.

 

Заканчивая наш сегодняшний урок, хочу рассказать еще кое-что. Кто-то мог догадаться, почему я пару строками выше написал cmp r0,5, а кто-то и не мог.

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

В прицнипе, это не так трудно. Но даже для не очень больших патчей - это доставляет неудобства. Вообщем, делаем следующее:

1) Открываем редактор патчей (Инструменты - Редактор патчей) ;

2) Открываем в нем нужный патч (на примере - открываем рассматриваемый выше патч на G600XEGL1) ;

3) Если редактор патч для дела - запускается в первый раз, то мы толком ничего не увидим. Поэтому приводим окно к нужному виду (пример на скрине. Тянем за черные полоски и наслаждаемся).

4) Возьмем любой из адресов, размер изменения которых 4 байта. Допустим первый в списке, это 00B422D0. Снизу появляется 3 вкладки - Свойства/HEX Код/Код. Нас интересует последняя вкладка - Код.

Открыв ее, мы увидим следующее:

20B422D0  C046      NOP ;Пустая команда. Аналог MOV R8,R8

20B422D2  0528      CMP R0, #0x5 ;Сравнить R0 и 5

Также в прицнипе есть и патчи, в которых по одному адресу изменения будут записаны как мсс-скрипты, так и thumb-код. В этом случае уже проще применять патч к прошивке, переходить по изменяемому адресу и уже переписать код оттуда.

 

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

94168-5-08-10)1281032432_thumb.jpg

  • Like 18
Ссылка на комментарий
Поделиться на другие сайты

Урок №6 - Данные.

Вот выкроил еще немножко времени на следующий урок. Начну я данный урок с портирования патча. Особенность данного патча в том, что здесь нужно лишь найти опять - только эквиваленты. Но теперь у нас придется заменить не только стартовый адрес, но еще и значение в поле to. А в конечном варианте, патч выходит с абслютно тремя разными между прошивками значения полей, но одинаковые по смыслу. А портировать мы будем патчик все с того же D900XEFK2 на G600XEGL1. В прицнипе различий толком нет, на какой телефон портировать - смысл в принципе один и тот же. Итак, начнем.

Вспоминаем урок №5, где мы учились открывать одновременно две прошивочки. Следовательно, открываем BinEdit, в нем открываем прошивку D900XEFK2, затем открываем связанную прошивку - G600XEGL1, отключаем синхронизацию. Ну, вообщем, Вы должны помнить это. Для начала - начнем с простого.

На сегодня наш патчик - это Исправить Bluetooth по горячей клавише. Довольно простой патчик, на самом деле, не правда ли? В данном уроке, мы начнем уже с Вами пытаться отличать уже на глаз - данные от мсс-скриптов? Что, не понятно? И не удивительно =) Итак, рассказывать суть данного патча. В телефоне есть таблица адресов функций (не стоит этого бояться, сейчас все подробно объясню). Вспомните - настройку меню пользователя, допустим. В ней есть строго определенные функции. А также - есть настройка функций клавиш на джойстик (влево, вправо, вниз) (замечу - на очень старых телефонах этого может и не быть, но даже на D600 это уже было) - и тут тоже есть те же самые функции. Так вот, установили мы допустим функцию на клавишу ВНИЗ любую из списка. Как при этом срабатывает телефон? У каждой функции есть свой адрес. Адрес - это 4 байта. И что - чтобы запомнить, какая функция у нас стоит на той или иной клавише - в EEPROMe тратить целых 4 байта? Посчитаем. Чтобы запомнить 3 функции, т.е. по функции на 3 кнопки - нам понадобится 12 байт (4 байта [1 функция] * 3 кнопки). А есть альтернативный способ решения - мы сохраним номер функции в EEPROM для каждой кнопки. Вместимость одного байта (0xFF) = 255. Т.е. если у нас функций меньше 255, мы можем взять номер функции, сохранить его в EEPROM, тем самым заняв всего лишь 3 байта в EEPROMe (1 байт [номер функции] * 1 кнопку). Так телефон и поступает. А потом, когда мы нажимаем на кнопку, телефон производит следующие действия:

1) Получаем номер функции ;

2) Умножаем его 4 (сейчас поймете зачем) (назовем получившееся число - числом Z) ;

3) В прошивке записана таблица функций, поэтому берем указатель на начало таблицы ;

4) Сдвигаем указатель на число Z ;

5) И по получившемуся адресу - забираем функцию.

 

Непонятно? Объясняю наглядно.

Вот наша таблица (обычный массив), выглядит примерно следующим образом:

{

1:Главное меню ;

2:Меню Bluetooth ;

3:MP3-плеер ;

и т.д.

}

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

Проанализируем работу телефона. В настройках мы выбрали функцию под номером 1, т.е. "Главное меню". В телефоне отсчет происходит с какого числа? С нуля. Поэтому на самом деле, эта функция имеет номер не 1, а 0. Итак, пошагово.

1) Получили мы номер функции. Это 0.

2) Умножаем его на 4, т.е. на размер ячейки. Получается, что Z = 0.

3) Получаем указатель на таблицу, сдвигаем указатель на число Z, и получаем адрес, записанный в нашу ячейку. Т.е. Указатель никуда не сдвинулся, он остался на месте (т.е. что будет с числом, если к нему прибавить 0? Оно и останется таким же).

4) Ну и забрали функцию с нулевой ячейки, а т.е. - Главное меню.

А если мы выберем функцию - Меню Bluetooth? То что будет записано в EEPROM? Функция Bluetooth для нас имеет номер 2, но на самом деле - телефон запишет в EEPROM - 1.

1) Получими номер функциию. Это 1.

2) Умножили на 4 -> Z = 4.

3) Получиил указатель на таблицу, сдвинули его на 4 байта, а т.е. на 1 ячейку, следовательно указатель стоит на том адресе, где написана функция Меню Bluetooth, получаем ее и переходим в нее.

 

Прикольно, не правда ли? А самое главное, мы сэкономили целых 9 байт в EEPROMe!

 

Надеюсь, Вы поняли, как выглядит этот процесс, вернемся к нашему патчу. Мы ставим функцию Bluetooth на кнопку, а при ее нажатии - открывается что? Меню Мои устройства. Что нам нужно делать в данном случае? Нам нужно найти эту таблицу.

Но для начала - мы откроем наш патч, и я покажу очень частую ошибку начинающих патчмейкеров. Открыли наш патч, смотрим:

<nord offset="0x0199FE80" from="4C252D11" to="3C486A10" />

Мы берем данные 4C252D11, вставляем в D900XEFK2 - поиск нам выдает аж целых 6 адресов! Ну значит дело хорошо, вставляем эти данные в G600XEGL1 - и.. Ой.. а почему ничего не находит?

Поясню. Взгляните внимательно - на те данные, что лежали в прошивке, и на те - что мы изменили? Ничего они Вам не напоминают? Вспоминаем, база у D900 - 0x1. Смотрим - патч изменяет 4 байта.

Берем данные из поля - from: 4C252D11. Еще раз приглядываемся: 4C 25 2D 11. А если здесь записано просто число в формате LittleEndian (т.е. перевернутое)? Давайте проверим эту теорию. Переворачиваем, 4C 25 2D 11 -> 11 2D 25 4C, т.е. получился адрес 112D254C. Входит он в пределы прошивки D900? Входит, т.е. прошивка начинается с базового адреса, 0x1 -> 0x10000000, заканчивается приблизительно на ~11A00000. Кто-то доходит до него, кто-то нет, но не суть. Входит число 112D254C в диапозон от 10000000 до 11A00000? Входит. Поэтому вставляем данный адрес, и переходим по нему.. Если Вы никуда не лазили, то Вы должны находится на вкладке HEX. Будучи более опытными, вы заметите приблизительно одинаковые строки. На самом деле, здесь записаны самые обычные мсс-скрипты, и поэтому для более приятного их интерпретирования, переходим на вкладку МСС в левой части BinEdit. Опа, что тут у нас? Таск, потом подготовка окна, потом опять таск.. Вообщем, это адрес менюшки Мои устройства. Теперь, нам нужно найти такой же адрес на G600. Первым делом, есть заветная кнопочка - Найти аналогичный код в BinEdit. Замечательная кнопочка, но так как это все-таки программа, а не человеческий мозг - поэтому ей до него - далеко. Но все же попробуем ее нажать. А находится она тут: Действия -> Связанный Bin -> Найти аналогичный код. Что нам программка отвечает? Не удается найти аналогичный код. Ну это и к лучшему, так как все нужно делать - руками, а не халявить, следовательно поэтому - поехали копать прошивку :Laie_67:

Задача наша на данный момент - такова: нужно найти адрес менюшки Мои устройства в G600. С чего надо начать? Как я уже говорил, практически любое действие начинается - с открытия главного меню. Открываем его в D900 и в G600 (напоминаю, адрес главного меню D900: 11350BB0, G600: 207AA114). Итак, открыли. Далее - вспоминаем, как добраться до главного меню Bluetooth.

D900XEFK2: Меню - Приложения - Bluetooth (Меню - 3 - 4).

G600XEGL1: Меню - Приложения - Bluetooth (Меню - * - 4).

;Здездочка - это 11 пункт, Решетка - 12).

Итак, открываем в D900: 11350BB0 -> 1177E3D8 -> 106A483C

G600: 207AA114 -> 214FB7C4 -> 202D70C8.

Открыли. Заметили, что и тут структура очень похожа между прошивками? Сначала идет "Пункт 1 будет выбран по умолчанию", а затем переход. Переходим на второй скрипт, а затем по адресу, куда он ведет (D900: 10ECBFD4, G600: 20771DAC). Уже сейчас замечу, нам понадобится адрес главного меню Bluetooth, это те, что указаны выше (а не те, на которые мы перешли по переходам). Т.е. адрес главного меню Bluetooth в D900: 106A483C, в G600: 202D70C8.

А теперь, вернемся ненадолго к открытому патчу на D900. Глядим в поле to - ничего нам не напоминают эти данные? Проводим ту же операцию, что и с полем from. Получаем:

3C486A10 -> 3C 48 6A 10 -> 10 6A 48 3C -> 106A483C. Входит в диапозон адресов? Входит. А теперь смотрим - ничего ли нам не напоминает этот адрес? Ого, дык это ж адрес главного меню Bluetooth! Правильно. Уже мы знаем, что писать в поле to для G600. Осталось лишь найти стартовый адрес для изменения. А он находится довольно просто.

Найдем сначала его на D900. Что нам нужно? Нужно найти таблицу, в котором будет лежать функция - "Мои устройства" как данные.

Как пользоваться поиском в BinEdit.

На данный момент нас будут интересовать только две кнопки в поиске (надеюсь все знают, где поиск в BinEdit? В правой части программы, первая вкладка - Поиск). Первая кнопка - буква А с лупой, вторая кнопка - буква D с биноклем. Все видят? Ну и хорошо. А если не видим - присматриваемся лучше.

В таблице функций - адреса лежат просто как данные. Точнее адреса везде лежат как данные, но в таблице - кроме них - больше ничего нет. Поэтому выполняем следующие действия. Кнопка поиска, которая A с лупой - она, в прицнипе, предназначена для ассемблера. Кнопка поиска данных, которая D с биноклем - нам и потребуется в данный момент. Берем адрес менюшки Мои устройства (возьмем мы его уже не из патча, а из головы. А как это, спросите Вы? Отвечаю. На данный момент у нас в D900 открыт адрес - 10ECBFD4, и мы видим, по какому пункту меню в какой адрес телефон переходит. Мои устройства - какой по счету это пункт меню? Второй для нас. Если у Вас человеческий файл binedit.ini, то описание скрипта mcc_menu_select (По пункту меню XX переход в адрес YYYYYYYY) будет человеческого вида, т.е. будут описываться пункты, начиная с 1. Тогда нас интересует строка - "По пункту меню 2 переход в адрес YYYYYYYY". Если же, у вас начинаются описание переходов по пунктам, начиная с 0 - то интересует строка - "По пункту 1 переход в адрес YYYYYYYY". Но в любом случае, адрес второго пункта в этом меню - это 112D254C. Вот он нам и понадобится.

Вставляем этот адрес в поиск по данным (т.е. нажали кнопку D с биноклем), нам выдало 6 адресов:

1:0x106A52D4

2:0x10ECC02C

3:0x10ECC96C

4:0x112D23E4

5:0x112D2CF4

6:0x1199FE80

Какой же нам именно нужен для изменения? Рассказываю секрет - 5 из 6 адресов - это мсс-скрипты. И только 1 из 6 - это таблица. Как нам ее найти? Давайте поглядим глазами. Берем первый адрес - 106A52D4. Похоже на мсс-скрипт? Нет? Да что вы :128: Нам нужно всего лишь выровнять код МСС, для этого нажнем кнопку 94168-9-08-10)1281303775_thumb.jpg. И опа - мы попали на какие-то условия. МСС-скрипты? МСС-скрипты.

Берем следующий адрес - 10ECC02C и проделываем те же самые действия. Похоже на МСС-скрипты? Похоже.

Итак, мы прошуршали так все шесть адресов. Интересно, заметите ли Вы тот самый, который не содержит в себе МСС-скрипты?

А теперь маленький секрет. Нажмем черненький треугольничек между нашими двумя нужными кнопками поиска и выберем там пункт "МСС команды с использованием адреса". Сколько адресов нашел? 5? А почему не 6? Да потому что, один из шести - и не является мсс-скриптами. Путем анализирования и поиска шестого лишнего, мы сравнивая два списка поиска, обнаруживаем, что адрес 1199FE80 и не является мсс-скриптом. А как раз таки, и хранит в себе таблицу функций. Открываем наш патч, преобразуем наш адрес для изменения к нормальному виду и смотрим - правильно ли мы нашли? Правильно! Теперь нам ничего не стоит, как проделать все тоже самое, только на примере прошивки G600XEGL1, тем самым портанув этот нетрудный патчик.

Полностью объяснив структуру патча, я уже не буду зацикливаться на моментах, и просто вкратце портирую данный патч вместе с Вами на G600XEGL1.

Итак, у нас есть адрес главного меню Bluetooth в G600 - это 202D70C8. Берем адрес менюшки `Мои устройства` - это вспоминаем - переход по пункту меню 2 по адресу 21010660. Вставляем в поиск как данные, получаем шесть адресов,

1:0x202D7140

2:0x20771E04

3:0x21010C28

4:0x21915BB4

5:0x21915D24

6:0x21AE5F24

отсеиваем их, получаем, что адрес ячейки в таблице опять под номером #6, т.е. 0x21AE5F24.

Все, мы все нашли, осталось лишь открыть компилятор, да компильнуть наш патчик. Итак, открываем компилятор, создаем наш проект, пишем следующее:

.start 0x21AE5F24

А дальше? Что нам требуется? Заменить 4 байта, т.е. положить нормальный адрес в адрес в формате LittleEndian. Как это делается? Вспоминаем урок №3. Для этого существует операнда .word, которая записывает адрес в человеческом виде - в формат LittleEndian, как раз заменяя ровно 4 байта, что нам и требуется. На какой адрес будем заменять? На адрес главного меню Bluetooth, поэтому в компиляторе пишет следующее:

.start 0x21AE5F24

.word 0x202D70C8

Все, патч готов :) Компилируем, и радуемся новоиспеченому патчу.

 

Пару нюансов. На некоторых телефонах, в данном патче мы можем обнаружить следующее (буду брать на примере D900, т.к. в данный момент не нашел патча этого, где сделано следующим образом).

Допустим, что в G600 адрес меню Bluetooth - 212D70C8, тогда при компиляции мы получаем следующее:

<nord offset="0x01AE5FD4" from="60060121" to="C8702D21" type="CODE" />

Все видят, что последний байт (21) - одинаков и в поле from, и в поле to? Некоторые патчмейкеры после компиляции правят SMP на наличие одинаковых байт, т.е. получается следующее из патча:

<nord offset="0x01AE5FD4" from="600601" to="C8702D" type="CODE" />

Уже сразу и не скажешь, что тут адрес, верно? В таких случаях обычно делается вот что - открывается адрес для изменения, то бишь - 0x21AE5FD4, там мы можем заметить следующее:

60060121B80F0E2180FD452168DD3120

Вот еще подсказка, как легко замечать данные.

1) Разбиваем строку по 4 байта:

60060121 B80F0E21 80FD4521 68DD3120

Затем обращаем внимание на каждый четвертый байт:

60060121 B80F0E21 80FD4521 68DD3120

Замечаем, что он или 20, или 21. Следовательно, нужно насторажиться (хотя я уже сразу же проверяю, что за данные тут лежат. Проверить просто - копируем это число, превращаем в человеческий формат, а далее уже по обстоятельствам). Тут, в основном, может быть:

1) Это указатель на строку, следовательно нужно смотреть на вкладке HEX в самой правой колонке - там BE интерпретирует байты как текст. Текст может быть как вменяемым, например:

..`.0.Poland..............b.1.Germany...........,

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

???°?·?°??.?’N‹?·????.?zN‚???µ?? (в основном через каждый символ-два повторяется буква Р (здесь возможно и в основном знак вопроса идет, т.к. в блокноте ANSI кодировка была указана при сохранении, поэтому данные символы из другой кодировки он не понимает :)).

2) Это указатель на мсс-скрипты. Как, например, в рассмотренном выше патче. Т.е. чтобы посмотреть, что там - копируем адрес, превращаем в человеческий вид - и переходим на вкладку МСС в левой часте BinEdit'a.

3) Указатель на функцию. Опять же, копируем адрес, превращаем в человеческий вид, затем переходим на вкладку Код, и уже смотрим, что там за функция. Будучи более опытными в этом деле, также зная асм - уже сможете отличать код от подобия кода (т.е. от не кода, но BinEdit будет пытаться интерпретировать его как код).

4) Ну может быть еще указатель на указатель. Т.е. копируем число, превращаем в человеческий формат, а там мы попадаем на еще какую-нибудь таблицу, в котором опять же лежат - указатели.

Ну это основные случаи я рассмотрел, скажу сразу - далеко не все. Еще также быть - указатель на RAW графику, может быть указатель на IFEG-графику.. многое чего.

Какие еще могут быть нюансы. Скажу еще вот что. Мы рассмотрели понятие "таблица", кажкая ячейка - 4 байта. Но не стоит думать, что в любой таблице ячейки все по 4 байта. Есть таблицы, в которых идут данные по байту, а также по два. У Вас может появиться вопрос - а где по 3 байта? Отвечу. Наш мобильный ARM-процессор умеет работать с числами, типа:

1) byte (один байт) ;

2) half-word (два байта) ;

3) word (четыре байта).

[Спасибо FRAER за исправление]

;названия привел из типов переменных =)

По три байта - он работать не умеет. Есть еще исключения - динамичноячеечные таблицы (т.е. таблицы, в которых размер ячейки может варьироваться). Но это немного другой разговор, так как из них данные берутся вручную, нежели из статичноячеечных (т.е. размер каждой ячейки исключительно одинаков). Для чего это делается (хотя возможно еще рано о них говорить, но все же - потом если что, вернемся). В таблицах, где каждая ячейка имеет одинаковый размер - легко работать. Там можно получать данные просто по номеру, как в рассмотренном выше примере. Или же пройдясь циклом по всем ячейкам таблицы, хоть и тут же опять будут номера. Также могу сказать - о таблице иконок в прошивке. Таблицы имеют размер ячеек одинаковый - 24 байта. Если Вы хорошо вчитываетесь, то можете задать сейчас вопрос - что за бред, если размер ячейки может быть 1, 2 или 4 байта? Тут я поясню. Размер ячеек может быть каким угодно, но данные в них - могут быть уже по 1, 2 или 4 байтам. В таких больших ячейках, работа идет через указатель. Т.е. здесь получается - номер * 24, получаем указатель на нужную ячейку. А уже данные с нее - собираем по крупицам, т.е. по одному, двум или четырем байтам.

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

 

Ну на сегодня мой урок подходит к концу, единственное, что еще хотелось бы сказать. Мы научились заменять данные, это понятно. Мы заменили в таблице указатель на менюшку "Мои устройства" (это переход по пункту 2) на адрес главного меню Bluetooth. Но ведь мы также можем сделать и переход не в главное меню Bluetooth, а в менюшку активации Bluetooth. Просто для закрепления урока.

P.S. Переход по пункту меню 1 ;)

 

Ну все, на сегодня все - собирался рассказывать совсем не про этот патч, а про немного сложней. Но пока пусть будет так, главное - Вы познакомились с таким понятием, как данные, и что если мы видем данные - то не стоит искать тупо такой же в другой прошивке, я имею ввиду то, что не стоит брать адрес с одной прошивки и его же вставлять другую для поиска. Надеюсь, урок не покажется Вам трудным, и Вы его переварите. В очень ближайшее время, будет рассмотрен более трудный патч, уже с использованием ассемблера.

 

Enjoy.

  • Like 17
Ссылка на комментарий
Поделиться на другие сайты

  • 2 недели спустя...

Урок №7 - Ассемблер.Начало

Ну чтож, Вы готовы к дальнейшему покорению гор патчей?

В данном уроке мы перейдем к новой ступени нашего совершенствования - ассемблер :arrow:

Если Вы не знаете, что такое ассемблер и с чем его едят - я Вам заверю, что расстраиваться - причин нет. Это абсолютно не означает, что теперь придется откладывать патчи в очень долгий ящик, и идти совершенствовать свои знания в ассемблере. Совсем нет. Конечно это плохо, что Вы не знаете его, больше времени уйдет на обучение, но все же - я когда писал первые патчи, для меня ассемблер был абсолютно нулем. Что такое это вообще - я не знал. Вообще, ассемблер это язык программирования низшего уровня, команды процессора, так сказать - в текстовом виде. Если Вы программировали хоть чуток на языках высшего уровня (Cи/Паскаль/Бейсик) - Вам будет уже проще, т.к. в ассемблере, как и в любом языке программирования, нужно понять, в прицнипе, одну простейшую конструкцию: Если - то, иначе. Вот и все, такое вот логическое программистическое мышление очень помогает, и тогда ассемблер быстро начнет пониматься. Он отличается от языков программирования высокого уровня в том, что вся основная задача программы возлагается на программиста. В ассемблере нет операнд for для циклов, case, if..then..else и т.д. Все это придется делать вручную!. Конечно BinEdit нам облегчает эту задачу, но это уже халява, нужно обучиться делать все исключительно руками. Итак, наш патч на сегодня будет - это портирование патча ScreenShot. Смысл патча заключается в том, что:

1) Подменяется точка входа в код обработки команды AT+SOS

2) Точка входа переадресуется на наш код, в котором мы производим сохранение участка оперативки (что отвечает за то, что находится в данный момент на экране телефона) в файл.

В данном патче нет ни одного мсс-скрипта, а только чистый пречистый ассемблер :) Ну и данные. Статей по ARM-ассемблеру достаточно, и переписывать мне их нет смысла, я лишь объясню в краткости. Все операции ассемблера происходят через регистры - так сказать, ячейки памяти центрального процессора, все команды проходят через них. Существуют младшие регистры, которые мы и будем в основном использовать - это регистры от R0 до R7, т.е. 8 регистров (R0,R1,R2,R3,R4,R5,R6 и R7). Я постараюсь не грузить Вас лишней сильной информацией, а постараюсь вкратце объяснить основные понятия ассемблера для начинающего программиста. Основные команды процессора, которые мы будем использовать:

1) Команда MOV. Как могли догадаться наши люди, знающие английский язык, это перемещение данных. Я тоже вначале так подумал, но как оказалось - это неверно. Это операция - копирования данных. Команда mov может быть использована для двух случаев:

а) Копирование значения одного регистра в другой, например:

mov r1,r0

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

б) Копирование (запись) определенного значения в регистр:

mov r0,10.

Данная запись расшифровывается так: записать значение `10` в регистр R0.

BinEdit позволяет нам оперировать числами в различных системах счисления. В данном примере - записана десятичная система счисления. Но также поддерживает BinEdit - в двоичной системе и шестнадцатеричной. Скажу сразу, что бы Вы не писали (двоичную/десятичную/шестнадцатеричную) - все, в конце концов, хранится в шестнадцатеричной (точнее - это представление для нас, поэтому и будет так думать).

Как отличить шестнадцатеричное число в ассемблере от чисел, записанных в других системах счисления. Как будеть выглядеть число `10` в двоичной системе счисления? 10 (десятичная) = 1010 (двоичная). Число в двоичной системе счисления записывается с префиксом 0b, т.е. b = binary. Поэтому запись mov r0,10 равна записи:

mov r0,0b1010.

Также объясню про шестнадцатеричную систему счисления. Переведем десятичное число 10 в шестнадцатеричную систему счисления - это будет 0xA. Число в ней записывается в компиляторе - с префиксом 0x. Т.е. запись:

mov r0,10 равна записи mov r0,0b1010, и равна записи mov r0,0xA.

ПОМНИТЕ! Числа 10 и 0x10 = это два разных числа! Потому что 0x10 в шестнадцатеричной - это 16 в десятичной, ведь 10 никак не может быть равно 16?

Также скажу сразу - практически любая команда ассемблера нашего процессора имеет свое ограничение. В команде mov я знаю следующее ограничение: нельзя записывать числа больше 255 (0xFF). Объясняю почему. В режиме процессора - THUMB каждая команда имеет размер 2 байта (кроме команд вызова функций). Один байт - идет на идентификатор команды+регистра, и один байт (в данном случае) на значение. Т.е. команда mov r0,10 при компиляции - выглядит так: 0A20. Первый байт - 0A - это число наше. А во втором байте - 20 записано "mov r0". При такой записи - да, команда mov имеет ограничение в запись лишь одного байта в регистр. НО - при копировании значения регистра в другой регистр - копируется его полное значение, т.е. 4 байта. Сложно? Поначалу да, не спорю - я вообще думал, что я никогда не пойму ассемблер. Но толк ж все-таки вышел, так что - все возможно при желании, главное желание - а не учитель.

2) Команда ldrX. Загрузка значения в регистр.

Объясняю сразу - команды ldrX нету. Я это написал к тому, что вместо X могут быть следующие значения:

а) b, т.е. команда ldrb.

б) h, т.е. команда ldrh.

б) Ничего :) Т.е. команда ldr.

Сразу у Вас появится куча вопросов, буду сразу же все объяснять, Вы главное - хорошо вчитайтесь.

Данные три команды, в основном, предназначены для считывания определенного количества байт с какого-нибудь адреса.

Каковы различия между этими четырьмя командами? Вспоминаем урок №6, где я рассказывал про то, что процессор может оперировать с числами, типа:

- byte [один байт] (ldrb, типа - ldr byte);

- half-word [два байта] (ldrh, типа - ldr half-word);

- word [четыре байта] (ldr).

Далее может возникнуть вопрос - а зачем тот же ldrb, когда один байт мы можем записать и через команду mov. Объясняю. Команда ldrb предназначена для считывания откуда-то одного байта, например - с той же оперативки (в основном, оттуда это и происходит, но также, в редких случаях - происходит с прошивки). А команда mov rX,YY - для записи статитических данных, позже все будет понятней.

Также вопрос - если команда mov rX,YY - записывает только 1 байт, тогда как записать нам допустим - 2 байта или 4? Для этого существует команда ldr. Код:

ldr r0,=0x12345678 помещает в регистр 4-х байтное число, т.е. 0x12345678. Также мы могли записать ldr r0,=0x1234 - тогда в регистр поместиться по сути два байта - это 0x1234, но на самом деле - все равно будет четыре, остальные два байта регистра забьются нулями. Команда ldr загружает в регистр только 4 байта. Как происходит загрузка значения из оперативки. Попробуем считать в регистр R0 значение из оперативки 0x30000000 (это начало оперативки у телефонов с базой 0x2, для телефонов с базой 0x1 - начало оперативки в 0x18000000). Для этого нам нужно написать две команды ассемблера:

ldr r0,=0x30000000 ;загружаем число 0x30000000 (4 байта) в регистр R0

ldrh r0,[r0] ;получаем два байта из адреса, записанного в регистре R0 в регистр R0

Т.е. сначала мы загрузили адрес оперативки, зачем считали два байта (думаю, что сначала в нерегистровую память процессора), и записали их в регистр R0. Объясню на примере двух регистров:

ldr r1,=0x30000000 ;загружаем начало оперативки в регистр R1

ldrh r0,[r1] ;считываем 2 байта с адреса, записанного в R1, и помещаем это значение в регистр R0

Так понятнее? Опять же - читается справа налево :)

Команда ldrb - идентична данной, т.е. код:

ldr r1,=0x30000000 ;загружаем число 0x30000000 в регистр R1

ldrb r0,[r1] ;считываем байт с адреса, записанного в R1 и помещаем это значение в регистр R0

Ну вот, в прицнипе - мы разобрались с данной командой. Но - напомню, что практически каждая команда ассемблера имеет свое ограничение. Команда ldrh может считывать данные только с адреса, КРАТНОГО 2! Т.е. мы не можем считать 2 байта с адреса, допустим - 0x30000001. Он не кратен двум, поэтому в регистр попадет чушевое значение, не то, что нам нужно. Команда ldr может считывать данные только с адреса, кратного четырем!. Т.е. командой ldr мы не можем считать данные с подобным адресов: 0x30000001,0x30000002,0x30000003 - только с кратных четырем: 0x30000000,0x30000004,0x30000008. И только лишь ldrb может с любого адреса считывать данные.

3) Следующая команда strX. Запись значения в память. Команда идентична командам ldrX, только смысл не в загрузке, а в записи.

Итак, тут также существует три команды - strb, strh и str. Также - strb - записывает байт, strh - два байта и str - четыре байта. Изначально, трудно представить, а в чем смысл одного/двух/четырех байт. В принципе, для экономии места. Например, если мы знаем, что какое-то значение ни при каком случае не может быть больше 255 (0xFF = одного байта), зачем нам хранить данное число в четырех-байтной ячейке оперативки ? Например, тот же статус активации какой-нибудь функции - Выключено или включено, ноль или единица, другим оно никак не может быть, нам хватит и одного байта для хранения данного значения в оперативки, четыре байта - не зачем под него.

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

ldr r1,=0x30000000 ;загружаем адрес начало оперативки в регистр R1

mov r0,1 ;записываем в R0 единичку (а можем и нолик, можем вообще любое число от нуля до 255 (0xFF))

strb r0,[r1] ;сохраняем в регистр R1 один байт с регистра R0

Для записи в начало оперативки числа большего 255, допустим - два байта, запишем число 1024 в начало оперативки.

ldr r1,=0x30000000 ;-//-

ldr r0,=1234 ;ведь мы не можем командой mov записать такое большое число

strh r0,[r1] ;используем команду strh, т.е. число 1024 (0x400) - это больше одного байта, но меньше четырех - поэтому хватит и двух байт.

Ну и, напоследок, запишем число 0xFFFFFFFF в начало оперативки.

ldr r1,=0x30000000 ;-//-

ldr r0,=0xFFFFFFFF ;используем опять же команду ldr для записи в регистр числа, больше одного байта

str r0,[r1] ;используем команду str, т.к. число 0xFFFFFFFF оно как больше одного байта, так и больше двух байт. Это четыре байта, максимальное значение для регистра.

Ну вообщем вот так, ограничения - имеет такие же, как у ldr. Т.е. записать два байта командой strh можно лишь в адрес, кратным двум и четыре байта командой ldr можно лишь в адрес, кратный четырем.

4) Команда cmp. Это команда сравнения. Имеет также два "режима", как команда mov:

а) Сравнение регистра с числом:

cmp r0,1

В данной команде сравнивается: равно ли значение регистра R0 единице.

б) Сравнение регистров (т.е. одного регистра с другим):

cmp r0,r1

В данной команде сравнивается - равно ли значение регистра R0 со значением регистром R1. Прошу заметить, что в данной команде сравнивается левое значение с правым, т.е. только в этом команде читается слево направо!

Какие здесь есть ограничения. Есть ограничение для команды cmp rX,YY - можно сравнивать значение регистра с числом, не большим 255 (0xFF = одного байта). Почему? Объяснение точно такое же, как у mov - в двухбайтовой команде один байт идет на число, и второй байт - на идентификатор команды cmp и нашего регистра, в данном случае R0.

А как сравнить значение регистра с числом, большим 255? Для этого придется уже использовать сравнение регистра с регистром. Прицнип такой (на примере доп. регистра R1):

ldr r1,=1000 ;помещаем в регистр R1 значение, большее одного байта, допустим 1000.

cmp r0,r1 ;и теперь уже и сравниваем - значение регистра R0 со значением регистра R1, т.е. сравнием значение регистра R0 с числом "1000"

У кого-то может возникнуть вопрос - а что дальше то, ну сравнили мы регистры, а как нам объявлять, допустим такие вещи - что выполнить, если регистры равны, или наоборот - не равны, или если одно больше другого и т.д. Скажу, что об этом - попозже.

5) Команда bl. Вызывает thumb-функцию/процедуры. Чтобы было Вам понятней - это полный аналог мсс-скрипта mcc_call. Точь-в-точь одинаков, только на асм-уровне.

6) Команды push и pop. Команда push предназначена для сохранения значений регистров во временной памяти процессора, в стеке. Непонятно? Ну и отлично. Просто скажу, что практически все функции телефона должны начинаться с команды push, чтобы не испортить значения регистров после выполнения функции, и заканчиваться - командой pop, чтобы восстановить сохраненные регистры.

На заметку: если в команде `pop` присутствует восстановление регистра PC - то данная команда является завершением функции. Если же в команде `pop` нет восстановления PC - то значит выполняется код дальше.

Объясняю поподробней. Команда pop является аналогом мсс-скрипта mcc_return, т.е. телефон понимает, что происходит конец функции. А вот команда push - она встроена в сам mcc_call, а тут придется вручную нам это делать.

Напишем функцию, которая выдаст нам в регистр R0 ширину нашего дисплея.

... ;здесь какой-то код, неважно какой

bl lk_GetDisplayWidth ;вызываем функцию lk_GetDisplayWidth, новая наша функция, будет возвращать в регистр R0 ширину дисплея, ну в данном случае - число 240. Это аналог mcc_call 0 0 0 0 адрес_мсс_функции_выдачи_ширины_дисплея

... ;здесь тоже какой-то код, неважно какой

.........

lk_GetDisplayWidth: ;объявляем в свободном месте нашу функцию.

push {lr} ;сохраняем в стек регистр LR. Что такое регистр LR: в нем хранится адрес следующей команды процессора после вызова функции, т.е. куда нам надо будет выйти после выполнения нашей функции

mov r0,240 ;записываем в регистр R0 число 240, т.е. ширину дисплея

pop {pc} ;восстанавливаем в регистр PC то, что мы сохранили командой push. Тем самым, завершая функцию.

В регистре PC хранится адрес следующей команды (если быть точнее, то следующей, после следующей) [спасибо за исправление - AlexeyK], т.е. при выполнении каждой команды ассемблера - там постоянно изменяется значение - указатель на следующую команду. Вообще, должно быть - какие регистры мы сохранили, такие и восстановить. Но для lr и pc немного другое дело. В чем здесь прицнип - мы сохранили значение следующей команды после вызова нашей функции, а потом командой pop записываем значения следующей команды в регистр pc. Немного трудновато представить себе еще, но запомним следующее: что мы сохранили, то мы должны восстановить.

Для примера, напишим функцию, в которой будут изменено несколько регистров, но на выходе из функции - изменен должен быть лишь регистр R0, а все остальные регистры - остаться теми же, что были до вызова нашей функции:

push {r1-r7,lr} ;сохраняем все младшие регистры (кроме R0) в стек и регистр lr

mov r7,25

mov r6,250

mul r5,r6,r7

lsl r4,r5,2

lsr r3,r6,2

... ;здесь какой-нибудь код, с использованием регистров, не только R0

pop {r1-r7,pc} ; обратно все регистры сохраненные восстанавливаем, а также в pc помещаем значение следующей команды, после вызова нашей функции

Просто запоминаем такое правило и все. Сильно запариваться не нужно, потому что это все очень и очень непонятно.

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

Итак, рассмотрим еще и их, только уже побыстрее.

7) Команда add. "Англичане" сразу догадаются, это команда сложения. Имеет такие же ограничения, как у mov, т.е. нельзя прибавлять к регистру число, больше 255.

Пример, как получить число 0x100 (0xFF + 1)

mov r0,0xFF ;записываем в регистр R0 значение 0xFF

add r0,1 ;прибавляем к значению регистра R0 число 1. Т.е. 0xFF + 1 = 0x100, то есть 256.

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

add r0,r1 - прибавляем значение регистра R1 к значению регистра R0.

8 ) Команда sub - вычитание. Все тоже самое, что и add.

9) Команда mul - умножение. Умеет умножать только регистр на регистр, прямо на число умножать не умеет. Поэтому умножение на число происходит через промежуточный регистр. Пример, умножим число 2 на 6.

mov r0,2 ;записываем число 2 в регистр R0

mov r1,6 ;записываем число 6 в регистр R1

mul r0,r1 ;умножаем значение регистра R0 на значение регистра R1, и результат окажется в первом записанном регистре. Т.е. в R0

Если же было третьей командой записано:

mul r1,r0, т.е. регистр R1 умножили бы на R0, то результат бы оказался в R1.

10) Ну и напоследок - операции по переходам после сравнения.

Скажу сразу, все команды переходов я сам не помню, поэтому обращаюсь к документации компилятора BinEdit, в котором написано, какие переходы какие обозначают. Скачать документацию можно в данном посте. Итак, открываем его, ищем текст "Условные и безусловные переходы", и видим такое:

BEQ label ; если равно

BNE label ; если не равно

BCS label ; если больше без учёта знака

BCC label ; если меньше без учета знака

BMI label ; если отрицательное

BPL label ; если положительное

BVS label ; если переполнение разрядов

BVC label ; если нет переполнения разрядов

BHI label ; если больше без учета знака

BLS label ; если равно или меньше без учёта знака

BGE label ; если больше или равно с учётом знака

BLT label ; если меньше с учетом знака

BGT label ; если больше с учётом знака

BLE label ; если меньше или равно с учётом знака

Т.е. каким образом образом у нас происходит сравнение. Объясню на примере функции, которая будет нам в регистр R0 выводит единицу, если значение регистра R1 больше 0xFF, и нолик, если значение регистра меньше или равно 0xFF.

Итак, код:

my_func: ;объявление метки my_func

push {lr} ;сохраняем в стек регистр lr для последующего корректного выхода из функции

mov r0,1 ;изначально устанавливаем единицу в регистр R1

cmp r1,0xFF ;сравниваем значение регистра R1 с 0xFF

bhi exit ;обращаемся к цитате выше. Нам нужно условие "если больше". Можно взять bcs, можно взять bhi - разницы в них не знаю, ну я использую bhi. При выполнении условия переходим в метку exit

 

mov r0,0 ;устанавливаем нолик в регистр R0

exit:

pop {pc} ;восстанавливаем значение из стека в регистр PC, т.е. выходим из функции.

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

Если число больше 0xFF, то записать в R0 1 и выйти.

Если число не больше 0xFF то записать в R0 0 и выйти.

Вышло бы больше кода, а в выше написанной функции вышло меньше кода, но сохранен смысл функции, и, соответственно, ее работоспособность.

 

Нагрузили мы достаточно знаний, которые никак не укладываются в наших головах, но будем вершить дальше. Скажу только одно - практически все сразу же все не поймут, это приходит просто с опытом.

Открываем наш патч ScreenShot, будем портировать с D900XEFK2 на G600XEGL1, как обычно. Открываем два связанных редактора, отключаем синхронизацию, т.е. все как обычно.

Смотрим патч, наша первая строка:

<nord offset="0x146AF68" from="75649710" to="99452C11" />

Вспоминаем урок №6, при отточенном зрении - видим, что здесь записаны данные. Объясню - здесь и меняется точка входа функции обработки команды AT+SOS. Как нам найти эквивалент? Для начала, переходим по адресу 1146AF68 (адрес изменения патча), видим там такие данные 10976475 (75 64 97 10 -> 10 97 64 75). Это адрес функции. Скажу, что все функции должны обязательно начинаться на адресе, кратном двум (т.к. команды занимают 2 байта (и пара команд - 4)), поэтому на адресе, не кратном двум - располагаться не может. Что же тут такое? А здесь лежат данные: адрес_функции+1. Вот это наше "+1" - здесь очень важно, пока не буду говорить для чего это, но скажу, что асм-функции, когда они лежат как данные - должны быть записаны как адрес+1. Поэтому от 10976475 отнимаем единичку, получаем 10976474 и переходим по этому адресу в D900. Видим какую-то функцию, смысл ее нам не понятен, но расшифровать мы ее в прицнипе можно.

push {r2,r3,r7,lr} ;сохраняем в стек значения регистров R2,R3,R7 и lr

mov r0,8 ;помещаем в регистр R0 число 8

bl RtkGetMemory ;вызываем функцию RtkGetMemory

mov r2,r0 ;помещаем в регистр R2 значение регистра R0

ldr r1,=0x3ABB ;помещаем в регистр R1 число 0x00003ABB

str r2,[sp,4] ;сохраняем все значение регистра R2 (четыре байта) в регистр sp+4

str r1,[sp] ;сохраняем значение регистра R1 в регистр SP

mov r1,0 ;записываем в регистр R1 нолик

mov r2,0x38 ;записываем в регистр R2 число 0x38

mov r3,0 ;записываем в регистр R3 ноль

mov r0,0x3A ;записываем в регистр R0 число 0x3A

bl off_111C7004 ;вызываем функцию 0x111C7004

mov r0,0 ;записываем в R0 ноль

pop {r2,r3,r7,pc} ;восстанавливаем сохраненные регистры и в PC записываем значение следующей функции для выполнения

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

Итак, возвращаемся к нашему патчу. Первое, что нам нужно сделать - это найти аналогичный код в G600XEGL1. Я в прицнипе пользуюсь кнопкой автоматического поиска похожего кода, и конечно она не идеальна, и приходится самому удостовериваться, что найден код верно. Ну значит делаем следующее: мы стоим в прошивке D900XEFK2 на адресе 10976474. Вставляем адрес в поиск, нажимаем кнопку "А с лупой", что нашлось? Явное указание 10976475 в коде как данные, кол-во адресов - один 0x1146AF68, как раз тот, что и изменяется в патче. То, что найден всего один адрес - это хорошо. Ищем аналогичный код в G600XEGL1: в D900XEFK2 возвращаемся на адрес 10976474, затем вызываем в главном меню `Действия -> Связанный bin -> Найти аналогичный код". Поиск оказался успешен, во всяком случае не было выдано ошибки, что код аналогичный не найден. Итак, в G600 открылся адрес 0x2134F678, сравниваем код на D900 и G600. Сильно отличается? Различия есть, но очень маленькие. Надеемся, что все верно BinEdit нашел нам адрес аналогичного кода, значит берем адрес 2134F678, вставляем в поиск и нажимаем также "А с лупой". Поиск выдал аналогичное - Явное указание 2134F679 в коде как данные, кол-во адресов - один 0x219F5938. Шансы повышаются, что найден аналогичный код. Итак, у нас нашелся первый стартовый адрес. Значит открываем в G600 компилятор, создаем новый проект, пишем следующее:

.start 0x219F5938

.word screenshot+1

По тому же прицнипу, что было до патча - мы меняем точку входа в функцию обработки команды AT+SOS (как автор патча (SergeyL) нашел изначально этот адрес - я не знаю, и на данный момент пока это не главное), т.е. записываем четыре байта - адрес нашей функции +1.

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

.start адрес_свободный

screenshot:

Мы объявили метку screenshot, теперь осталось лишь написать код функции screenshot. Дизассемблируем патч через редактор патчей (т.е. восстанавливаем исх из готового машинного кода), или вставляем мой (а точнее из исха, который мне передал многоуважаемый -=AsteriX=-):

screenshot:

PUSH {R1-R7,LR}

SUB SP,0x24

ADR R0,=ScreenShot_name

LDR R1,=0x202

BL tfs4_open

MOV R5,R0

BMI end

MOV R0,R5

LDR R1,=vScreenMem

LDR R2,=0x25800 ; vScreenBufer

BL tfs4_write

CMP R0,0

BLE end

MOV R0,R5

BL tfs4_close

MOV R0,0

end:

ADD SP,0x24

POP {R1-R7,PC}

.data

 

ScreenShot_name: .string "/a/multimedia/images/downloaded images/screenshot.raw"

Итак, какова наша задача? У нас есть уже оба найденных стартовых адреса, но для окончания патча нам нужно найти следующие эквиваленты функции в G600:

1) tfs4_open

2) tfs4_write

3) tfs4_close

4) vScreenMem

ScreenShot_name нам искать не нужно, т.к. это данные самого патча, а именно - адрес полного пути к файлу для скриншота.

Еще скажу: видите данные 25800? Что это? Мы не знаем. Но скажу заранее - это размер буфера, а проще - куска оперативной памяти, в котором хранится текущее изображение на экране. Его нужно тоже изменять, если мы портируем патч на телефон, ширина/высота дисплея которого отличается от 240х320, а также если изменяется глубина цвета. Как этого высчитывается? Для D900 и подобных просто. Объясняю: Ширина дисплея * Высота дисплея * Кол-во байт, отвечающих за один цвет..

а) Ширина дисплея в D900 = 240

б) Высота дисплея в D900 = 320

в) Глубина цвета = 16 бит, т.е. на один цвет приходится по два байта.

Следовательно получаем: 240*320*2 = 153600, поэтому скрин в формате RAW весит 153600 байт.

Если в телефоне глубина цвета 18 бит, то тогда на один цвет приходится по 3 байта, поэтому нужно ставить ширина*высота*3.

Примечание, поглядев патч на E780 я пришел к тому выводу, что на каждый цвет при 18 битном дисплее - приходится по 4 байта. Странно это, необходимо проверять на других телефонах. НО в любом случае, если Вы столкнетесь с такой проблемой, будет Вам над чем поколдовать :)

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

Открываем наш первый эквивалент на D900. Это функция tfs4_open, ее адрес на D900 - 11270DB0. Первое, что я пытаюсь - полагаюсь на BinEdit, и нажимаю кнопку нахождения аналогичного кода. BinEdit находит в G600 адрес 20FB26B0. Довольно сильно изменен код по сравнению с D900, и сразу появляется впечатление, что найден адрес неверно. Но на самом деле адрес найден верно. Значит куда-нибудь в отдельном месте (например в начале или конце исходника добавляем такое):

.equ tfs4_open 0x20FB26B0

Директива .equ объявляет эквивалент, прицнип: .equ название адрес.

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

Следующая функция, которую нам надо искать - tfs4_write. Ее адрес в D900 - 11271328. Ищем аналогичный код, находит BE адрес 205ACCAC. Опять код сильно различается, и - найден адрес неверно. Значит что делаем? Ручками-ручками начинаем копать. Как другим способом искать эквиваленты функций? Можно по трассировке, если такая есть в функции, или по другому - ищем адреса вызовов этой функции, и уже находим аналогичный код вызова нужной функции, и сравниваем код.

Итак, вставляем адрес tfs4_write на D900 в строку поиска 11271328, нажимаем кнопку "А с лупой", BinEdit находит три группы:

1) Адрес комманды BL с переходом на адрес "11271328"

2) Ссылка на данные 11271329 (11271328+1)

3) Явное указание "11271329" в коде как данные.

Пока нас интересует лишь первая группа. Смотрим первый найденный в этой группе адрес - 0x1117F52E. Один раз кликаем по нему в списке найденных адресов, попадаем на адрес 1117F51E (потому что в BinEdit изначально при таких стоит вычитание на 16 (0x10) байт при переходе на адрес. Ищем аналогичный код - получился адрес для G600 212EF282. Кое-что похожее нашлось, точнее - практически идентичный код, отличия крайне минимальны. Скажу, что адрес BinEdit нашел верно, т.е. эквивалент tfs4_write для G600 - это адрес 218BF374. Также проверить можно по трассировке, вызывается в обоих прошивках функция трассировки upv2_1SpvTrace (адрес на D900 - 10A2DD0C), строка трассировки для нее передается в регистр R1. Смотрим текст, передаваемый в D900 (10C14C70):

tfs4_write : [%d]

Смотрим в G600 (upv2_1SpvTrace на G600, если приглядеться - имеет адрес 0x20B75A98). Тот же самый текст? Тот же самый. Значит хорошо.

Возвращаемся обратно на адрес 1117F51E [D900XEKF2] и 212EF282 [G600XEGL1]. Добавляем эквивалент в компилятор функции tfs4_write для G600:

.equ tfs4_write 0x218BF374.

Далее нам нужно найти эквивалент функции tfs4_close. Ее адрес в D900 - 101B60F0. Если у нас BinEdit отображен на весь экран, то мы видим, что в этом же блоке команд виден вызов данной функции на D900, следовательно, чтобы найти на G600 эквивалент - нужно лишь увидеть, что в G600 по адресу 212EF2CC вызывается функция 20FB0C58, что и есть tfs4_close. Найти элементарно, просто проследив за вызовами функции с D900 и G600. Следовательно, у нас найден эквивалент уже и tfs4_close. Добавляем эквивалент в компилятор:

.equ tfs4_close 0x20FB0C58

Остался последний адрес - vScreenMem. Ее адрес в D900 - 1844D9F6. Пробуем перейти по этому адресу - и.. BinEdit скидывает адрес на 0. Почему? Да потому что это не облать прошивки, а адрес в оперативке. Тогда как ж такие адреса искать? Все просто :) Вставляем адрес в поиск, нажимаем треугольник, правее кнопки "D с биноклем" и выбираем "Адрес относительных ссылок на данные (число)". BinEdit нашел немало адресов, это даже очень хорошо. Кстати, подскажу - этот адрес и является адресом буфера текущего изображения на дисплее, поэтому если вдруг у вас создается файл скриншота, но вместо изображения разные непонятные пиксели - скорее всего, данный эквивалент найден неверно. Итак, возвращаемся к патчу. Первый найденный адрес - 0x100A55A0. Переходим, ищем аналогичный код - код не найден. Идем дальше.

0x101588D0 - ищем код, найден - но черезчур отличается, неверно.

0x1015A70A - неверно.

0x1015A7D0 - не найден.

0x101887DE - не найден.

0x101BECD4 - найдено неверно.

0x101BEFEC - не найден.

0x104C93B4 - не найден.

0x104DB10E - не найден.

Что-то много не находит. Попробуем на более статические команды перейдем, например - встанем на адрес 104DB106, на статическую команду с числом - MOV R3,#0x4B. Ищем аналогичный код - нашелся. Нашелся верно, возможно функция и не та, но адрес оперативки найден верно, vScreenMem на G600 - 0x30306820. Все, все эквиваленты собраны, можем компилировать патч и радоваться готовому рабочему патчу :) Для других, кто не нашел все равно аналогичный адрес нормальный - пробуйте дальше по списку, где-нибудь да должен адрес всплыть, пытайтесь не сразу же искать, а немного двигаться ниже/выше, авось и получиться. На данной ноте, я вынужден урок прекратить и пожелать Вам удачи в столь нелегком для начинающего - патче.

  • Like 31
Ссылка на комментарий
Поделиться на другие сайты

Гость
Эта тема закрыта для публикации ответов.


×
×
  • Создать...