Эта история началась с новогоднего подарка. Младшей дочери подарили обучающую игрушку - небольшой граммофон с маленькими картонными пластинками. Ставишь пластинку на проигрыватель, поворачиваешь маленький пластиковый тонарм и игрушка играет песенку или рассказывает сказку на английском языке. О том как заставить её заговорить на других языках и о других её недокументированных возможностях - эта статья. Жесткого реверсинга и хардкорного моддинга не будет, а будет история проб и ошибок со счастливым концом.
Первое знакомство
Комплект состоит из коробки, самого устройства набора картонных пластинок и инструкции. В комплекте 96 готовых пластинок с записями на английском и 8 пластинок DIY. Каждая пластинка кодирует одну запись. При включении DIY пластинки можно записать свой собственный трек с микрофона. На устройстве есть кнопки паузы/воспроизведения, перехода на следующий трек (в тех случаях, когда он есть) и громкости. Устройство имеет встроенный аккумулятор, емкостью 500 MAh, который заряжается через USB Type-C порт.

При первом включении нас ожидал сюрприз. Оказалось, что игрушка разговаривает не на английском, а на китайском языке. Пришлось писать продавцу, а он сделал широкий жест - прислал ещё один проигрыватель с набором пластинок и вернул деньги за предыдущий. На этот раз всё было в порядке, треки были английские. Для эксперимента мы поставили пластинку от предыдущего набора на новый проигрыватель. И внезапно он тоже заговорил на китайском. Так выяснилось, что каждый прибор содержит набор и английских и китайских треков (позже оказалось, что это не совсем так и существуют варианты с набором только английских треков).
Это открытие привело к новому интересному вопросу: может быть есть пластинки, которые не входят в набор, но при этом воспроизводят ранее неизвестные мелодии? Возможно также, что прибор имеет записи не только на английском и китайском, но также и на других языках. Так родилась мысль попробовать изготовить свои собственные пластинки. Помимо исследовательских соображений хотелось обезопасить себя от утери и порчи тех пластинок, что уже были в комплекте. Ведь маленький картонный диск легко потерять или порвать.
“Расшифровываем” пластинки
На лицевой стороне пластинки нарисована картинка и нанесен номер. На рабочей стороне пластинки напечатан штрихкод. На проигрывателе в районе диска есть пара светодиодов. Очевидно это инфракрасный светодиод и фотодиод который реагирует на отраженный свет. Вроде бы можно просто скопировать пластинку и поставить на проигрыватель для проверки. Но такая простая мысль почему-то не пришла мне в голову первой. Вместо этого я решил понять как формируется код и написать программу для генерирования изображений пластинок.

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

Всего секторов кодирующих биты одиннадцать. Это число немного необычно, ведь десять бит выглядело бы куда стандартнее. Понадобилось некоторое время чтобы догадаться, что последний бит может быть вовсе и не битом числа, а контрольной суммой. На всех пластинках число единиц чётное. Если отбросить младший бит, то номера идут по порядку и английская пластинка номер 1 имеет код 587.
EN-1 1001001011(1) = 587 ... EN-41 1001110011(0) = 627 EN-42 1001110100(1) = 628 ... EN-96 1010101010(1) = 682
Английские пластинки пронумерованы с 1 до 96 (так же как и китайские). DIY пластинки тоже идут по номерам начиная с 577 и получается, что всего их должно быть 10 штук, хотя в комплекте только 8:
DIY-1 1001000001(1) = 577 ... DIY-10 1001001000(1) = 586
В наборе китайских пластинок DIY пластинки те же самые, а мелодии начинаются с кода 1 и кончаются 96. Есть огромный пробел в почти 500 кодов между последней китайской пластинкой и DIY-1. А кроме того ещё более 300 штук после последней английской пластики. Это даёт неплохие шансы на то, что есть какие-то неизвестные треки. Но чтобы это выяснить нужно научиться рисовать свои пластинки.
Рисуем пластинки
Итак, код пластинки состоит из 10 бит, плюс 1 контрольный. Ноль кодируется черным сектором в 6 градусов, за которым следует белый сектор в 18 градусов. Единица - это черный сектор в 18 градусов, за которым следует белый сектор в 8 градусов. После старшего бита идет сплошной черный сектор в 24 градуса. Это если считать против часовой стрелки. Теперь и ребёнок нашёл бы сокровища написал бы программу. Первый вариант я набросал на Python, но позже, решил переписать её на JavaScript, чтобы можно было выложить её в виде статической HTML странички. Исходный код можно найти в GitHub репозитории.
Самое интересное в этой программе то, как напечатать диски правильного размера. Картинка имеет такой параметр как размер точки. Выражается он обычно в единицах количества точек на дюйм или dpi (dots per inch). Чтобы корректно нарисовать страницу A4 устанавливаем размер canvas для рисования равным 210x297мм. При этом устанавливаем значения canvas.width и canvas.height в пикселах, а canvas.style.width и canvas.style.height в миллиметрах. В результате получаем картинку с нужным dpi, которая при выводе на печать полностью займет лист A4 и элементы на ней будут правильного размера. Размеры всех элементов нужно задавать в миллиметрах и переводить их в количество точек через умножение на dpi.
<div> <canvas id="a4canvas" class="sheet"></canvas> </div> ... <script> const DPI = 300; function mmToPx(mm) { return Math.round(mm * DPI / 25.4); } const paper_mm = { width: 210, height: 297 }; const canvas = document.getElementById('a4canvas'); canvas.width = mmToPx(paper_mm.width); canvas.height = mmToPx(paper_mm.height); canvas.style.width = paper_mm.width + 'mm'; canvas.style.height = paper_mm.height + 'mm'; const ctx = canvas.getContext('2d'); </script>
Вначале у меня не получалось заполнить A4 без выхода за его границы. Браузер упорно хотел добавить поля к итоговой картинке и она вылезала на следующую страницу. В итоге помогла ручная установка width и height в настройках CSS для печати. Кроме того, некоторые принтеры не умеют печатать на всей поверхности страницы (borderless режим), им нужны поля. Чтобы не запутаться в настройках стилей, поля добавляются на картинку при рисовании, поэтому в CSS поля для печати установлены в 0. Поля рассчитываются так, чтобы выровнять картинку по центру страницы. Это нужно для того, чтобы можно было совместить лицевую и изнаночную часть страницы (front_canvas и back_canvas) после печати. Итоговые стили выглядят так:
<head> ... <style> @media print { @page { size: A4 portrait; margin: 0; /* margins are set by drawing on canvas */ } .sheet { /* make sure the printed canvas is not bigger than A4 */ width: 100% !important; height: 100% !important; } /* control elements should not be printed */ .no-print { display: none; } } </style> </head> <body> ... <canvas id="back_canvas" class="sheet"></canvas> <canvas id="front_canvas" class="sheet"></canvas> ... </body>
Стиль no-print добавлен для того, чтобы скрыть часть страницы, которая содержит элементы управления и не будет печататься.
Печатаем диски
Генератор готов и можно приступать к печати. Для пробы я сгенерировал уже имеющийся диск и несколько выходящих за границы диапазонов: EN-97, DIY-9, DIY-10, CN-97. Распечатал их на своём домашнем струйном принтере, вырезал, поставил на проигрыватель и … ничего не воспроизвелось. Фонограф сказал, что диск не обнаружен. Признаться я был очень обескуражен и вначале подумал, что где-то ошибся с размером секторов, хотя и маловероятно, что датчики реагируют на столь малые расхождения. Для проверки пришлось распечатать полную копию имеющегося диска, но и это не помогло.
Видимо чернила струйного принтера не поглощают как следует ИК лучи и датчик просто не видит черных полосок. На следующий день я распечатал те же диски на лазерном принтере и это сработало! Прибор проиграл диски без ошибок. Позволил записать на DIY-9 и DIY-10 свои треки. А главное - воспроизвел диск CN-97, проиграв абсолютно новый трек. К сожалению, EN-97 не воспроизвелся. Похоже, есть только 96 английских треков, но китайских точно больше, чем 96.
Оставался вопрос сколько всего китайских треков есть. Я раздумывал над тем, как бы это выяснить попроще - не заморачиваясь с печатью кучи дисков. Дочка увидела распечатанный диск и спросила: “Что если я нарисую сектора карандашом - будет ли это работать?”. Действительно, ведь 0 можно превратить в 1 просто расширив чёрный сектор. Она взяла китайский диск с кодом 1 и начала закрашивать биты от младших к старшим. Фонограф нормально читал карандаш и ни разу не сбился. Все треки были на китайском. Когда мы дошли до 511 он сообщил, что пластинка отсутствует. Значит китайских треков как минимум 255. Также выснилось, что контрольная сумма не имеет никакого значения.
На этом я остановился. Младшая дочь крайне ревностно относилась к экспериментам с её игрушкой и каждый раз просила её не трогать. Даже идея появления новых пластинок её не прельщала. Поэтому мне не особо хотелось лезть внутрь устройства. Но пройти немного дальше помог случай.
Лезем под капот
Подруга, которая подарила нам эту игрушку, решила купить такую же себе. Но, как и в прошлый раз, прибор пришел нерабочим. Продавец и тут не ударил в грязь лицом и снова вернул деньги, подарив прибор. Так неисправный фонограф попал ко мне, чтобы посмотреть можно ли что-нибудь сделать. Проигрываль отказывался принимать какие-либо пластинки. Играл что-то в одном случае из десяти. Чтобы выяснить в чем проблема, я его наконец разобрал. Внутри меня ждал новый приятный сюрприз.

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

На небольшой плате распаян разъем для microSD, в котором, закрепленная капелькой клея, находится карточка памяти и она и является хранилищем для всех треков. Кроме того на плате видна антенна, хотя никакого упоминания о Bluetooth в инструкции к устройству или на коробке нет.

На карточке две папки: CARD и JL_REC. В папке CARD обнаружился набор файлов вида <номер>-1.smp и номера в точности соответствовали кодам английских треков. Но сами файлы были непонятного формата. Я погуглил немного, убедившись, что никакой из форматов SMP не соответствует их содержимому. А затем набрёл на Reddit на репозиторий декодировщика файлов SMP для других устройств: smp2mp3.
Стало ясно, что это, скорее всего, такие же зашифрованные MP3. Но компилировать Pascal код или запускать исполняемый файл не хотелось. К тому же было мало надежды на то, что пароль совпадет с одим из опубликованных. Я решил посмотреть на код декодировщика.
function ProcessByte(AByte : Byte; aByteIndex : integer; AEncrypt : boolean) : Byte; ... begin result := AByte; if AEncrypt then lOperations := GlobalAlgorithm.Operations else //if is decrypting, operations must be inverted lOperations := InvertOperations(GlobalAlgorithm.Operations); for i := 0 to Length(lOperations) - 1 do begin if lOperations[i].OperationType = otXOR then begin //XOR lXORKeyLength := length(lOperations[i].XorKey); if lXORKeyLength > 0 then //if length of XOR key = 0, do nothing begin lXORKeyIndex := aByteIndex mod lXORKeyLength; result := result xor lOperations[i].XorKey[lXORKeyIndex]; end; end else if lOperations[i].OperationType = otRotate then begin //Rotate Byte result := lRotateByte(result,lOperations[i].RotateDirection,lOperations[i].RotateCount); end; end; end;
Выше приведено только тело функции для шифрования или дешифрования последовательности байтов. Кроме этого в коде определены вспомогательные операции вроде lRotateByte. Полностью на код функции можно посмотреть здесь.
Алгоритм шифрования в общем случае состоит из последовательности двух инструкций: XOR с заданным ключом и сдвиг. Каждый прибор может иметь различное количество инструкций, содержимое ключа и направление и дистанцию сдвига. Но, судя по содержимому известных ключей, большинство приборов использует просто XOR, а эта операция легко обратима. К тому же заголовок MP3 файла частенько начинается с ID3v2 тэга, то есть с последовательности ID3 за которой идет два байта версии.
Я открыл первый попавшийся MP3, взял первые 4 байта: 0x49443303 и в калькуляторе выполнил операцию исключающего ИЛИ с первыми 4 байтами SMP файла. 0xC1CCBB8B. 0x49443303 ^ 0xC1CCBB8B = 0x88888888 - это и есть ключ. Сразу после этого стало понятно, что можно было бы даже не заморачиваться с калькулятором. Ведь все последовательности нулей в зашифрованном файле (а в заголовке ID3 их хватает) просто демонстрируют ключ в открытом виде. Простенькая утилита для XOR файла с этим ключом, есть в репозитории. Есть также версия на C для тех, кому не лень компилировать. После расшифровки SMP получился полный набор английских MP3 треков.
Оказалось, что этот экземпляр не содержит китайских треков вообще. Так что всё-таки нам повезло с универсальным первым экземпляром. Чтобы в этом убедиться я разобрал свой экземпляр и нашел там полный набор английских и китайских треков. Ключ оказался тем же самым. Выяснилось, что китайских треков 288, а DIY треки хранятся в папке JL_REC прямо в MP3 формате без шифрования. DIY треки имеют имена вида AC69000<цифра>.MP3.
Подключаемся по USB и Bluetooth
На боку проигрывателя есть USB Type-C разъем для зарядки устройства. Я, конечно, пробовал подключать его к компьютеру, но компьютер на устройство никак не реагировал. Тогда я решил, что линии данных USB просто не подведены к микроконтроллеру на плате. Но, разобрав прибор, я убедился, что это не так. Я то был в полной уверенности, что при подключении к USB порту никакого дополнительного питания не нужно. Но по всей видимости контроллер питания устройства выдает питание только на батарею.
Я снова подключил игрушку к USB, но на этот раз включил питание, повернув тонарм. И компьютер обнаружил устройство хранения данных. То есть содержимое SD карточки можно читать и писать просто через USB кабель. Модификация треков оказывается не требует разборки игрушки! Но и это ещё не всё. Кроме устройства хранения данных интерфейс предоставляет доступ к устройствам ввода/вывода звука: можно воспроизводить на нём звук через встроенную звуковую карту и записывать звук с микрофона. Кроме того, клавиши устройства доступны через USB как HID и можно обрабатывать нажатия кнопок.
Оставалось подключиться к встроенному Bluetooth. Устройство никак не хотело обнаруживать себя при включении. На этот раз решение нашлось в интернете: чтобы активировать или выключить Bluetooth нужно зажать на несколько секунд кнопку регулировки громкости. После этого можно использовать устройство в качестве Bluetooth колонки. Довольно странно, но микрофон через Bluetooth не доступен. Хотя, для игрушки это по большому счету и не нужно.
Заключение
Чтобы окончательно убедиться, что всё работает я решил добавить на наше устройство набор из французских песенок. Я скачал десяток треков, пронумеровал их начиная с 700 и записал на устройство. Поставил пластинку и в очередной раз ничего не услышал. Так выяснилось, что устройство не воспринимает номера, которые превышают номер последнего английского трека. Довольно странное ограничение, но что есть, то есть. Тогда сменил диапазон на начинающийся с 300 и всё наконец заработало.
Итак, чтобы добавить на проигрыватель набор своих собственных записей, нужно:
выбрать незанятый диапазон кодов пластинок: от 289 до 575 включительно или переписать ненужные треки;
подготовить набор MP3 файлов;
присвоить файлам имена вида:
<код-пластинки>-1.mp3;зашифровать файлы утилитой smp2mp3 и сменить расширение на
.smp;сгенерировать набор изображений дисков с помощью JavaScript генератора и распечатать их на лазерном принтере;
подключить прибор к USB порту, включить питание и залить треки в папку
CARD.
Кроме того, устройство позволяет:
воспользовавшись генератором, добавить себе в коллекцию почти 200 пластинок на китайском языке, если вы изучаете этот язык;
проапгейдить карту памяти, если вдруг не хватает места;
использовать игрушку как Bluetooth колонку;
использовать игрушку как USB звуковую карту с микрофоном;
скачать все оригинальные треки и слушать их на компьютере;
скачать все DIY треки, записанные на устройстве. И всё это по цене от 1200 рублей за штуку.
Для тех, у кого есть такое устройство, я хотел бы поделиться готовым примером: небольшим набором французских стихов для малышей. Стихи начитаны ведущей Telegram канала о раннем изучении французского языка, нашей хорошей знакомой Дарьей (канал ЛингвоМама Дарья). SMP файлы и PDF файл с изображениями пластинок можно скачать отсюда
В заключение признаюсь, что мой интерес к этому фонографу во многом вызван тем, что несколько лет назад я сам раздумывал над игрушкой, где дети могли бы менять треки с помощью каких-нибудь физических идентификаторов. И вот, китайцы практически буквально воплотили то, что мне тогда пришло в голову. На мой взгляд получилось отличное устройство. Особенно, если принять во внимание все его недокументированные возможности.