macOS Universal binaries & Mach-O Format

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Informaci贸n B谩sica

Los binarios de Mac OS generalmente se compilan como universal binaries. Un universal binary puede soportar m煤ltiples arquitecturas en el mismo archivo.

Estos binarios siguen la estructura Mach-O que est谩 compuesta b谩sicamente de:

  • Encabezado
  • Comandos de Carga
  • Datos

https://alexdremov.me/content/images/2022/10/6XLCD.gif

Encabezado Fat

Busca el archivo con: mdfind fat.h | grep -i mach-o | grep -E "fat.h$"

#define FAT_MAGIC	0xcafebabe
#define FAT_CIGAM	0xbebafeca	/* NXSwapLong(FAT_MAGIC) */

struct fat_header {
	uint32_t	magic;		/* FAT_MAGIC o FAT_MAGIC_64 */
	uint32_t	nfat_arch;	/* n煤mero de estructuras que siguen */
};

struct fat_arch {
cpu_type_t	cputype;	/* especificador de cpu (int) */
cpu_subtype_t	cpusubtype;	/* especificador de m谩quina (int) */
uint32_t	offset;		/* desplazamiento del archivo a este archivo objeto */
uint32_t	size;		/* tama帽o de este archivo objeto */
uint32_t	align;		/* alineaci贸n como una potencia de 2 */
};

El encabezado tiene los bytes m谩gicos seguidos por el n煤mero de archs que el archivo contiene (nfat_arch) y cada arch tendr谩 una estructura fat_arch.

Verif铆calo con:

% file /bin/ls
/bin/ls: Mach-O universal binary con 2 arquitecturas: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e]
/bin/ls (para arquitectura x86_64):	Mach-O 64-bit executable x86_64
/bin/ls (para arquitectura arm64e):	Mach-O 64-bit executable arm64e

% otool -f -v /bin/ls
Fat headers
fat_magic FAT_MAGIC
nfat_arch 2
arquitectura x86_64
    cputype CPU_TYPE_X86_64
cpusubtype CPU_SUBTYPE_X86_64_ALL
capabilities 0x0
    offset 16384
    size 72896
    align 2^14 (16384)
arquitectura arm64e
    cputype CPU_TYPE_ARM64
cpusubtype CPU_SUBTYPE_ARM64E
capabilities PTR_AUTH_VERSION USERSPACE 0
    offset 98304
    size 88816
    align 2^14 (16384)

o usando la herramienta Mach-O View:

Como puedes estar pensando, generalmente un universal binary compilado para 2 arquitecturas duplica el tama帽o de uno compilado para solo 1 arch.

Encabezado Mach-O

El encabezado contiene informaci贸n b谩sica sobre el archivo, como bytes m谩gicos para identificarlo como un archivo Mach-O e informaci贸n sobre la arquitectura objetivo. Puedes encontrarlo en: mdfind loader.h | grep -i mach-o | grep -E "loader.h$"

c
#define	MH_MAGIC	0xfeedface	/* the mach magic number */
#define MH_CIGAM	0xcefaedfe	/* NXSwapInt(MH_MAGIC) */
struct mach_header {
uint32_t	magic;		/* mach magic number identifier */
cpu_type_t	cputype;	/* cpu specifier (e.g. I386) */
cpu_subtype_t	cpusubtype;	/* machine specifier */
uint32_t	filetype;	/* type of file (usage and alignment for the file) */
uint32_t	ncmds;		/* number of load commands */
uint32_t	sizeofcmds;	/* the size of all the load commands */
uint32_t	flags;		/* flags */
};

#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */
#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */
struct mach_header_64 {
uint32_t	magic;		/* mach magic number identifier */
int32_t		cputype;	/* cpu specifier */
int32_t		cpusubtype;	/* machine specifier */
uint32_t	filetype;	/* type of file */
uint32_t	ncmds;		/* number of load commands */
uint32_t	sizeofcmds;	/* the size of all the load commands */
uint32_t	flags;		/* flags */
uint32_t	reserved;	/* reserved */
};

Tipos de Archivos Mach-O

