/* Cydia Substrate - Powerful Code Insertion Platform
* Copyright (C) 2008-2010 Jay Freeman (saurik)
*/
/* GNU Lesser General Public License, Version 3 {{{ */
/*
* Substrate is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Substrate is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Substrate. If not, see .
**/
/* }}} */
#ifdef __APPLE__
#include
#include
#include
#include
#include
extern "C" {
#include
}
#define BSD_KERNEL_PRIVATE
#include
#include
#include
#include
#include "CydiaSubstrate.h"
#define _trace() do { \
fprintf(stderr, "_trace(%u)\n", __LINE__); \
} while (false)
struct MSSymbolData {
const char *name_;
uint8_t type_;
uint8_t sect_;
int16_t desc_;
uintptr_t value_;
};
#ifdef __LP64__
typedef struct mach_header_64 mach_header_xx;
typedef struct nlist_64 nlist_xx;
typedef struct segment_command_64 segment_command_xx;
static const uint32_t LC_SEGMENT_XX = LC_SEGMENT_64;
static const uint32_t MH_MAGIC_XX = MH_MAGIC_64;
#else
typedef struct mach_header mach_header_xx;
typedef struct nlist nlist_xx;
typedef struct segment_command segment_command_xx;
static const uint32_t LC_SEGMENT_XX = LC_SEGMENT;
static const uint32_t MH_MAGIC_XX = MH_MAGIC;
#endif
static ssize_t MSMachONameList_(const void *stuff, struct MSSymbolData *list, size_t nreq) {
// XXX: ok, this is just pathetic; API fail much?
size_t slide(0);
for (uint32_t image(0), images(_dyld_image_count()); image != images; ++image)
if (_dyld_get_image_header(image) == stuff) {
slide = _dyld_get_image_vmaddr_slide(image);
goto fat;
}
return -1;
fat:
const uint8_t *base(reinterpret_cast(stuff));
const struct exec *buf(reinterpret_cast(base));
if (OSSwapBigToHostInt32(buf->a_magic) == FAT_MAGIC) {
struct host_basic_info hbi; {
host_t host(mach_host_self());
mach_msg_type_number_t count(HOST_BASIC_INFO_COUNT);
if (host_info(host, HOST_BASIC_INFO, reinterpret_cast(&hbi), &count) != KERN_SUCCESS)
return -1;
mach_port_deallocate(mach_task_self(), host);
}
const struct fat_header *fh(reinterpret_cast(base));
uint32_t nfat_arch(OSSwapBigToHostInt32(fh->nfat_arch));
const struct fat_arch *fat_archs(reinterpret_cast(fh + 1));
for (uint32_t i(0); i != nfat_arch; ++i)
if (static_cast(OSSwapBigToHostInt32(fat_archs[i].cputype)) == hbi.cpu_type) {
buf = reinterpret_cast(base + OSSwapBigToHostInt32(fat_archs[i].offset));
goto thin;
}
return -1;
}
thin:
const nlist_xx *symbols;
const char *strings;
size_t n;
// XXX: this check looks really scary when it fails
if (buf->a_magic == MH_MAGIC_XX) {
const mach_header_xx *mh(reinterpret_cast(base));
const struct load_command *load_commands(reinterpret_cast(mh + 1));
const struct symtab_command *stp(NULL);
const struct load_command *lcp;
/* forlc (command, mh, LC_SYMTAB, struct symtab_command) {
stp = command;
goto found;
} */
lcp = load_commands;
for (uint32_t i(0); i != mh->ncmds; ++i) {
if (
lcp->cmdsize % sizeof(long) != 0 || lcp->cmdsize <= 0 ||
reinterpret_cast(lcp) + lcp->cmdsize > reinterpret_cast(load_commands) + mh->sizeofcmds
)
return -1;
if (lcp->cmd == LC_SYMTAB) {
if (lcp->cmdsize != sizeof(struct symtab_command))
return -1;
stp = reinterpret_cast(lcp);
goto found;
}
lcp = reinterpret_cast(reinterpret_cast(lcp) + lcp->cmdsize);
}
return -1;
found:
n = stp->nsyms;
symbols = NULL;
strings = NULL;
/* forlc (command, mh, LC_SEGMENT_XX, segment_command_xx) {
stp = command;
goto found;
} */
lcp = load_commands;
for (uint32_t i(0); i != mh->ncmds; ++i) {
if (
lcp->cmdsize % sizeof(long) != 0 || lcp->cmdsize <= 0 ||
reinterpret_cast(lcp) + lcp->cmdsize > reinterpret_cast(load_commands) + mh->sizeofcmds
)
return -1;
if (lcp->cmd == LC_SEGMENT_XX) {
if (lcp->cmdsize < sizeof(segment_command_xx))
return -1;
const segment_command_xx *segment(reinterpret_cast(lcp));
if (stp->symoff >= segment->fileoff && stp->symoff < segment->fileoff + segment->filesize)
symbols = reinterpret_cast(stp->symoff - segment->fileoff + segment->vmaddr + slide);
if (stp->stroff >= segment->fileoff && stp->stroff < segment->fileoff + segment->filesize)
strings = reinterpret_cast(stp->stroff - segment->fileoff + segment->vmaddr + slide);
}
lcp = reinterpret_cast(reinterpret_cast(lcp) + lcp->cmdsize);
}
if (symbols == NULL || strings == NULL)
return -1;
// XXX: detect a.out somehow?
} else if (false) {
/* XXX: is this right anymore?!? */
symbols = reinterpret_cast(base + N_SYMOFF(*buf));
strings = reinterpret_cast(reinterpret_cast(symbols) + buf->a_syms);
n = buf->a_syms / sizeof(nlist_xx);
} else return -1;
size_t result(nreq);
for (size_t m(0); m != n; ++m) {
const nlist_xx *q(&symbols[m]);
if (q->n_un.n_strx == 0 || (q->n_type & N_STAB) != 0)
continue;
const char *nambuf(strings + q->n_un.n_strx);
//fprintf(stderr, " == %s\n", nambuf);
for (size_t item(0); item != nreq; ++item) {
struct MSSymbolData *p(list + item);
if (p->name_ == NULL || strcmp(p->name_, nambuf) != 0)
continue;
p->name_ = NULL;
p->value_ = q->n_value;
p->type_ = q->n_type;
p->desc_ = q->n_desc;
p->sect_ = q->n_sect;
if (--result == 0)
return 0;
break;
}
}
return result;
}
_extern const void *MSGetImageByName(const char *file) {
for (uint32_t image(0), images(_dyld_image_count()); image != images; ++image)
if (strcmp(_dyld_get_image_name(image), file) == 0)
return _dyld_get_image_header(image);
return NULL;
}
static void MSFindSymbols(const void *image, size_t count, const char *names[], void *values[]) {
MSSymbolData items[count];
for (size_t index(0); index != count; ++index) {
MSSymbolData &item(items[index]);
item.name_ = names[index];
item.type_ = 0;
item.sect_ = 0;
item.desc_ = 0;
item.value_ = 0;
}
if (image != NULL)
MSMachONameList_(image, items, count);
else {
size_t remain(count);
for (uint32_t image(0), images(_dyld_image_count()); image != images; ++image) {
//fprintf(stderr, ":: %s\n", _dyld_get_image_name(image));
ssize_t result(MSMachONameList_(_dyld_get_image_header(image), items, count));
if (result == -1)
continue;
// XXX: maybe avoid this happening at all? a flag to NSMachONameList_?
for (size_t index(0); index != count; ++index) {
MSSymbolData &item(items[index]);
if (item.name_ == NULL && item.value_ == 0)
item.name_ = names[index];
}
remain -= count - result;
if (remain == 0)
break;
}
}
for (size_t index(0); index != count; ++index) {
MSSymbolData &item(items[index]);
uintptr_t value(item.value_);
#ifdef __arm__
if ((item.desc_ & N_ARM_THUMB_DEF) != 0)
value |= 0x00000001;
#endif
values[index] = reinterpret_cast(value);
}
}
_extern void *MSFindSymbol(const void *image, const char *name) {
void *value;
MSFindSymbols(image, 1, &name, &value);
return value;
}
#ifdef __arm__
MSHook(int, nlist, const char *file, struct nlist *names) {
const void *image(MSGetImageByName(file));
if (image == NULL)
return (*_nlist)(file, names);
size_t count(0);
for (struct nlist *list(names); list->n_un.n_name != NULL; ++list)
++count;
MSSymbolData items[count];
for (size_t index(0); index != count; ++index) {
MSSymbolData &item(items[index]);
struct nlist &name(names[index]);
item.name_ = name.n_un.n_name;
item.type_ = 0;
item.sect_ = 0;
item.desc_ = 0;
item.value_ = 0;
}
int result(MSMachONameList_(image, items, count));
for (size_t index(0); index != count; ++index) {
MSSymbolData &item(items[index]);
struct nlist &name(names[index]);
name.n_type = item.type_;
name.n_sect = item.sect_;
name.n_desc = item.desc_;
name.n_value = item.value_;
}
return result;
}
MSInitialize {
MSHookFunction(&nlist, MSHake(nlist));
}
#endif
#endif