macOS MIG - Mach Interface Generator

Reading time: 10 minutes

tip

Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Basic Information

MIG iliumbwa ili kurahisisha mchakato wa uundaji wa Mach IPC. Kimsingi inazalisha msimbo unaohitajika kwa server na mteja kuwasiliana na ufafanuzi uliopewa. Hata kama msimbo uliozalishwa ni mbaya, mendelezi atahitaji tu kuingiza na msimbo wake utakuwa rahisi zaidi kuliko kabla.

Ufafanuzi umeainishwa katika Lugha ya Ufafanuzi wa Kiolesura (IDL) kwa kutumia kiambishi cha .defs.

Mafafanuzi haya yana sehemu 5:

  • Tangazo la subsystem: Neno muhimu subsystem linatumika kuashiria jina na id. Pia inawezekana kuashiria kama KernelServer ikiwa server inapaswa kukimbia katika kernel.
  • Inclusions and imports: MIG inatumia C-preprocessor, hivyo ina uwezo wa kutumia imports. Aidha, inawezekana kutumia uimport na simport kwa msimbo ulioandikwa na mtumiaji au server.
  • Matangazo ya aina: Inawezekana kufafanua aina za data ingawa kawaida itakuwa inafanya import ya mach_types.defs na std_types.defs. Kwa zile za kawaida baadhi ya sintaks inaweza kutumika:
  • [in/out]tran: Kazi ambayo inahitaji kutafsiriwa kutoka ujumbe unaoingia au kwenda ujumbe unaotoka
  • c[user/server]type: Mchoro kwa aina nyingine ya C.
  • destructor: Piga simu kazi hii wakati aina inachukuliwa.
  • Operesheni: Hizi ni ufafanuzi wa mbinu za RPC. Kuna aina 5 tofauti:
  • routine: Inatarajia jibu
  • simpleroutine: Haitarajii jibu
  • procedure: Inatarajia jibu
  • simpleprocedure: Haitarajii jibu
  • function: Inatarajia jibu

Example

Create a definition file, in this case with a very simple function:

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

Kumbuka kwamba hoja ya kwanza ni bandari ya kuunganisha na MIG itashughulikia bandari ya majibu kiotomatiki (isipokuwa unaita mig_get_reply_port() katika msimbo wa mteja). Aidha, ID ya operesheni itakuwa mfuatano ikianza na ID ya mfumo ulioonyeshwa (hivyo ikiwa operesheni imeondolewa inafutwa na skip inatumika ili bado kutumia ID yake).

Sasa tumia MIG kuunda msimbo wa seva na mteja ambao utaweza kuwasiliana kati yao ili kuita kazi ya Subtract:

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

Kadhaa ya faili mpya zitaundwa katika saraka ya sasa.

tip

Unaweza kupata mfano mgumu zaidi katika mfumo wako kwa kutumia: mdfind mach_port.defs
Na unaweza kuikamilisha kutoka kwenye folda ile ile kama faili kwa kutumia: mig -DLIBSYSCALL_INTERFACE mach_ports.defs

Katika faili myipcServer.c na myipcServer.h unaweza kupata tangazo na ufafanuzi wa struct SERVERPREFmyipc_subsystem, ambayo kimsingi inafafanua kazi ya kuita kulingana na kitambulisho cha ujumbe kilichopokelewa (tulionyesha nambari ya kuanzia ya 500):

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

Kulingana na muundo wa awali, kazi myipc_server_routine itapata kitambulisho cha ujumbe na kurudisha kazi sahihi ya kuita:

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

Katika mfano huu tumefafanua tu kazi 1 katika maelezo, lakini kama tungeweza kufafanua kazi zaidi, zingekuwa ndani ya array ya SERVERPREFmyipc_subsystem na ya kwanza ingekuwa imepewa ID 500, ya pili ingekuwa na ID 501...

Ikiwa kazi ilitarajiwa kutuma reply kazi mig_internal kern_return_t __MIG_check__Reply__<name> pia ingekuwepo.

Kwa kweli inawezekana kubaini uhusiano huu katika struct subsystem_to_name_map_myipc kutoka myipcServer.h (**subsystem*to_name_map*\***** katika faili zingine):

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

Hatimaye, kazi nyingine muhimu ili kufanya seva ifanye kazi itakuwa myipc_server, ambayo ndiyo itakayofanya kuita kazi inayohusiana na kitambulisho kilichopokelewa:

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;
/* Ukubwa wa chini: routine() itasasisha ikiwa tofauti */
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;
}