Hay diferentes tipos de archivos, puedes encontrarlos definidos en el c贸digo fuente, por ejemplo aqu铆. Los m谩s importantes son:

  • MH_OBJECT: Archivo objeto relocatable (productos intermedios de la compilaci贸n, a煤n no ejecutables).
  • MH_EXECUTE: Archivos ejecutables.
  • MH_FVMLIB: Archivo de biblioteca VM fija.
  • MH_CORE: Volcados de c贸digo
  • MH_PRELOAD: Archivo ejecutable pre-cargado (ya no soportado en XNU)
  • MH_DYLIB: Bibliotecas din谩micas
  • MH_DYLINKER: V铆nculo din谩mico
  • MH_BUNDLE: "Archivos de plugin". Generados usando -bundle en gcc y cargados expl铆citamente por NSBundle o dlopen.
  • MH_DYSM: Archivo compa帽ero .dSym (archivo con s铆mbolos para depuraci贸n).
  • MH_KEXT_BUNDLE: Extensiones del n煤cleo.
bash
# Checking the mac header of a binary
otool -arch arm64e -hv /bin/ls
Mach header
magic  cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64    ARM64          E USR00     EXECUTE    19       1728   NOUNDEFS DYLDLINK TWOLEVEL PIE

O usando Mach-O View:

Flags de Mach-O

El c贸digo fuente tambi茅n define varios flags 煤tiles para cargar bibliotecas:

  • MH_NOUNDEFS: Sin referencias indefinidas (totalmente enlazado)
  • MH_DYLDLINK: Enlace Dyld
  • MH_PREBOUND: Referencias din谩micas preenlazadas.
  • MH_SPLIT_SEGS: El archivo divide segmentos r/o y r/w.
  • MH_WEAK_DEFINES: El binario tiene s铆mbolos definidos d茅biles
  • MH_BINDS_TO_WEAK: El binario utiliza s铆mbolos d茅biles
  • MH_ALLOW_STACK_EXECUTION: Hacer que la pila sea ejecutable
  • MH_NO_REEXPORTED_DYLIBS: Biblioteca no tiene comandos LC_REEXPORT
  • MH_PIE: Ejecutable Independiente de Posici贸n
  • MH_HAS_TLV_DESCRIPTORS: Hay una secci贸n con variables locales de hilo
  • MH_NO_HEAP_EXECUTION: Sin ejecuci贸n para p谩ginas de heap/datos
  • MH_HAS_OBJC: El binario tiene secciones de oBject-C
  • MH_SIM_SUPPORT: Soporte para simuladores
  • MH_DYLIB_IN_CACHE: Usado en dylibs/frameworks en la cach茅 de bibliotecas compartidas.

Comandos de carga de Mach-O

El dise帽o del archivo en memoria se especifica aqu铆, detallando la ubicaci贸n de la tabla de s铆mbolos, el contexto del hilo principal al inicio de la ejecuci贸n y las bibliotecas compartidas requeridas. Se proporcionan instrucciones al cargador din谩mico (dyld) sobre el proceso de carga del binario en memoria.

Utiliza la estructura load_command, definida en el mencionado loader.h:

objectivec
struct load_command {
uint32_t cmd;           /* type of load command */
uint32_t cmdsize;       /* total size of command in bytes */
};

Hay alrededor de 50 tipos diferentes de comandos de carga que el sistema maneja de manera diferente. Los m谩s comunes son: LC_SEGMENT_64, LC_LOAD_DYLINKER, LC_MAIN, LC_LOAD_DYLIB y LC_CODE_SIGNATURE.

LC_SEGMENT/LC_SEGMENT_64

tip

B谩sicamente, este tipo de comando de carga define c贸mo cargar el __TEXT (c贸digo ejecutable) y __DATA (datos para el proceso) segmentos de acuerdo con los desplazamientos indicados en la secci贸n de datos cuando se ejecuta el binario.

Estos comandos definen segmentos que son mapeados en el espacio de memoria virtual de un proceso cuando se ejecuta.

Hay diferentes tipos de segmentos, como el __TEXT segmento, que contiene el c贸digo ejecutable de un programa, y el __DATA segmento, que contiene datos utilizados por el proceso. Estos segmentos se encuentran en la secci贸n de datos del archivo Mach-O.

