/* Cydia Substrate - Powerful Code Insertion Platform
* Copyright (C) 2012 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 .
**/
/* }}} */
%apt Package: com.saurik.iphone.aspectivec
%apt Author: Jay Freeman (saurik)
%apt Name: Aspective-C
%apt "Description: I can't believe I logged the whole thing"
%apt Depends: mobilesubstrate (>= 0.9.3367+38)
%bundle com.apple.mobileipod
#include
#include
#include
#include
#include
#include
#include
#include
MSClassHook(__NSArrayI)
MSClassHook(__NSArrayM)
MSClassHook(__NSCFArray)
MSClassHook(NSNumber)
MSClassHook(NSString)
static pthread_key_t key_;
static std::set banned_;
struct Data {
FILE *file_;
std::vector stack_;
bool lock_;
Data() :
file_(NULL),
lock_(false)
{
}
};
struct Lock {
Data *data_;
Lock(Data *data) :
data_(data)
{
data_->lock_ = true;
}
~Lock() {
data_->lock_ = false;
}
};
void DestroyData(void *pointer) {
Data *data(reinterpret_cast(pointer));
fclose(data->file_);
delete data;
}
static void log(FILE *file, const char *value) {
// XXX: quotation escape
fprintf(file, "\"%s\"", value);
}
static void log(FILE *file, id value) {
if (value == nil) {
fprintf(file, "nil");
return;
}
Class kind(object_getClass(value));
if (class_isMetaClass(kind)) {
fprintf(file, "[%s class]", class_getName(value));
return;
}
if (
kind == $__NSArrayI ||
kind == $__NSArrayM ||
kind == $__NSCFArray ||
false) {
fprintf(file, "@[");
for (size_t index(0), count([value count]); index != count; ++index) {
if (index != 0)
fprintf(file, ", ");
if (index == 16) {
fprintf(file, "...%zu", count);
break;
}
log(file, [value objectAtIndex:index]);
fflush(file);
}
fprintf(file, "]");
return;
}
if ([value isKindOfClass:$NSNumber]) {
double number([value doubleValue]);
fprintf(file, "@%g", number);
} else if ([value isKindOfClass:$NSString]) {
fprintf(file, "@");
log(file, [value UTF8String]);
} else {
fprintf(file, "<%s@0x%08lx>", class_getName(kind), reinterpret_cast(value));
}
}
static bool log(FILE *file, const char *type, va_list &args) {
type: switch (*type) {
case '#':
case '@': {
id value(va_arg(args, id));
log(file, value);
} break;
case ':': {
SEL value(va_arg(args, SEL));
//fprintf(file, "{%p}", value); fflush(file);
if (value == NULL)
fprintf(file, "NULL");
else
fprintf(file, "@selector(%s)", sel_getName(value));
} break;
case '*': {
const char *value(va_arg(args, const char *));
log(file, value);
} break;
case '^': {
void *value(va_arg(args, void *));
if (value == NULL)
fprintf(file, "NULL");
else
fprintf(file, "0x%08lx", reinterpret_cast(value));
} break;
case 'B': {
bool value(va_arg(args, int));
fprintf(file, "%s", value ? "true" : "false");
} break;
case 'c': {
signed char value(va_arg(args, int));
fprintf(file, "%d", value);
} break;
case 'C': {
unsigned char value(va_arg(args, unsigned int));
fprintf(file, "%d", value);
} break;
case 's': {
short value(va_arg(args, int));
fprintf(file, "%d", value);
} break;
case 'S': {
unsigned short value(va_arg(args, unsigned int));
fprintf(file, "%u", value);
} break;
case 'i': {
int value(va_arg(args, int));
if (value == INT_MAX)
fprintf(file, "INT_MAX");
else
fprintf(file, "%d", value);
} break;
case 'I': {
unsigned int value(va_arg(args, unsigned int));
fprintf(file, "%u", value);
} break;
case 'l': {
long value(va_arg(args, long));
fprintf(file, "%ld", value);
} break;
case 'L': {
unsigned long value(va_arg(args, unsigned long));
fprintf(file, "%lu", value);
} break;
case 'q': {
long long value(va_arg(args, long long));
fprintf(file, "%lld", value);
} break;
case 'Q': {
unsigned long long value(va_arg(args, unsigned long long));
fprintf(file, "%llu", value);
} break;
case 'f': {
union {
uint32_t i;
float f;
} value = {va_arg(args, uint32_t)};
fprintf(file, "%g", value.f);
} break;
case 'd': {
double value(va_arg(args, double));
fprintf(file, "%g", value);
} break;
case 'N':
case 'n':
case 'O':
case 'o':
case 'R':
case 'r':
case 'V':
++type;
goto type;
default:
return false;
}
return true;
}
static Method (*_class_getMethodNoSuper)(Class, SEL);
static Class (*realizeClass)(Class);
extern "C" char ***_NSGetArgv(void);
static void $objc_msgSend$(id self, Class _class, SEL _cmd, va_list args) {
Data *data(reinterpret_cast(pthread_getspecific(key_)));
if (data == NULL) {
data = new Data();
pthread_setspecific(key_, data);
}
if (data->lock_)
return;
Lock lock(data);
FILE *&file(data->file_);
if (file == NULL) {
const char *argv0(**_NSGetArgv());
if (argv0 == NULL)
argv0 = "(null)";
else if (const char *slash = strrchr(argv0, '/'))
argv0 = slash + 1;
pid_t pid(getpid());
pthread_t thread(pthread_self());
char path[1024];
sprintf(path, "/tmp/aspectivec_%s_%d_%p.log", argv0, pid, thread);
fprintf(stderr, "%s", path);
file = fopen(path, "a");
}
if (file == NULL)
return;
uintptr_t pointer(reinterpret_cast(&data));
std::vector &stack(data->stack_);
size_t index;
for (index = 0; index != stack.size(); ++index)
if (stack[index] <= pointer)
break;
if (banned_.find(_cmd) != banned_.end())
return;
bool meta;
const char *name;
NSMethodSignature *signature;
if (self == nil) {
meta = false;
name = "nil";
signature = nil;
return;
} else {
Class direct(object_getClass(self));
if (realizeClass != NULL)
direct = (*realizeClass)(direct);
meta = class_isMetaClass(direct);
if (_class == nil)
_class = direct;
Method method;
if (_class_getMethodNoSuper == NULL)
method = class_getInstanceMethod(_class, _cmd);
else while (_class != nil) {
method = (*_class_getMethodNoSuper)(_class, _cmd);
if (method != NULL)
break;
_class = class_getSuperclass(_class);
}
if (method == NULL)
signature = nil;
else @try {
signature = [NSMethodSignature signatureWithObjCTypes:method_getTypeEncoding(method)];
} @catch (NSException *) {
}
if (_class == nil)
name = "?";
else
name = class_getName(_class);
if (const char *alpha = name) {
while (*alpha == '_')
++alpha;
if (
strncmp(alpha, "CA", 2) == 0 ||
strncmp(alpha, "CF", 2) == 0 ||
strncmp(alpha, "NS", 2) == 0 ||
strncmp(alpha, "UI", 2) == 0 ||
false)
return;
}
}
{
timeval time;
gettimeofday(&time, NULL);
fprintf(file, "[%lu.%.6u] 0x%.08lx %*s%c[%s",
time.tv_sec, time.tv_usec,
reinterpret_cast(self),
int(index * 2), "",
meta ? '+' : '-', name
);
}
const char *rest(sel_getName(_cmd));
if (signature == nil)
fprintf(file, " %s", rest);
else {
size_t index(2);
for (const char *colon; *rest != '\0'; rest = colon) {
colon = strchr(rest, ':');
if (colon == NULL) {
fprintf(file, " %s", rest);
break;
}
size_t length(++colon - rest);
char part[length + 1];
memcpy(part, rest, length);
part[length] = '\0';
fprintf(file, " %s", part);
const char *type([signature getArgumentTypeAtIndex:index++]);
//fprintf(file, "[%s]", type ?: "?"); fflush(file);
if (!log(file, type, args)) {
fprintf(file, "<%s>", type);
if (*colon != '\0') {
fprintf(file, " %s", colon);
colon = "";
}
}
fflush(file);
}
}
fprintf(file, "]\n");
fflush(file);
stack.resize(index);
stack.push_back(pointer);
}
#define call(b, value) \
__asm volatile ("push {r0}\n"); \
__asm volatile ("mov r12, %0\n" :: "r"(value)); \
__asm volatile ("pop {r0}\n" #b " r12\n");
#define save() \
__asm volatile ("push {r0, r1, r2, r3}\n");
#define load() \
__asm volatile ("pop {r0, r1, r2, r3}\n");
#define link(b, value) \
__asm volatile ("push {lr}\n"); \
__asm volatile ("sub sp, #4\n"); \
call(b, value) \
__asm volatile ("add sp, #4\n"); \
__asm volatile ("pop {lr}\n");
static id (*_objc_msgSend)(id, SEL, ...);
__attribute__((__naked__))
static void $objc_msgSend() {
save()
__asm volatile ("mov r2, r1\n");
__asm volatile ("mov r1, #0\n");
__asm volatile ("add r3, sp, #8\n");
link(blx, &$objc_msgSend$)
load()
call(bx, _objc_msgSend)
}
static void (*_objc_msgSend_stret)(id, SEL, ...);
__attribute__((__naked__))
static void $objc_msgSend_stret() {
save()
__asm volatile ("mov r0, r1\n");
__asm volatile ("mov r1, #0\n");
__asm volatile ("add r3, sp, #12\n");
link(blx, &$objc_msgSend$)
load()
call(bx, _objc_msgSend_stret)
}
static id (*_objc_msgSendSuper)(struct objc_super *, SEL, ...);
__attribute__((__naked__))
static void $objc_msgSendSuper() {
save()
__asm volatile ("mov r2, r1\n");
__asm volatile ("ldr r1, [r0, #4]\n");
__asm volatile ("ldr r0, [r0, #0]\n");
__asm volatile ("add r3, sp, #8\n");
link(blx, &$objc_msgSend$)
load()
call(bx, _objc_msgSendSuper)
}
static void (*_objc_msgSendSuper_stret)(struct objc_super *, SEL, ...);
__attribute__((__naked__))
static void $objc_msgSendSuper_stret() {
save()
__asm volatile ("ldr r0, [r1, #0]\n");
__asm volatile ("ldr r1, [r1, #4]\n");
__asm volatile ("add r3, sp, #12\n");
link(blx, &$objc_msgSend$)
load()
call(bx, _objc_msgSendSuper_stret)
}
static id (*_objc_msgSendSuper2)(struct objc_super *, SEL, ...);
__attribute__((__naked__))
static void $objc_msgSendSuper2() {
save()
__asm volatile ("ldr r0, [r0, #4]\n");
link(blx, class_getSuperclass)
__asm volatile ("mov r12, r0");
load()
save()
__asm volatile ("mov r2, r1\n");
__asm volatile ("ldr r0, [r0, #0]\n");
__asm volatile ("mov r1, r12\n");
__asm volatile ("add r3, sp, #8\n");
link(blx, &$objc_msgSend$)
load()
call(bx, _objc_msgSendSuper2)
}
static void (*_objc_msgSendSuper2_stret)(struct objc_super *, SEL, ...);
__attribute__((__naked__))
static void $objc_msgSendSuper2_stret() {
save()
__asm volatile ("ldr r0, [r1, #4]\n");
link(blx, class_getSuperclass)
__asm volatile ("mov r12, r0");
load()
save()
__asm volatile ("ldr r0, [r1, #0]\n");
__asm volatile ("mov r1, r12\n");
__asm volatile ("add r3, sp, #12\n");
link(blx, &$objc_msgSend$)
load()
call(bx, _objc_msgSendSuper2_stret)
}
MSInitialize {
pthread_key_create(&key_, &DestroyData);
banned_.insert(@selector(alloc));
banned_.insert(@selector(autorelease));
banned_.insert(@selector(class));
banned_.insert(@selector(copy));
banned_.insert(@selector(copyWithZone:));
banned_.insert(@selector(dealloc));
banned_.insert(@selector(delegate));
banned_.insert(@selector(isKindOfClass:));
banned_.insert(@selector(lock));
banned_.insert(@selector(retain));
banned_.insert(@selector(release));
banned_.insert(@selector(unlock));
MSImageRef libobjc(MSGetImageByName("/usr/lib/libobjc.A.dylib"));
MSHookSymbol(_class_getMethodNoSuper, "__class_getMethodNoSuper", libobjc);
MSHookSymbol(realizeClass, "__ZL12realizeClassP7class_t", libobjc);
if (realizeClass == NULL)
MSHookSymbol(realizeClass, "_realizeClass", libobjc);
MSHookFunction(&objc_msgSend, (id (*)(id, SEL, ...)) MSHake(objc_msgSend));
MSHookFunction(&objc_msgSend_stret, (void (*)(id, SEL, ...)) MSHake(objc_msgSend_stret));
MSHookFunction(&objc_msgSendSuper, (id (*)(struct objc_super *, SEL, ...)) MSHake(objc_msgSendSuper));
MSHookFunction(&objc_msgSendSuper_stret, (void (*)(struct objc_super *, SEL, ...)) MSHake(objc_msgSendSuper_stret));
MSHookFunction(libobjc, "objc_msgSendSuper2", (id (*)(struct objc_super *, SEL, ...)) MSHake(objc_msgSendSuper2));
MSHookFunction(libobjc, "objc_msgSendSuper2_stret", (void (*)(struct objc_super *, SEL, ...)) MSHake(objc_msgSendSuper2_stret));
}