MIG is geskep om die proses van Mach IPC-kode-skepping te vereenvoudig. Dit genereer basies die benodigde kode vir die bediener en klient om met 'n gegewe definisie te kommunikeer. Selfs as die gegenereerde kode lelik is, sal 'n ontwikkelaar dit net hoef in te voer en sy kode sal baie eenvoudiger wees as voorheen.
Die definisie word gespesifiseer in die Interface Definisie Taal (IDL) deur die gebruik van die .defs-uitbreiding.
Hierdie definisies het 5 afdelings:
Onderstelselverklaring: Die sleutelwoord onderstelsel word gebruik om die naam en die id aan te dui. Dit is ook moontlik om dit as KernelServer te merk as die bediener in die kernel moet loop.
Insluitings en invoere: MIG gebruik die C-preprosessor, sodat dit invoere kan gebruik. Daarbenewens is dit moontlik om uimport en simport vir gebruiker- of bedieners gegenereerde kode te gebruik.
Tipeverklarings: Dit is moontlik om datatipes te definieer, alhoewel dit gewoonlik mach_types.defs en std_types.defs sal invoer. Vir aangepaste tipes kan 'n paar sintaksis gebruik word:
[in/out]tran: Funksie wat van 'n inkomende of na 'n uitgaande boodskap vertaal moet word
c[user/server]type: Koppeling na 'n ander C-tipe.
destructor: Roep hierdie funksie aan wanneer die tipe vrygestel word.
Operasies: Dit is die definisies van die RPC-metodes. Daar is 5 verskillende tipes:
routine: Verwag antwoord
simpleroutine: Verwag nie antwoord nie
procedure: Verwag antwoord
simpleprocedure: Verwag nie antwoord nie
function: Verwag antwoord
Voorbeeld
Skep 'n definisie-lêer, in hierdie geval met 'n baie eenvoudige funksie:
myipc.defs
subsystem myipc 500; // Arbitrary name and iduserprefix USERPREF; // Prefix for created functions in the clientserverprefix SERVERPREF; // Prefix for created functions in the server#include<mach/mach_types.defs>#include<mach/std_types.defs>simpleroutineSubtract(server_port : mach_port_t;n1 : uint32_t;n2 : uint32_t);
Merk op dat die eerste argument die poort is om te bind en MIG sal outomaties die antwoordpoort hanteer (tensy mig_get_reply_port() geroep word in die kliëntkode). Verder sal die ID van die operasiesopeenvolgend wees beginnende met die aangeduide subsisteem ID (so as 'n operasie verouderd is, word dit verwyder en skip word gebruik om steeds sy ID te gebruik).
Gebruik nou MIG om die bediener- en kliëntkode te genereer wat in staat sal wees om binne mekaar te kommunikeer om die Aftrek-funksie te roep:
Verskeie nuwe lêers sal geskep word in die huidige gids.
Jy kan 'n meer komplekse voorbeeld in jou stelsel vind met: mdfind mach_port.defs
En jy kan dit van dieselfde gids as die lêer kompileer met: mig -DLIBSYSCALL_INTERFACE mach_ports.defs
In die lêers myipcServer.c en myipcServer.h kan jy die deklarasie en definisie van die struktuur SERVERPREFmyipc_subsystem vind, wat basies die funksie definieer om te roep gebaseer op die ontvangsboodskap-ID (ons het 'n beginnommer van 500 aangedui):
/* Description of this subsystem, for use in direct RPC */conststruct SERVERPREFmyipc_subsystem SERVERPREFmyipc_subsystem = {myipc_server_routine,500, // start ID501, // 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)},}};
macOS MIG (Mach Interface Generator)
MIG is a tool used to define inter-process communication (IPC) for macOS. It generates client-server communication code based on the interfaces defined in a .defs file. This allows processes to communicate with each other using messages.
In this example, myipc_server is the function that will be called when a message is received by the server. The server processes the message and sends a response back to the client.
MIG simplifies the process of defining IPC interfaces and handling messages between processes in macOS.
/* Description of this subsystem, for use in direct RPC */externconststruct 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 */unsignedint maxsize; /* Max msg size */vm_address_t reserved; /* Reserved */struct routine_descriptor /* Array of routine descriptors */routine[1];} SERVERPREFmyipc_subsystem;
Gebaseer op die vorige struktuur sal die funksie myipc_server_routine die boodskap ID kry en die korrekte funksie teruggee om te roep:
In hierdie voorbeeld het ons slegs 1 funksie in die definisies gedefinieer, maar as ons meer funksies sou definieer, sou hulle binne die array van SERVERPREFmyipc_subsystem gewees het en die eerste een sou aan die ID 500 toegewys gewees het, die tweede een aan die ID 501...
As die funksie verwag het om 'n antwoord te stuur, sou die funksie mig_internal kern_return_t __MIG_check__Reply__<name> ook bestaan.
Eintlik is dit moontlik om hierdie verhouding in die struktuur subsystem_to_name_map_myipc van myipcServer.h (subsystem_to_name_map_*** in ander lêers) te identifiseer:
Uiteindelik, nog 'n belangrike funksie om die bediener te laat werk, sal myipc_server wees, wat die een is wat werklik die funksie oproep wat verband hou met die ontvangste id:
Kontroleer die voorheen uitgeligte lyne deur toegang tot die funksie om te roep volgens ID.
Die volgende is die kode om 'n eenvoudige bediener en kliënt te skep waar die kliënt die funksies van die bediener kan oproep:
// gcc myipc_server.c myipcServer.c -o myipc_server#include<stdio.h>#include<mach/mach.h>#include<servers/bootstrap.h>#include"myipcServer.h"kern_return_tSERVERPREFSubtract(mach_port_t server_port,uint32_t n1,uint32_t n2){printf("Received: %d - %d = %d\n", n1, n2, n1 - n2);return KERN_SUCCESS;}intmain() {mach_port_t port;kern_return_t kr;// Register the mach servicekr =bootstrap_check_in(bootstrap_port,"xyz.hacktricks.mig",&port);if (kr != KERN_SUCCESS) {printf("bootstrap_check_in() failed with code 0x%x\n", kr);return1;}// 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);}
Afrikaans Translation:
#include<stdio.h>#include<mach/mach.h>#include<servers/bootstrap.h>#include"myipc.h"intmain() {kern_return_t kr;mach_port_t server_port;myipc_msg_t msg; kr =bootstrap_look_up(bootstrap_port,"com.example.myipc_server",&server_port);if (kr != KERN_SUCCESS) {printf("Failed to look up server port: %s\n", mach_error_string(kr));return1; }msg.hdr.msgh_bits =MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);msg.hdr.msgh_size =sizeof(msg);msg.hdr.msgh_remote_port = server_port;msg.hdr.msgh_local_port = MACH_PORT_NULL;msg.hdr.msgh_id =0;msg.body.msgh_descriptor_count =0; kr = mach_msg(&msg.hdr, MACH_SEND_MSG, msg.hdr.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (kr != KERN_SUCCESS) {printf("Failed to send message: %s\n", mach_error_string(kr));return1; }return0;}
// gcc myipc_client.c myipcUser.c -o myipc_client#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<mach/mach.h>#include<servers/bootstrap.h>#include"myipcUser.h"intmain() {// 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);return1;}printf("Port right name %d\n", port);USERPREFSubtract(port,40,2);}
Die NDR_record
Die NDR_record word uitgevoer deur libsystem_kernel.dylib, en dit is 'n struktuur wat MIG toelaat om data te transformeer sodat dit onverskillig is van die stelsel waarvoor dit gebruik word aangesien MIG gedink was om tussen verskillende stelsels gebruik te word (en nie net op dieselfde masjien nie).
Dit is interessant omdat as _NDR_record gevind word in 'n binêre lêer as 'n afhanklikheid (jtool2 -S <binary> | grep NDR of nm), beteken dit dat die binêre lêer 'n MIG-kliënt of -bediener is.
Verder het MIG-bedieners die verspreidingstabel in __DATA.__const (of in __CONST.__constdata in macOS-kernel en __DATA_CONST.__const in ander *OS-kernelle). Dit kan gedump word met jtool2.
En MIG-kliënte sal die __NDR_record gebruik om met __mach_msg na die bedieners te stuur.
Binêre Analise
jtool
Aangesien baie binêre lêers nou MIG gebruik om mach-poorte bloot te stel, is dit interessant om te weet hoe om te identifiseer dat MIG gebruik is en die funksies wat MIG uitvoer met elke boodskap-ID.
jtool2 kan MIG-inligting van 'n Mach-O binêre lêer ontleding wat die boodskap-ID aandui en die funksie identifiseer om uit te voer:
jtool2-d__DATA.__constmyipc_server|grepMIG
Verder is MIG-funksies net omhulsels van die werklike funksie wat opgeroep word, wat beteken dat deur sy disassemblage te kry en te soek vir BL, jy moontlik die werklike funksie wat opgeroep word, kan vind:
jtool2-d__DATA.__constmyipc_server|grepBL
Monteer
Dit is voorheen genoem dat die funksie wat sal sorg vir die oproep van die korrekte funksie afhangende van die ontvangste boodskap-IDmyipc_server was. Gewoonlik sal jy egter nie die simbole van die binêre lêer hê nie (geen funksienames nie), dus is dit interessant om te kyk hoe dit gedeaktiveer lyk aangesien dit altyd baie soortgelyk sal wees (die kode van hierdie funksie is onafhanklik van die blootgestelde funksies):
int_myipc_server(int arg0,int arg1) {var_10 = arg0;var_18 = arg1;// Aanvanklike instruksies om die regte funksiepunte te vind*(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);// Oproep na sign_extend_64 wat kan help om hierdie funksie te identifiseer// Dit stoor in rax die wyser na die oproep wat gemaak moet word// Kyk na die gebruik van die adres 0x100004040 (funksie-adresse-reeks)// 0x1f4 = 500 (die begin-ID) rax =*(sign_extend_64(rax -0x1f4)*0x28+0x100004040); var_20 = rax;// If - else, die if gee vals terug, terwyl die else die regte funksie oproep en waar teruggeeif (rax ==0x0) {*(var_18 +0x18) =**_NDR_record;*(int32_t*)(var_18 +0x20) =0xfffffffffffffed1;var_4 =0x0;}else {// Gekalibreerde adres wat die regte funksie met 2 argumente oproep (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;}
Hierdie is dieselfde funksie gedeaktiveer in 'n ander Hopper gratis weergawe:
int_myipc_server(int arg0,int arg1) {r31 = r31 -0x40;saved_fp = r29;stack[-8] = r30;var_10 = arg0;var_18 = arg1;// Aanvanklike instruksies om die regte funksiepunte te vind*(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 (die begin-ID) 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;}}// Dieselfde if anders as in die vorige weergawe// Kyk na die gebruik van die adres 0x100004040 (funksie-adresse-reeks)if ((r8 &0x1) ==0x0) {*(var_18 +0x18) =**0x100004000;*(int32_t*)(var_18 +0x20) =0xfffffed1;var_4 =0x0;}else {// Oproep na die gekalibreerde adres waar die funksie moet wees (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;}
Eintlik, as jy na die funksie 0x100004000 gaan, sal jy die reeks van routine_descriptor strukture vind. Die eerste element van die struktuur is die adres waar die funksie geïmplementeer is, en die struktuur neem 0x28 byte, dus elke 0x28 byte (beginnend vanaf byte 0) kan jy 8 byte kry en dit sal die adres van die funksie wees wat geroep sal word:
Die kode wat deur MIG gegenereer word, roep ook kernel_debug aan om logboeke oor operasies by in- en uitgang te genereer. Dit is moontlik om hulle te kontroleer met behulp van trace of kdv: kdv all | grep MIG