mirror of
https://github.com/carlospolop/hacktricks
synced 2025-01-10 12:18:52 +00:00
397 lines
16 KiB
Markdown
397 lines
16 KiB
Markdown
|
# Générateur d'interface MIG pour macOS
|
||
|
|
||
|
<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>
|
||
|
|
||
|
* Travaillez-vous dans une **entreprise de cybersécurité** ? Voulez-vous voir votre **entreprise annoncée dans HackTricks** ? Ou voulez-vous avoir accès à la **dernière version de PEASS ou télécharger HackTricks en PDF** ? Consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop) !
|
||
|
* Découvrez [**The PEASS Family**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||
|
* Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com)
|
||
|
* **Rejoignez le** [**💬**](https://emojipedia.org/speech-balloon/) [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez** moi sur **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||
|
* **Partagez vos astuces de piratage en soumettant des PR au** [**repo hacktricks**](https://github.com/carlospolop/hacktricks) **et au** [**repo hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||
|
|
||
|
</details>
|
||
|
|
||
|
MIG a été créé pour **simplifier le processus de création de code Mach IPC**. Il génère essentiellement le code nécessaire pour que le serveur et le client puissent communiquer avec une définition donnée. Même si le code généré est moche, un développeur n'aura qu'à l'importer et son code sera beaucoup plus simple qu'auparavant.
|
||
|
|
||
|
### Exemple
|
||
|
|
||
|
Créez un fichier de définition, dans ce cas avec une fonction très simple :
|
||
|
|
||
|
{% 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 %}
|
||
|
|
||
|
Maintenant, utilisez mig pour générer le code serveur et client qui pourra communiquer entre eux pour appeler la fonction Subtract :
|
||
|
```bash
|
||
|
mig -header myipcUser.h -sheader myipcServer.h myipc.defs
|
||
|
```
|
||
|
Plusieurs nouveaux fichiers seront créés dans le répertoire actuel.
|
||
|
|
||
|
Dans les fichiers **`myipcServer.c`** et **`myipcServer.h`**, vous pouvez trouver la déclaration et la définition de la structure **`SERVERPREFmyipc_subsystem`**, qui définit essentiellement la fonction à appeler en fonction de l'ID du message reçu (nous avons indiqué un numéro de départ 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 <mach/mach_error.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 %}
|
||
|
|
||
|
Basé sur la structure précédente, la fonction **`myipc_server_routine`** recevra l'**ID du message** et renverra la fonction appropriée à appeler :
|
||
|
```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;
|
||
|
}
|
||
|
```
|
||
|
Dans cet exemple, nous avons seulement défini une fonction dans les définitions, mais si nous en avions défini davantage, elles auraient été incluses dans le tableau **`SERVERPREFmyipc_subsystem`** et la première serait assignée à l'ID **500**, la deuxième à l'ID **501**...
|
||
|
|
||
|
En réalité, il est possible d'identifier cette relation dans la structure **`subsystem_to_name_map_myipc`** de **`myipcServer.h`**:
|
||
|
```c
|
||
|
#ifndef subsystem_to_name_map_myipc
|
||
|
#define subsystem_to_name_map_myipc \
|
||
|
{ "Subtract", 500 }
|
||
|
#endif
|
||
|
```
|
||
|
Enfin, une autre fonction importante pour faire fonctionner le serveur sera **`myipc_server`**, qui est celle qui va réellement **appeler la fonction** liée à l'identifiant reçu :
|
||
|
|
||
|
<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 routine;
|
||
|
|
||
|
OutHeadP->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REPLY(InHeadP->msgh_bits), 0);
|
||
|
OutHeadP->msgh_remote_port = InHeadP->msgh_reply_port;
|
||
|
/* Taille minimale : routine() la mettra à jour si elle est différente */
|
||
|
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> ((routine = SERVERPREFmyipc_subsystem.routine[InHeadP->msgh_id - 500].stub_routine) == 0)) {
|
||
|
</strong> ((mig_reply_error_t *)OutHeadP)->NDR = NDR_record;
|
||
|
((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID;
|
||
|
return FALSE;
|
||
|
}
|
||
|
<strong> (*routine) (InHeadP, OutHeadP);
|
||
|
</strong> return TRUE;
|
||
|
}
|
||
|
</code></pre>
|
||
|
|
||
|
Vérifiez le code suivant pour utiliser le code généré afin de créer un serveur et un client simples où le client peut appeler les fonctions Subtract du serveur :
|
||
|
|
||
|
{% 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);
|
||
|
}
|
||
|
|
||
|
// Print the server's reply
|
||
|
printf("Server replied: %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);
|
||
|
}
|
||
|
```
|
||
|
### Analyse binaire
|
||
|
|
||
|
Comme de nombreux binaires utilisent désormais MIG pour exposer des ports mach, il est intéressant de savoir comment **identifier l'utilisation de MIG** et les **fonctions exécutées par MIG** avec chaque ID de message.
|
||
|
|
||
|
[**jtool2**](../../macos-apps-inspecting-debugging-and-fuzzing/#jtool2) peut analyser les informations MIG d'un binaire Mach-O, indiquant l'ID du message et identifiant la fonction à exécuter :
|
||
|
```bash
|
||
|
jtool2 -d __DATA.__const myipc_server | grep MIG
|
||
|
```
|
||
|
Il a été mentionné précédemment que la fonction qui se chargera d'**appeler la fonction correcte en fonction de l'ID du message reçu** était `myipc_server`. Cependant, vous n'aurez généralement pas les symboles du binaire (pas de noms de fonctions), il est donc intéressant de **vérifier à quoi cela ressemble décompilé** car cela sera toujours très similaire (le code de cette fonction est indépendant des fonctions exposées) :
|
||
|
|
||
|
{% tabs %}
|
||
|
{% tab title="myipc_server décompilé 1" %}
|
||
|
<pre class="language-c"><code class="lang-c">int _myipc_server(int arg0, int arg1) {
|
||
|
var_10 = arg0;
|
||
|
var_18 = arg1;
|
||
|
// Instructions initiales pour trouver les bons pointeurs de fonction
|
||
|
*(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);
|
||
|
// Appel à sign_extend_64 qui peut aider à identifier cette fonction
|
||
|
// Cela stocke dans rax le pointeur de l'appel qui doit être appelé
|
||
|
// Vérifiez l'utilisation de l'adresse 0x100004040 (tableau des adresses des fonctions)
|
||
|
// 0x1f4 = 500 (l'ID de départ)
|
||
|
<strong> rax = *(sign_extend_64(rax - 0x1f4) * 0x28 + 0x100004040);
|
||
|
</strong> var_20 = rax;
|
||
|
// If - else, le if renvoie false, tandis que le else appelle la bonne fonction et renvoie true
|
||
|
<strong> if (rax == 0x0) {
|
||
|
</strong> *(var_18 + 0x18) = **_NDR_record;
|
||
|
*(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
|
||
|
var_4 = 0x0;
|
||
|
}
|
||
|
else {
|
||
|
// Adresse calculée qui appelle la bonne fonction avec 2 arguments
|
||
|
<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 décompilé 2" %}
|
||
|
Il s'agit de la même fonction décompilée dans une version différente de Hopper free :
|
||
|
|
||
|
<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;
|
||
|
// Instructions initiales pour trouver les bons pointeurs de fonction
|
||
|
*(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 (l'ID de départ)
|
||
|
<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;
|
||
|
}
|
||
|
}
|
||
|
// Même if else que dans la version précédente
|
||
|
// Vérifiez l'utilisation de l'adresse 0x100004040 (tableau des adresses des fonctions)
|
||
|
<strong> if ((r8 & 0x1) == 0x0) {
|
||
|
</strong><strong> *(var_18 + 0x18) = **0x100004000;
|
||
|
</strong> *(int32_t *)(var_18 + 0x20) = 0xfffffed1;
|
||
|
var_4 = 0x0;
|
||
|
}
|
||
|
else {
|
||
|
// Appel à l'adresse calculée où la fonction devrait être
|
||
|
<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 %}
|
||
|
|
||
|
En fait, si vous allez à la fonction **`0x100004000`**, vous trouverez le tableau des structures **`routine_descriptor`**, le premier élément de la structure est l'adresse où la fonction est implémentée et la **structure prend 0x28 octets**, donc tous les 0x28 octets (à partir de l'octet 0) vous pouvez obtenir 8 octets et cela sera l'**adresse de la fonction** qui sera appelée :
|
||
|
|
||
|
<figure><img src="../../../../.gitbook/assets/image.png" alt=""><figcaption></figcaption></figure>
|
||
|
|
||
|
<figure><img src="../../../../.gitbook/assets/image (1).png" alt=""><figcaption></figcaption></figure>
|
||
|
|
||
|
Ces données peuvent être extraites [**en utilisant ce script 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>
|
||
|
|
||
|
* Travaillez-vous dans une **entreprise de cybersécurité** ? Voulez-vous voir votre **entreprise annoncée dans HackTricks** ? Ou voulez-vous avoir accès à la **dernière version de PEASS ou télécharger HackTricks en PDF** ? Consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop) !
|
||
|
* Découvrez [**La famille PEASS**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFT**](https://opensea.io/collection/the-peass-family)
|
||
|
* Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com)
|
||
|
* **Rejoignez le** [**💬**](https://emojipedia.org/speech-balloon/) [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez** moi sur **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||
|
* **Partagez vos astuces de piratage en soumettant des PR au** [**repo hacktricks**](https://github.com/carlospolop/hacktricks) **et au** [**repo hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||
|
|
||
|
</details>
|