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

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

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

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
am335xbootrom — TI AM3358 부트 ROM 리버스 엔지니어링 | Kitploit
도구/GitHubGitHub/sjgallagher2/am335xbootrom
Embedded Systems SecurityReverse EngineeringDebuggersHardware HackingBinary AnalysisLearning & EducationFirmware Analysis
GitHubsjgallagher2/am335xbootrom

am335xbootrom

TI AM3358 부트 ROM 리버스 엔지니어링

저장소 보기
615182년 전Kitploit 검토 완료

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

AM335x 부트 ROM 리버스 엔지니어링

이 보드들(Beaglebone Black)을 처음 손에 넣은 지 18개월쯤 된 것 같다. 쓰레기통에서 구해낸 보드였다. 불행히도 보드들은 처음부터 제대로 작동하지 않았다. 사실 이 보드를 다루는 것도, 아니면 어떤 싱글 보드 컴퓨터든 처음이라서, 문제가 내가 뭔가 잘못하고 있는 건지, 아니면 보드 자체에 문제가 있는 건지(어쩌면 그것이 애초에 쓰레기통으로 향했던 이유일지도) 확신할 수 없었다. 이 보드들이 실제로 부팅을 시작하게 만드는 데 꽤 오랜 시간과 많은 노력이 걸렸지만, 그래도 해냈다. 그리고 그 과정에서 배운 것들을 여기에 정리한다.

참고: Ghidra XML 파일 사용 방법

이 저장소에는 지금까지 리버스 엔지니어링으로 얻은 모든 심볼이 담긴 Ghidra에서 내보낸 xml 파일과 함께 몇 가지 유틸리티를 포함했다. 저작권 문제를 피하기 위해 실제 펌웨어 없이 내보내는 데 이 글을 사용했다. 부팅 ROM을 직접 디버그하고 싶다면 이미 JTAG를 연결했을 테니, 부팅 ROM(0x20000부터 0x2BFFF까지)을 직접 덤프할 수 있다.

심볼을 로드하려면:

  1. 새 Ghidra 프로젝트를 만든다. Ghidra에 바이너리(XML 아님)를 임포트한다: ARMv7 Little Endian을 사용하고, Options에서 기본 주소를 0x20000으로 설정하고 블록 이름을 bootrom으로 설정하면 된다.
  2. CodeBrowser에서 이 바이너리를 연다. 분석하지 마라.
  3. File > Add program으로 이동하여 XML 파일을 선택한다. 기본값으로 충분하다. 이제 리셋 핸들러를 살펴보거나 main() 또는 MMC/SD 카드 부트 핸들러로 이동할 수 있다.

문제

우선, 이 보드들이 표준 Beaglebone Black의 커스텀 버전이라는 것을 알고 있었기 때문에 초기에 보드 자체에 보드 식별자 같은 뭔가가 빠져 있는 것일 수도 있다고 판단했다. balenaEtcher로 포맷된 표준 SD 카드로 부팅했을 때 보이는 것은 아무것도 없었다. 보드의 LED가 깜빡이기 시작할 것으로 예상했고, UART-USB 케이블을 연결하면 U-Boot 프로세스를 볼 수 있을 것으로 예상했다. 그러나 UART는 조용했다. SD 카드를 빼면 UART/시리얼 부팅의 예상된 동작인 문자 C가 반복해서 출력되었다. 분명히 부팅을 시도하고 있었고 SD 카드가 이 동작을 바꾸고 있었지만, 더 이상의 가시성은 없었다. 웹상의 대부분의 문제 해결 방법은 U-Boot 출력을 진단의 출발점으로 삼았다. 나는 그런 사치를 누릴 수 없을 것 같았다.

이 시점에서 디버그 프로브를 연결하는 것이 가치가 있겠다고 판단했다. 불행히도 기존 풋프린트에 맞는 헤더가 없어서 직접 만들었다.

Beaglebone 보드에는 JTAG 연결을 노출하는 P2라는 지정자를 가진 헤더가 있다. 여기에 전선을 연결해 암컷 헤더에 연결해서 J-Link를 통해 통신할 수 있게 했다.

