Skip to content
KitploitKITPLOIT
도구블로그
Log in
제출
도구블로그
제출

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

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

피드문의개인정보© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
Philips-PM-5139-5138A-5136-Firmware-Project — Philips PM5139 / PM5138A / PM5136 함수 발생기의 펌웨어 리버스 엔지니어링: 측정 기기로 사용되는 8051 에뮬레이터, 문서화된 하드웨어 35개 섹션, 그리고 수정된 펌웨어 V2.0 | Kitploit
도구/GitHubGitHub/doctormord/philips-pm-5139-5138a-5136-firmware-project
Embedded Systems SecurityStatic AnalysisDynamic Analysis (Sandboxing)Reverse EngineeringHardware SecurityBinary AnalysisPapers & ResearchLearning & EducationFirmware Analysis

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유
GitHubdoctormord/philips-pm-5139-5138a-5136-firmware-project

Philips-PM-5139-5138A-5136-Firmware-Project

Philips PM5139 / PM5138A / PM5136 함수 발생기의 펌웨어 리버스 엔지니어링: 측정 기기로 사용되는 8051 에뮬레이터, 문서화된 하드웨어 35개 섹션, 그리고 수정된 펌웨어 V2.0

저장소 보기
47421일 전아직 검토되지 않음

Philips PM5139 — 펌웨어 리버스 엔지니어링

1994년경의 20 MHz 함수 발생기를 소프트웨어로 분해한 기록: 두 개의 EPROM 덤프, 측정 도구로 사용한 8051 에뮬레이터, 그리고 모든 주장이 리스트 주소, 에뮬레이터 측정값, 또는 회로도로 뒷받침되는 35개 섹션의 문서.

그 끝에는 Philips가 출하한 결함을 수정한 펌웨어 V2.0, 우리가 직접 만든 여섯 개의 임의 파형, 그리고 원본 ROM을 명령어 단위로 실행하는 브라우저 시뮬레이터가 있다.

V1.3 ROM의 모든 파형 테이블

프로그램 EPROM에 있는 모든 파형 테이블을 바이너리에서 바로 플롯한 것. 오른쪽 아래에 있는 것이 이 프로젝트에서 가장 흥미로운 부분을 시작하게 만든 파형이다.


목차

  • 이것은 무엇인가
  • 한눈에 보는 결과
  • 계측기
  • 방법: 에뮬레이터가 측정 도구다
  • 여기까지 오기까지
  • 좋은 부분들
  • 펌웨어 V2.0 — 새로운 점
  • 이스터 에그
  • 그리고 알고 보니 폴리포닉이었다
  • 우리가 직접 만든 여섯 개의 임의 파형
  • 브라우저 시뮬레이터
  • 저장소 구조
  • 도구 사용법
  • 모든 것을 재현하기
  • 다시 플래싱하기
  • 얼마나 신뢰할 수 있는가?
  • 아직 열려 있는 것들
  • 출처

이것은 무엇인가

Philips PM5139는 세 계측기 제품군(PM5136 / PM5138A / PM5139)의 20 MHz 최상위 모델이다. 내부에는 PCB80C652 — 하드웨어 I²C가 있는 8051 코어 — 와 27512 프로그램 EPROM, 그리고 직렬 버스에 연결된 여섯 개의 아날로그 어셈블리가 들어 있다.

PM5139 서비스 매뉴얼은 없다. 사람들은 2010년부터 포럼에서 이 매뉴얼을 찾아 헤매고 있다. 존재하는 것은 내부적으로 거의 동일한 10 MHz 자매 모델인 PM5138A의 매뉴얼뿐이다.

그래서 이 프로젝트는 반대쪽 끝에서 시작했다: EPROM을 덤프하고, 계측기를 수정할 수 있을 만큼 충분히 이해될 때까지 코드가 무엇을 하는지 파악하는 것이다.

두 가지 펌웨어 버전, V1.3과 V1.5를 구할 수 있었고, 둘 다 64 KiB M27512 덤프였다.


한눈에 보는 결과

