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 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);
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:
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 */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)},}};
/* 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;
Na osnovu prethodne strukture, funkcija myipc_server_routine će dobiti ID poruke i vratiti odgovarajuću funkciju za pozivanje:
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):
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_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);}
// 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);}
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.__constmyipc_server|grepMIG
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.__constmyipc_server|grepBL
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 trueif (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;}
Ovo je ista funkcija dekompilirana u drugačijoj besplatnoj verziji Hoppera:
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:
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