17 KiB
macOS Binarios universales y formato Mach-O
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- ¿Trabajas en una empresa de ciberseguridad? ¿Quieres ver tu empresa anunciada en HackTricks? ¿O quieres tener acceso a la última versión de PEASS o descargar HackTricks en PDF? ¡Consulta los PLANES DE SUSCRIPCIÓN!
- Descubre The PEASS Family, nuestra colección exclusiva de NFTs
- Obtén el swag oficial de PEASS y HackTricks
- Únete al 💬 grupo de Discord o al grupo de Telegram o sígueme en Twitter 🐦@carlospolopm.
- Comparte tus trucos de hacking enviando PRs al repositorio de hacktricks y al repositorio de hacktricks-cloud.
Información básica
Los binarios de Mac OS generalmente se compilan como binarios universales. Un binario universal puede soportar múltiples arquitecturas en el mismo archivo.
Estos binarios siguen la estructura Mach-O que básicamente está compuesta por:
- Encabezado
- Comandos de carga
- Datos
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 or 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 objeto */
uint32_t size; /* tamaño de este objeto */
uint32_t align; /* alineación como una potencia de 2 */
};
El encabezado tiene los bytes mágicos seguidos del número de arquitecturas que el archivo contiene (nfat_arch
) y cada arquitectura tendrá una estructura fat_arch
.
Verifícalo con:
% file /bin/ls
/bin/ls: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e]
/bin/ls (for architecture x86_64): Mach-O 64-bit executable x86_64
/bin/ls (for architecture arm64e): Mach-O 64-bit executable arm64e
% otool -f -v /bin/ls
Fat headers
fat_magic FAT_MAGIC
nfat_arch 2
architecture x86_64
cputype CPU_TYPE_X86_64
cpusubtype CPU_SUBTYPE_X86_64_ALL
capabilities 0x0
offset 16384
size 72896
align 2^14 (16384)
architecture arm64e
cputype CPU_TYPE_ARM64
cpusubtype CPU_SUBTYPE_ARM64E
capabilities PTR_AUTH_VERSION USERSPACE 0
offset 98304
size 88816
align 2^14 (16384)
o utilizando la herramienta Mach-O View:
Como podrás pensar, generalmente un binario universal compilado para 2 arquitecturas duplica el tamaño de uno compilado para solo 1 arquitectura.
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$"
#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:
- MH_EXECUTE (0x2): Ejecutable Mach-O estándar
- MH_DYLIB (0x6): Una biblioteca enlazada dinámicamente Mach-O (es decir, .dylib)
- MH_BUNDLE (0x8): Un paquete Mach-O (es decir, .bundle)
# 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 utilizando Mach-O View:
Comandos de carga de Mach-O
Esto especifica la estructura del archivo en memoria. Contiene la ubicación de la tabla de símbolos, el contexto del hilo principal al comienzo de la ejecución y las bibliotecas compartidas requeridas.
Los comandos básicamente instruyen al cargador dinámico (dyld) cómo cargar el binario en memoria.
Todos los comandos de carga comienzan con una estructura load_command, definida en el loader.h
mencionado anteriormente:
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
{% hint style="success" %} Básicamente, este tipo de comando de carga define cómo cargar los segmentos __TEXT (código ejecutable) y __DATA (datos para el proceso) según los desplazamientos indicados en la sección de Datos cuando se ejecuta el binario. {% endhint %}
Estos comandos definen segmentos que se mapean en el espacio de memoria virtual de un proceso cuando se ejecuta.
Existen diferentes tipos de segmentos, como el segmento __TEXT, que contiene el código ejecutable de un programa, y el segmento __DATA, que contiene datos utilizados por el proceso. Estos segmentos se encuentran en la sección de datos del archivo Mach-O.
Cada segmento se puede dividir aún más en múltiples secciones. La estructura del comando de carga contiene información sobre estas secciones dentro del segmento correspondiente.
En el encabezado primero se encuentra el encabezado del segmento:
struct segment_command_64 { /* para arquitecturas de 64 bits */
uint32_t cmd; /* LC_SEGMENT_64 */
uint32_t cmdsize; /* incluye el tamaño de las estructuras section_64 */
char segname[16]; /* nombre del segmento */
uint64_t vmaddr; /* dirección de memoria de este segmento */
uint64_t vmsize; /* tamaño de memoria de este segmento */
uint64_t fileoff; /* desplazamiento del archivo de este segmento */
uint64_t filesize; /* cantidad a mapear desde el archivo */
int32_t maxprot; /* protección VM máxima */
int32_t initprot; /* protección VM inicial */
uint32_t nsects; /* número de secciones en el segmento */
uint32_t flags; /* banderas */
};
Ejemplo de encabezado de segmento:
Este encabezado define el número de secciones cuyos encabezados aparecen después de él:
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:
otool -lv /bin/ls
Segmentos comunes cargados por este comando:
__PAGEZERO
: Instruye al kernel a mapear la dirección cero para 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 las vulnerabilidades de referencia a puntero nulo.
__TEXT
: Contiene código ejecutable con permisos de lectura y ejecución (sin escritura). Secciones comunes de este segmento:__text
: Código binario compilado__const
: Datos constantes__cstring
: Constantes de cadena__stubs
y__stubs_helper
: Involucrados durante el proceso de carga de bibliotecas dinámicas__DATA
: Contiene datos que son legibles y escribibles (sin ejecución).__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__LINKEDIT
: Contiene información para el enlazador (dyld) como "entradas de tabla de símbolos, cadenas y reubicación".__OBJC
: Contiene información utilizada por el tiempo de ejecución de Objective-C. Aunque esta información también puede encontrarse en el segmento __DATA, dentro de varias secciones __objc_*.
LC_MAIN
Contiene el punto de entrada en el atributo entryoff. En el momento de carga, dyld simplemente suma este valor a la base del binario en memoria y luego salta a esta instrucción para iniciar la ejecución del código del binario.
LC_CODE_SIGNATURE
Contiene información sobre la firma de código del archivo Mach-O. Solo contiene un desplazamiento que apunta al bloque 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_LOAD_DYLINKER
Contiene la ruta del 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 tener en cuenta que en macOS, el mapeo de dylib ocurre en modo de usuario, no en modo de kernel.
LC_LOAD_DYLIB
Este comando de carga describe una dependencia de biblioteca dinámica que instruye al cargador (dyld) a cargar y enlazar dicha biblioteca. Hay un comando de carga LC_LOAD_DYLIB para cada biblioteca que requiere el binario Mach-O.
- Este comando de carga es una estructura de tipo
dylib_command
(que contiene una estructura dylib, que describe la biblioteca dinámica dependiente real):
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:
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: Escaneo de Wifi.
{% hint style="info" %}
Un binario Mach-O puede contener uno o más constructores, que se ejecutarán antes de la dirección especificada en LC_MAIN.
Los desplazamientos de cualquier constructor se encuentran en la sección __mod_init_func del segmento __DATA_CONST.
{% endhint %}
Datos Mach-O
El corazón del archivo es la región final, los datos, que consiste en una serie de segmentos como se muestra en la región de comandos de carga. Cada segmento puede contener una serie de secciones de datos. Cada una de estas secciones contiene código o datos de un tipo particular.
{% hint style="success" %} Los datos son básicamente la parte que contiene toda la información que se carga mediante los comandos de carga LC_SEGMENTS_64 {% endhint %}
Esto incluye:
- Tabla de funciones: Que contiene información sobre las funciones del programa.
- Tabla de símbolos: Que contiene información sobre las funciones externas utilizadas por el binario.
- También podría contener funciones internas, nombres de variables y más.
Para verificarlo, puedes usar la herramienta Mach-O View:
O desde la línea de comandos:
size -m /bin/ls
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- ¿Trabajas en una empresa de ciberseguridad? ¿Quieres ver tu empresa anunciada en HackTricks? ¿O quieres tener acceso a la última versión de PEASS o descargar HackTricks en PDF? ¡Consulta los PLANES DE SUSCRIPCIÓN!
- Descubre La Familia PEASS, nuestra colección exclusiva de NFTs
- Obtén el merchandising oficial de PEASS y HackTricks
- Únete al 💬 grupo de Discord o al grupo de Telegram o sígueme en Twitter 🐦@carlospolopm.
- Comparte tus trucos de hacking enviando PRs al repositorio de hacktricks y al repositorio de hacktricks-cloud.