디스어셈블리두 버전 모두 완료, 약 23 000줄, 상호 참조 포함
주석이 달린 리스팅이름 붙은 루틴 147개, 헤더 주석 145개, 주석이 달린 줄 3 826개
문서35개 섹션, 4 600줄, 모든 주장에 출처 명시
신호 경로주파수, 진폭, 오프셋, AM, FM, 버스트, 대칭, 스윕 — 모두 계산되고 원본 코드와 대조 검증됨
하드웨어10개 스트로브 전부, C-버스, 모든 참여자가 있는 I²C, 포트, 키보드, 로터리 노브, 디스플레이 비트맵
상태 비트128개 중 75개에 문서화된 효과 존재
버전 차이V1.3 대 V1.5는 구조적으로 91.4 % 동일; 모든 변경 사항 명명됨
에뮬레이터Python 하나, JavaScript 하나(~8 M 명령어/초), 그리고 단일 파일 브라우저 시뮬레이터
우리 펌웨어V2.0 — 공장 결함 수정, 체크섬 처리, 에뮬레이터와 실제 하드웨어에서 검증됨

계측기

위치유형기능
D301PCB80C652하드웨어 I²C가 있는 8051 코어, 12 MHz
D30627512프로그램 EPROM — V1.3은 0000h–AC70h 차지
D310X28C64MOVX 버스의 임의 EEPROM
D305PCF8570I²C(A0h)의 배터리 백업 NVRAM 256바이트
D304-APCF8576I²C(70h)의 LCD 드라이버, 20바이트 버퍼
D302-ASAA3007키보드 인코더, 단일 라인에 펄스 폭으로 코딩됨
D30774HCT4514스트로브 디코더 — 스트로브 번호는 주소 비트 A8…A11

아날로그 쪽은 직렬 C-버스다: 8051의 UART가 시프트 레지스터 모드로 동작하고, TXD가 클록, RXD가 데이터이며, 스트로브가 열 개의 시프트 레지스터 중 어느 것이 바이트를 래치할지 결정한다. MOV DPH,#8nh 다음에 MOVX @DPTR,A를 실행하면 스트로브 n이 발동된다. 이 한 줄이 전체 아날로그 섹션의 열쇠다.


방법: 에뮬레이터가 측정 도구다

이 부분은 여러분 자신의 프로젝트에 훔쳐 쓸 가치가 있다.

44 KB의 8051 바이너리를 눈으로 읽으면 아마 3분의 1 정도까지밖에 갈 수 없다. 그 이후의 모든 것은 원본 코드를 실행하고 무엇이 나오는지 지켜보는 것에서 나왔다:```python

What formula turns the entered amplitude into the byte on the bus?

Don't read the routine. Call it.

c = CPU(rom) for w in test_values: set_amplitude(c, w) c.call(0x0AAC) # the original routine, untouched print(w, c.ram[0x1C]) # the byte that goes out on STR9

입력을 바꾸고, 출력을 읽고, 가설과 대조한다. 이 방식은 주파수, 진폭, 오프셋, AM 깊이, FM 편차, 버스트 횟수, 대칭성, 그리고 두 가지 스윕 특성 모두에 대해 통했다. 문서의 각 수식에는 그것이 검증된 샘플 포인트가 함께 제공된다.

세 가지 개선을 통해 실제로 생산적으로 만들었다:

**디스플레이가 아니라 버스를 보라.** 섹션 15는 상태 비트가 디스플레이 버퍼에 어떤 영향을 미치는지 측정하는데, 128개 비트 중 74개는 아무것도 하지 않는 것으로 보인다. 하지만 그중 많은 것은 디스플레이를 구동하지 않고 *아날로그 어셈블리*를 구동한다 — 그리고 그것들은 C-버스의 텔레그램으로만 보인다. `MOV SBUF,…`와 종료 `MOVX @DPTR`를 기록함으로써 문서화된 비트 수가 54개에서 75개로 늘어났다.

**RAM을 찌르지 말고 키를 누르라.** RAM 바이트를 수동으로 설정하면 계기가 결코 취하지 않는 상태가 만들어진다. 그로 인해 두 개의 잘못된 발견과 명령 테이블로의 한 번의 충돌이 발생했다. 에뮬레이트된 SAA3007을 통해 실제 키 코드를 주입하면 펌웨어가 실제로 도달하는 상태를 얻을 수 있다 — 그리고 256개 키 코드 전체에 대한 무차별 대입 스윕을 통해 어떤 키가 어떤 핸들러를 트리거하는지 밝혀졌다.

**먼저 자신의 에뮬레이터를 의심하라.** 우리 코어의 세 가지 버그가 "설명할 수 없는" 펌웨어 동작을 만들어냈다: `ACALL`이 `AJMP`로 실행됨, 보조 캐리 플래그 누락(`DA A`가 오작동하여 펌웨어가 이진수로 세는 것처럼 보임), 그리고 키보드 인터럽트 중복. 그 시기의 모든 발견은 이후 다시 측정되었다.

---

## 여기까지 온 길

**먼저 정적 분석.** 전체 opcode 테이블을 갖춘 디스어셈블러, 그다음 점프 테이블 휴리스틱을 사용한 재귀적 하강. 그 결과 30,508바이트의 코드가 생성되었고 13,637바이트가 설명되지 않은 채 남았다.

**그다음 동적 분석.** 트레이스 실행 — 콜드 스타트, 23개 전면 패널 키 전부, 노브 양방향, 모든 동작 모드, 8,600만 사이클 — 실제로 실행된 모든 주소를 표시. 정적 분석과 대조했을 때, 하강이 놓친 영역을 정확히 **하나** 발견했고, 설명되지 않은 바이트 중 10,686개는 다섯 개의 알려진 테이블 블록으로 밝혀졌다.

**그다음 회로도.** 서비스 매뉴얼의 OCR은 회로도에 쓸모없지만, 400 dpi의 페이지 이미지는 훌륭하다. 겹치는 타일로 잘라내면 핀 번호까지 읽을 수 있다. 이런 방식으로 여섯 장의 시트를 판독했다 — 그리고 다섯 개의 평행 트레이스가 90픽셀 간격으로 달리는 곳에서는 육안 확인이 비트맵에서 선분을 추출하는 스크립트(`lines.py`)로 대체되었다.

**그다음 뽑아낸 두 개의 칩.** "SINUS 1.1"이라고 표시된 27C64와 X28C64를 판독했다. 둘 다 회로도에 배치하고 그 내용을 디코딩했다.

**그다음 버전 비교.** 두 ROM을 토큰화하고(절대 대상 대신 상대 점프 거리) `SequenceMatcher`를 실행하면 코드 이동에도 견디는 주소 매핑을 얻을 수 있다 — 이것이 V1.3 심볼을 V1.5로 옮기는 방법이다.

---

## 좋은 부분들

### Philips는 잡음이 있는 파형을 출하했다

세 개의 내장 임의 곡선은 `A047h`, `A447h`, `A847h`에 있다. 세 번째 것은 이미 ROM에 계산된 형태로 존재하는 테이블과 같은 모양이다 — 하지만 **방향 전환이 563회 대 13회**이고, 표준 편차가 4.1 LSB이다.

이것은 계산된 것이 아니라 아날로그 소스에서 샘플링되었다. 편차의 평균은 0이고, 1024개 포인트 중 2개만 10 이상 벗어난다 — 이것은 다른 파형이 아니라, 잡음이 실린 *같은* 파형이다.

### 그 테이블은 30 dB 레벨 사다리이다

깨끗한 버전은 이전 초안에서 "10개의 AM 깊이를 가진 사인"으로 설명되었는데, 이는 코드가 말하는 것이 아니라 플롯을 육안으로 읽은 것이었다. 계산해 보면 1024개 포인트는 열 개의 사인 호로 나뉘며 그 범위는```
255  171  120  80  56  38  26  17  12  8

비율 **0.681 = 10^(−1/6)**의 기하급수, 즉 단계당 3.33 dB, 전체 30.1 dB입니다. 반감 모델은 최대 56만큼, 3 dB 모델은 10만큼 어긋납니다. 이는 로그 레벨 사다리 — 진폭 또는 감쇠 테스트 패턴입니다.

STR9는 16비트를 두 개의 1바이트 텔레그램으로 전송한다

진폭 컨트롤러에는 하나의 스트로브에 두 개의 시프트 레지스터가 있지만, 펌웨어는 텔레그램당 한 바이트만 전송합니다. 회로도가 이를 설명합니다: 두 개의 4094가 **QS' (핀 10)**을 통해 캐스케이드되어 있고, 핀 9는 사용되지 않습니다 — 그리고 텔레그램은 쌍으로, 약 42,000 사이클 간격으로, 쌍 사이에는 수백만 사이클의 침묵이 있습니다. 먼저 전송된 바이트가 두 번째 레지스터로 밀려 들어갑니다.

동일한 캐스케이드 패턴이 하나 이상의 시프트 레지스터가 있는 모든 어셈블리에서 나타났습니다 — E라는 라인을 통해 체인이 어셈블리 경계를 넘는 경우도 포함해서요.

감쇠기는 계산이 아니다

STR9 텔레그램의 5비트가 릴레이를 직접 구동합니다: S1은 DC 제너레이터 범위를, S2…S5는 감쇠기 릴레이를 전환합니다. 20 dB (for 40dB), 20 dB, 50/600 ohms — 회로도에 인쇄되어 있습니다. 계산할 임계값은 없습니다.

눈에 띄지 않게 숨어 있는 핸들러

0301h의 점프 테이블은 JMP @A+DPTR로 읽힙니다. 항목 15는 0301h + 30 = 031Fh에 위치합니다 — 그리고 거기에는, 평소의 AJMP 대신, 핸들러 자체가 인라인으로 자리 잡고 있어 점프를 절약합니다. ROM 어디에도 이 핸들러를 가리키는 점프 명령이 없어서, 정적 분석은 이를 놓쳤습니다. 이것은 DIAL LOCK 핸들러이며, 오직 동적 추적만이 이를 찾아냈습니다.

24개가 아닌 6개의 임의 슬롯

데이터 시트는 24개의 파형 메모리를 약속합니다. EEPROM의 디렉터리는 6개라고 말합니다. 산술이 이를 확정합니다:``` 1024 points × 10 bit, packed 4 values per 5 bytes -> 1280 bytes per curve 6 × 1280 = 7 680 bytes, 0100h…1EFFh (X28C64, 8 KB) <- what was fitted 24 × 1280 = 30 720 bytes, 0100h…78FFh (X28C256, 32 KB) <- what the schematic says

펌웨어의 측정된 판독 범위는 `0100h–1EFFh`이며, 이는 바이트 단위로 정확히 6개의 곡선에 해당한다. 이 기기는 소형 칩으로 제작되었다.

### 존재하지 않는 장치와 대화하는 죽은 코드

`9AFFh`의 186바이트는 주소 `5Ah`로 I²C 트래픽을 수행하는데, 이 주소는 다른 어디에도 나타나지 않는다. **두** 펌웨어 버전 모두에서 어떤 점프도 이곳을 가리키지 않는다. 이는 `5Eh`의 인터페이스 카드와 동일한 장치 유형 블록에 위치하지만 뱅크 비트만 다르며, 수신 버퍼와 산술 레지스터를 두 개의 10바이트 텔레그램으로 전송한다. 이는 출시되지 않은 장치를 위한 공장 진단으로 보인다.

### 임의 EEPROM에서 코드를 실행할 수 없다

임의 파형 슬롯에 코드를 넣고 그곳으로 점프한다는 명백한 아이디어는 처음부터 불가능하다. 8051은 하버드 구조이다. 명령어는 프로그램 EPROM에서 `/PSEN`을 통해 들어오고, 데이터는 임의 EEPROM에서 `/RD`를 통해 들어온다. 차단된 것이 아니라, 배선 자체가 존재하지 않는다.

### 그리고 마지막으로 주파수 인코딩

디스플레이 숫자 행은 PCF8576으로 전송되는 이미지의 `3Eh–43h`에 위치하며, 모든 위치가 하나의 세그먼트 인코딩을 공유하고, 바이트 `43h`는 10년 7과 8 사이에서 kHz에서 MHz로 전환된다. 이로부터:```
f = M · 10^(D−8) kHz

실제 계측기에서 측정한 세 개의 주파수 스테핑 시퀀스가 이것에 의해 정확히 재현된다 — 가수 2500이 25 MHz를 의미하게 되어 한계를 초과하므로 조기에 중단되는 것까지 포함해서.


펌웨어 V2.0 — 새로운 점

임의 곡선 3: V1.5 대 V2.0

왼쪽은 출하된 곡선, 오른쪽은 수정된 곡선. 왼쪽 아래는 계산된 테이블과의 편차 — 그 ±5 LSB의 띠는 샘플링된 아날로그 소스가 남긴 흔적이다.

mkv20.py는 V1.5(또는 V1.3)로부터 V2.0을 빌드한다. 주소를 하드코딩하는 대신 시그니처로 모든 주소를 찾아내므로, 동일한 스크립트가 두 소스 버전 모두에서 작동한다:

도구 다운로드