macOS MIG - Mach Interface Generator
Informações Básicas
O MIG foi criado para simplificar o processo de criação de código Mach IPC. Basicamente, ele gera o código necessário para o servidor e o cliente se comunicarem com uma definição fornecida. Mesmo que o código gerado seja feio, um desenvolvedor só precisará importá-lo e seu código será muito mais simples do que antes.
A definição é especificada na Linguagem de Definição de Interface (IDL) usando a extensão .defs
.
Essas definições têm 5 seções:
Declaração de subsistema: A palavra-chave subsistema é usada para indicar o nome e o id. Também é possível marcá-lo como
KernelServer
se o servidor deve ser executado no kernel.Inclusões e importações: O MIG usa o pré-processador C, então é capaz de usar importações. Além disso, é possível usar
uimport
esimport
para código gerado pelo usuário ou servidor.Declarações de tipo: É possível definir tipos de dados, embora geralmente importe
mach_types.defs
estd_types.defs
. Para tipos personalizados, pode ser usada alguma sintaxe:[i
n/out]tran
: Função que precisa ser traduzida de uma mensagem de entrada ou para uma mensagem de saídac[user/server]type
: Mapeamento para outro tipo C.destructor
: Chama esta função quando o tipo é liberado.
Operações: Estas são as definições dos métodos RPC. Existem 5 tipos diferentes:
routine
: Espera respostasimpleroutine
: Não espera respostaprocedure
: Espera respostasimpleprocedure
: Não espera respostafunction
: Espera resposta
Exemplo
Crie um arquivo de definição, neste caso com uma função muito simples:
Observe que o primeiro argumento é a porta a ser vinculada e o MIG irá lidar automaticamente com a porta de resposta (a menos que seja chamado mig_get_reply_port()
no código do cliente). Além disso, o ID das operações será sequencial começando pelo ID do subsistema indicado (então, se uma operação for descontinuada, ela será excluída e skip
é usado para continuar usando seu ID).
Agora use o MIG para gerar o código do servidor e do cliente que serão capazes de se comunicar entre si para chamar a função Subtrair:
Vários novos arquivos serão criados no diretório atual.
Você pode encontrar um exemplo mais complexo em seu sistema com: mdfind mach_port.defs
E você pode compilá-lo a partir da mesma pasta do arquivo com: mig -DLIBSYSCALL_INTERFACE mach_ports.defs
Nos arquivos myipcServer.c
e myipcServer.h
você pode encontrar a declaração e definição da struct SERVERPREFmyipc_subsystem
, que basicamente define a função a ser chamada com base no ID da mensagem recebida (indicamos um número inicial de 500):
macOS MIG (Mach Interface Generator)
O macOS MIG (Mach Interface Generator) é uma ferramenta que gera interfaces de comunicação entre processos para comunicação entre processos em sistemas baseados em Mach. Ele é amplamente utilizado para comunicação entre processos em sistemas macOS e iOS. O MIG gera código C que lida com a comunicação entre processos, permitindo que os processos se comuniquem de forma eficiente e segura. É importante entender como o MIG funciona para aproveitar ao máximo a comunicação entre processos em sistemas macOS.
Exemplo de uso do MIG
Aqui está um exemplo simples de como usar o MIG para comunicação entre processos em sistemas macOS:
Defina as mensagens que os processos podem enviar e receber.
Compile o arquivo de definição de interface usando o MIG.
Implemente o código do servidor e do cliente para lidar com as mensagens definidas.
Compile e execute o servidor e o cliente para iniciar a comunicação entre processos.
Compreender o funcionamento do MIG e como usá-lo adequadamente pode ser útil para desenvolver aplicativos que se comunicam de forma eficiente e segura em sistemas macOS e iOS.
Com base na estrutura anterior, a função myipc_server_routine
receberá o ID da mensagem e retornará a função apropriada a ser chamada:
Neste exemplo, apenas definimos 1 função nas definições, mas se tivéssemos definido mais funções, elas estariam dentro do array de SERVERPREFmyipc_subsystem
e a primeira teria sido atribuída ao ID 500, a segunda ao ID 501...
Se a função fosse esperada para enviar uma resposta, a função mig_internal kern_return_t __MIG_check__Reply__<nome>
também existiria.
Na verdade, é possível identificar essa relação na struct subsystem_to_name_map_myipc
de myipcServer.h
(subsystem_to_name_map_***
em outros arquivos):
Finalmente, outra função importante para fazer o servidor funcionar será myipc_server
, que é aquela que realmente chama a função relacionada ao ID recebido:
Verifique as linhas anteriormente destacadas acessando a função a ser chamada por ID.
O código a seguir cria um servidor e um cliente simples onde o cliente pode chamar as funções Subtrair do servidor:
O registro NDR
O registro NDR é exportado por libsystem_kernel.dylib
e é uma estrutura que permite ao MIG transformar dados de forma que seja agnóstico ao sistema no qual está sendo utilizado, já que o MIG foi projetado para ser usado entre diferentes sistemas (e não apenas na mesma máquina).
Isso é interessante porque se o _NDR_record
for encontrado em um binário como uma dependência (jtool2 -S <binary> | grep NDR
ou nm
), significa que o binário é um cliente ou servidor MIG.
Além disso, os servidores MIG têm a tabela de despacho em __DATA.__const
(ou em __CONST.__constdata
no kernel do macOS e __DATA_CONST.__const
em outros kernels *OS). Isso pode ser extraído com o jtool2
.
E os clientes MIG usarão o __NDR_record
para enviar com __mach_msg
para os servidores.
Análise Binária
jtool
Como muitos binários agora usam MIG para expor portas mach, é interessante saber como identificar que o MIG foi usado e as funções que o MIG executa com cada ID de mensagem.
jtool2 pode analisar informações do MIG de um binário Mach-O indicando o ID da mensagem e identificando a função a ser executada:
Além disso, as funções MIG são apenas invólucros da função real que é chamada, o que significa que ao obter seu desmontagem e procurar por BL, você pode ser capaz de encontrar a função real sendo chamada:
Assembly
Foi mencionado anteriormente que a função que irá chamar a função correta dependendo do ID da mensagem recebida era myipc_server
. No entanto, geralmente você não terá os símbolos do binário (nomes de funções), então é interessante ver como ela se parece decompilada, pois sempre será muito semelhante (o código desta função é independente das funções expostas):
Na verdade, se você for para a função 0x100004000
, você encontrará o array de structs routine_descriptor
. O primeiro elemento da struct é o endereço onde a função é implementada, e a struct tem 0x28 bytes, então a cada 0x28 bytes (começando do byte 0) você pode obter 8 bytes e esse será o endereço da função que será chamada:
Esses dados podem ser extraídos usando este script do Hopper.
Depuração
O código gerado pelo MIG também chama kernel_debug
para gerar logs sobre operações na entrada e saída. É possível verificá-los usando trace
ou kdv
: kdv all | grep MIG
Referências
Last updated