# Generador de Interfaz Mach (MIG) de macOS
☁️ 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**](https://github.com/sponsors/carlospolop)! * Descubre [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family) * Obtén el [**swag oficial de PEASS y HackTricks**](https://peass.creator-spring.com) * **Únete al** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de Telegram**](https://t.me/peass) o **sígueme** en **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.** * **Comparte tus trucos de hacking enviando PRs al** [**repositorio de hacktricks**](https://github.com/carlospolop/hacktricks) **y al** [**repositorio de hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
MIG fue creado para **simplificar el proceso de creación de código de IPC de Mach**. Básicamente, **genera el código necesario** para que el servidor y el cliente se comuniquen con una definición dada. Aunque el código generado sea feo, un desarrollador solo necesitará importarlo y su código será mucho más simple que antes. ### Ejemplo Crea un archivo de definición, en este caso con una función muy simple: {% code title="myipc.defs" %} ```cpp subsystem myipc 500; // Arbitrary name and id userprefix USERPREF; // Prefix for created functions in the client serverprefix SERVERPREF; // Prefix for created functions in the server #include #include simpleroutine Subtract( server_port : mach_port_t; n1 : uint32_t; n2 : uint32_t); ``` {% endcode %} Ahora utiliza mig para generar el código del servidor y del cliente que podrán comunicarse entre sí para llamar a la función Restar: ```bash mig -header myipcUser.h -sheader myipcServer.h myipc.defs ``` Se crearán varios archivos nuevos en el directorio actual. En los archivos **`myipcServer.c`** y **`myipcServer.h`** se puede encontrar la declaración y definición de la estructura **`SERVERPREFmyipc_subsystem`**, que básicamente define la función a llamar en función del ID del mensaje recibido (indicamos un número de inicio de 500): {% tabs %} {% tab title="myipcServer.c" %} ```c /* Description of this subsystem, for use in direct RPC */ const struct SERVERPREFmyipc_subsystem SERVERPREFmyipc_subsystem = { myipc_server_routine, 500, // start ID 501, // end ID (mach_msg_size_t)sizeof(union __ReplyUnion__SERVERPREFmyipc_subsystem), (vm_address_t)0, { { (mig_impl_routine_t) 0, // Function to call (mig_stub_routine_t) _XSubtract, 3, 0, (routine_arg_descriptor_t)0, (mach_msg_size_t)sizeof(__Reply__Subtract_t)}, } }; ``` {% tab title="myipcServer.h" %} ```c #ifndef myipcServer_h #define myipcServer_h #include #include #include #include #include extern mach_port_t myipc_server_port; kern_return_t myipc_server(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP); boolean_t myipc_server_demux(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP); #endif /* myipcServer_h */ ``` {% endtab %} ```c /* Description of this subsystem, for use in direct RPC */ extern const struct SERVERPREFmyipc_subsystem { mig_server_routine_t server; /* Server routine */ mach_msg_id_t start; /* Min routine number */ mach_msg_id_t end; /* Max routine number + 1 */ unsigned int maxsize; /* Max msg size */ vm_address_t reserved; /* Reserved */ struct routine_descriptor /* Array of routine descriptors */ routine[1]; } SERVERPREFmyipc_subsystem; ``` {% endtab %} {% endtabs %} Basándose en la estructura anterior, la función **`myipc_server_routine`** obtendrá el **ID del mensaje** y devolverá la función adecuada para llamar: ```c mig_external mig_routine_t myipc_server_routine (mach_msg_header_t *InHeadP) { int msgh_id; msgh_id = InHeadP->msgh_id - 500; if ((msgh_id > 0) || (msgh_id < 0)) return 0; return SERVERPREFmyipc_subsystem.routine[msgh_id].stub_routine; } ``` En este ejemplo solo hemos definido 1 función en las definiciones, pero si hubiéramos definido más funciones, estarían dentro del array de **`SERVERPREFmyipc_subsystem`** y la primera se asignaría al ID **500**, la segunda al ID **501**... En realidad, es posible identificar esta relación en la estructura **`subsystem_to_name_map_myipc`** de **`myipcServer.h`**: ```c #ifndef subsystem_to_name_map_myipc #define subsystem_to_name_map_myipc \ { "Subtract", 500 } #endif ``` Finalmente, otra función importante para hacer que el servidor funcione será **`myipc_server`**, que es la que realmente **llamará a la función** relacionada con el id recibido:
mig_external boolean_t myipc_server
(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP)
{
/*
* typedef struct {
* 	mach_msg_header_t Head;
* 	NDR_record_t NDR;
* 	kern_return_t RetCode;
* } mig_reply_error_t;
*/

mig_routine_t routine;

OutHeadP->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REPLY(InHeadP->msgh_bits), 0);
OutHeadP->msgh_remote_port = InHeadP->msgh_reply_port;
/* Tamaño mínimo: se actualizará si es diferente */
OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t);
OutHeadP->msgh_local_port = MACH_PORT_NULL;
OutHeadP->msgh_id = InHeadP->msgh_id + 100;
OutHeadP->msgh_reserved = 0;

if ((InHeadP->msgh_id > 500) || (InHeadP->msgh_id < 500) ||
	    ((routine = SERVERPREFmyipc_subsystem.routine[InHeadP->msgh_id - 500].stub_routine) == 0)) {
		((mig_reply_error_t *)OutHeadP)->NDR = NDR_record;
((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID;
return FALSE;
}
	(*routine) (InHeadP, OutHeadP);
	return TRUE;
}
Verifique las líneas previamente resaltadas que acceden a la función a llamar por ID. A continuación se muestra el código para crear un **servidor** y un **cliente** simples donde el cliente puede llamar a la función Restar del servidor: {% tabs %} {% tab title="myipc_server.c" %} ```c // gcc myipc_server.c myipcServer.c -o myipc_server #include #include #include #include "myipcServer.h" kern_return_t SERVERPREFSubtract(mach_port_t server_port, uint32_t n1, uint32_t n2) { printf("Received: %d - %d = %d\n", n1, n2, n1 - n2); return KERN_SUCCESS; } int main() { mach_port_t port; kern_return_t kr; // Register the mach service kr = bootstrap_check_in(bootstrap_port, "xyz.hacktricks.mig", &port); if (kr != KERN_SUCCESS) { printf("bootstrap_check_in() failed with code 0x%x\n", kr); return 1; } // myipc_server is the function that handles incoming messages (check previous exlpanation) mach_msg_server(myipc_server, sizeof(union __RequestUnion__SERVERPREFmyipc_subsystem), port, MACH_MSG_TIMEOUT_NONE); } ``` {% tab title="myipc_client.c" %} ```c #include #include #include #include "myipc.h" int main(int argc, char *argv[]) { mach_port_t server_port; kern_return_t kr; char *message = "Hello, server!"; char reply[256]; // Look up the server port kr = bootstrap_look_up(bootstrap_port, "com.example.myipc_server", &server_port); if (kr != KERN_SUCCESS) { fprintf(stderr, "Failed to look up server port: %s\n", mach_error_string(kr)); exit(1); } // Send a message to the server kr = myipc_send_message(server_port, message, reply, sizeof(reply)); if (kr != KERN_SUCCESS) { fprintf(stderr, "Failed to send message: %s\n", mach_error_string(kr)); exit(1); } printf("Received reply: %s\n", reply); return 0; } ``` {% endtab %} ```c // gcc myipc_client.c myipcUser.c -o myipc_client #include #include #include #include #include #include "myipcUser.h" int main() { // Lookup the receiver port using the bootstrap server. mach_port_t port; kern_return_t kr = bootstrap_look_up(bootstrap_port, "xyz.hacktricks.mig", &port); if (kr != KERN_SUCCESS) { printf("bootstrap_look_up() failed with code 0x%x\n", kr); return 1; } printf("Port right name %d\n", port); USERPREFSubtract(port, 40, 2); } ``` ### Análisis binario Dado que muchos binarios ahora utilizan MIG para exponer puertos mach, es interesante saber cómo **identificar que se utilizó MIG** y las **funciones que MIG ejecuta** con cada ID de mensaje. [**jtool2**](../../macos-apps-inspecting-debugging-and-fuzzing/#jtool2) puede analizar la información de MIG de un binario Mach-O indicando el ID del mensaje e identificando la función a ejecutar: ```bash jtool2 -d __DATA.__const myipc_server | grep MIG ``` Se mencionó anteriormente que la función que se encargará de **llamar a la función correcta dependiendo del ID del mensaje recibido** es `myipc_server`. Sin embargo, normalmente no tendrás los símbolos del binario (sin nombres de funciones), por lo que es interesante **ver cómo se ve descompilado** ya que siempre será muy similar (el código de esta función es independiente de las funciones expuestas): {% tabs %} {% tab title="myipc_server descompilado 1" %}
int _myipc_server(int arg0, int arg1) {
var_10 = arg0;
var_18 = arg1;
// Instrucciones iniciales para encontrar los punteros de función adecuados
*(int32_t *)var_18 = *(int32_t *)var_10 & 0x1f;
*(int32_t *)(var_18 + 0x8) = *(int32_t *)(var_10 + 0x8);
*(int32_t *)(var_18 + 0x4) = 0x24;
*(int32_t *)(var_18 + 0xc) = 0x0;
*(int32_t *)(var_18 + 0x14) = *(int32_t *)(var_10 + 0x14) + 0x64;
*(int32_t *)(var_18 + 0x10) = 0x0;
if (*(int32_t *)(var_10 + 0x14) <= 0x1f4 && *(int32_t *)(var_10 + 0x14) >= 0x1f4) {
rax = *(int32_t *)(var_10 + 0x14);
// Llamada a sign_extend_64 que puede ayudar a identificar esta función
// Esto almacena en rax el puntero a la llamada que debe ser realizada
// Verificar el uso de la dirección 0x100004040 (array de direcciones de funciones)
// 0x1f4 = 500 (el ID de inicio)
            rax = *(sign_extend_64(rax - 0x1f4) * 0x28 + 0x100004040);
            var_20 = rax;
// If - else, el if devuelve falso, mientras que el else llama a la función correcta y devuelve verdadero
            if (rax == 0x0) {
                    *(var_18 + 0x18) = **_NDR_record;
*(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
var_4 = 0x0;
}
else {
// Dirección calculada que llama a la función adecuada con 2 argumentos
                    (var_20)(var_10, var_18);
                    var_4 = 0x1;
}
}
else {
*(var_18 + 0x18) = **_NDR_record;
*(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
var_4 = 0x0;
}
rax = var_4;
return rax;
}
{% endtab %} {% tab title="myipc_server descompilado 2" %} Esta es la misma función descompilada en una versión gratuita diferente de Hopper:
int _myipc_server(int arg0, int arg1) {
r31 = r31 - 0x40;
saved_fp = r29;
stack[-8] = r30;
var_10 = arg0;
var_18 = arg1;
// Instrucciones iniciales para encontrar los punteros de función adecuados
*(int32_t *)var_18 = *(int32_t *)var_10 & 0x1f | 0x0;
*(int32_t *)(var_18 + 0x8) = *(int32_t *)(var_10 + 0x8);
*(int32_t *)(var_18 + 0x4) = 0x24;
*(int32_t *)(var_18 + 0xc) = 0x0;
*(int32_t *)(var_18 + 0x14) = *(int32_t *)(var_10 + 0x14) + 0x64;
*(int32_t *)(var_18 + 0x10) = 0x0;
r8 = *(int32_t *)(var_10 + 0x14);
r8 = r8 - 0x1f4;
if (r8 > 0x0) {
if (CPU_FLAGS & G) {
r8 = 0x1;
}
}
if ((r8 & 0x1) == 0x0) {
r8 = *(int32_t *)(var_10 + 0x14);
r8 = r8 - 0x1f4;
if (r8 < 0x0) {
if (CPU_FLAGS & L) {
r8 = 0x1;
}
}
if ((r8 & 0x1) == 0x0) {
r8 = *(int32_t *)(var_10 + 0x14);
// 0x1f4 = 500 (el ID de inicio)
                    r8 = r8 - 0x1f4;
                    asm { smaddl     x8, w8, w9, x10 };
r8 = *(r8 + 0x8);
var_20 = r8;
r8 = r8 - 0x0;
if (r8 != 0x0) {
if (CPU_FLAGS & NE) {
r8 = 0x1;
}
}
// Mismo if else que en la versión anterior
// Verificar el uso de la dirección 0x100004040 (array de direcciones de funciones)
                    if ((r8 & 0x1) == 0x0) {
                            *(var_18 + 0x18) = **0x100004000;
                            *(int32_t *)(var_18 + 0x20) = 0xfffffed1;
var_4 = 0x0;
}
else {
// Llamada a la dirección calculada donde debería estar la función
                            (var_20)(var_10, var_18);
                            var_4 = 0x1;
}
}
else {
*(var_18 + 0x18) = **0x100004000;
*(int32_t *)(var_18 + 0x20) = 0xfffffed1;
var_4 = 0x0;
}
}
else {
*(var_18 + 0x18) = **0x100004000;
*(int32_t *)(var_18 + 0x20) = 0xfffffed1;
var_4 = 0x0;
}
r0 = var_4;
return r0;
}

{% endtab %} {% endtabs %} En realidad, si vas a la función **`0x100004000`**, encontrarás el array de estructuras **`routine_descriptor`**. El primer elemento de la estructura es la **dirección** donde se implementa la **función**, y la **estructura ocupa 0x28 bytes**, por lo que cada 0x28 bytes (a partir del byte 0) puedes obtener 8 bytes y esa será la **dirección de la función** que se llamará:
Estos datos se pueden extraer [**usando este script de Hopper**](https://github.com/knightsc/hopper/blob/master/scripts/MIG%20Detect.py).
☁️ 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**](https://github.com/sponsors/carlospolop)! * Descubre [**La Familia PEASS**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family) * Obtén el [**merchandising oficial de PEASS y HackTricks**](https://peass.creator-spring.com) * **Únete al** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de Telegram**](https://t.me/peass) o **sígueme en** **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.** * **Comparte tus trucos de hacking enviando PRs al** [**repositorio de hacktricks**](https://github.com/carlospolop/hacktricks) **y al** [**repositorio de hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).