Ozone(Segger 디버거)을 실행하면서 J-Link를 구성하고 엔트리 포인트를 찾는 것부터 시작했다. 리셋-홀트(reset-halt)가 나를 필요한 위치로 데려다줄 것이라고 생각했고, 그 결과 엔트리 포인트가 0x2148a라는 (잘못된) 추정에 이르렀다. 물론 이 값이 일관되지 않다는 점은 확실히 알아차렸다. 나중에 AM335x 보드들은 J-Link의 리셋-홀트와 잘 맞지 않는다는 것을 깨달았다. 실제로 수백 클럭 사이클 정도의 지연이 있어서 부트 핸들러 내부 어딘가에 비결정적으로 도달하게 되는 것이다. (결국 J-Link 디버깅을 지원하는 TI의 Code Composer Studio용 GEL 파일을 작성하여 이 문제를 우회했다. 리셋 시 PC 레지스터가 리셋 핸들러로 설정되고, 레지스터가 클리어되며, 명령어 모드가 ARM으로 강제된다.)

TI 포럼의 스레드(AM335x: TI employees, where can I get the ROM Bootloader source code/symbols?)에서 몇 가지 디버깅 심볼을 얻었다: 0x231e0의 SPI Initialize, 0x23230의 SPI ReadSectors, 그리고 0x24bfa는 UART 읽기를 수행하는 루틴이다. 꽤 도움이 되는 정보였다. 부팅이 0x402f0440의 무한 루프, 즉 죽은 루프에 빠지면서 실패한다는 것을 알아차렸다. 음, 나머지 부팅 ROM과는 꽤 거리가 있는데, RAM 어딘가에 있는 게 분명하다. 이제 기술 참조 매뉴얼(TRM)을 살펴볼 때가 된 것 같다!

TRM 26장에는 부팅에 관한 엄청난 양의 정보가 들어 있다. 다음은 부팅 ROM의 구조를 보여준다:

설명:

Public ROM Code의 아키텍처는 그림 26-1에 나와 있다. 하향식(top-down) 방식으로 high-level 계층, 드라이버 계층, 하드웨어 추상화 계층(HAL)의 세 가지 주요 계층으로 나뉜다. 한 계층은 통합 인터페이스를 통해 하위 계층과 통신한다. high-level 계층은 Public ROM Code의 주요 작업, 즉 워치독 및 클록 구성과 메인 부팅 루틴을 담당한다. 드라이버 계층은 인터페이스 사양에 따라 모든 부팅 장치에 대한 논리 및 통신 프로토콜을 구현한다. 마지막으로 HAL은 하드웨어 인프라 IP와 상호 작용하기 위한 최하위 수준의 코드를 구현한다. 최종 부팅 장치는 디바이스 I/O 패드에 연결된다.

그림 26-2는 Public ROM Code 부팅 절차의 높은 수준의 흐름을 보여준다. 이 디바이스에서 Public ROM Code는 보안 시작(Secure ROM Code에 의해 수행됨)이 완료되면 시작된다. 그런 다음 ROM Code는 공용 시작 절차의 일부로 플랫폼 구성과 초기화를 수행한다. 부팅 장치 목록은 SYSBOOT 핀을 기반으로 생성된다. 부팅 장치는 메모리 부팅 장치(납땜된 플래시 메모리 또는 메모리 카드와 같은 임시 부팅 장치)이거나 호스트에 연결된 주변 인터페이스일 수 있다. 부팅 절차의 메인 루프는 부팅 장치 목록을 순회하면서 현재 선택된 부팅 장치에서 이미지를 검색하려고 시도한다. 유효한 부팅 이미지를 찾아 성공적으로 실행하거나 워치독이 만료되면 이 루프를 벗어난다. 이미지 인증 절차는 HS 디바이스에서 이미지 실행 전에 수행된다. 인증 절차가 실패하면 Secure ROM의 "dead loop"로 분기하여 워치독 리셋을 기다리게 된다.

메모리 맵! 예외 벡터! 순서도! 이 섹션에는 많은 정보가 있다. 내 작업이 훨씬 쉬워졌다.

이 시점에서 JTAG 프로브를 사용해 펌웨어를 여러 파일로 다운로드하고 Ghidra에 로드하기 시작했다. 편리한 형식으로 제공되는 SVD 파일이나 다른 레지스터 매핑은 없는 것 같았다. 정말 안타까운 일이었다. 메모리 영역과 레지스터, 그 밖의 모든 것을 수동으로 정의해야 하기 때문이다. 지루한 과정이었지만, 시간이 지나자 AM3358용 심볼을 Ghidra에 로드할 수 있는 파이썬 스크립트가 생겼다. 걱정거리가 하나 줄었다!

리버스 엔지니어링

내가 가진 파일들은 다음과 같이 매핑할 수 있는 것 같다:

영역시작 주소길이
Boot ROM (Public)0x4002_00000xBFFF
Boot ROM (Public, alias)0x0002_00000xBFFF
SRAM Internal0x402F_04000xFC00
L3 OCM00x4030_00000x10000

흥미롭게도 0x402f_0440의 무한 루프는 내부 SRAM에 있는 "다운로드된 이미지"의 최상단에 위치하는 반면, 예외 벡터는 다른 곳에 저장된다. 어쩌면 이는 나중에 중요한 힌트가 될지도 모른다...

리셋 시 프라이빗 부트 ROM이 보안 관련 작업을 처리하고, 리셋 벡터가 들어 있는 0x2 0000으로 분기한다. 첫 번째 명령어는 엔트리 포인트로 보이는 0x2 08d0로의 분기이다. BX 명령어가 아니므로, 아마도 그 시점에는 여전히 ARM 모드에 있을 것이다.

리셋 핸들러

이것은 가장 먼저 실행되는 코드이므로, 매개변수가 있는 "함수"라기보다는 컴파일러가 생성한 시작 스크립트에 가깝다. 첫 번째 기본 블록은 다음과 같다:```arm ldr r4,[->Peripherals::CM_PER] mov r0,#0x2c ldr r6,[r4,r0]=>CM_PER.CM_PER_OCMCRAM_CLKCTRL mov r6,#0x2 str r6,[r4,r0]=>CM_PER.CM_PER_OCMCRAM_CLKCTRL mov r0,#0x2c poll: ldr r6,[r4,r0]=>CM_PER.CM_PER_OCMCRAM_CLKCTRL cmp r6,#0x2 bne poll

이 블록은 OCMC RAM 클럭을 활성화하도록 설정합니다:
1. `CM_PER_OCMCRAM_CLKCTRL=0x2`로 설정
2. 레지스터가 설정되었는지 확인하고, 설정되지 않았다면 계속 폴링합니다
`CM_PER_OCMCRAM_CLKCTRL` 레지스터는 `MODULEMODE` 필드에 비트 0과 1을 사용하며, 이 값을 `=0x2`로 설정하면 OCMC RAM에 클럭이 활성화됩니다.

다음 기본 블록:```arm
	    ldr        r0,[PTR_control_status] 
	    ldr        r0,[r0,#0x0]=>control_status 
	    and        r0,r0,#0x700
	    mov        r0,r0, lsr #0x8
	    cmp        r0,#0x3
	    bne        skip
		ldr        r0,[PTR_control_status] 
	    ldr        r0,[r0,#0x0]=>control_status
	    cpy        r6,r0
	    and        r0,r0,#0x1f
	    cmp        r0,#0x1f
	    bleq       GPMIC_init 
skip:   ...

이 블록은 다음을 수행합니다:

  1. (control_status & 0x700) >> 8 == 0x3인지 확인하고, 아니면 건너뜁니다.
  2. control_status & 0x1f == 0x1f인지 확인하고, 그렇다면 control_status를 r6에 로드한 후 GPMC_init 함수를 호출합니다.
도구 다운로드