La suma de comprobación en tiempo de ejecución de Mach-O difiere de la suma de comprobación ejecutable

0

En nuestra aplicación para iOS, estamos tratando de hacer una verificación contra la manipulación indebida. Lo que nos gustaría aplicar es un procedimiento común que se utiliza en las técnicas de reducción. Estamos tratando de obtener la sección __text de un archivo de Mach-O y obtener una suma de control de la misma, esta suma de comprobación se confunde en un archivo separado y se compara con una suma de comprobación obtenida en tiempo de ejecución por la aplicación en ejecución. En particular, el procedimiento que estamos aplicando se basa en ese código (crédito para 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 se obtiene mediante una herramienta de línea de comandos después de la fase de compilación iniciada en el archivo Mach-O que se acaba de producir.

Hemos logrado obtener la sección __text del tiempo de ejecución y del archivo, el problema es que las sumas de comprobación son diferentes entre sí. Al inspeccionar el problema, hemos descubierto que el mapa de memoria obtenido en el tiempo de ejecución y al leer el archivo de Mach-O es casi idéntico, excepto por unos pocos valores hexadecimales (en un programa pequeño de aproximadamente 6). Al usar un desensamblador, parece que esos valores diferentes provienen de la implementación printf o sprintf . Usamos esas funciones para procesar la firma.

¿Cómo es posible tal comportamiento? ¿Existe alguna otra metodología que pueda usarse para recuperar la firma original del archivo Mach-O sin modificar la fuente?

Es importante tener en cuenta que no podemos codificar de ninguna manera el valor de la firma original, ya que ese cambio invalidaría la anterior.

    
pregunta Andrea 05.10.2016 - 15:47
fuente

1 respuesta

2

Esto se debe al vinculador dinámico, dyld .

Ciertas secciones del segmento __TEXT , como __stubs se modifican en la memoria por dyld para poder llamar a los símbolos vinculados. Básicamente, está insertando dinámicamente la dirección de memoria correcta para sus funciones vinculadas externamente como printf y sprintf .

Por cierto, toda esta configuración suena bastante tonta, ya que la firma de código ya hace esto y más. Mientras estén modificando el código, también podrían modificar sus controles de integridad adicionales. Es difícil creer que no terminarás gastando más tiempo en esto que lo que un pirata informático medio decente gastará en derrotarlo.

    
respondido por el Alexander O'Mara 05.10.2016 - 18:03
fuente

Lea otras preguntas en las etiquetas