/* 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 "CydiaSubstrate.h"
#import
// XXX: this is required by some code below
#ifdef __arm__
#include "Struct.hpp"
#include "ARM.hpp"
#endif
#include
#include
#include
#include "Debug.hpp"
extern "C" void *NSPushAutoreleasePool(unsigned);
extern "C" void NSPopAutoreleasePool(void *);
static void MSHookMessageInternal(Class _class, SEL sel, IMP imp, IMP *result, const char *prefix) {
if (MSDebug)
fprintf(stderr, "MSHookMessageInternal(%s, %s, %p, %p, \"%s\")\n",
_class == nil ? "nil" : class_getName(_class),
sel == NULL ? "NULL" : sel_getName(sel),
imp, result, prefix
);
if (_class == nil) {
fprintf(stderr, "MS:Warning: nil class argument\n");
return;
} else if (sel == nil) {
fprintf(stderr, "MS:Warning: nil sel argument\n");
return;
} else if (imp == nil) {
fprintf(stderr, "MS:Warning: nil imp argument\n");
return;
}
const char *name(sel_getName(sel));
Method method(class_getInstanceMethod(_class, sel));
if (method == nil) {
fprintf(stderr, "MS:Warning: message not found [%s %s]\n", class_getName(_class), name);
return;
}
const char *type(method_getTypeEncoding(method));
bool direct(false);
unsigned count;
Method *methods(class_copyMethodList(_class, &count));
for (unsigned i(0); i != count; ++i)
if (methods[i] == method) {
direct = true;
break;
}
free(methods);
IMP old(NULL);
// XXX: port this to x86
#if defined(__arm__)
if (!direct) {
size_t length(13 * sizeof(uint32_t));
uint32_t *buffer(reinterpret_cast(mmap(
NULL, length, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0
)));
if (buffer == MAP_FAILED)
fprintf(stderr, "MS:Error:mmap() = %d\n", errno);
else if (false) fail:
munmap(buffer, length);
else {
bool stret;
// XXX: you can't return an array in C, but really... check for '['?!
// http://www.opensource.apple.com/source/gcc3/gcc3-1175/libobjc/sendmsg.c
if (*type != '[' && *type != '(' && *type != '{')
stret = false;
else {
void *pool(NSPushAutoreleasePool(0));
NSMethodSignature *signature([NSMethodSignature signatureWithObjCTypes:type]);
NSUInteger rlength([signature methodReturnLength]);
stret = rlength > OBJC_MAX_STRUCT_BY_VALUE || struct_forward_array[rlength];
NSPopAutoreleasePool(pool);
}
A$r rs(stret ? A$r1 : A$r0);
A$r rc(stret ? A$r2 : A$r1);
A$r re(stret ? A$r0 : A$r2);
buffer[ 0] = A$stmdb_sp$_$rs$((1 << rs) | (1 << re) | (1 << A$r3) | (1 << A$lr));
buffer[ 1] = A$ldr_rd_$rn_im$(A$r0, A$pc, (10 - 1 - 2) * 4);
buffer[ 2] = A$ldr_rd_$rn_im$(A$r1, A$pc, (11 - 2 - 2) * 4);
buffer[ 3] = A$ldr_rd_$rn_im$(A$lr, A$pc, (12 - 3 - 2) * 4);
buffer[ 4] = A$blx_rm(A$lr);
// XXX: if you store this value to the stack now you can avoid instruction 7 later
buffer[ 5] = A$mov_rd_rm(rc, A$r0);
buffer[ 6] = A$ldmia_sp$_$rs$((1 << rs) | (1 << re) | (1 << A$r3) | (1 << A$lr));
buffer[ 7] = A$str_rd_$rn_im$(rc, A$sp, -4);
buffer[ 8] = A$ldr_rd_$rn_im$(rc, A$pc, (11 - 8 - 2) * 4);
buffer[ 9] = A$ldr_rd_$rn_im$(A$pc, A$sp, -4);
buffer[10] = reinterpret_cast(class_getSuperclass(_class));
buffer[11] = reinterpret_cast(sel);
buffer[12] = reinterpret_cast(stret ? &class_getMethodImplementation_stret : &class_getMethodImplementation);
if (mprotect(buffer, length, PROT_READ | PROT_EXEC) == -1) {
fprintf(stderr, "MS:Error:mprotect():%d\n", errno);
goto fail;
}
old = reinterpret_cast(buffer);
if (MSDebug) {
char name[16];
sprintf(name, "%p", old);
MSLogHex(buffer, length, name);
}
}
}
#endif
if (old == NULL)
old = method_getImplementation(method);
if (result != NULL)
*result = old;
if (prefix != NULL) {
size_t namelen(strlen(name));
size_t fixlen(strlen(prefix));
char *newname(reinterpret_cast(alloca(fixlen + namelen + 1)));
memcpy(newname, prefix, fixlen);
memcpy(newname + fixlen, name, namelen + 1);
if (!class_addMethod(_class, sel_registerName(newname), old, type))
fprintf(stderr, "MS:Error: failed to rename [%s %s]\n", class_getName(_class), name);
}
if (direct)
method_setImplementation(method, imp);
else
class_addMethod(_class, sel, imp, type);
}
_extern void MSHookMessageEx(Class _class, SEL sel, IMP imp, IMP *result) {
MSHookMessageInternal(_class, sel, imp, result, NULL);
}
#ifdef __arm__
_extern IMP MSHookMessage(Class _class, SEL sel, IMP imp, const char *prefix) {
IMP result(NULL);
MSHookMessageInternal(_class, sel, imp, &result, prefix);
return result;
}
#endif
#ifdef __arm__
_extern void _Z13MSHookMessageP10objc_classP13objc_selectorPFP11objc_objectS4_S2_zEPKc(Class _class, SEL sel, IMP imp, const char *prefix) {
MSHookMessageInternal(_class, sel, imp, NULL, prefix);
}
#endif
#endif