Cada segmento puede ser dividido en m煤ltiples secciones. La estructura del comando de carga contiene informaci贸n sobre estas secciones dentro del segmento respectivo.

En el encabezado primero encuentras el encabezado del segmento:

struct segment_command_64 { /* for 64-bit architectures */
uint32_t	cmd;		/* LC_SEGMENT_64 */
uint32_t	cmdsize;	/* includes sizeof section_64 structs */
char		segname[16];	/* segment name */
uint64_t	vmaddr;		/* memory address of this segment */
uint64_t	vmsize;		/* memory size of this segment */
uint64_t	fileoff;	/* file offset of this segment */
uint64_t	filesize;	/* amount to map from the file */
int32_t		maxprot;	/* maximum VM protection */
int32_t		initprot;	/* initial VM protection */
	uint32_t	nsects;		/* number of sections in segment */
	uint32_t	flags;		/* flags */
};

Ejemplo de encabezado de segmento:

Este encabezado define el n煤mero de secciones cuyos encabezados aparecen despu茅s de 茅l:

c
struct section_64 { /* for 64-bit architectures */
char		sectname[16];	/* name of this section */
char		segname[16];	/* segment this section goes in */
uint64_t	addr;		/* memory address of this section */
uint64_t	size;		/* size in bytes of this section */
uint32_t	offset;		/* file offset of this section */
uint32_t	align;		/* section alignment (power of 2) */
uint32_t	reloff;		/* file offset of relocation entries */
uint32_t	nreloc;		/* number of relocation entries */
uint32_t	flags;		/* flags (section type and attributes)*/
uint32_t	reserved1;	/* reserved (for offset or index) */
uint32_t	reserved2;	/* reserved (for count or sizeof) */
uint32_t	reserved3;	/* reserved */
};

Ejemplo de encabezado de secci贸n:

Si agregas el desplazamiento de secci贸n (0x37DC) + el desplazamiento donde comienza la arquitectura, en este caso 0x18000 --> 0x37DC + 0x18000 = 0x1B7DC

Tambi茅n es posible obtener informaci贸n de encabezados desde la l铆nea de comandos con:

bash
otool -lv /bin/ls

Segmentos comunes cargados por este cmd:

  • __PAGEZERO: Instruye al kernel para mapear la direcci贸n cero de modo que no se pueda leer, escribir o ejecutar. Las variables maxprot y minprot en la estructura se establecen en cero para indicar que no hay derechos de lectura-escritura-ejecuci贸n en esta p谩gina.
  • Esta asignaci贸n es importante para mitigar vulnerabilidades de desreferencia de punteros NULL. Esto se debe a que XNU impone una p谩gina cero dura que asegura que la primera p谩gina (solo la primera) de la memoria sea inaccesible (excepto en i386). Un binario podr铆a cumplir con estos requisitos creando un peque帽o __PAGEZERO (usando -pagezero_size) para cubrir los primeros 4k y teniendo el resto de la memoria de 32 bits accesible tanto en modo usuario como en modo kernel.
  • __TEXT: Contiene c贸digo ejecutable con permisos de lectura y ejecuci贸n (no escribible). Secciones comunes de este segmento:
  • __text: C贸digo binario compilado
  • __const: Datos constantes (solo lectura)
  • __[c/u/os_log]string: Constantes de cadenas C, Unicode o os logs
  • __stubs y __stubs_helper: Involucrados durante el proceso de carga de bibliotecas din谩micas
  • __unwind_info: Datos de deshacer la pila.
  • Tenga en cuenta que todo este contenido est谩 firmado pero tambi茅n marcado como ejecutable (creando m谩s opciones para la explotaci贸n de secciones que no necesariamente necesitan este privilegio, como secciones dedicadas a cadenas).
  • __DATA: Contiene datos que son legibles y escribibles (no ejecutables).
  • __got: Tabla de Desplazamiento Global
  • __nl_symbol_ptr: Puntero de s铆mbolo no perezoso (vinculaci贸n al cargar)
  • __la_symbol_ptr: Puntero de s铆mbolo perezoso (vinculaci贸n al usar)
  • __const: Deber铆a ser datos de solo lectura (no realmente)
  • __cfstring: Cadenas de CoreFoundation
  • __data: Variables globales (que han sido inicializadas)
  • __bss: Variables est谩ticas (que no han sido inicializadas)
  • __objc_* (__objc_classlist, __objc_protolist, etc): Informaci贸n utilizada por el tiempo de ejecuci贸n de Objective-C
  • __DATA_CONST: __DATA.__const no est谩 garantizado que sea constante (permisos de escritura), ni lo son otros punteros y la GOT. Esta secci贸n hace que __const, algunos inicializadores y la tabla GOT (una vez resuelta) sean solo lectura usando mprotect.
  • __LINKEDIT: Contiene informaci贸n para el enlazador (dyld) como, s铆mbolos, cadenas y entradas de tabla de reubicaci贸n. Es un contenedor gen茅rico para contenidos que no est谩n en __TEXT o __DATA y su contenido se describe en otros comandos de carga.
  • Informaci贸n de dyld: Rebase, opcodes de vinculaci贸n no perezosa/perezosa/d茅bil e informaci贸n de exportaci贸n
  • Comienzos de funciones: Tabla de direcciones de inicio de funciones
  • Datos en C贸digo: Islas de datos en __text
  • Tabla de S铆mbolos: S铆mbolos en binario
  • Tabla de S铆mbolos Indirectos: Punteros/s铆mbolos de stub
  • Tabla de Cadenas
  • Firma de C贸digo
  • __OBJC: Contiene informaci贸n utilizada por el tiempo de ejecuci贸n de Objective-C. Aunque esta informaci贸n tambi茅n podr铆a encontrarse en el segmento __DATA, dentro de varias secciones en __objc_*.
  • __RESTRICT: Un segmento sin contenido con una sola secci贸n llamada __restrict (tambi茅n vac铆a) que asegura que al ejecutar el binario, ignorar谩 las variables de entorno de DYLD.

Como se pudo ver en el c贸digo, los segmentos tambi茅n admiten flags (aunque no se utilizan mucho):

  • SG_HIGHVM: Solo n煤cleo (no utilizado)
  • SG_FVMLIB: No utilizado
  • SG_NORELOC: El segmento no tiene reubicaci贸n
  • SG_PROTECTED_VERSION_1: Cifrado. Utilizado, por ejemplo, por Finder para cifrar el segmento de texto __TEXT.

LC_UNIXTHREAD/LC_MAIN

LC_MAIN contiene el punto de entrada en el atributo entryoff. En el momento de la carga, dyld simplemente agrega este valor a la base del binario (en memoria), luego salta a esta instrucci贸n para comenzar la ejecuci贸n del c贸digo del binario.

LC_UNIXTHREAD contiene los valores que el registro debe tener al iniciar el hilo principal. Esto ya fue desaprobado, pero dyld a煤n lo utiliza. Es posible ver los valores de los registros establecidos por esto con:

bash
otool -l /usr/lib/dyld
[...]
Load command 13
cmd LC_UNIXTHREAD
cmdsize 288
flavor ARM_THREAD_STATE64
count ARM_THREAD_STATE64_COUNT
x0  0x0000000000000000 x1  0x0000000000000000 x2  0x0000000000000000
x3  0x0000000000000000 x4  0x0000000000000000 x5  0x0000000000000000
x6  0x0000000000000000 x7  0x0000000000000000 x8  0x0000000000000000
x9  0x0000000000000000 x10 0x0000000000000000 x11 0x0000000000000000
x12 0x0000000000000000 x13 0x0000000000000000 x14 0x0000000000000000
x15 0x0000000000000000 x16 0x0000000000000000 x17 0x0000000000000000
x18 0x0000000000000000 x19 0x0000000000000000 x20 0x0000000000000000
x21 0x0000000000000000 x22 0x0000000000000000 x23 0x0000000000000000
x24 0x0000000000000000 x25 0x0000000000000000 x26 0x0000000000000000
x27 0x0000000000000000 x28 0x0000000000000000  fp 0x0000000000000000
lr 0x0000000000000000 sp  0x0000000000000000  pc 0x0000000000004b70
cpsr 0x00000000

[...]

LC_CODE_SIGNATURE

