TrustZone

TrustZone

2025년 1월 12일

ref #

Quarkslab: ARM의 TrustZone
중국의 TrustZone 추가설명

TrustZone이란? #

fe569b12-a603-4c49-9556-ed92d1549d6f

ARM에서 설계해서 제공하는 보안 확장 기술로 하드웨어를 논리적으로 실행환경을 나눠 격리된 환경에서 민감한 데이터 작업을 처리하는 기술이다.

ARM은 ISA(명령어와 로직게이트 회로)와 전성비 최적화를 위한 설계 같은 설계도와 어셈블러 정도만 SoC 제조사에게 전달하기 때문에 TrustZone 이 포함된 설계도를 전달한 것이고, SoC 제조사에서는 자신의 하드웨어적 특성에 맞춰서 설계를 좀 더 확장하거나 최적화 하기 때문에 제조사나 기기마다 구현은 조금씩 다를 수 있다.

닌텐도의 경우 SoC 제조사가 Nvidia인데, SecureMonitor로만 TrustZone이 구현되어 키 관리나 간단한 암복호화 검증을 수행하기도 한다.

하드웨어 구조 #

TrustZone을 구현하기 위해서는 하드웨어 단에서 부터 여러 칩들이 추가되고 이 세트는 ARM 코어 설계 단계에서 ARM IP를 받을때부터 Security Extension이 포함된 코어인지 아닌지가 결정된다.

CPU #

CPU에서 NS 비트를 사용하여 World 모드를 지정할 수 있고, ISA에 SMC 명령어가 추가되어 CPU가 명령어를 실행하면 트랩되어 Monitor 모드로 변경될 수 있도록 하고 Monitor가 CPU의 NS 비트를 조작해서 World를 변경한다.

TZASC(TrustZone Address Space Controller) #

메모리 컨트롤러 주변에 위치해서 SDRAM의 특정 영역과 접근권한을 매핑해 보안 영역 설정 레지스터에 등록하고 접근 권한(NS bit)이 맞지 않으면 버스 에러로 접근을 차단한다. 칩/펌웨어 설계에 따라 고정적으로 특정 메모리 범위를 보안 영역으로 설정하는 경우도 있고, 동적으로 늘리거나 줄일 수 있는 경우도 있다.

TZMA(TrustZone Memory Adapter) #

SoC 내부 메모리(BootROM, SRAM)를 보호 모드 설정을 하기 위한 어댑터이다. TZASC와 동일한 역할을 수행한다.

TZPC(TrustZone Protection Controller) #

주변장치 역시 보안이 필요한 자원은 Secure, Non-secure 로 모드를 구분해서 TZPC를 통해 주변장치도 Secure 또는 Non-secure 상태에서 접근할 수 있도록 보호 레지스터에 등록하고, 주변장치에 접근할때 TZPC를 통해 접근해서 권한에 따라 차단하는 역할을 한다.


하드웨어 적으로 각 장치끼리 통신 시 Secure/Non-secure 신호를 위한 라인 하나가 더 전달되고, CPU에서 설정된 NS bit에 따라 And 연산을 해서 동일하면 허용, 다르다면 차단(이후 Abort 발생) 하게된다. 하드웨어 회로상 메모리에 접근할수가 없도록 구현되어 있다.


소프트웨어 구조 #

SecureMonitor #

보통 SoC 제조사(삼성, 퀄컴, 미디어텍) 에서 자신의 칩(엑시노스, 스냅드래곤, 미디어텍)에 맞게 구현하고, 벤더가 커스터마이징을 하거나 추가 기능을 포함시키기도 한다.

  • Normal World와 Secure World를 전환하는 SMC 명령어 처리를 맡아서 CPU의 NS비트를 변경해주고 Context를 저장해서 World간 실행환경을 격리한다.
  • TZASC(TrustZone Address Space Controller)와 협력해서 권한에 따른 메모리 접근 제어를 수행한다.
  • 두 World 에서 발생하는 인터럽트를 관리한다. FIQ(Fast Interrupt Request)는 Secure World로 전달하고, IRQ(Interrupt ReQuest)는 Normal World로 전달한다.

TEE(Trusted Execution Environment) #

이것도 보통 SoC 제조사에서 개발하고, 벤더사에서 커스터마이징 하는 형식으로 개발된다.
퀄컴의 QSEE, 미디어텍의 M-TEE, 삼성의 Teegris 등이 있다.

  • 표준 암호화 기능, 무결성 검증 로직들이 API로 구현되어 있어서 TA에서 호출할 수 있다.
  • SecureWorld에서만 접근할 수 있는 키 생성, 삭제 등의 관리

TA(Trusted Applet) #

특정 보안 기능(DRM, 생체인증, 결제)을 실행하는 애플리케이션이며 주로 벤더사에서 구현한다.

  • Secure World에서 제공하는 API를 통해 디바이스에 필요한 보안 기능을 구현하며, Normal World의 사용자 앱에서 호출 가능한 엔드포인트 역할을 담당한다.

동작 방법 #

예시코드와 작업 흐름 #

https://github.dev/OP-TEE/optee_os

f7e24d77-b024-4f4f-a4cc-5e270f3e9917

  1. 사용자 앱에서 TEE Client API 를 호출해서 TEE 드라이버를 통해 커널레벨로 전달한다.
     1#define CMD_ENCRYPT 0x00000001
     2
     3TEEC_Operation op;
     4TEEC_Result res;
     5
     6memset(&op, 0, sizeof(op));
     7
     8// 암호화에 필요한 데이터를 공유 메모리에 설정
     9op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT, TEEC_MEMREF_TEMP_OUTPUT, TEEC_NONE, TEEC_NONE);
    10op.params[0].tmpref.buffer = input_data;
    11op.params[0].tmpref.size = input_size;
    12op.params[1].tmpref.buffer = output_data;
    13op.params[1].tmpref.size = output_size;
    14
    15res = TEEC_InvokeCommand(&session, CMD_ENCRYPT, &op, NULL);
    16if (res != TEEC_SUCCESS) {
    17    printf("TEEC_InvokeCommand failed: 0x%x\n", res);
    18}
    
  2. 커널에서는 SMC(Secure Monitor Call) 명령어를 실행한다. 이 명령어는 ARM CPU가 제공하는 ISA 명령어이다.
     1 // CMD_ENCRYPT 호출
     2 LDR     r0, =0x01          // r0에 CMD_ENCRYPT 설정
     3 LDR     r1, =0x1000        // r1에 입력 데이터 포인터 설정
     4 LDR     r2, =64            // r2에 입력 데이터 크기 설정 (64 바이트)
     5 LDR     r3, =0x2000        // r3에 출력 데이터 포인터 설정
     6
     7 SMC     #0                 // SMC 호출 실행 (Secure World로 전환)
     8
     9 CMP     r0, #0             // r0에 반환된 결과 확인 (0이면 성공)
    10 BNE     smc_error          // 실패 시 오류 처리
    
  3. Secure Monitor가 CPU 컨텍스트를 저장한 뒤 Secure World로 전환하고 TA를 호출한다.
     1TEE_Result TA_InvokeCommandEntryPoint(void *session_context,
     2                                   uint32_t cmd_id,
     3                                   uint32_t param_types,
     4                                   TEE_Param params[4]) {
     5    switch (cmd_id) {
     6    case CMD_ENCRYPT:
     7        return do_encrypt(param_types, params);
     8    default:
     9        return TEE_ERROR_BAD_PARAMETERS;
    10    }
    11}
    
  4. TA가 요청을 처리하고 Secure Monitor를 통해 CPU 컨텍스트를 복원하면서 결과를 사용자 앱으로 전달해준다.
     1TEE_Result do_encrypt(uint32_t param_types, TEE_Param params[4]) {
     2    if (param_types != TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
     3                                    TEE_PARAM_TYPE_MEMREF_OUTPUT,
     4                                    TEE_PARAM_TYPE_NONE,
     5                                    TEE_PARAM_TYPE_NONE)) {
     6        return TEE_ERROR_BAD_PARAMETERS;
     7    }
     8
     9    void *input = params[0].memref.buffer;
    10    size_t input_size = params[0].memref.size;
    11    void *output = params[1].memref.buffer;
    12    size_t output_size = params[1].memref.size;
    13
    14    // AES 암호화 예제
    15    TEE_OperationHandle op;
    16    TEE_AllocateOperation(&op, TEE_ALG_AES_CBC_NOPAD, TEE_MODE_ENCRYPT, 128);
    17    TEE_SetOperationKey(op, aes_key);
    18    TEE_CipherInit(op, iv, iv_size);
    19    TEE_CipherUpdate(op, input, input_size, output, &output_size);
    20
    21    TEE_FreeOperation(op);
    22    return TEE_SUCCESS;
    23}
    

SMC 처리 방법 - ARMv7 #

기본적으로 SMC명령이 실행되면 예외가 발생돼 핸들러로 처리하며, armv7에서 예외 핸들러는 MVBAR(Monitor Vector Base Address Register)에 저장되고, 이 레지스터는 Monitor 모드에서만 접근할 수 있다.

SMC 예외 핸들러는 MVBAR + 0x8 위치에 정의된다.


SMC 처리 방법 - ARMv8 #

armv8 에서는 예외처리 모델이 변경됐는데, Exception Levels(EL0~EL3) 이 도입됐다. 예외 레벨은 실행하고 있는 권한 수준을 결정하고, EL0(User Mode) < … < EL3(Secure Monitor) 으로 높은 권한이 된다.

SMC 명령어는 EL3에서 동작하며 EL3 예외 벡터 테이블 VBAR_EL3 + 0x600 에서 처리되며 이 위치는 항상 Synchronous EL3 Exception Handler로 예약되어 있다.

SCR_EL3, VBAR_EL3 레지스터 뿐만 아니라 메모리를 관리하는 MMU, SMMU 도 예외 레벨에 따라 메모리 접근권한을 제어하고, 인터럽트도 GIC(Global Interrupt Controller)를 통해서 다른 인터럽트(IRQ, FIQ)가 동작하기 때문에 Normal World 에서 Secure World의 인터럽트를 트리거하거나 제어할수가 없다.

b395ecab-a04b-4efd-af64-27b56a09102f


공유메모리? #

공유메모리는 Normal World와 Secure World가 데이터를 교환하기 위해서는 필수적이며 Secure World는 Normal World의 메모리 영역을 사용할 수 있기 때문에 Normal World의 메모리 영역이 공유 메모리라고 보면 된다.

TA에서 실제 중요한 데이터는 Secure World에서만 사용되도록 해야하고, Normal World로 노출되지 않도록 해야한다.


TEE/TA 추출 방법 #

TrustZone 소프트웨어들은 펌웨어 이미지에 포함되는데, 제조사나 기기에 따라 저장하는 tz 이미지 파일이름이 다르다.
SecureMonitor와 TEE는 부트로더 이미지 안에 있거나 tz.img, tz.mbn, secure.img 일수도 있고 삼성이나 화웨이 같은 경우엔 sboot.bin 파일일 수 있다.
TA는 TEE 이미지 내에 포함되어 있거나 별도의 암호화된 .ta, .trustlet 형태로 보관되는 경우도 있고 TEE가 로드된 이후 동적으로 설치되기도 한다.

binwalk 로 tee 추출 #

펌웨어 이미지 안에 images/tee.img 파일을 꺼내 binwalk -e tee.img 로 추출하면 _tee.img.extracted 라는 폴더 안에 soter.img 파일이 생기는데, 이게 각각의 tee1, tee2 … 파티션에 쓰여지는 파일들이 포함된 이미지 파일이다.

4ff6b086-70f6-4348-98ad-61d505634858

hxd로 열어봤을때 중간중간 elf 파일이 섞여있는 것을 확인할 수 있다.

6a748eb3-f936-40b7-9f03-401f8781b478

원하는 문자열을 검색하고 다시 binwalk 로 soter.img를 분석한 뒤 dd 명령으로 추출하면 된다.

7619afe7-a60f-489c-acae-7ee2e139443b


아래 캡쳐된 이미지를 확인해보면 가장 가까운 ELF는 0x277000 끝지점은 0x2F9000 이다. 블록 사이즈를 4096(0x1000) 으로 지정하고 631(0x277)개의 블록을 건너뛰고 82(0x2F9-0x277)개 만큼 가져와야 한다.

69ddbb94-4a0a-4b11-b902-53f3f141096b

bs를 1byte로 해서 그냥 범위를 지정해도 되지만, 속도가 느리다.

1// dd if=<입력파일> of=<출력파일> bs=<블록크기> skip=<스킵할 블록수> count=<복사할 블록수>
2
3dd if=soter.img of=sha256.img bs=4096 skip=631 count=82

정상적으로 추출이 된것을 확인할 수 있다.

a0c3a76a-bddc-4156-a84e-19d9bf021d40

이게 TEE의 서비스(라이브러리 등)일 수도 있고 TEE에 포함된 TA일 수도 있다(하지만 TA는 보통 다른 파티션에 있음). 이제 ghidra에서 분석하면 된다.


기기에서 바로 덤프 #

펌웨어 이미지에 있는 tee 이미지를 바로 로드하는 경우도 있겠지만, 압축 해제나 복호화 후 펌웨어를 플래시하는 경우도 있을 것이다. 이런 경우엔 기기에서 직접 덤프하는 방법이 유용하다.

리눅스는 /dev/block에 블록장치 파일을 만들고 실제 하드웨어와 마운트시켜 하드웨어의 데이터를 블록단위로 읽고 쓸 수 있도록 되어 있다.

/dev/blobk/by-name/ 경로에 각 블록이 파티션 이름으로 심볼릭 링크가 걸려 있는데, dd로 덤프할 수 있다.
/dev/block/by-name/tee 로 접근해도 된다

83447148-2b37-4cb1-b5ae-ac589f74fca2


갤럭시 TA #

5e4f6e28-6070-4362-8a2e-48382baa27a7

/vendor/tee 또는 /system/tee 경로에서 숫자로 이뤄진 파일들이 TA이다. 파일의 맨 뒷부분을 잘 보면 ASCII HEX값 형태로 되어있는 것을 알 수 있고, 블럭으로 표시한 474154454b45 는 ASCII 로 표현하면 GATEKE 가 되며 이 TA가 Gatekeeper인 것을 알 수 있다.

8a59e09d-71c3-4ed0-9393-1389092de2ad

SEC3..VX (TEEgris 계열의 TA컨테이너 프롤로그) 이후에 ELF 파일 포맷이 이어지기 때문에 앞부분을 제거하면 디버거로 파일을 열 수 있게된다.

comments powered by Disqus