macOS MIG - Mach Interface Generator

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

Support HackTricks

Basic Information

MIG je kreiran da ** pojednostavi proces kreiranja Mach IPC** koda. U suštini, generiše potrebni kod za server i klijent da komuniciraju sa datom definicijom. Čak i ako je generisani kod ružan, programer će samo morati da ga uveze i njegov kod će biti mnogo jednostavniji nego pre.

Definicija se specificira u jeziku za definiciju interfejsa (IDL) koristeći ekstenziju .defs.

Ove definicije imaju 5 sekcija:

  • Deklaracija pod sistema: Ključna reč subsystem se koristi da označi ime i id. Takođe je moguće označiti ga kao KernelServer ako server treba da radi u kernelu.

  • Uključivanja i uvozi: MIG koristi C-preprocesor, tako da može da koristi uvoze. Štaviše, moguće je koristiti uimport i simport za kod generisan od strane korisnika ili servera.

  • Deklaracije tipova: Moguće je definisati tipove podataka iako obično će uvesti mach_types.defs i std_types.defs. Za prilagođene tipove može se koristiti neka sintaksa:

  • [in/out]tran: Funkcija koja treba da bude prevedena iz dolazne ili u odlaznu poruku

  • c[user/server]type: Mapiranje na drugi C tip.

  • destructor: Pozvati ovu funkciju kada se tip oslobodi.

  • Operacije: Ovo su definicije RPC metoda. Postoji 5 različitih tipova:

  • routine: Očekuje odgovor

  • simpleroutine: Ne očekuje odgovor

  • procedure: Očekuje odgovor

  • simpleprocedure: Ne očekuje odgovor

  • function: Očekuje odgovor

Example

Kreirajte datoteku definicije, u ovom slučaju sa vrlo jednostavnom funkcijom:

myipc.defs
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 <mach/mach_types.defs>
#include <mach/std_types.defs>

simpleroutine Subtract(
server_port :  mach_port_t;
n1          :  uint32_t;
n2          :  uint32_t);

Napomena da je prvi argument port koji se vezuje i MIG će automatski obraditi port za odgovor (osim ako se ne poziva mig_get_reply_port() u klijentskom kodu). Štaviše, ID operacija će biti sekvencijalni počinjući od naznačenog ID-a podsistema (tako da ako je neka operacija zastarela, ona se briše i koristi se skip da bi se i dalje koristio njen ID).

Sada koristite MIG da generišete server i klijentski kod koji će moći da komuniciraju jedni s drugima kako bi pozvali funkciju Oduzmi:

mig -header myipcUser.h -sheader myipcServer.h myipc.defs

Nekoliko novih fajlova biće kreirano u trenutnom direktorijumu.

Možete pronaći složeniji primer u vašem sistemu sa: mdfind mach_port.defs I možete ga kompajlirati iz iste fascikle kao fajl sa: mig -DLIBSYSCALL_INTERFACE mach_ports.defs

U fajlovima myipcServer.c i myipcServer.h možete pronaći deklaraciju i definiciju strukture SERVERPREFmyipc_subsystem, koja u suštini definiše funkciju koja se poziva na osnovu primljenog ID-a poruke (naveli smo početni broj 500):

/* 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)},
}
};

Na osnovu prethodne strukture, funkcija myipc_server_routine će dobiti ID poruke i vratiti odgovarajuću funkciju za pozivanje:

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;
}

U ovom primeru smo definisali samo 1 funkciju u definicijama, ali da smo definisali više funkcija, one bi bile unutar niza SERVERPREFmyipc_subsystem i prva bi bila dodeljena ID-u 500, druga ID-u 501...

Ako se očekivalo da funkcija pošalje reply, funkcija mig_internal kern_return_t __MIG_check__Reply__<name> bi takođe postojala.

U stvari, moguće je identifikovati ovu vezu u strukturi subsystem_to_name_map_myipc iz myipcServer.h (subsystem_to_name_map_*** u drugim datotekama):

#ifndef subsystem_to_name_map_myipc
#define subsystem_to_name_map_myipc \
{ "Subtract", 500 }
#endif

Na kraju, još jedna važna funkcija koja će omogućiti radu servera biće myipc_server, koja će zapravo pozvati funkciju vezanu za primljeni id:

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;
/* Minimalna veličina: routine() će je ažurirati ako je drugačija */
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;
}

Proverite prethodno istaknute linije koje pristupaju funkciji za pozivanje po ID-u.

Sledeći je kod za kreiranje jednostavnog servera i klijenta gde klijent može pozvati funkcije Oduzimanje sa servera:

// 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_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);
}

NDR_record

NDR_record se izvozi iz libsystem_kernel.dylib, i to je struktura koja omogućava MIG-u da transformiše podatke tako da budu agnostični prema sistemu na kojem se koristi, jer je MIG zamišljen da se koristi između različitih sistema (a ne samo na istoj mašini).

To je zanimljivo jer ako se _NDR_record pronađe u binarnom fajlu kao zavisnost (jtool2 -S <binary> | grep NDR ili nm), to znači da je binarni fajl MIG klijent ili server.

Štaviše, MIG serveri imaju dispatch tabelu u __DATA.__const (ili u __CONST.__constdata u macOS kernelu i __DATA_CONST.__const u drugim *OS kernelima). Ovo se može dumpovati sa jtool2.

A MIG klijenti će koristiti __NDR_record da pošalju sa __mach_msg serverima.

Analiza binarnih fajlova

jtool

Kako mnogi binarni fajlovi sada koriste MIG za izlaganje mach portova, zanimljivo je znati kako identifikovati da je MIG korišćen i funkcije koje MIG izvršava sa svakim ID-om poruke.

jtool2 može da analizira MIG informacije iz Mach-O binarnog fajla, ukazujući na ID poruke i identifikujući funkciju koja treba da se izvrši:

jtool2 -d __DATA.__const myipc_server | grep MIG

Pored toga, MIG funkcije su samo omotači stvarne funkcije koja se poziva, što znači da dobijanjem njenog disassembliranja i pretraživanjem za BL možda možete pronaći stvarnu funkciju koja se poziva:

jtool2 -d __DATA.__const myipc_server | grep BL

Assembly

Prethodno je pomenuta funkcija koja će se pobrinuti za pozivanje ispravne funkcije u zavisnosti od primljenog ID-a poruke bila myipc_server. Međutim, obično nećete imati simbole binarnog fajla (nema imena funkcija), pa je zanimljivo proveriti kako izgleda dekompilirana jer će uvek biti vrlo slična (kod ove funkcije je nezavistan od izloženih funkcija):

int _myipc_server(int arg0, int arg1) {
var_10 = arg0;
var_18 = arg1;
// Početne instrukcije za pronalaženje ispravnih pokazivača funkcija
*(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);
// Poziv na sign_extend_64 koji može pomoći u identifikaciji ove funkcije
// Ovo čuva u rax pokazivač na poziv koji treba da se pozove
// Proverite korišćenje adrese 0x100004040 (niz adresa funkcija)
// 0x1f4 = 500 (početni ID)
            rax = *(sign_extend_64(rax - 0x1f4) * 0x28 + 0x100004040);
            var_20 = rax;
// Ako - else, if vraća false, dok else poziva ispravnu funkciju i vraća true
            if (rax == 0x0) {
                    *(var_18 + 0x18) = **_NDR_record;
*(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
var_4 = 0x0;
}
else {
// Izračunata adresa koja poziva ispravnu funkciju sa 2 argumenta
                    (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;
}

U stvari, ako odete na funkciju 0x100004000 naći ćete niz routine_descriptor struktura. Prvi element strukture je adresa gde je funkcija implementirana, a struktura zauzima 0x28 bajtova, tako da svakih 0x28 bajtova (počinjajući od bajta 0) možete dobiti 8 bajtova i to će biti adresa funkcije koja će biti pozvana:

Ovi podaci se mogu izvući koristeći ovaj Hopper skript.

Debug

Kod koji generiše MIG takođe poziva kernel_debug da generiše logove o operacijama na ulazu i izlazu. Moguće je proveriti ih koristeći trace ili kdv: kdv all | grep MIG

References

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

Support HackTricks

Last updated