Angalia mistari iliyosisitizwa hapo awali inayoingia kwenye kazi ya kuita kwa ID.

Ifuatayo ni msimbo wa kuunda seva na mteja ambapo mteja anaweza kuita kazi ya Kupunguza kutoka kwa seva:

c
// 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 inasafirishwa na libsystem_kernel.dylib, na ni struct inayoruhusu MIG kubadilisha data ili iwe huru na mfumo ambao inatumika kwani MIG ilidhaniwa kutumika kati ya mifumo tofauti (na sio tu kwenye mashine moja).

Hii ni ya kuvutia kwa sababu ikiwa _NDR_record inapatikana katika binary kama utegemezi (jtool2 -S <binary> | grep NDR au nm), inamaanisha kwamba binary ni mteja au seva ya MIG.

Zaidi ya hayo, seva za MIG zina meza ya dispatch katika __DATA.__const (au katika __CONST.__constdata katika kernel ya macOS na __DATA_CONST.__const katika kernel nyingine za *OS). Hii inaweza kutolewa kwa jtool2.

Na wateja wa MIG watatumia __NDR_record kutuma na __mach_msg kwa seva.

Uchambuzi wa Binary

jtool

Kama binaries nyingi sasa zinatumia MIG kufichua mach ports, ni ya kuvutia kujua jinsi ya kutambua kwamba MIG ilitumika na kazi ambazo MIG inatekeleza na kila kitambulisho cha ujumbe.

jtool2 inaweza kuchambua taarifa za MIG kutoka kwa binary ya Mach-O ikionyesha kitambulisho cha ujumbe na kutambua kazi ya kutekeleza:

bash
jtool2 -d __DATA.__const myipc_server | grep MIG

Zaidi ya hayo, kazi za MIG ni vifungashio vya kazi halisi inayoitwa, ambayo inamaanisha kwamba kupata usambazaji wake na kutafuta BL unaweza kukuwezesha kupata kazi halisi inayoitwa:

bash
jtool2 -d __DATA.__const myipc_server | grep BL

Assembly

Ilielezwa awali kwamba kazi ambayo itashughulikia kuita kazi sahihi kulingana na kitambulisho cha ujumbe kilichopokelewa ilikuwa myipc_server. Hata hivyo, kwa kawaida hutakuwa na alama za binary (hakuna majina ya kazi), hivyo ni ya kuvutia kuangalia jinsi inavyoonekana baada ya kutolewa kwani itakuwa karibu sana (kanuni ya kazi hii ni huru kutoka kwa kazi zilizowekwa):

int _myipc_server(int arg0, int arg1) {
var_10 = arg0;
var_18 = arg1;
// Maagizo ya awali ya kutafuta viashiria sahihi vya kazi
*(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);
// Kuitisha sign_extend_64 ambayo inaweza kusaidia kutambua kazi hii
// Hii inahifadhi katika rax kiashiria cha wito ambacho kinahitaji kuitwa
// Angalia matumizi ya anwani 0x100004040 (array ya anwani za kazi)
// 0x1f4 = 500 (kitambulisho cha kuanzia)
            rax = *(sign_extend_64(rax - 0x1f4) * 0x28 + 0x100004040);
            var_20 = rax;
// Ikiwa - vinginevyo, ikiwa inarudi uongo, wakati vinginevyo inaita kazi sahihi na inarudi kweli
            if (rax == 0x0) {
                    *(var_18 + 0x18) = **_NDR_record;
*(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
var_4 = 0x0;
}
else {
// Anwani iliyohesabiwa inayoiita kazi sahihi na hoja 2
                    (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;
}

Kwa kweli ikiwa utaenda kwenye kazi 0x100004000 utapata array ya routine_descriptor structs. Kigezo cha kwanza cha struct ni anwani ambapo kazi imeanzishwa, na struct inachukua 0x28 bytes, hivyo kila byte 0x28 (kuanzia byte 0) unaweza kupata byte 8 na hiyo itakuwa anwani ya kazi ambayo itaitwa:

Data hii inaweza kutolewa kwa kutumia script hii ya Hopper.

Debug

Kanuni iliyozalishwa na MIG pia inaita kernel_debug ili kuzalisha kumbukumbu kuhusu operesheni kwenye kuingia na kutoka. Inawezekana kuangalia hizo kwa kutumia trace au kdv: kdv all | grep MIG

References

tip

Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks