Here is how far I have gotten.
Code:
//
// DumpBinary.c
// DumpBinary
// Look for Object manager list status
// Created by Alexandre Colucci on 04.08.2016.
// Copyright © 2016 Alexandre Colucci. All rights reserved.
//
// To compile:
// gcc -o DumpBinary.dylib -dynamiclib DumpBinary.c
//
// To run:
// DYLD_INSERT_LIBRARIES=./DumpBinary.dylib /Applications/World\ of\ Warcraft/_retail_/World\ of\ Warcraft.app/Contents/MacOS/World\ of\ Warcraft
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mach-o/loader.h>
#include <mach-o/dyld.h>
#include <mach-o/fat.h>
#include <mach-o/swap.h>
// For PATH_MAX
#include <sys/syslimits.h>
const struct mach_header_64 *get_mach_header_64()
{
for (uint32_t i = 0; i < _dyld_image_count(); ++i)
{
const struct mach_header_64 *mach_header_64 = (const struct mach_header_64 *)_dyld_get_image_header(i);
if (mach_header_64->filetype == MH_EXECUTE)
{
if ((mach_header_64->cputype & CPU_TYPE_ARM64) != CPU_TYPE_ARM64)
{
fprintf(stderr, "[ERROR] Executable is not an ARM64 file\n");
exit(1);
}
fprintf(stderr, "[INFO] Found MH_EXECUTE Header\n");
const char *name = _dyld_get_image_name(i);
fprintf(stderr, "[INFO] Executable is %s\n", "ARM64");
fprintf(stdout, "[INFO] Executable header for '%s' found.\n", name);
return mach_header_64;
}
}
return NULL;
}
uint8_t *get_file_from_disk(const char *path, size_t *start, size_t *size)
{
fprintf(stdout, "[INFO] Loading '%s' from disk.\n", path);
FILE *file = fopen(path, "r");
if (file == NULL)
{
fprintf(stderr, "[ERROR] Could not open executable path '%s'\n", path);
exit(1);
}
fseek(file, 0, SEEK_END);
long file_size = ftell(file);
fseek(file, 0, SEEK_SET);
*size = file_size;
uint8_t *buffer = (uint8_t *)calloc(file_size, 1);
if (buffer == NULL)
{
fclose(file);
fprintf(stderr, "[ERROR] Could not allocate buffer\n");
exit(1);
}
if (fread(buffer, 1, file_size, file) != file_size)
{
fclose(file);
free(buffer);
fprintf(stderr, "[ERROR] Could not read the file '%s' to buffer\n", path);
exit(1);
}
fclose(file);
struct fat_header *fat_header = (struct fat_header *)buffer;
if (fat_header->magic == FAT_CIGAM || fat_header->magic == FAT_MAGIC || fat_header->magic == FAT_CIGAM_64 || fat_header->magic == FAT_MAGIC_64)
{
bool byteswap = fat_header->magic == FAT_CIGAM || fat_header->magic == FAT_CIGAM_64;
if (byteswap)
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
swap_fat_header(fat_header, 0);
struct fat_arch *fat_arch = (struct fat_arch *)(fat_header + 1);
for (uint32_t i = 0; i < fat_header->nfat_arch; ++i)
{
if (byteswap)
swap_fat_arch(fat_arch, 1, 0);
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
bool is_arm64 = (fat_arch->cputype & CPU_TYPE_ARM64) == CPU_TYPE_ARM64;
bool is_x86_64 = (fat_arch->cputype & CPU_TYPE_X86_64) == CPU_TYPE_X86_64;
if (is_arm64)
{
*size = fat_arch->size;
*start = fat_arch->offset;
break;
}
++fat_arch;
}
}
return buffer;
}
void __attribute__((destructor)) DumpBinaryDestructor()
{
char destinationPath[PATH_MAX];
fprintf(stderr, "[INFO] Destructor CALLED\n");
const struct mach_header_64 *mh64 = get_mach_header_64();
if (mh64 == NULL)
{
fprintf(stderr, "[ERROR] Could not find the main executable\n");
exit(1);
}
char executablePath[PATH_MAX];
/*
_NSGetExecutablePath() copies the path of the main executable into the
buffer buf. The bufsize parameter should initially be the size of the
buffer. This function returns 0 if the path was successfully copied, and
* bufsize is left unchanged. It returns -1 if the buffer is not large
enough, and * bufsize is set to the size required. Note that
_NSGetExecutablePath() will return "a path" to the executable not a "real
path" to the executable. That is, the path may be a symbolic link and
not the real file. With deep directories the total bufsize needed could
be more than MAXPATHLEN.
*/
uint32_t len = sizeof(executablePath);
if (_NSGetExecutablePath(executablePath, &len) != 0)
{
fprintf(stderr, "[ERROR] Buffer is not large enough to copy the executable path\n");
exit(1);
}
//
// Get the canonicalized absolute path
//
char *canonicalPath = realpath(executablePath, NULL);
if (canonicalPath != NULL)
{
strlcpy(executablePath, canonicalPath, sizeof(executablePath));
free(canonicalPath);
}
fprintf(stderr, "[INFO] Found absolute path: '%s'\n", executablePath);
size_t buffer_start = 0, buffer_size = 0;
uint8_t *buffer = get_file_from_disk(executablePath, &buffer_start, &buffer_size);
//
// Loop through each section
//
size_t segmentOffset = sizeof(struct mach_header_64);
fprintf(stderr, "[INFO] Commnd Size %d\n", mh64->sizeofcmds);
for (uint32_t i = 0; i < mh64->ncmds; i++)
{
struct load_command *loadCommand = (struct load_command *)((uint8_t *)mh64 + segmentOffset);
if (loadCommand->cmd == LC_SEGMENT_64)
{
// Found a 64-bit segment
struct segment_command_64 *segCommand = (struct segment_command_64 *)loadCommand;
void *sectionPtr = (void *)(segCommand + 1);
for (uint32_t nsect = 0; nsect < segCommand->nsects; ++nsect)
{
struct section_64 *section = (struct section_64 *)sectionPtr;
fprintf(stderr, "\t[INFO] Found the section (%s, %s)\n", section->segname, section->sectname);
if (strncmp(segCommand->segname, SEG_TEXT, 16) == 0)
{
fprintf(stderr, "\t[INFO] Save the unencrypted (%s, %s) section to the buffer\n", section->segname, section->sectname);
memcpy(buffer + section->offset, (uint8_t *)mh64 + section->offset, section->size);
}
sectionPtr += sizeof(struct section_64);
}
}
segmentOffset += loadCommand->cmdsize;
}
start_dump:
//
// Create the output file
//
// char appendName[12] = mh64->cputype == CPU_TYPE_ARM64 ? "_Decrypted_a" : "_Decrypted_x";
strlcpy(destinationPath, executablePath, sizeof(destinationPath));
strlcat(destinationPath, mh64->cputype == CPU_TYPE_ARM64 ? "_Decrypted_a" : "_Decrypted_x", sizeof(destinationPath));
fprintf(stderr, "[INFO] Creating the output file '%s'\n", destinationPath);
FILE *destinationFile = fopen(destinationPath, "w");
if (destinationFile == NULL)
{
free(buffer);
fprintf(stderr, "[ERROR] Could create the output file '%s'\n", destinationPath);
exit(1);
}
//
// Save the data into the output file
//
if (fwrite(buffer + buffer_start, 1, buffer_size, destinationFile) != buffer_size)
{
free(buffer);
fclose(destinationFile);
fprintf(stderr, "[ERROR] Could not write to the output file\n");
exit(1);
}
free(buffer);
fclose(destinationFile);
fprintf(stderr, "[INFO] Decryption completed\n");
}