mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-20 10:03:51 +00:00
395 lines
15 KiB
Markdown
395 lines
15 KiB
Markdown
|
# macOS MIG - Gerador de Interface Mach
|
||
|
|
||
|
<details>
|
||
|
|
||
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||
|
|
||
|
* Você trabalha em uma **empresa de segurança cibernética**? Você quer ver sua **empresa anunciada no HackTricks**? ou você quer ter acesso à **última versão do PEASS ou baixar o HackTricks em PDF**? Verifique os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
|
||
|
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||
|
* Adquira o [**swag oficial do PEASS & HackTricks**](https://peass.creator-spring.com)
|
||
|
* **Junte-se ao** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo telegram**](https://t.me/peass) ou **siga-me** no **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||
|
* **Compartilhe seus truques de hacking enviando PRs para o** [**repositório hacktricks**](https://github.com/carlospolop/hacktricks) **e** [**repositório hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||
|
|
||
|
</details>
|
||
|
|
||
|
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 específica. 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.
|
||
|
|
||
|
### Exemplo
|
||
|
|
||
|
Crie um arquivo de definição, neste caso com uma função muito simples:
|
||
|
|
||
|
{% code title="myipc.defs" %}
|
||
|
```cpp
|
||
|
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);
|
||
|
```
|
||
|
{% endcode %}
|
||
|
|
||
|
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 Subtract:
|
||
|
```bash
|
||
|
mig -header myipcUser.h -sheader myipcServer.h myipc.defs
|
||
|
```
|
||
|
Vários novos arquivos serão criados no diretório atual.
|
||
|
|
||
|
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):
|
||
|
|
||
|
{% tabs %}
|
||
|
{% tab title="myipcServer.c" %}
|
||
|
```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)},
|
||
|
}
|
||
|
};
|
||
|
```
|
||
|
{% tab title="myipcServer.h" %}
|
||
|
|
||
|
```c
|
||
|
#ifndef myipcServer_h
|
||
|
#define myipcServer_h
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <mach/mach.h>
|
||
|
#include <servers/bootstrap.h>
|
||
|
#include "myipcServerUser.h"
|
||
|
|
||
|
#define MACH_PORT_NAME "com.example.myipc"
|
||
|
|
||
|
kern_return_t myipc_server(mach_port_t server_port);
|
||
|
|
||
|
#endif /* myipcServer_h */
|
||
|
```
|
||
|
|
||
|
{% endtab %}
|
||
|
```c
|
||
|
/* Description of this subsystem, for use in direct RPC */
|
||
|
extern const struct 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 */
|
||
|
unsigned int maxsize; /* Max msg size */
|
||
|
vm_address_t reserved; /* Reserved */
|
||
|
struct routine_descriptor /* Array of routine descriptors */
|
||
|
routine[1];
|
||
|
} SERVERPREFmyipc_subsystem;
|
||
|
```
|
||
|
{% endtab %}
|
||
|
{% endtabs %}
|
||
|
|
||
|
Com base na estrutura anterior, a função **`myipc_server_routine`** receberá o **ID da mensagem** e retornará a função adequada a ser chamada:
|
||
|
```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;
|
||
|
}
|
||
|
```
|
||
|
Neste exemplo, definimos apenas 1 função nas definições, mas se tivéssemos definido mais, elas estariam dentro do array **`SERVERPREFmyipc_subsystem`** e a primeira seria atribuída ao ID **500**, a segunda ao ID **501**...
|
||
|
|
||
|
Na verdade, é possível identificar essa relação na struct **`subsystem_to_name_map_myipc`** do arquivo **`myipcServer.h`**:
|
||
|
```c
|
||
|
#ifndef subsystem_to_name_map_myipc
|
||
|
#define subsystem_to_name_map_myipc \
|
||
|
{ "Subtract", 500 }
|
||
|
#endif
|
||
|
```
|
||
|
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:
|
||
|
|
||
|
<pre class="language-c"><code class="lang-c">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 rotina;
|
||
|
|
||
|
OutHeadP->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REPLY(InHeadP->msgh_bits), 0);
|
||
|
OutHeadP->msgh_remote_port = InHeadP->msgh_reply_port;
|
||
|
/* Tamanho mínimo: a rotina() irá atualizá-lo se for diferente */
|
||
|
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) ||
|
||
|
<strong> ((rotina = SERVERPREFmyipc_subsystem.rotina[InHeadP->msgh_id - 500].stub_rotina) == 0)) {
|
||
|
</strong> ((mig_reply_error_t *)OutHeadP)->NDR = NDR_record;
|
||
|
((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID;
|
||
|
return FALSE;
|
||
|
}
|
||
|
<strong> (*rotina) (InHeadP, OutHeadP);
|
||
|
</strong> return TRUE;
|
||
|
}
|
||
|
</code></pre>
|
||
|
|
||
|
Verifique o seguinte código para usar o código gerado para criar um servidor e cliente simples onde o cliente pode chamar as funções Subtrair do servidor:
|
||
|
|
||
|
{% tabs %}
|
||
|
{% tab title="myipc_server.c" %}
|
||
|
```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);
|
||
|
}
|
||
|
```
|
||
|
```c
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <servers/bootstrap.h>
|
||
|
#include "myipc.h"
|
||
|
|
||
|
int main(int argc, char *argv[]) {
|
||
|
mach_port_t server_port;
|
||
|
kern_return_t kr;
|
||
|
char *message = "Hello, server!";
|
||
|
char reply[256];
|
||
|
|
||
|
// Look up the server port
|
||
|
kr = bootstrap_look_up(bootstrap_port, "com.example.myipc_server", &server_port);
|
||
|
if (kr != KERN_SUCCESS) {
|
||
|
fprintf(stderr, "Failed to look up server port: %s\n", mach_error_string(kr));
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
// Send a message to the server
|
||
|
kr = myipc_send_message(server_port, message, reply, sizeof(reply));
|
||
|
if (kr != KERN_SUCCESS) {
|
||
|
fprintf(stderr, "Failed to send message: %s\n", mach_error_string(kr));
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
printf("Received reply: %s\n", reply);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
```
|
||
|
{% endtab %}
|
||
|
|
||
|
{% tab title="myipc_server.c" %}
|
||
|
```c
|
||
|
// 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"
|
||
|
|
||
|
int main() {
|
||
|
|
||
|
// 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);
|
||
|
return 1;
|
||
|
}
|
||
|
printf("Port right name %d\n", port);
|
||
|
USERPREFSubtract(port, 40, 2);
|
||
|
}
|
||
|
```
|
||
|
### Análise Binária
|
||
|
|
||
|
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.
|
||
|
|
||
|
O **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:
|
||
|
```bash
|
||
|
jtool2 -d __DATA.__const myipc_server | grep MIG
|
||
|
```
|
||
|
Foi mencionado anteriormente que a função que cuidará de **chamar a função correta dependendo do ID da mensagem recebida** é `myipc_server`. No entanto, geralmente você não terá os símbolos do binário (sem nomes de funções), então é interessante **ver como ela é descompilada** já que sempre será muito semelhante (o código desta função é independente das funções expostas):
|
||
|
|
||
|
{% tabs %}
|
||
|
{% tab title="myipc_server descompilada 1" %}
|
||
|
<pre class="language-c"><code class="lang-c">int _myipc_server(int arg0, int arg1) {
|
||
|
var_10 = arg0;
|
||
|
var_18 = arg1;
|
||
|
// Instruções iniciais para encontrar os ponteiros de função corretos
|
||
|
*(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);
|
||
|
// Chamada para sign_extend_64 que pode ajudar a identificar esta função
|
||
|
// Isso armazena em rax o ponteiro para a chamada que precisa ser feita
|
||
|
// Verifique o uso do endereço 0x100004040 (array de endereços de funções)
|
||
|
// 0x1f4 = 500 (o ID de início)
|
||
|
<strong> rax = *(sign_extend_64(rax - 0x1f4) * 0x28 + 0x100004040);
|
||
|
</strong> var_20 = rax;
|
||
|
// Se - senão, o se retorna falso, enquanto o senão chama a função correta e retorna verdadeiro
|
||
|
<strong> if (rax == 0x0) {
|
||
|
</strong> *(var_18 + 0x18) = **_NDR_record;
|
||
|
*(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
|
||
|
var_4 = 0x0;
|
||
|
}
|
||
|
else {
|
||
|
// Endereço calculado que chama a função correta com 2 argumentos
|
||
|
<strong> (var_20)(var_10, var_18);
|
||
|
</strong> var_4 = 0x1;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
*(var_18 + 0x18) = **_NDR_record;
|
||
|
*(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
|
||
|
var_4 = 0x0;
|
||
|
}
|
||
|
rax = var_4;
|
||
|
return rax;
|
||
|
}
|
||
|
</code></pre>
|
||
|
{% endtab %}
|
||
|
|
||
|
{% tab title="myipc_server descompilada 2" %}
|
||
|
Esta é a mesma função descompilada em uma versão gratuita diferente do Hopper:
|
||
|
|
||
|
<pre class="language-c"><code class="lang-c">int _myipc_server(int arg0, int arg1) {
|
||
|
r31 = r31 - 0x40;
|
||
|
saved_fp = r29;
|
||
|
stack[-8] = r30;
|
||
|
var_10 = arg0;
|
||
|
var_18 = arg1;
|
||
|
// Instruções iniciais para encontrar os ponteiros de função corretos
|
||
|
*(int32_t *)var_18 = *(int32_t *)var_10 & 0x1f | 0x0;
|
||
|
*(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;
|
||
|
r8 = *(int32_t *)(var_10 + 0x14);
|
||
|
r8 = r8 - 0x1f4;
|
||
|
if (r8 > 0x0) {
|
||
|
if (CPU_FLAGS & G) {
|
||
|
r8 = 0x1;
|
||
|
}
|
||
|
}
|
||
|
if ((r8 & 0x1) == 0x0) {
|
||
|
r8 = *(int32_t *)(var_10 + 0x14);
|
||
|
r8 = r8 - 0x1f4;
|
||
|
if (r8 < 0x0) {
|
||
|
if (CPU_FLAGS & L) {
|
||
|
r8 = 0x1;
|
||
|
}
|
||
|
}
|
||
|
if ((r8 & 0x1) == 0x0) {
|
||
|
r8 = *(int32_t *)(var_10 + 0x14);
|
||
|
// 0x1f4 = 500 (o ID de início)
|
||
|
<strong> r8 = r8 - 0x1f4;
|
||
|
</strong> asm { smaddl x8, w8, w9, x10 };
|
||
|
r8 = *(r8 + 0x8);
|
||
|
var_20 = r8;
|
||
|
r8 = r8 - 0x0;
|
||
|
if (r8 != 0x0) {
|
||
|
if (CPU_FLAGS & NE) {
|
||
|
r8 = 0x1;
|
||
|
}
|
||
|
}
|
||
|
// Mesmo se senão que na versão anterior
|
||
|
// Verifique o uso do endereço 0x100004040 (array de endereços de funções)
|
||
|
<strong> if ((r8 & 0x1) == 0x0) {
|
||
|
</strong><strong> *(var_18 + 0x18) = **0x100004000;
|
||
|
</strong> *(int32_t *)(var_18 + 0x20) = 0xfffffed1;
|
||
|
var_4 = 0x0;
|
||
|
}
|
||
|
else {
|
||
|
// Chamada para o endereço calculado onde a função deve estar
|
||
|
<strong> (var_20)(var_10, var_18);
|
||
|
</strong> var_4 = 0x1;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
*(var_18 + 0x18) = **0x100004000;
|
||
|
*(int32_t *)(var_18 + 0x20) = 0xfffffed1;
|
||
|
var_4 = 0x0;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
*(var_18 + 0x18) = **0x100004000;
|
||
|
*(int32_t *)(var_18 + 0x20) = 0xfffffed1;
|
||
|
var_4 = 0x0;
|
||
|
}
|
||
|
r0 = var_4;
|
||
|
return r0;
|
||
|
}
|
||
|
|
||
|
</code></pre>
|
||
|
{% endtab %}
|
||
|
{% endtabs %}
|
||
|
|
||
|
Na verdade, se você for para a função **`0x100004000`**, encontrará o array de structs **`routine_descriptor`**, o primeiro elemento da struct é o endereço onde a função é implementada e a **struct ocupa 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:
|
||
|
|
||
|
<figure><img src="../../../../.gitbook/assets/image.png" alt=""><figcaption></figcaption></figure>
|
||
|
|
||
|
<figure><img src="../../../../.gitbook/assets/image (1).png" alt=""><figcaption></figcaption></figure>
|
||
|
|
||
|
Esses dados podem ser extraídos [**usando este script do Hopper**](https://github.com/knightsc/hopper/blob/master/scripts/MIG%20Detect.py).
|
||
|
|
||
|
<details>
|
||
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||
|
|
||
|
* Você trabalha em uma **empresa de cibersegurança**? Você quer ver sua **empresa anunciada no HackTricks**? ou você quer ter acesso à **última versão do PEASS ou baixar o HackTricks em PDF**? Verifique os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
|
||
|
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||
|
* Adquira o [**swag oficial do PEASS & HackTricks**](https://peass.creator-spring.com)
|
||
|
* **Junte-se ao** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo telegram**](https://t.me/peass) ou **siga-me** no **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||
|
* **Compartilhe seus truques de hacking enviando PRs para o** [**repositório hacktricks**](https://github.com/carlospolop/hacktricks) **e** [**repositório hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||
|
|
||
|
</details>
|