80386 마이크로코드 역어셈블됨
4 days ago
7
- 80386 마이크로코드 ROM은 94,720비트로 8086의 10,752비트보다 훨씬 커서 이미지 변환과 검증이 까다로웠음
- 고해상도 다이 이미지에서 이미지 처리·신경망·사람 보조 자동화를 조합해 며칠 만에 바이너리 블롭을 추출하고 교차 검증함
- 역어셈블 과정에서 μ-op 배열, 비트 필드, 명령 종료 패턴, 명령 디코더와 보호 테스트 PLA 구조가 점진적으로 드러남
- 80386은 모든 명령에 대응하는 마이크로코드가 있으며, 많은 루틴은 알고리듬보다 곱셈·나눗셈 하드웨어와 배럴 시프터 설정을 담당함
- 보호 모드의 IO 권한 비트맵 처리에서 4바이트 포트 접근 시 처음 3개 주소만 검사하는 듯한 잠재 결함이 발견됐지만 아직 확정되지는 않음
80386 마이크로코드 추출과 역어셈블
- 8086 마이크로코드 역어셈블 이후 Ken Shirriff가 80386의 마이크로코드 ROM 고해상도 이미지를 제공했지만, 80386 ROM은 94,720비트로 8086의 10,752비트보다 훨씬 커 변환과 검증이 훨씬 어려웠음
- 8086에는 전체 구조와 일부 코드 조각을 담은 특허가 있어 검색 단서가 있었지만, 80386은 완전한 블랙박스에 가까워 큰 바이너리 덩어리에서 구조를 찾기 어려웠음
- Discord에서 GloriousCow와 Smartest Blob 등이 80386 다이의 고해상도 이미지에서 마이크로코드를 추출하는 작업을 이어갔고, 이미지→바이너리→이해 가능한 마이크로코드로 바꾸는 과정이 핵심 난관이었음
- 이미지 처리, 신경망, 사람 보조 자동화를 조합해 며칠 만에 이미지에서 바이너리 블롭을 추출하고 교차 검증함
역어셈블 과정에서 드러난 구조
- 바이너리 추출 뒤에도 역어셈블은 쉽지 않았고, 여러 패턴을 맞춰 가며 한 축에는 μ-op, 다른 축에는 μ-op 비트가 오도록 재배열하는 방식을 파악함
- 사용되지 않은 μ-op 블록이 한쪽 끝에 있어 μ-op을 읽는 순서를 파악하는 단서가 됨
- μ-op 비트를 여러 필드로 나누는 과정에서는 8086 마이크로코드 작업 경험을 바탕으로 소스 레지스터와 목적지 레지스터 필드를 찾아감
- 80386은 ALU 연산을 2사이클에 수행할 수 있으므로, 첫 번째 사이클에서 두 피연산자를 ALU에 적재하고 두 번째 사이클에서 결과를 목적지로 보내려면 ALU의 두 번째 입력을 지정하는 필드가 필요했음
- 반복적으로 나타나는 패턴은 명령의 끝을 나타내는 것으로 추정됐고, 이후 실제로 맞는 것으로 확인됨
- Ken은 80386 다이의 여러 선과 논리 비트를 추적해 내부 연결 방식을 파악하는 데 기여했고, 새로 밝혀진 구조는 같은 구성물을 쓰는 다른 마이크로코드 조각을 해석하는 단서가 됨
- 마이크로코드와 함께 여러 작은 PLA로 구성된 명령 디코더와 보호 테스트 PLA도 해독되면서 386 명령을 마이크로코드 조각과 연결할 수 있게 됨
8086과 다른 80386의 실행 방식
- 80386은 대부분의 명령에서 8086보다 사이클당 훨씬 빠르며, 이를 위해 더 많은 트랜지스터를 사용했음
- 8086에서 마이크로코드로 구현되던 여러 알고리듬은 80386에서 사실상 하드웨어 가속기가 맡음
- 80386 마이크로코드의 상당 부분은 알고리듬 자체보다 곱셈·나눗셈 하드웨어, 배럴 시프터, 보호 테스트 유닛 같은 가속기를 설정하는 역할을 함
- 역어셈블 작업의 큰 비중은 이러한 가속기 인터페이스와 마이크로코드 사이의 연결 방식을 파악하는 데 있었음
마이크로코드 엔트리와 명령 처리
- 디코딩 ROM에서 진입하는 마이크로코드 엔트리 포인트는 215개로, 8086의 60개보다 크게 늘었음
- 엔트리 증가는 새 명령 추가뿐 아니라 피연산자가 레지스터인지 메모리인지, CPU가 실모드인지 보호 모드인지, REP 접두사가 사용 중인지에 따라 같은 명령도 서로 다른 루틴으로 처리되기 때문임
- 모든 엔트리 목록은 fields.txt 파일에 있으며, 서브루틴과 공유 코드도 함께 들어 있음
- 많은 상위 마이크로코드 루틴은 적은 작업만 수행한 뒤 다른 엔트리 포인트와 공유하는 루틴으로 점프하므로, 상위 루틴의 크기만으로는 의미를 파악하기 어려움
- 명령 디코더가 사용할 루틴을 정할 때 opcode만 쓰지 않기 때문에, 각 엔트리 포인트가 처리하는 opcode 개수만으로도 구조를 설명하기 어려움
모든 명령은 마이크로코드로 처리됨
- 80386은 8086이나 현대 CPU와 달리 항상 μ-op을 실행하며, 모든 명령에 대응하는 마이크로코드가 있음
- 마이크로코드로 처리되지 않는 명령은 없는 것으로 확인됨
사용되지 않는 코드와 예외 처리 흔적
- 0x849부터 0x856까지의 루틴은 마이크로코드 역어셈블에서 unused?로 표시되어 있으며, 연결된 엔트리 포인트가 없는 것으로 보임
- 이 루틴의 정확한 동작은 완전히 확실하지 않지만, 0x8e9부터 0x8f5까지의 #PF(PAGE_FAULT) 루틴과 공통점이 많음
- 두 루틴 모두 paging unit의 마지막 오류 코드를 설정한 뒤 interrupt 0x0e로 이어짐
- 차이는 이 루틴이 fault linear address 대신 paging unit에서 나온 알 수 없는 값을 CR2에 설정한다는 점임
- 나머지 마이크로코드는 CPU의 문서화된 동작을 구현하도록 설계된 것으로 보이며, 저수준 디버깅용 ICE(In-Circuit Emulator) 하드웨어와 상호작용하는 루틴은 문서화되지 않은 동작에 해당함
숨겨진 기능과 가능한 IO 권한 비트맵 결함
- 아직 실제 386 머신에서 시험하지 못해 확정할 수는 없지만, 보호 모드 OS 일부가 사용자 모드 프로세스에 IO 포트 접근을 제한적으로 허용할 때 쓰던 IO 권한 비트맵 처리에 결함 가능성이 있음
- 4바이트 포트 접근이 발생할 때 마이크로코드는 처음 3개 주소의 권한 비트만 검사하는 것처럼 보임
- 프로세스가 권한을 가진 IO 포트 공간의 경계에서 이런 접근을 수행하면, 마지막 바이트 접근이 잘못 성공해 OS가 사용자 접근을 의도하지 않은 하드웨어 레지스터에 닿을 가능성이 있음
- 이 버그는 매우 특이한 경우라 마이크로코드 역어셈블 없이는 놓쳤을 수 있지만, 40년 넘게 널리 쓰인 하드웨어의 보안 버그가 발견되지 않았다는 점은 이례적임
- 해당 동작은 일부 CPU 버전에만 있었을 수도 있고, 루틴 해석이 잘못되어 실제로는 올바르게 동작할 수도 있음
- 이 마이크로코드는 80386 초기 버전의 것으로 보이지 않으며, XBTS와 IBTS 명령은 디코더를 제외하면 흔적이 없음
학습 자료와 다운로드 위치
-
Homepage
-
개발자
- 80386 마이크로코드 역어셈블됨