--- /dev/null
+#include <dlfcn.h>
+#include <mach/mach.h>
+
+struct Baton {
+ void (*_pthread_set_self)(pthread_t);
+ int (*pthread_create)(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *);
+ int (*pthread_detach)(pthread_t);
+ void *(*dlopen)(const char *, int);
+ mach_port_t (*mach_thread_self)();
+ kern_return_t (*thread_terminate)(thread_act_t);
+ char library[];
+};
}
MSInitialize {
+ CFLog(kCFLogLevelError, CFSTR("CY:Notice: library loaded"));
+
_aprcall(apr_initialize());
CYServer *server(new CYServer());
return base;
}
-int main(int argc, char const * const argv[], char const * const envp[]) {
- _aprcall(apr_app_initialize(&argc, &argv, &envp));
+void InjectLibrary(pid_t pid);
+int Main(int argc, char const * const argv[], char const * const envp[]) {
bool tty(isatty(STDIN_FILENO));
bool compile(false);
if (pid == _not(pid_t))
socket = -1;
else {
+ InjectLibrary(pid);
+
socket = _syscall(::socket(PF_UNIX, SOCK_STREAM, 0));
struct sockaddr_un address;
return 0;
}
+
+int main(int argc, char const * const argv[], char const * const envp[]) {
+ apr_status_t status(apr_app_initialize(&argc, &argv, &envp));
+ if (status != APR_SUCCESS) {
+ fprintf(stderr, "apr_app_initialize() != APR_SUCCESS\n");
+ return 1;
+ } else try {
+ return Main(argc, argv, envp);
+ } catch (const CYException &error) {
+ CYPool pool;
+ fprintf(stderr, "%s\n", error.PoolCString(pool));
+ return 1;
+ }
+}
all += Cycript.$(dll) #cyrver
arch := iphoneos-arm
-ldid := ldid -S
console += -framework UIKit
depends += readline libffi mobilesubstrate sqlite3-lib
code += Handler.o
+inject += Mach/Inject.o
+
+Mach/Inject.o: Trampoline.t.hpp Baton.hpp
+
+%.t.hpp: %.t.cpp
+ $(target)gcc -c -o $*.t.o $< && $(target)otool -s __TEXT __text $*.t.o | tail -n +3 | sed -e 's/^[^ ]* //;s/ $$//;s/ /\n/g' | sed -e 's/\(..\)\(..\)\(..\)\(..\)/0\x\4,0\x\3,0\x\2,0\x\1/' | tr '\n' ',' | sed -e '$$ s/,$$//; s/^/static const char $*_[] = {/;s/$$/};\n/' >$@ && rm -f $*.t.o
+
+ldid := ldid -S
+entitle := ldid -Scycript.xml
Cycript.$(dll): Connector.o
$(target)g++ $(flags) -dynamiclib -o $@ $(filter %.o,$^) \
#include <JavaScriptCore/JSBase.h>
+#include <apr_pools.h>
#include "Standard.hpp"
struct CYException {
_assert(_aprstatus == APR_SUCCESS); \
} while (false)
+#define _krncall(expr) \
+ do { \
+ kern_return_t _krnstatus((expr)); \
+ _assert(_krnstatus == KERN_SUCCESS); \
+ } while (false)
+
#define _sqlcall(expr) ({ \
__typeof__(expr) _value = (expr); \
if (_value != 0 && (_value < 100 || _value >= 200)) \
--- /dev/null
+#include <dlfcn.h>
+#include <mach/mach.h>
+
+extern "C" {
+#include <mach-o/nlist.h>
+}
+
+#include <cstdio>
+#include <pthread.h>
+
+#include "Baton.hpp"
+#include "Exception.hpp"
+#include "Pooling.hpp"
+#include "Trampoline.t.hpp"
+
+extern "C" void _pthread_set_self(pthread_t);
+
+template <typename Type_>
+static void nlset(Type_ &function, struct nlist *nl, size_t index) {
+ struct nlist &name(nl[index]);
+ uintptr_t value(name.n_value);
+ if ((name.n_desc & N_ARM_THUMB_DEF) != 0)
+ value |= 0x00000001;
+ function = reinterpret_cast<Type_>(value);
+}
+
+void InjectLibrary(pid_t pid) {
+ const char *library("/Library/MobileSubstrate/DynamicLibraries/Cycript.dylib");
+
+ static const size_t Stack_(8 * 1024);
+ size_t length(strlen(library) + 1), depth(sizeof(Baton) + length);
+ depth = (depth + sizeof(uintptr_t) + 1) / sizeof(uintptr_t) * sizeof(uintptr_t);
+
+ CYPool pool;
+ uint8_t *local(reinterpret_cast<uint8_t *>(apr_palloc(pool, depth)));
+
+ Baton *baton(reinterpret_cast<Baton *>(local));
+ baton->pthread_create = &pthread_create;
+ baton->pthread_detach = &pthread_detach;
+ baton->dlopen = &dlopen;
+ baton->mach_thread_self = &mach_thread_self;
+ baton->thread_terminate = &thread_terminate;
+ memcpy(baton->library, library, length);
+
+ struct nlist nl[2];
+ memset(nl, 0, sizeof(nl));
+ nl[0].n_un.n_name = (char *) "__pthread_set_self";
+ nlist("/usr/lib/libSystem.B.dylib", nl);
+ nlset(baton->_pthread_set_self, nl, 0);
+
+ vm_size_t size(depth + Stack_);
+
+ mach_port_t self(mach_task_self()), task;
+ _krncall(task_for_pid(self, pid, &task));
+
+ vm_address_t data;
+ _krncall(vm_allocate(task, &data, size, true));
+ vm_address_t stack(data + depth);
+ vm_write(task, data, reinterpret_cast<vm_address_t>(baton), depth);
+
+ vm_address_t code;
+ _krncall(vm_allocate(task, &code, sizeof(Trampoline_), true));
+ vm_write(task, code, reinterpret_cast<vm_address_t>(Trampoline_), sizeof(Trampoline_));
+ _krncall(vm_protect(task, code, sizeof(Trampoline_), false, VM_PROT_READ | VM_PROT_EXECUTE));
+
+ thread_act_t thread;
+ _krncall(thread_create(task, &thread));
+
+ thread_state_flavor_t flavor;
+ mach_msg_type_number_t count;
+
+#if defined(__arm__)
+ arm_thread_state_t state;
+ memset(&state, 0, sizeof(state));
+
+ flavor = ARM_THREAD_STATE;
+ count = ARM_THREAD_STATE_COUNT;
+
+ _krncall(thread_get_state(thread, flavor, reinterpret_cast<thread_state_t>(&state), &count));
+ _assert(count == ARM_THREAD_STATE_COUNT);
+
+ state.r[0] = data;
+ state.r[1] = RTLD_LAZY | RTLD_GLOBAL;
+ state.sp = stack + Stack_;
+ state.pc = code;
+
+ if ((state.pc & 0x1) != 0) {
+ state.pc &= ~0x1;
+ state.cpsr |= 0x20;
+ }
+
+ _krncall(thread_set_state(thread, flavor, reinterpret_cast<thread_state_t>(&state), count));
+#else
+ #error XXX: implement
+#endif
+
+ _krncall(thread_resume(thread));
+
+ //_krncall(thread_create_running(task, flavor, reinterpret_cast<thread_state_t>(&state), count, &thread));
+
+ //_krncall(mach_port_deallocate(self, task));
+}
--- /dev/null
+#define _PTHREAD_ATTR_T
+#include <pthread_internals.h>
+
+#include "Baton.hpp"
+
+void *Routine(void *);
+
+extern "C" void Start(Baton *baton) {
+ struct _pthread self;
+ baton->_pthread_set_self(&self);
+
+ pthread_t thread;
+ baton->pthread_create(&thread, NULL, &Routine, baton);
+
+ void *result;
+ baton->pthread_detach(thread);
+
+ baton->thread_terminate(baton->mach_thread_self());
+}
+
+void *Routine(void *arg) {
+ Baton *baton(reinterpret_cast<Baton *>(arg));
+ baton->dlopen(baton->library, RTLD_LAZY | RTLD_GLOBAL);
+ return arg;
+}
--- /dev/null
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.springboard.debugapplications</key>
+ <true/>
+ <key>get-task-allow</key>
+ <true/>
+ <key>task_for_pid-allow</key>
+ <true/>
+</dict>
+</plist>
+
code += Network.o Parser.o
code += JavaScriptCore.o Library.o
+inject :=
+
filters := C #E4X
ldid := true
+entitle := $(ldid)
dll := so
apr := $(shell apr-1-config --link-ld)
library := $(apr) -lffi -lsqlite3
$(target)g++ $(flags) -shared -dynamiclib -o $@ $(filter %.o,$^) $(library) $(link)
$(ldid) $@
-cycript: Console.o libcycript.$(dll)
+cycript: Console.o libcycript.$(dll) $(inject)
$(target)g++ $(flags) -o $@ $(filter %.o,$^) -L. -lcycript $(console) $(link)
- $(ldid) cycript
+ $(entitle) cycript
package: $(deb)
support unions (right now 0-1 fields parsed as struct)
\\\n escapes in strings aren't handled in the console
look into what String is, and whether to bridge it
-the console frontend's error handling, well, doesn't
some JS callbacks don't use exception pointers at all...
a newline needs to not be allowed after a unary *
finish implementing default xml namespace statement