Il checksum di runtime di Mach-O differisce dal checksum dell'eseguibile

0

Nella nostra applicazione iOS stiamo cercando di effettuare un controllo anti-manomissione. Quello che vorremmo applicare è una procedura comune utilizzata nelle tecniche anti-tapering. Stiamo cercando di ottenere la sezione __text di un file Mach-O e ottenere un checksum da esso, questo checksum sarebbe offuscato in un file separato e abbinato a un checksum ottenuto in fase di esecuzione dall'app in esecuzione. In particolare la procedura che stiamo applicando si basa su quel codice (credito a applidium ):

#include <CommonCrypto/CommonCrypto.h>
#include <dlfcn.h>
#include <mach-o/dyld.h>

int correctCheckSumForTextSection(const char * originalSignature) {
  const struct mach_header * header;
  Dl_info dlinfo;
  //
  if (dladdr(main, &dlinfo) == 0 || dlinfo.dli_fbase == NULL)
    return 0; // Can't find symbol for main
  //
  header = dlinfo.dli_fbase;  // Pointer on the Mach-O header
  struct load_command * cmd = (struct load_command *)(header + 1); // First load command
  // Now iterate through load command
  //to find __text section of __TEXT segment
  for (uint32_t i = 0; cmd != NULL && i < header->ncmds; i++) {
    if (cmd->cmd == LC_SEGMENT) {
      // __TEXT load command is a LC_SEGMENT load command
      struct segment_command * segment = (struct segment_command *)cmd;
      if (!strcmp(segment->segname, "__TEXT")) {
        // Stop on __TEXT segment load command and go through sections
        // to find __text section
        struct section * section = (struct section *)(segment + 1);
        for (uint32_t j = 0; section != NULL && j < segment->nsects; j++) {
          if (!strcmp(section->sectname, "__text"))
            break; //Stop on __text section load command
          section = (struct section *)(section + 1);
        }
        // Get here the __text section address, the __text section size
        // and the virtual memory address so we can calculate
        // a pointer on the __text section
        uint32_t * textSectionAddr = (uint32_t *)section->addr;
        uint32_t textSectionSize = section->size;
        uint32_t * vmaddr = segment->vmaddr;
        char * textSectionPtr = (char *)((int)header + (int)textSectionAddr - (int)vmaddr);
        // Calculate the signature of the data,
        // store the result in a string
        // and compare to the original one
        unsigned char digest[CC_MD5_DIGEST_LENGTH];
        char signature[2 * CC_MD5_DIGEST_LENGTH];            // will hold the signature
        CC_MD5(textSectionPtr, textSectionSize, digest);     // calculate the signature
        for (int i = 0; i < sizeof(digest); i++)             // fill signature
          sprintf(signature + (2 * i), "%02x", digest[i]);
        return strcmp(originalSignature, signature) == 0;    // verify signatures match
      }
    }
    cmd = (struct load_command *)((uint8_t *)cmd + cmd->cmdsize);
  }
  return 0;
}

originalSignature è ottenuto da uno strumento a linea di comando dopo la fase di build avviata contro il file Mach-O appena prodotto.

Siamo riusciti a ottenere la sezione __text dal runtime e dal file, il problema è che i checksum sono diversi l'uno dall'altro. Ispezionando il problema abbiamo scoperto che la mappa della memoria ottenuta dal runtime e dalla lettura del file Mach-O è quasi identica, tranne che da alcuni valori esadecimali (in un piccolo programma circa 6). Usando un disassemblatore sembra che quei diversi valori provengano da printf o sprintf di implementazione. Usiamo queste funzioni per elaborare la firma.

Come è possibile un simile comportamento? C'è qualche altra metodologia che può essere utilizzata per recuperare la firma originale dal file Mach-O senza modificare la fonte?

È importante notare che non possiamo in alcun modo codificare il valore della firma originale poiché tale modifica annullerebbe la precedente.

    
posta Andrea 05.10.2016 - 15:47
fonte

1 risposta

2

Questo è dovuto al linker dinamico, dyld .

Alcune sezioni del segmento __TEXT , come __stubs , vengono modificate in memoria da dyld in modo da poter richiamare i simboli collegati. Fondamentalmente inserisce dinamicamente l'indirizzo di memoria corretto per le funzioni collegate esternamente come printf e sprintf .

Per inciso, l'intera configurazione sembra piuttosto sciocca, dato che la firma del codice già fa questo e altro. Finché stanno modificando il codice, potrebbero anche modificare i controlli di integrità aggiuntivi. È difficile credere che non finirai per spendere più del tuo tempo in questo rispetto a un hacker decente a metà strada che lo spenderà per sconfiggerlo.

    
risposta data 05.10.2016 - 18:03
fonte

Leggi altre domande sui tag