
Цифры нажимались как бы сами собой, быстро следуя в чётком порядке. Это было очень круто! Кряк даже пустил слезу от умиления, а енот довольно потирал свои ловкие ручонки.) Наверняка ему не терпится сделать ещё один интересный проект.
+++
Проанализировав данные, полученные с помощью обратной разработки в части 1 и части 2, можем прикинуть алгоритм работы имитатора касаний, написать приложение и взломать пароль! Этим сегодня и займёмся.
– Кто-нибудь, разбудите программиста!
В видеоформате какие-то вещи получаются нагляднее и проще для понимания, чем в тексте. Поэтому предлагаю вам посмотреть видео об этом проекте.
Содержания видео и статей в данном случае отличаются и дополняют друг друга.
❯ Начинаем шкодить прошивку для STM32
Использовать будем библиотеку HAL, так как хочется максимально быстро и просто получить рабочий прототип и проверить идеи. Хорошие уроки по STM32.

При настройке микроконтроллера в Куб МХ включаем отладку serial wire.
Настраиваем тактирование.
RST подаём на вход мк. Включаем подтяжку к +, так как активный низкий уровень. Без подтяжки могут быть шумы.
VIBRO подаём на вход мк. Включаем подтяжку к –, так как активный высокий уровень.
INT – выход, push-pull.
PWR_KEY – выход управления кнопкой блокировки через транзистор.
Настраиваем I2C. Режим ведомого, указываем такой же адрес, как у контроллера сенсорной панели. 0b111000 = 56
Обратного разработчика и монтажника вы уже знаете, а это наш

А вот так она обычно работает. Только её начальству не показывайте.)

❯ Что делает телефон, если не принять ни одного байта по I2C
Компилируем, прошиваем, пробуем включить.
Телефон (или только экран?) даже не включается. Мы питаем преобразователь уровней от питания экрана. Почему-то так не хочет работать. Поэтому запитаем преобразователь уровней не от питания LCD, а от +1,8 В на плате преобразователя. Теперь экран включается без проблем.
Может показаться, что STM32 откликнется на свой адрес, даже если не принимать посылаемые ему посылки. Ведь мы сконфигурировали I2C в Кубе.
Но нет. Первая посылка от телефона остаётся без ответа (вместо ACK видим NACK, на картинке просто N).

Интересно, что, не получив ответа от адреса 70, телефон один раз сбрасывает контроллер тача и 10 раз (с паузами и активацией сброса) пробует писать по адресу ВА, затем по адресу 80. Возможно, такие адреса у других моделей контроллеров сенсорной панели. Это обычное дело встроить в прошивку смартфона несколько драйверов для разных моделей модуля (экрана, сенсорной панели, и т д). Например, вот участок схемы для Explay A351:

Пишем строчку HAL_I2C_Slave_Receive(&hi2c1,&i2c_data[0],1,300);
(принимаем 1 байт по I2C1, сохраняем в i2c_data[0], 300 мс– время ожидания посылки до формирования ошибки приёма)
и микроконтроллер начинает откликаться на свой адрес (получаем ACK, сокращённо А).

❯ Прошивка STM32 версии 1
Файл main.c можно посмотреть тут.
В коде достаточно просто разобраться, везде есть комментарии.
Контроллер в бесконечном цикле ждёт команд от компьютера по UART.
Каждый раз передаётся 4 байта.
Формат такой: код команды, XHYH, XL, YL.
XH и YH занимают по 4 бита, вместе 1 байт.
Команда 10 – сделать касание.
Например, для касания цифры 0 X=120, Y=2E1 [0x10, 0x12,0x20,0xE1]
Команда 20 – включить телефон [0x20,0x00,0x00,0x00]
Команда 30 – разблокировать телефон [0x30,0x00,0x00,0x00]
Команда 40 – смах заставки. Начальная точка X=06F, Y=35E (можно ввести другие координаты).
[0x40,0x03,0x6F,0x5E]
В зависимости от полученной команды выполняются нужные действия.
Для простоты и быстроты решения основных задач проекта используются простые вещи (например, блокирующие циклы вместо прерываний, библиотека HAL и т. д.) и реализован только основной код без каких-то защит и проверок. Поэтому контроллер может зависнуть, если что-то пойдёт не так или команды будут не в задуманном порядке. Тем не менее, это работает и решает основные и самые интересные задачи.
При имитации касаний пакеты посылаются процессору смартфона так же, как это делает контроллер сенсорной панели.
При приёме команды и после выполнения действий микроконтроллер посылает приложению строки вида Command received, Making touch, OK, и др.
Отлаживать прошивку stm32 и взаимодействовать с ним можно с помощью UART через терминал, отправляя, например, код команды и координаты касаний. Но удобнее и интереснее делать это в своём приложении. Поэтому параллельно с прошивкой будем разрабатывать управляющее приложение-интерфейс на Питоне.
❯ Пишем приложение
На чём лучше написать оконное приложение на Python: Tkinter или PyQT?
Tkinter лучше документирован, и с ним немного проще работать. Но в библиотеке PyQt5 есть больше виджетов, включая редакторы дат, индикатор выполнения, ползунок, средство выбора шрифта, панель инструментов и многострочное поле ввода текста. Он также поддерживает шаблон MVC и включает всплывающие подсказки, которые могут быть полезны новым пользователям.
Tkinter против PyQt: выбор правильной библиотеки GUI для ваших проектов на Python – хороший обзор (коротко и по делу). Сравнение.
Мне понравилась библиотека PyQt. Все проекты, созданные на PyQt, кроссплатформенные. То есть созданную программу можно запустить на любой операционной системе.
С помощью QT Designer можно создать симпатичные интерфейсы. Например:


Как будет выглядеть интерфейс нашего приложения?
Мы работаем с com портом. Поэтому можно сделать элементы управления для него.
В дальнейшем можно будет убрать настройки ком порта на отдельную форму, которая будет вызываться из меню сверху.
А сейчас для простоты сделаем всё в основном окне.
Сначала попробовал использовать PyQT 6, но приложение не запускалось. И я перешёл на PyQT 5. Дело в том, для 6й версии нужна Windows 10 или выше, а у меня стояла семёрка.
Как можно подключить графический интерфейс в приложение?
- После создания интерфейса в Qt Designer преобразовать файл.ui в Питон файл через pyqt tools.
- Загрузить пользовательский интерфейс из файла.ui в наше приложение при его запуске.
Второй способ удобен, так как все изменения интерфейса после сохранения автоматически подтягиваются в наш Питон файл. А ещё сам интерфейс отделён от логики, описываемой в Питон файле.
class Ui(QtWidgets.QMainWindow):
def __init__(self):
super(Ui, self).__init__() # Call the inherited classes __init__ method
uic.loadUi('basic.ui', self) # Load the .ui file
self.show() # Show the GUI
Полезные ссылки
- How to Import a PyQt5 .ui File in a Python GUI
- PyQt5 для начинающих.
- Отличная статья про оформление графического интерфейса, как создать и загрузить значок приложения. Простой GUI калькулятор на Python #1. Дизайн приложения
- Хорошее видео с объяснением конструкции
if __name__ == '__main__': #Что-то вроде "Если запускаем данный файл как основной (верхнего уровня), то выполняем этот иф".
Хотелось бы, чтобы приложение отслеживало подключение нашего устройства и автоматически подключалось к нему. Но, видимо, при использовании последовательного (COM) порта так не получится.
Файлы приложения можно посмотреть здесь.
Интерфейс версии 1 получился такой:

Работу системы вживую можно посмотреть здесь.
Вот мы добрались до взлома пароля последовательным перебором вариантов.
Пароль 4-значный, цифры 0…9 – значит, возможно 10^4 вариантов.
❯ Кто будет перебирать пароли: микроконтроллер или Питон приложение?
Вариант 1.
Приложение на компьютере – это лишь удобный интерфейс для посылки команд микроконтроллеру. Нажимаем кнопочку «Взломать пароль», приложение посылает контроллеру соответствующую команду. А он делает всё необходимое для её выполнения:
- Отправляет на телефон касания четырёх цифр с некоторой паузой.
- Проверяет активность вибромотора.
Если есть вибрация – пароль неверный. Увеличиваем цифру на 1 и посылаем следующий пароль (4 цифры).
Если вибрации нет, то пароль верный. Это предположительно, будем наблюдать за телефоном во время экспериментов.
То есть основная роль у микроконтроллера.
Вариант 2.
Микроконтроллер выполняет только простые действия, вроде посылки телефону касания цифры, а всей логикой занимается приложение на компьютере. В таком случае приложению нужно чётко отслеживать состояние смартфона и микроконтроллера.
Вариант, когда перебором занимается микроконтроллер, мне кажется проще. В таком случае он сам выполняет все нужные действия и не нужно постоянно передавать приложению на компьютер посылки с текущим состоянием смартфона и самого микроконтроллера.
Во второй версии приложения и прошивки добавляем кнопку «Взломать пароль с обработчиком и соответствующий код.
Файлы приложения и прошивки лежат в репозитории проекта.
Интерфейс версии 2 выглядит так:

Всё то же самое, только добавилась кнопка «Взломать пароль».
В версии 2 добавилась команда 50 – перебрать пароль, начиная от N=Ц1Ц2Ц3Ц4 (Ц – это цифра от 0 до 9)
Команда, Ц1Ц2, Ц3Ц4, 00
[0x50,0x00,0x00,0x00]
При приёме команды на взлом пароля в микроконтроллере запускается цикл, в котором последовательно перебираются все варианты от 0000 до 9999 с шагом 1. Каждой цифре соответствуют свои координаты касания на тачскрине, которые мы выяснили ранее.
По UART отправляется текущий пароль и слово WRONG, если он неверный.
Управлять имитатором можно из простого терминала, но удобнее работать из специального приложения, которое мы написали. Особенно, если добавлять какие-то более сложные функции. Например, делать касания в произвольных местах экрана, кликая мышкой по картинке.

Пароль найден! ☺ 2171


Работу системы вживую можно посмотреть здесь.
❯ Убираем пропуски паролей
На этапе отладки я заснял процесс перебора паролей. На видео заметно, что некоторые варианты пропускаются.
Только что было касание цифры 2, она ввелась в пароль, и очень быстро перескок на 1. Двойка ещё не погасла. Стоит увеличить паузу между касаниями цифр (меняем с 20 на 50 мс).

На другом кадре видно, что анимация (дёрганье точек) при неверном пароле ещё не закончилась, а уже есть новое касание.

В итоге первая цифра не ввелась в пароль и вместо четырёх цифр ввелось только 3. Следующий вариант пароля начинает вводится в 4-ю цифру предыдущего. То есть пропускается сразу два варианта пароля.
Увеличим задержку до ввода следующего варианта ключа с 200 до 400 мс.
Всё становится замечательно, пропусков больше нет.

Как видно, по видео весьма удобно наблюдать и отлаживать относительно быстрые процессы.
❯ Полезные хитрости
«Программирование» комментариями.
Очень удобно сначала придумать и словесно записать алгоритм в комментарии, а потом по пунктам описывать его кодом. Так и комментарии для кода сразу будут готовы, и можно придумать и описать алгоритм целиком.
Ведь в комментарии можно «напрограммировать» что угодно и компилятор не будет ругаться. Это позволяет сосредоточиться на идее, а не отвлекаться на написание кода именно тем способом, как этого требует язык программирования и как это понравится компилятору.
Иногда в комментарии сразу писался и готовый код, если сразу приходила такая идея. Часто код писался сразу, без комментариев. Когда что удобнее.
Комментарии получаются довольно подробными, что весьма помогает быстро разобраться с кодом, если видишь его впервые (например, читателю статьи), или вспомнить что ж ты такое делал несколько месяцев/лет назад в своём проекте.
Пример.

Отладка работы с СОМ портом в приложении.
Работу приложения с СОМ портом удобно отлаживать через терминал. Мне нравится Terminal v1.93b.

А создать виртуальное соединение между приложением и терминалом поможет программка com0com. Создаём пару виртуальных портов, соединённых между собой. Подключаем к одному приложение, ко второму терминал и отлаживаем.
Весьма удобно, сразу видно что передаётся и почему не работает.
Макросы в терминале.
Для удобства отправки сложных конструкций можно сделать макросы. Знак $ нужен для записи числа в 16-ричном виде. Потом просто нажимаем М1, М2, М3 (в примере на картинке разблокировать, смахнуть заставку, подобрать пароли, начиная с 2141).

❯ Заставляем строки приниматься целиком, а не кусками
Микроконтроллер отправляет через UART-USB преобразователь данные в виртуальный COM порт строки вида Command received. В коде микроконтроллера это выглядит так:
uint8_t str[] = «Command received\0»;
HAL_UART_Transmit(&huart1, str, 16, 30);
Приложение на PyQt читает строки так:
rx_data = self.serial.readLine()
rx_str = str(rx_data, 'utf-8')# Переводим массив байт в строку
#Помещаем строку в поле для вывода текста
self.plainTextEditLog.appendPlainText(rx_str)
Проблема в том, что строки читаются кусками (частями). Причём части время от времени меняются. Пример вывода в текстовое поле:
C
ommand recei
ved
E
nab
led
C
ommand r
eceived
C
ommand
received
Задав вопрос на QnA, я получил ответ от уважаемого Александра (NeiroNx):
«Они отображаются по мере приема данных и опроса буфера приема (аппаратного).
Само собой опрос буфера приема не синхронизирован с отправкой данных.
Он опрашивается настолько часто что там только всегда куски передачи.
Принимайте в свой буфер, накапливайте и обрабатывайте по приему символа конца строки("\r\n")».
Спасибо вам, добрый человек!
Проблема ясна, решение примерно понятно. Так и сделаем.
Объявляем в конструкторе окна строку — свой буфер. Накапливаем её до прихода символа конца строки, потом разделяем строку на этом символе и помещаем в список. Затем, если конечный элемент списка – пустая строка, то выводим все элементы списка кроме последнего в своё текстовое поле.
#Буфер для накапливания строк из ком порта.
self.serialStrbuffer = ""
def onRead(self):
rx_data = self.serial.readAll()#Считываем куски строк из буфера
rx_str = str(rx_data, 'utf-8')# Переводим массив байт в строку
#Соединяем куски в цельные строки в нашем буфере. Разделитель "", т е без разделителя.
self.serialStrbuffer = "".join([self.serialStrbuffer, rx_str])
#print(self.serialStrbuffer)
# Разделяем строки на подстроки на символе конца строки
tempList = self.serialStrbuffer.split("\r\n")
#print(tempList)
#Как только приходит кусок строки, содержащий символ конца строки \r\n,
# в списке становится на одну строку больше. Значит в первом элементе списка целая строка.
#Иногда прилетает сразу 2 целых строки. Поэтому сделаем цикл. Последний элемент списка
# содержит символ конца строки.
length = len(tempList)
if length>1 and tempList[length-1] == '':# Если строка не одна и последняя пустая
#for string in tempList:
#self.plainTextEditLog.appendPlainText(string)
#Выводим все строки из списка, кроме последней.
for i in range(0,length-1,1):
self.plainTextEditLog.appendPlainText(tempList[i])
self.serialStrbuffer = ""#очищаем буфер
# очищаем список, (он и так очистится, т к очистили буфер)
При отладке удобно посмотреть значения переменных в терминале (код писался в VS Code):

❯ Что можно ещё доработать?
Многое можно оптимизировать и сделать по-другому. Основные и самые интересные задачи проекта (сделать обратную разработку тачскрина, создать имитатор касаний и взломать пароль) решены. Второстепенное хотелось сделать проще и быстрее.
Основные доработки:
- Текущая версия прошивки имитатора работает без прерываний, использует приём данных по интерфейсам в блокирующем режиме (на циклах). Каких-то защит и проверок не предусмотрено. Поэтому если делать определённые действия (например, отправлять команды имитатору) не в логическом задуманном по порядке, то программа зависнет.
- Для простоты и скорости написания прошивки использовалась библиотека HAL. При оптимизации можно заменить её на LL или CMSIS.
- Можно продумать и улучшить протокол обмена (добавить контрольную сумму, тщательнее продумать структуру посылок и т. д.). Пример.
- Доработать компьютерное приложение. Интерфейс, логику работы и т. д. При желании можно делать касания в любой точке экрана, а не только цифр пароля.
- Убрать UART преобразователь и сделать подключение микроконтроллера напрямую по USB.
- И др.
❯ Коротко о главном
Если просто настроить STM32 в Кубе, но не принимать ни одного байта по I2C, то он не сформирует ACK.
В смартфоне часто встроено несколько драйверов для разных моделей одного и того же модуля (экрана, тачскрина и т. д.).
На PyQt можно быстро и просто написать приложение для проверки каких-то идей. В Qt Designer получаются симпатичные интерфейсы. Есть 2 варианта использования созданного интерфейса в коде приложения.
Проект развивался постепенно. После обратной разработки тачскрина мы начали проектировать имитатор касаний. Железную часть, затем программную. Сначала удалось смахнуть заставку и передать касания нужных цифр процессору смартфона, а позже взломать пароль автоматическим перебором.
Для отладки процессов бывает весьма удобно заснять их на видео.
Часто удобно использовать пару виртуальных COM портов, соединённых друг с другом (например, через программку com0com).
В терминале можно записывать макросы для сложных посылок и отправлять их одной кнопкой.
Идею можно сперва записать-«запрограммировать» комментариями, а потом кодом. В итоге останутся подробные комментарии, весьма помогающие быстро и просто разобраться с кодом впервые или по прошествии длительного времени.
Чтобы строки из COM порта в приложении принимались не кусками, можно накапливать их в своём буфере и разделять по символу конца строки.
❯ Конец.)
Мне этот проект очень понравился, сделал его залпом. Удалось попробовать много интересного, проверить предположения, получить новые навыки и сделать забавные вещи.
При желании можно делать касания не только цифр, но и в любой точке. Кликая мышкой по картинке.
Поставленные цели достигнуты, всё основное работает, а впереди

Программист наелся и спит, а Кряку с монтажником уже не терпится сделать что-нибудь интересное. Поэтому пойдём дальше.
Понравился проект, статья – поделитесь впечатлением!
Не стесняйтесь писать комментарии, интересно ваше мнение :)
Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud — в нашем Telegram-канале ↩

Перед оплатой в разделе «Бонусы и промокоды» в панели управления активируйте промокод и получите кэшбэк на баланс.