Привет, Хабр!
Пришло время реверсить протоколы снова. Apple я делал. Google тоже. Угадайте какая компания следующая?
Зачем вообще
У Microsoft есть отличная штука: Link to Windows (бывший Your Phone / Phone Link). Буфер обмена и прочий cross-device опыт, который на Windows работает красиво. А если у тебя Mac, то, пожалуйста, используй KDE Connect, ClipRelay... you name it. Однако, они требуют еще один клиент на Android, имеют проблемы с автоматическим копипастом, надо копировать вручную. Но почему-то Link to Windows на Samsung (по крайней мере тут) получила заветное разрешение смотреть в буфер без вашего участия. Все равны, но кое-кто равнее. Ну что ж. Раз получили, давайте от этого отталкиваться.
Microsoft не выпускает Phone Link для Mac, ибо вы должны сидеть только на священной Win 11, отправлять телеметрию и использовать Microsoft Account. Впрочем, MSA сам по себе не так уж и плох, у вас же есть уже аккаунты Apple и Google, не так ли?
Так вот. На Mac клиента нет. Android-приложение смотрит на кандидатов и фильтрует примерно так: osName: Windows, clientType: PL. Честный macOS-идентификатор как "линкуемый компьютер" не появляется. Итог: у тебя есть Microsoft-аккаунт, есть Android, есть Mac — а экосистема говорит, что надо бы таки взять бедный Lenovo X250 и реверсить на нем.
Я не хотел поднимать свой sync-сервис, городить еще одну крипт и просить всех ставить мой клиент, ведь уже есть такие штуки. Хотел говорить их (msft) протоколом, через их OAuth, их DCG и их релай.
Поехали?
Что получилось
Link to Pane — нативное приложение на Swift/SwiftUI. Оно прикидывается облачно-видимым Phone Link PC:
clientType:PLDCG-AppId:Microsoft.YourPhone*DCG-OS:Windows(да, cloud-visible identity — иначе телефон тебя не увидит)
Сценарий пользователя простой:
Поставить Link to Windows на Android
Открыть Link to Pane на Mac, залогиниться Microsoft-аккаунтом
Отсканировать QR — как с настоящим Phone Link
Копируешь ↔ вставляешь. Автоматически. Даже если устройства не в одной Wi‑Fi: релай облачный.
Главная боль, ради которой всё затевалось — двусторонний текстовый буфер. И он получился. Это не E2EE, но Microsoft об этом и не говорит. Ваш буфер будет приятен для обучения ИИ, наверное, но это не точно. А так там надо идти достаточно глубоко.

Протокол: луковица страданий
Трафик слоями выглядит примерно так:
HTTPS / WebSocket Azure SignalR framing SignalR MessagePack hub protocol SendMessageAsync / OnReceiveMessage ms-dcg multiplex envelope counted Device Resource Manager envelope protobuf PBValueSet / feature protobuf / JSON
Привязка и управление устройствами — JSON REST к dcg.microsoft.com. Далее все в пайпе (буфер и фичи) — через SignalR relay hub.
Аутентификация — тот же публичный native-client OAuth flow, что у Link to Windows (client id canary-клиента, scope на DCG token service). Дальше: P-384 identity, enroll в Device Auth Proxy, сертификат/токен устройства, QR-пейринг через Device Pairing Proxy — как у Windows Phone Link.
Они сделали хорошо — у каждого свой сертификат и ключ, но не для E2EE, а для подключения к Azure SignalR. В теории никто вас не перехватит (если вы уже установили канал), но что мешало сделать еще и E2EE, Сатья?

Но этим все не закончилось. Как только я добрался до последнего слоя, там началось веселье. Они успели сменить несколько версий протокола. На win10 одно, на 11 другое. На одном Phone Link одно, на другом уже не совсем. Думаю версии 3 сменили точно. Я декомпилил последнюю canary, так что используйте все самое новое.
Документацию по исследованию я выложил открыто в GitHub: msft-phone-link-docs. MIT на доки — пользуйтесь, не благодарите (ладно, можно и поблагодарить).
Буфер обмена: где я реально страдал
Сначала клиент говорил на Windows CDEH clipboard-протоколе: SyncFeatureEnable, requestContent, protobuf через /internal/response… Canary Android аппка смотрит на это и ничего не делает. SyncFeatureEnable игнорируется, на requestStatus ответа нет. Окей, там внутри есть ContentTransfer протокол, куда идут и текст, и, скорее всего, файлы. С этим я разберусь попозже.
Переключился на ContentTransfer — и сразу три удара по яйцам подряд:
NotSubscribedToRoute—/clipboardзарегистрирован на APP-роутере (MessageType=0), а не PLATFORM (MessageType=1). Если шлешь по HAR-дампам винды — получаешь реджект. Тогда я полностью переключился на APK и восстанавливал входящий трафик уже по нему, игнорируя траффик с Windows полностьюJSON vs PBValueSet — APP-хендлеры ждут бинарный
PBValueSet.ValueSet, а не JSON как раньше....Метаданные — большие пакеты разделяются на части, видимо для поддержки до 512МБ файлов, но это уже будет попозже, мы же занимаемся буфером обмена
В итоге. Оба девайса после enroll подключаются к релаю DCG, потом windows-компьютер Mac сообщает о том, что "включаем копипаст" - и полетело - Android клиент начинает сообщать его буфер - прямо в пайпу - а на Mac мы используем доступ к буферу и также кидаем в пайпу релая.
Enable: в
connect/v1выставитьContentTransferCopyPaste: true(отдельного enable-шага нет; у меня это патч фиксированного DRM-слота + правка длин protobuf)Mac → Android:
PCPhoneCopyкак PBValueSet binary на APP →/clipboard,application/x-binaryAndroid → Mac: Rome sync map с
/legacy/phonecontent, scenariocopypaste_metadata_content/CLIPBOARD_CHANGEDНа Mac — запись в pasteboard с echo suppression, чтобы не устроить бесконечный пинг-понг сам с собой
Copied to your connected devices.
Copied from Macbook Pro.
Автоматически. Кра-со-та.

Итоговый Tech Stack:
Swift 6, SwiftUI, SPM-модули: protocol / core / auth / cloud / pairing / clipboard / app
Реверс: APK, JADX, protobuf-схемы из APK, куски Windows CrossDevice, HAR + живые сессии
MSA через WebKit-логин, токены в Keychain, refresh до протухания
Так как я потратил очень много времени на это, решив свою проблему, делюсь решением с вами, то я сделал это как Shareware с WinRAR-style лицензий (сами знаете как это работает:))
Скачать и радоваться. В MAS (Mac App Store) попробую опубликовать, но у нас Apple давным-давно напряженные отношения, посмотрим как пойдет.
Опять же, жду фидбека, feature реквестов, покупки лицензии. Enjoy.
