Skip to content
KitploitKITPLOIT
도구익스플로잇블로그
Log in
제출
도구익스플로잇블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CMF-Watch-Pro-2-BLE-Protocol — CMF Watch Pro 2용 역설계된 BLE 프로토콜로, GATT 레이아웃, AES-128-CBC 암호화 명령 프레임, 인증 핸드셰이크 및 대체 컴패니언 앱 개발을 위한 건강 데이터 동기화를 문서화합니다. | Kitploit
도구/GitHubGitHub/joshuapassos/cmf-watch-pro-2-ble-protocol
Embedded Systems SecurityBluetooth SecurityIoT SecurityReverse EngineeringWireless SecurityCryptographyMobile SecurityHardware & IoT SecurityFirmware Analysis

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유
GitHubjoshuapassos/cmf-watch-pro-2-ble-protocol

CMF-Watch-Pro-2-BLE-Protocol

CMF Watch Pro 2용 역설계된 BLE 프로토콜로, GATT 레이아웃, AES-128-CBC 암호화 명령 프레임, 인증 핸드셰이크 및 대체 컴패니언 앱 개발을 위한 건강 데이터 동기화를 문서화합니다.

저장소 보기웹사이트
3422개월 전아직 검토되지 않음

CMF Watch Pro 2 — BLE 프로토콜 (리버스 엔지니어링)

비공식. 이 문서는 CMF Watch Pro 2 (CMF by Nothing)의 Bluetooth Low Energy (BLE) 프로토콜을 대체 컴패니언 앱을 위해 리버스 엔지니어링으로 재구성한 것입니다. Nothing/CMF와 제휴하거나 보증하지 않습니다. 사용에 따른 책임은 본인에게 있습니다.

프레임 헤더와 opcode의 모든 멀티바이트 정수는 빅엔디언입니다. 명령 페이로드 내부의 정수는 별도로 명시되지 않는 한 리틀엔디언입니다(이는 기기 펌웨어를 반영함) — 예외(GOALS_SET, GPS_PUSH, 대량 전송 오프셋/길이)에 주의하세요.

신뢰도 표시

아래의 모든 명확하지 않은 주장에는 확립 방법이 태그로 표시됩니다:

  • ✅ 기기에서 검증됨 — 복호화된 실시간 캡처에서 관찰되었거나 실제 시계에 대해 테스트됨.
  • 🔎 펌웨어 / APK RE에서 확인 — 펌웨어(1.0.0.73) 또는 공식 APK(3.5.7)를 디컴파일하여 추출됨; 코드와 일치하지만 런타임 테스트는 아님.
  • 🟡 부분적으로 입증됨 — 구조적으로 확립되었지만(오프라인, 코퍼스 전체 또는 RE를 통해) 나머지 단계는 시계가 필요하며 아직 실행되지 않음.
  • ⚠️ [불확실] — 추론된 것이며 확인되지 않음; 틀릴 수 있음.

이후 섹션이 이전 섹션을 수정하는 경우, 이전 텍스트는 삭제하지 않고 포인터와 함께 유지됩니다 — 시도되고 반증된 판독값을 아는 것은 다음 사람이 같은 우회로를 피하게 해줍니다.

모든 캡처의 테스트 기기: CMF Watch Pro 2-5485, 펌웨어 1.0.0.73, 일련번호 CI04102520008192, MCU Actions ATS3089C (Cortex-M4), 화면 466×360.


1. GATT 레이아웃

휴대폰이 GATT 클라이언트이고, 시계가 주변 기기로 CMF Watch Pro 2-XXXX(4자리 16진수)로 광고합니다.

용도서비스특성속성
명령 쓰기0000fff0-0000-1000-8000-00805f9b34fb0000fff2-…Write
명령 알림0000fff0-…0000fff1-…Notify
셸 쓰기 (AT)—77d4ff01-2fe2-2334-0d35-9ccd078f529cWrite
셸 알림 (AT)—77d4ff02-…Notify
대량 데이터 쓰기—02f00000-0000-0000-0000-00000000ffe1Write
대량 데이터 알림—02f00000-…ffe2Notify

각 CCCD(00002902-…)에 01 00을 써서 알림을 활성화합니다. 명령 채널(fff1/fff2)은 아래의 프레임 프로토콜을 전달합니다. 셸 채널(77d4…)은 일반 AT 스타일 텍스트(예: AT GETSECRET; §14 참조)를 전달합니다. 데이터 채널(02f0…)은 명령 채널의 제어 opcode로 조정되는 대용량 바이너리 블롭(워치페이스, 펌웨어, AGPS)을 전달합니다.

서비스 UUID — 시계는 약 10개의 기본 서비스를 광고합니다. 실제 기기에서 열거됨: 0xfff0 (명령), 0x180f (배터리), 0x180a (기기 정보), 0xefe7, 0xffd0, 02f00000-…ffe0 및 02f00000-…fe00 (데이터), 77d4e67c-2fe2-2334-0d35-9ccd078f529c (셸 / 페어링), e49a3001-f69a-11e8-8eb2-f2801f1b9fd1, f48a23c0-f69a-11e8-8eb2-f2801f1b9fd1.

⚠️ 셸 서비스 UUID는 77d4e67c-… 이며 77d4ff00-…이 아닙니다. 이 문서의 이전 개정판에서는 서비스가 특성(77d4ff01/77d4ff02, §14)의 ff00 접두사를 공유한다고 가정했지만 실제로는 그렇지 않습니다(적어도 확인된 기기에서는 그렇지 않음 — freethinkel/fmc의 발견, §출처 참조). 특성 UUID는 변경되지 않습니다. 77d4e67c가 기기 간에 안정적인지 확인되지 않았습니다 — 하드코딩보다는 열거하세요.

🌐 Web Bluetooth 참고. Chromium은 필터링되지 않은 getPrimaryServices() 호출에서도 페이지가 optionalServices에 나열한 서비스만 발견합니다 — 3개의 서비스를 나열한 페이지는 3개만 보는 반면, chrome://bluetooth-internals(Chrome 자체 C++ 레이어, 범위 제한 없음)는 10개 모두를 표시합니다. 브라우저 클라이언트를 작성하는 경우 위의 모든 UUID를 미리 나열해야 합니다. 그렇지 않으면 분명히 존재하는 서비스로 페어링이 실패합니다. Firefox/Safari에는 Web Bluetooth가 없습니다. 사용자 제스처 + HTTPS/localhost가 필요합니다.

✅ 전체 실제 세션이 단일 명령 채널에서 실행되었습니다 — 160초의 과도한 사용 캡처 동안 명시적인 OTA/워치페이스 전송 중을 제외하고 데이터/펌웨어 또는 셸 채널에서 트래픽이 없었습니다.


2. 프레임 형식 (0xF5)

