macOS Universal binaries & Mach-O Format
기본 정보
Mac OS 바이너리는 일반적으로 유니버설 바이너리로 컴파일됩니다. 유니버설 바이너리는 동일한 파일에서 여러 아키텍처를 지원할 수 있습니다.
이러한 바이너리는 기본적으로 Mach-O 구조를 따릅니다. 이 구조는 다음과 같이 구성됩니다:
헤더(Header)
로드 명령(Load Commands)
데이터(Data)
Fat Header
다음과 같이 파일을 검색합니다: mdfind fat.h | grep -i mach-o | grep -E "fat.h$"
헤더에는 매직 바이트가 있고 파일이 포함하는 아키텍처의 수(nfat_arch
)가 뒤따르며 각 아키텍처는 fat_arch
구조체를 가집니다.
다음과 같이 확인합니다:
또는 Mach-O View 도구를 사용하여 확인할 수 있습니다:
일반적으로 2개의 아키텍처를 위해 컴파일된 유니버설 바이너리는 일반적으로 1개의 아키텍처를 위해 컴파일된 것의 크기를 두 배로 만듭니다.
Mach-O 헤더
헤더에는 Mach-O 파일로 식별하기 위한 매직 바이트와 대상 아키텍처에 대한 정보와 같은 파일에 대한 기본 정보가 포함되어 있습니다. 다음 위치에서 찾을 수 있습니다: mdfind loader.h | grep -i mach-o | grep -E "loader.h$"
Mach-O 파일 유형
다양한 파일 유형이 있으며 이를 찾을 수 있습니다. 예시 소스 코드는 여기에서 확인할 수 있습니다. 가장 중요한 것들은:
MH_OBJECT
: 재배치 가능한 오브젝트 파일 (컴파일의 중간 결과물로, 아직 실행 파일이 아님).MH_EXECUTE
: 실행 파일.MH_FVMLIB
: 고정 VM 라이브러리 파일.MH_CORE
: 코드 덤프MH_PRELOAD
: 사전로드된 실행 파일 (XNU에서 더 이상 지원되지 않음)MH_DYLIB
: 동적 라이브러리MH_DYLINKER
: 동적 링커MH_BUNDLE
: "플러그인 파일". gcc의 -bundle을 사용하여 생성되며NSBundle
또는dlopen
에 의해 명시적으로 로드됨.MH_DYSM
: 동반.dSym
파일 (디버깅을 위한 심볼이 있는 파일).MH_KEXT_BUNDLE
: 커널 확장.
또는 Mach-O View를 사용하십시오:
Mach-O 플래그
소스 코드는 또한 라이브러리를 로드하는 데 유용한 여러 플래그를 정의합니다:
MH_NOUNDEFS
: 정의되지 않은 참조 없음 (완전히 링크됨)MH_DYLDLINK
: Dyld 링킹MH_PREBOUND
: 동적 참조 사전 바인딩.MH_SPLIT_SEGS
: 파일이 r/o 및 r/w 세그먼트로 분할됨.MH_WEAK_DEFINES
: 이진 파일에 약한 정의된 심볼이 있음MH_BINDS_TO_WEAK
: 이진 파일이 약한 심볼을 사용함MH_ALLOW_STACK_EXECUTION
: 스택을 실행 가능하게 만듦MH_NO_REEXPORTED_DYLIBS
: LC_REEXPORT 명령이 없는 라이브러리MH_PIE
: 위치 독립 실행 파일MH_HAS_TLV_DESCRIPTORS
: 쓰레드 로컬 변수가 있는 섹션이 있음MH_NO_HEAP_EXECUTION
: 힙/데이터 페이지에 대한 실행 없음MH_HAS_OBJC
: 이진 파일에 Objective-C 섹션이 있음MH_SIM_SUPPORT
: 시뮬레이터 지원MH_DYLIB_IN_CACHE
: 공유 라이브러리 캐시에 있는 dylibs/frameworks에서 사용됨.
Mach-O 로드 명령어
메모리에 파일의 레이아웃이 여기에 지정되어 있으며, 심볼 테이블의 위치, 실행 시작 시 주 스레드의 컨텍스트 및 필요한 공유 라이브러리에 대한 내용이 설명됩니다. 메모리로의 바이너리 로딩 프로세스에 대한 동적 로더 **(dyld)**에 대한 지침이 제공됩니다.
이는 **loader.h
**에 정의된 load_command 구조를 사용합니다:
시스템이 다르게 처리하는 약 50가지의 로드 명령어 유형이 있습니다. 가장 일반적인 것들은: LC_SEGMENT_64
, LC_LOAD_DYLINKER
, LC_MAIN
, LC_LOAD_DYLIB
, 그리고 LC_CODE_SIGNATURE
입니다.
LC_SEGMENT/LC_SEGMENT_64
기본적으로 이 유형의 로드 명령어는 __TEXT (실행 코드)와 __DATA (프로세스용 데이터) 세그먼트를 실행 파일이 실행될 때 데이터 섹션에 표시된 오프셋에 따라 어떻게 로드할지를 정의합니다.
이러한 명령어는 프로세스의 가상 메모리 공간에 매핑되는 세그먼트를 정의합니다.
__TEXT 세그먼트는 프로그램의 실행 코드를 보유하며, __DATA 세그먼트는 프로세스에서 사용되는 데이터를 포함합니다. 이러한 세그먼트는 Mach-O 파일의 데이터 섹션에 위치합니다.
각 세그먼트는 더 작은 섹션으로 나눌 수 있습니다. 로드 명령어 구조에는 해당 세그먼트 내의 이러한 섹션에 대한 정보가 포함되어 있습니다.
헤더에서 먼저 세그먼트 헤더를 찾을 수 있습니다:
세그먼트 헤더의 예시:
이 헤더는 그 뒤에 나타나는 섹션 헤더의 수를 정의합니다.
예시 섹션 헤더:
만약 섹션 오프셋 (0x37DC)에 아키텍처 시작 오프셋을 더한다면, 이 경우 0x18000
--> 0x37DC + 0x18000 = 0x1B7DC
커맨드 라인에서도 헤더 정보를 얻는 것이 가능합니다:
다음은이 cmd에 의해로드되는 일반 세그먼트입니다:
__PAGEZERO
: 커널에 주소 0을 매핑하도록 지시하여 읽을 수 없고 쓸 수 없고 실행할 수 없게합니다. 구조체의 maxprot 및 minprot 변수는 이 페이지에 읽기-쓰기-실행 권한이 없음을 나타내도록 0으로 설정됩니다.이 할당은 NULL 포인터 역참조 취약점을 완화하는 데 중요합니다. 이는 XNU가 첫 번째 페이지 (i386 제외)가 접근할 수 없게 하는 강력한 페이지 제로를 강제하기 때문입니다. 바이너리는 작은 __PAGEZERO를 만들어 (
-pagezero_size
를 사용하여) 처음 4k를 커버하고 나머지 32비트 메모리를 사용자 및 커널 모드에서 모두 접근 가능하게 할 수 있습니다.__TEXT
: 읽기 및 실행 권한이 있는 실행 가능한 코드를 포함합니다 (쓰기 권한 없음). 이 세그먼트의 일반 섹션:__text
: 컴파일된 이진 코드__const
: 상수 데이터 (읽기 전용)__[c/u/os_log]string
: C, Unicode 또는 os 로그 문자열 상수__stubs
및__stubs_helper
: 동적 라이브러리 로드 프로세스 중 관련됨__unwind_info
: 스택 언와인드 데이터
모든 이 내용이 서명되었지만 실행 가능으로 표시되었음을 유의하십시오 (이 권한이 필요하지 않은 섹션의 악용 가능성을 만들어 문자열 전용 섹션과 같은 섹션을 악용하는 옵션을 더 만듭니다).
__DATA
: 읽기 및 쓰기 가능한 데이터를 포함합니다 (실행 불가능).__got:
글로벌 오프셋 테이블__nl_symbol_ptr
: 지연 로드(bind at load) 심볼 포인터__la_symbol_ptr
: 사용 시 바인딩(bind on use) 심볼 포인터__const
: 읽기 전용 데이터여야 함 (실제로는 그렇지 않음)__cfstring
: CoreFoundation 문자열__data
: 초기화된 전역 변수__bss
: 초기화되지 않은 정적 변수__objc_*
(__objc_classlist, __objc_protolist 등): Objective-C 런타임에서 사용되는 정보
__DATA_CONST
: __DATA.__const는 상수임이 보장되지 않으며 (쓰기 권한), 다른 포인터 및 GOT도 그렇지 않습니다. 이 섹션은mprotect
를 사용하여__const
, 일부 초기화자 및 GOT 테이블 (한 번 해결된 후)을 읽기 전용으로 만듭니다.__LINKEDIT
: 링커 (dyld)를 위한 정보를 포함하며, 심볼, 문자열 및 재배치 테이블 항목이 포함됩니다. 이는__TEXT
또는__DATA
에 없는 콘텐츠의 일반적인 컨테이너이며, 해당 내용은 다른 로드 명령에서 설명됩니다.dyld 정보: Rebase, Non-lazy/lazy/weak 바인딩 옵코드 및 익스포트 정보
함수 시작: 함수의 시작 주소 테이블
코드 내 데이터: __text의 데이터 아일랜드
심볼 테이블: 이진 파일의 심볼
간접 심볼 테이블: 포인터/스텁 심볼
문자열 테이블
코드 서명
__OBJC
: Objective-C 런타임에서 사용되는 정보를 포함합니다. 이 정보는 __DATA 세그먼트에도 있을 수 있지만, 여러 __objc_* 섹션에 있습니다.__RESTRICT
: **__restrict
**라는 단일 섹션을 포함하고 내용이 없는 세그먼트로, 바이너리를 실행할 때 DYLD 환경 변수를 무시하도록 보장합니다.
코드에서 볼 수 있듯이 세그먼트는 플래그도 지원합니다 (그러나 그들은 많이 사용되지는 않음):
SG_HIGHVM
: 코어 전용 (사용되지 않음)SG_FVMLIB
: 사용되지 않음SG_NORELOC
: 세그먼트에 재배치가 없음SG_PROTECTED_VERSION_1
: 암호화. 예를 들어 Finder가 텍스트__TEXT
세그먼트를 암호화하는 데 사용됩니다.
LC_UNIXTHREAD/LC_MAIN
LC_UNIXTHREAD/LC_MAIN
**LC_MAIN
**은 entryoff 속성에 진입점을 포함합니다. 로드 시, dyld는 단순히 이 값을 (메모리 내) 바이너리의 베이스에 추가하고, 그런 다음 이 명령으로 이동하여 바이너리 코드의 실행을 시작합니다.
**LC_UNIXTHREAD
**에는 주 스레드를 시작할 때 레지스터가 가져야 하는 값이 포함됩니다. 이는 이미 사용되지 않았지만 **dyld
**가 여전히 사용합니다. 이를 통해 이 명령으로 설정된 레지스터의 값을 볼 수 있습니다:
LC_CODE_SIGNATURE
LC_CODE_SIGNATURE
Macho-O 파일의 코드 서명에 관한 정보를 포함합니다. 일반적으로 파일의 맨 끝에 있는 서명 블롭을 가리키는 오프셋만 포함합니다. 그러나 이 섹션에 대한 정보는 이 블로그 게시물 및 이 gist에서 찾을 수 있습니다.
LC_ENCRYPTION_INFO[_64]
LC_ENCRYPTION_INFO[_64]
바이너리 암호화를 지원합니다. 그러나 공격자가 프로세스를 침해하면 메모리를 암호화 해제할 수 있습니다.
LC_LOAD_DYLINKER
LC_LOAD_DYLINKER
공유 라이브러리를 프로세스 주소 공간에 매핑하는 동적 링커 실행 파일의 경로를 포함합니다. 값은 항상 /usr/lib/dyld
로 설정됩니다. macOS에서 dylib 매핑은 커널 모드가 아닌 사용자 모드에서 발생한다는 점을 중요하게 알아두어야 합니다.
LC_IDENT
LC_IDENT
사용되지 않지만 패닉 시 덤프를 생성하도록 구성된 경우 Mach-O 코어 덤프가 생성되고 커널 버전이 LC_IDENT
명령에 설정됩니다.
LC_UUID
LC_UUID
랜덤 UUID입니다. 직접적으로 유용하지는 않지만 XNU는 프로세스 정보와 함께 캐시합니다. 충돌 보고서에서 사용될 수 있습니다.
LC_DYLD_ENVIRONMENT
LC_DYLD_ENVIRONMENT
프로세스가 실행되기 전에 dyld에 환경 변수를 지정할 수 있습니다. 이는 프로세스 내에서 임의의 코드를 실행할 수 있기 때문에 매우 위험할 수 있습니다. 이 로드 명령은 #define SUPPORT_LC_DYLD_ENVIRONMENT
로 빌드된 dyld에서만 사용되며 DYLD_..._PATH
형식의 변수만 처리하도록 제한됩니다.
LC_LOAD_DYLIB
LC_LOAD_DYLIB
이 로드 명령은 동적 라이브러리 의존성을 설명하며 로더 (dyld)에게 해당 라이브러리를 로드하고 링크하도록 지시 합니다. Mach-O 바이너리가 필요로 하는 각 라이브러리에 대해 LC_LOAD_DYLIB
로드 명령이 있습니다.
이 로드 명령은 실제 종속 동적 라이브러리를 설명하는 구조체인
dylib_command
유형의 구조체입니다:
다음 명령어를 사용하여 CLI에서도 이 정보를 얻을 수 있습니다:
일부 잠재적인 악성 코드 관련 라이브러리는:
DiskArbitration: USB 드라이브 모니터링
AVFoundation: 오디오 및 비디오 캡처
CoreWLAN: Wifi 스캔
Mach-O 바이너리에는 하나 이상의 생성자가 포함될 수 있으며, 이는 LC_MAIN에 지정된 주소 앞에서 실행됩니다. 어떤 생성자의 오프셋은 __DATA_CONST 세그먼트의 __mod_init_func 섹션에 저장됩니다.
Mach-O 데이터
파일의 핵심에는 로드 명령 영역에서 정의된 여러 세그먼트로 구성된 데이터 영역이 있습니다. 각 세그먼트에는 여러 데이터 섹션이 포함될 수 있으며, 각 섹션은 특정 유형에 대한 코드 또는 데이터를 보유합니다.
데이터는 기본적으로 로드 명령 LC_SEGMENTS_64에 의해 로드되는 모든 정보를 포함하는 부분입니다.
이에는 다음이 포함됩니다:
함수 테이블: 프로그램 함수에 대한 정보를 보유
심볼 테이블: 바이너리에서 사용되는 외부 함수에 대한 정보를 포함
내부 함수, 변수 이름 등도 포함될 수 있습니다.
확인하려면 Mach-O View 도구를 사용할 수 있습니다:
또는 CLI에서:
Objective-C 공통 섹션
__TEXT
세그먼트 (r-x):
__objc_classname
: 클래스 이름 (문자열)__objc_methname
: 메서드 이름 (문자열)__objc_methtype
: 메서드 유형 (문자열)
__DATA
세그먼트 (rw-):
__objc_classlist
: 모든 Objective-C 클래스에 대한 포인터__objc_nlclslist
: Non-Lazy Objective-C 클래스에 대한 포인터__objc_catlist
: 카테고리에 대한 포인터__objc_nlcatlist
: Non-Lazy 카테고리에 대한 포인터__objc_protolist
: 프로토콜 목록__objc_const
: 상수 데이터__objc_imageinfo
,__objc_selrefs
,objc__protorefs
...
Swift
_swift_typeref
,_swift3_capture
,_swift3_assocty
,_swift3_types, _swift3_proto
,_swift3_fieldmd
,_swift3_builtin
,_swift3_reflstr
Last updated