상세 컨텐츠

본문 제목

[Paper Review] Syzgen: Automated generation of syscall specification of closed-source macos drivers (CCS'21)

ANALYSIS/Paper Review

by koharin 2024. 5. 25. 14:34

본문

728x90
반응형

BACKGROUND

드라이버는 특정 서비스 이름(하드코딩 문자열) 통해 서비스 노출 가능, 각 드라이버는 서로 다른 기능 제공하는 사용자 클라이언트가 있음. 사용자 클라이언트는 커널 공간 내 드라이버 일부

서비스 연결하려는 모든 사용자 애플리케이션은 IOServiceOpen 호출해야 함. 첫 번째 인자에는 서비스를 지정하고, 두 번째 인자에는 사용자 클라이언트 객체를 인스턴스화하여 지정한다. 애플리케이션이 드라이버에 보내는 모든 요청은 IOConnectCallMethod() 호출하여 이루어진다.

IOConnectCallMethod()는 일반적인 시스템 호출이며, 드라이버마다 다르게 구현됨

 

MOTIVATION

챌린지

  1. 드라이버 syscall은 드라이버의 구현에 따라 기능이 크게 다르다 → 템플릿을 한번만 만드는 것이 아닌 시간에 지남에 따라(다양한 실행 경로 샘플링하고 각 경로에서 얻은 지식 결합) 반복적으로 개선함
  2. 비공개 소스 드라이버 대상으로, syscall 인수 및 syscall 간의 종속성에 관한 정보 복구하기 훨씬 어렵다. → 실행 추적 기반으로 명시적 종속성 추출 및 추정

AppleUpstreamUserClientDriver의 일부 코드로, 두 함수 CloseLink, OpenLink 에서 반환된 식별자(linkID), 즉 2개의 명시적 종속성이 필요하다. OpenLink()가 새로운 커널 객체를 생성하고, **해당 ID(종속성 변수)**를 반환하므로 OpenLink() 함수는 생성 인터페이스라고 한다.

CloseLink, FlushLink는 이전에 생성된 객체에 의존하거나 사용하기 때문에 사용 인터페이스이다.

linkID를 통해 OpenLInk와 CloseLink 함수의 실행 추적을 관찰할 수 있으며, 두 함수의 인자 유형과 제약 조건을 성공적으로 추론했다고 가정하면, 명시적 종속성을 추론할 수 있다.

OpenLink 반환값과 CloseLink의첫 번째 인자값이 항상 일치한다. ⇒ OpenLink, CloseLink, FlushLink 의 세 가지 인터페이스를 모두 포함하는 초기 템플릿 생성할 수 있지만 명시적인 의존성은 하나만 설정한다.

OpenLink와의 종속성에서 FlushLink와 관련된 흔적이 없기 때문이다.

종속성 변수 LinkID를 LookupLink 함수 호출을 통해 커널 객체로 변환됨을 알 수 있다.

FlushLink의 인수에 있는 다른 필드 검사하고 전체 인수 유형과 제약조건 사용하면 템플릿 더 세분화 가능. 함수 더 깊은 곳에 도달하려면 인수의 두 번째 필드에 매직넘버가 필요하다. → 이는 샘플링된 실행경로(OpenLink, CloseLink 포함)에서 시작하여 반복적으로 사양을 세분화하여 드라이버에 대해 점진적으로 더 이해할 수 있다.

⇒ 이를 통해 복잡한 드라이버를 바이너리로 분석해야 하는 어려움을 덜고, 자동화에 적합한 프로세스임

 

DESIGN

macOS driver대상으로 최초로 syscall 명세를 자동으로 생성하여 interface-aware 퍼징에 도움

그림은 인터페이스 간 종속성 제거를 위해 macOS 드라이버의 명세를 생성하는 시스템 구조이다.

크게 (1) syscall 로거와 분석기, (2) 서비스 및 인터페이스 식별, (3) 인터페이스 복구, (4) 커버리지가 활성화된 퍼저

(1) syscall 로거와 분석기

  • 로거는 커널을 계측하여 대상 드라이버에 대한 모든 syscall 입력 및 출력을 기록한다. 이후 수집된 로그를 분석하여 실행 추적에서 관찰할 수 있는 명시적 의존성을 식별한다. (IMF의 접근 방식을 따름) 초기 corpus를 생성하기 위해 추론한 의존성에 따라 로그를 독립적인 테스트케이스로 분리한다.
  • IOConnectCallMethod() 의 주요 입력 및 출력 매개변수 inputStruct, outputStruct는 void형으로, 입력 구조체 중 어느 필드가 종속성인지 식별할 수 없음. 종속성 변수가 void 객체에 있는 경우 IMF는 명시적 종속성을 추적할 수 없는 한계가 있음. ⇒ 해결: 종속 변수가 1바이트에서 최대 8바이트까지 객체의 어느 곳에나 존재할 수 있다고 가정하고 void* 처리함. 기본적으로 2개의 서로 다른 인터페이스에서 동일한 입력 및 출력 바이트 쌍을 검색함.

(2) 서비스 및 명령 식별자 결정: 대상 바이너리 주어지면, 드라이버와 상호작용하는데 사용되는 서비스 이름, 유형번호를 감지. 인터페이스는 모두 동일하게 IOConnectCallMethod를 공유하기 때문에, 드라이버가 기대하는 명령 식별자 값을 찾고 입력의 위치를 파악해야 시스템 호출 분석기가 서로 다른 인터페이스를 구별할 수 있음.

(3) 인터페이스 복구: 인터페이스에 대해 동적 분석(샘플링된 실행경로)을 통해 입력 구조 및 제약조건과 함께 이전에 추출한 명시적 의존성 지식을 갖고 초기 템플릿 생성. 알려진 의존성에서 명시적 의존성을 자동으로 추정하거나 일반화함. 반복적으로 진행하여 새로 발견된 종속성을 점진적으로 템플릿을 발전시켜서 사용 인터페이스(예:FlushLink)의 구조와 제약조건을 구체화할 수 있다.

(4) 커널 커버리지가 있는 퍼저: 생성된 명세를 활용하여 퍼징을 진행함. 커버리지와 함께 암시적 의존성(시스템 호출 순서)을 추론하여 테스트케이스 생성의 효율성을 향상시킨다. → 이 방식은 macOS 이외에도 비공개 소스커널 모듈이 있는 다른 OS에도 적용할 수 있다.

 

IMPLEMENTATION

  • 인터페이스 복구: 7.2K 라인의 파이썬 코드로 구현함
  • 커널 커버리지: 1K 라인의 C 코드로 구현함
  • sys call 로거: 463 라인의 C 코드로 구현함
  • 퍼저: syzkaller에서 1K 라인의 Go 코드를 추가함
  • Basic block과 function signature 주소를 수집: IDA Pro 스크립트 구현
  • symbolic execution & 커널 디버깅: angr 기반으로 구현

 

RELATED WORK

syzkaller

  • structure & constraint of syscall argument와 syscall에 대한 dependency 정보 포함하여 템플릿 지원 ⇒ manually 제작
  • 시스템콜이 성공적으로 실행되려면 올바른 호출 순서(파일기술자)를 올바르게 전달(명시적 의존성 또는 값 의존성) 필요
  • 한계: 템플릿 메뉴얼하게 생성하면 불완전한 경우가 많아 최적이 아닌 퍼징 결과를 초래하게 됨

시스템콜 템플릿 생성 자동화 연구

  • DIFUZE: 리눅스 커널 모듈의 소스코드를 정적으로 분석하여 모듈에서 인수가 어떻게 복사되고 사용되는지에 따라 syscall 인수의 구조와 제약 조건 추론

 

728x90
반응형

관련글 더보기