Contiene informaci贸n sobre la firma de c贸digo del archivo Macho-O. Solo contiene un offset que apunta al blob de firma. Esto suele estar al final del archivo.
Sin embargo, puedes encontrar informaci贸n sobre esta secci贸n en esta publicaci贸n de blog y en este gist.

LC_ENCRYPTION_INFO[_64]

Soporte para la encriptaci贸n de binarios. Sin embargo, por supuesto, si un atacante logra comprometer el proceso, podr谩 volcar la memoria sin encriptar.

LC_LOAD_DYLINKER

Contiene la ruta al ejecutable del enlazador din谩mico que mapea bibliotecas compartidas en el espacio de direcciones del proceso. El valor siempre se establece en /usr/lib/dyld. Es importante notar que en macOS, el mapeo de dylib ocurre en modo de usuario, no en modo kernel.

LC_IDENT

Obsoleto, pero cuando se configura para generar volcado en caso de p谩nico, se crea un volcado de n煤cleo Mach-O y la versi贸n del kernel se establece en el comando `LC_IDENT

objectivec
struct dylib_command {
uint32_t        cmd;            /* LC_LOAD_{,WEAK_}DYLIB */
uint32_t        cmdsize;        /* includes pathname string */
struct dylib    dylib;          /* the library identification */
};

struct dylib {
union lc_str  name;                 /* library's path name */
uint32_t timestamp;                 /* library's build time stamp */
uint32_t current_version;           /* library's current version number */
uint32_t compatibility_version;     /* library's compatibility vers number*/
};

Tambi茅n puedes obtener esta informaci贸n desde la l铆nea de comandos con:

bash
otool -L /bin/ls
/bin/ls:
/usr/lib/libutil.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)

Algunas bibliotecas potencialmente relacionadas con malware son:

  • DiskArbitration: Monitoreo de unidades USB
  • AVFoundation: Captura de audio y video
  • CoreWLAN: Escaneos de Wifi.

note

Un binario Mach-O puede contener uno o m谩s constructores, que ser谩n ejecutados antes de la direcci贸n especificada en LC_MAIN.
Los desplazamientos de cualquier constructor se mantienen en la secci贸n __mod_init_func del segmento __DATA_CONST.

Datos de Mach-O

En el n煤cleo del archivo se encuentra la regi贸n de datos, que est谩 compuesta por varios segmentos como se define en la regi贸n de comandos de carga. Una variedad de secciones de datos puede estar contenida dentro de cada segmento, con cada secci贸n conteniendo c贸digo o datos espec铆ficos de un tipo.

tip

Los datos son b谩sicamente la parte que contiene toda la informaci贸n que es cargada por los comandos de carga LC_SEGMENTS_64

https://www.oreilly.com/api/v2/epubs/9781785883378/files/graphics/B05055_02_38.jpg

Esto incluye:

  • Tabla de funciones: Que contiene informaci贸n sobre las funciones del programa.
  • Tabla de s铆mbolos: Que contiene informaci贸n sobre la funci贸n externa utilizada por el binario
  • Tambi茅n podr铆a contener funciones internas, nombres de variables y m谩s.

Para verificarlo, podr铆as usar la herramienta Mach-O View:

O desde la cli:

bash
size -m /bin/ls

Secciones Comunes de Objetive-C

En el segmento __TEXT (r-x):

  • __objc_classname: Nombres de clases (cadenas)
  • __objc_methname: Nombres de m茅todos (cadenas)
  • __objc_methtype: Tipos de m茅todos (cadenas)

En el segmento __DATA (rw-):

  • __objc_classlist: Punteros a todas las clases de Objetive-C
  • __objc_nlclslist: Punteros a clases de Objective-C No Perezosas
  • __objc_catlist: Puntero a Categor铆as
  • __objc_nlcatlist: Puntero a Categor铆as No Perezosas
  • __objc_protolist: Lista de Protocolos
  • __objc_const: Datos constantes
  • __objc_imageinfo, __objc_selrefs, objc__protorefs...

Swift

  • _swift_typeref, _swift3_capture, _swift3_assocty, _swift3_types, _swift3_proto, _swift3_fieldmd, _swift3_builtin, _swift3_reflstr

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks