macOS MIG - Mach Interface Generator
Reading time: 10 minutes
tip
Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.
Osnovne informacije
MIG je kreiran da pojednostavi proces kreiranja Mach IPC koda. U suštini, generiše potrebni kod za komunikaciju servera i klijenta prema datoj definiciji. Č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 podsistema: 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
isimport
za kod generisan od strane korisnika ili servera. - Deklaracije tipova: Moguće je definisati tipove podataka, iako obično uvozi
mach_types.defs
istd_types.defs
. Za prilagođene tipove može se koristiti neka sintaksa: - [i
n/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 odgovorsimpleroutine
: Ne očekuje odgovorprocedure
: Očekuje odgovorsimpleprocedure
: Ne očekuje odgovorfunction
: Očekuje odgovor
Primer
Kreirajte datoteku definicije, u ovom slučaju sa vrlo jednostavnom funkcijom:
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 skip
se koristi da se i dalje koristi 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 Subtract:
mig -header myipcUser.h -sheader myipcServer.h myipc.defs
Nekoliko novih fajlova biće kreirano u trenutnom direktorijumu.
tip
Možete pronaći složeniji primer na vašem sistemu sa: mdfind mach_port.defs
I možete ga kompajlirati iz iste fascikle kao i 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 osnovi 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 odgovor, funkcija mig_internal kern_return_t __MIG_check__Reply__<name>
bi takođe postojala.
Zapravo, 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 koja se poziva 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 tabelu raspodele u __DATA.__const
(ili u __CONST.__constdata
u macOS kernelu i __DATA_CONST.__const
u drugim *OS kernelima). Ovo se može izdumpovati pomoću jtool2
.
A MIG klijenti će koristiti __NDR_record
za slanje sa __mach_msg
ka 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 disasembly-a 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 pomenuto da će funkcija koja će se pobrinuti za pozivanje ispravne funkcije u zavisnosti od primljenog ID-a poruke biti 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 sign_extend_64 koji može pomoći u identifikaciji ove funkcije
// Ovo čuva u rax pokazivač na poziv koji treba da se izvrši
// 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
pronać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:
.png)
.png)
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
tip
Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Podržite HackTricks
- Proverite planove pretplate!
- Pridružite se 💬 Discord grupi ili telegram grupi ili pratite nas na Twitteru 🐦 @hacktricks_live.
- Podelite hakerske trikove slanjem PR-ova na HackTricks i HackTricks Cloud github repozitorijume.