모든 명령 채널 메시지는 하나 이상의 11바이트 헤더 프레임으로 감싸집니다:``` +------+-----------+--------+-------------+-------------+--------+-------------------+ | 0xF5 | chunkLen | cmd1 | chunkCount | chunkIndex | cmd2 | chunk bytes … | | 1 B | 2 B (BE) | 2 B BE | 2 B BE | 2 B BE | 2 B BE | chunkLen bytes | +------+-----------+--------+-------------+-------------+--------+-------------------+ __________________________ 11-byte header ____________________________/

- `cmd1`/`cmd2`는 함께 **opcode**를 구성합니다(§6 참조). 🔎 공식 앱의 프레임 빌더(`C6117b.m30831g`)에서 확인됨.
- `chunkCount` = 이 명령의 총 청크 수; `chunkIndex`는 **1부터 시작**합니다.
- `chunkLen` = 이 프레임에서 `chunk`의 바이트 수.
- 단일 BLE 쓰기는 링크 MTU에 의해 분할될 수 있습니다. 수신기는 원시 바이트를 버퍼링하고 완전한 프레임을 다시 추출합니다. 대용량 페이로드는 여러 청크(동일한 `cmd1/cmd2`, 증가하는 `chunkIndex`)로 분할되어 순서대로 재조립됩니다.

### Opcode 규칙 (✅ 와이어에서 확인됨)

- `cmd1 = 0xFFFF`: `cmd2`가 `0x80xx`/`0x90xx` = 폰→워치(요청/설정); `0x00xx`/`0xa0xx` = 워치→폰(응답). 쌍은 하위 바이트로 매칭됩니다(`0x9055`↔`0xa055`, `0x8051`↔`0x0051`).
- 기능별 `cmd1`: `cmd2` 접미사 = `0x0001` **SET**, `0x0002` **GET**, `0x0003` **ACK**.

### 청크 본문

각 청크의 본문은 `payloadPiece ‖ CRC32_LE(payloadPiece)`(4바이트 CRC, 리틀엔디언, zlib/IEEE)입니다. 명령이 **암호화된** 경우(§3 참조), 전체 `payloadPiece ‖ CRC`가 AES-128-CBC/PKCS7로 암호화되고 해당 암호문이 프레임 `chunk`가 됩니다.

**평문 특이점:** 평문 opcode의 경우 워치가 `chunkLen`에 4바이트 CRC를 *포함하여 계산*하지만 이를 **전송하지는 않습니다**. 따라서 평문 프레임을 디코딩할 때 실제 데이터 길이는 `chunkLen − 4`입니다. (암호화된 프레임은 평소처럼 암호문 내부에 CRC를 포함합니다.)

청크 크기(암호화된 청크가 AES 블록 경계에 맞도록), `maxWrite = mtu − 3`:
- 암호화: `floor((maxWrite − 11) / 16) * 16 − 4 − 1`
- 평문: `maxWrite − 11 − 4 − 1`

✅ 관찰된 모든 암호화 프레임 `chunkLen` 값은 16의 배수였습니다(블록 정렬 유지됨).

---

## 3. 암호화 기본 요소

- **AES-128-CBC** with **PKCS7** 패딩 및 **고정 IV**(펌웨어 `CmfCharacteristic.AES_IV`에서):
  `50 51 52 53 54 55 56 57 60 61 62 63 64 65 66 5A`.
- **CRC32** (zlib/IEEE), 4바이트 리틀엔디언으로 출력.
- **SHA-256** over the concatenation of parts.

키 파생:```
authkey      = SHA256( rnd1 ‖ rnd2 ‖ secret )[0..16]      // persisted across sessions
sessionKey   = SHA256( nonce ‖ authkey )[0..16]           // per connection
  • secret = 16바이트 기기 비밀값(시계에서 셸 명령어 AT GETSECRET → GETSECRET:<32-hex>,OK로 획득 가능).
  • rnd1 = 휴대폰이 선택한 16바이트 난수; rnd2 = 시계에서 온 16바이트 난수.
  • nonce = 시계의 nonce 응답에서 온 바이트.

키가 설정된 후, 모든 명령 채널 프레임은 AES로 암호화되며, §5에 나열된 평문 opcode는 예외입니다.

✅ 두 파생 방식 모두 검증됨: 루팅된 휴대폰의 ntwatch.db에서 복구한 authkey가 캡처된 rnd1/rnd2/secret에서 파생된 값과 일치했고, 캡처된 nonce에서 재현한 sessionKey가 실시간 프레임을 복호화합니다.


4. 인증 / 페어링 핸드셰이크

두 진입 경로는 동일한 nonce/confirm 꼬리 부분을 공유합니다.

4.1 최초 페어링(기기 비밀값 보유)```

phone → (shell) AT GETSECRET watch → (shell) GETSECRET:<32hex>,OK phone: rnd1 = random16 ; signed1 = SHA256(rnd1 ‖ secret) phone → AUTH_PAIR_REQUEST (plaintext) payload = rnd1(16) ‖ signed1(32) // 48 B watch → AUTH_PAIR_REPLY (plaintext) payload = rnd2(16) ‖ signed2(32) // 48 B phone verifies signed2 == SHA256(rnd2 ‖ secret) phone: authkey = SHA256(rnd1 ‖ rnd2 ‖ secret)[0..16] → set crypto key = authkey phone → AUTH_PHONE_NAME (encrypted) payload = 0xA5 ‖ model(UTF-8) // e.g. "CMF Watch Pro 2" watch → AUTH_WATCH_MAC (encrypted) phone → AUTH_NONCE_REQUEST (encrypted) payload = 0xA5 watch → AUTH_NONCE_REPLY (encrypted) payload = nonce phone: sessionKey = SHA256(nonce ‖ authkey)[0..16] → set crypto key = sessionKey phone → AUTHENTICATED_CONFIRM_REQUEST (encrypted) payload = 0xA5 watch → AUTHENTICATED_CONFIRM_REPLY (encrypted) → state = Initialized

`AUTH_FAILED (0xFFFF,0xA061)` 또는 서명 불일치가 발생하면 인증이 실패합니다.

### 4.2 재연결 (authkey 이미 알려진 경우)```
        set crypto key = authkey (persisted)
phone → AUTH_PHONE_NAME      (encrypted)  payload = 0xA5 ‖ model
watch → AUTH_WATCH_MAC       (encrypted)
phone → AUTH_NONCE_REQUEST   (encrypted)  payload = 0xA5
watch → AUTH_NONCE_REPLY     (encrypted)  payload = nonce
        sessionKey = SHA256(nonce ‖ authkey)[0..16]   → set crypto key = sessionKey
phone → AUTHENTICATED_CONFIRM_REQUEST (encrypted)  payload = 0xA5
watch → AUTHENTICATED_CONFIRM_REPLY   (encrypted)  → Initialized

✅ 재연결 순서(셸 트래픽 없음)가 실제 캡처에서 그대로 유지되는 것이 관찰되었습니다.

4.3 인증 후 초기화(2단계)

⚠️→✅ 데이터 조회 전에 TIME이 필수입니다. Initialized 이후, 워치는 세션에서 TIME (FFFF 8004)가 전송될 때까지 BATTERY, SERIAL_NUMBER_GET, 또는 ACTIVITY_FETCH_* 핸드셰이크에 응답하지 않습니다 — 없으면 수신되지 않은 FIRMWARE_VERSION_RET만 도착하고 나머지는 모두 타임아웃됩니다. ✅ 실기기(Pixel 8a)로 확인: TIME 없이 세 개의 GET을 전송 → 펌웨어 응답만 수신; TIME을 먼저 전송 → 배터리 및 시리얼 응답 시작.

권장 2단계 순서: TIME → FIRMWARE_VERSION_GET → SERIAL_NUMBER_GET → BATTERY (0xA5) → 설정 푸시 → 건강 동기화(§8).

4.4 GET → SET 에코 패턴(✅)

대부분의 설정에는 별도의 "읽기" 명령 코드가 없습니다. *_GET(cmd2 = 0x0002, 페이로드 0xA5)을 전송하면 워치가 현재 값을 담은 SET 명령 코드(cmd2 = 0x0001)로 응답합니다. SET 명령은 cmd2 = 0x0003과 빈 본문으로 확인됩니다.


5. 평문 vs 암호화

키가 설정되면 프레임은 AES로 암호화되지만, 다음 명령 코드는 항상 평문으로 전송됩니다:

  • AUTH_PAIR_REQUEST (FFFF 8047), AUTH_PAIR_REPLY (FFFF 0048)
  • DATA_CHUNK_WRITE_WATCHFACE (FFFF 9064), DATA_CHUNK_WRITE_FIRMWARE (FFFF 9042), DATA_CHUNK_WRITE_AGPS (FFFF 905F)
도구 다운로드