From b6961e537b5468dc0458808fdacba4a3291e1d48 Mon Sep 17 00:00:00 2001 From: "Jay Freeman (saurik)" Date: Sat, 31 Oct 2009 10:23:51 +0000 Subject: [PATCH] Implemented Mach injection: Cycript into any process. --- Baton.hpp | 12 ++++++ Connector.cpp | 2 + Console.cpp | 20 +++++++++- Darwin-arm.mk | 10 ++++- Exception.hpp | 7 ++++ Mach/Inject.cpp | 102 +++++++++++++++++++++++++++++++++++++++++++++++ Trampoline.t.cpp | 25 ++++++++++++ cycript.xml | 12 ++++++ makefile | 7 +++- todo.txt | 1 - 10 files changed, 192 insertions(+), 6 deletions(-) create mode 100644 Baton.hpp create mode 100644 Mach/Inject.cpp create mode 100644 Trampoline.t.cpp create mode 100644 cycript.xml diff --git a/Baton.hpp b/Baton.hpp new file mode 100644 index 0000000..45f0ec9 --- /dev/null +++ b/Baton.hpp @@ -0,0 +1,12 @@ +#include +#include + +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[]; +}; diff --git a/Connector.cpp b/Connector.cpp index dc0d4cb..9d49a05 100644 --- a/Connector.cpp +++ b/Connector.cpp @@ -111,6 +111,8 @@ static void Unlink() { } MSInitialize { + CFLog(kCFLogLevelError, CFSTR("CY:Notice: library loaded")); + _aprcall(apr_initialize()); CYServer *server(new CYServer()); diff --git a/Console.cpp b/Console.cpp index ef3de63..569157a 100644 --- a/Console.cpp +++ b/Console.cpp @@ -354,9 +354,9 @@ static void *Map(const char *path, size_t *psize) { 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); @@ -523,6 +523,8 @@ int main(int argc, char const * const argv[], char const * const envp[]) { if (pid == _not(pid_t)) socket = -1; else { + InjectLibrary(pid); + socket = _syscall(::socket(PF_UNIX, SOCK_STREAM, 0)); struct sockaddr_un address; @@ -589,3 +591,17 @@ int main(int argc, char const * const argv[], char const * const envp[]) { 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; + } +} diff --git a/Darwin-arm.mk b/Darwin-arm.mk index c1858fe..10e4289 100644 --- a/Darwin-arm.mk +++ b/Darwin-arm.mk @@ -3,10 +3,18 @@ flags += -F${PKG_ROOT}/System/Library/PrivateFrameworks 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,$^) \ diff --git a/Exception.hpp b/Exception.hpp index a3be1a8..2828345 100644 --- a/Exception.hpp +++ b/Exception.hpp @@ -42,6 +42,7 @@ #include +#include #include "Standard.hpp" struct CYException { @@ -91,6 +92,12 @@ void CYThrow(JSContextRef context, JSValueRef value); _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)) \ diff --git a/Mach/Inject.cpp b/Mach/Inject.cpp new file mode 100644 index 0000000..ca1f07c --- /dev/null +++ b/Mach/Inject.cpp @@ -0,0 +1,102 @@ +#include +#include + +extern "C" { +#include +} + +#include +#include + +#include "Baton.hpp" +#include "Exception.hpp" +#include "Pooling.hpp" +#include "Trampoline.t.hpp" + +extern "C" void _pthread_set_self(pthread_t); + +template +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(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(apr_palloc(pool, depth))); + + Baton *baton(reinterpret_cast(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(baton), depth); + + vm_address_t code; + _krncall(vm_allocate(task, &code, sizeof(Trampoline_), true)); + vm_write(task, code, reinterpret_cast(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(&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(&state), count)); +#else + #error XXX: implement +#endif + + _krncall(thread_resume(thread)); + + //_krncall(thread_create_running(task, flavor, reinterpret_cast(&state), count, &thread)); + + //_krncall(mach_port_deallocate(self, task)); +} diff --git a/Trampoline.t.cpp b/Trampoline.t.cpp new file mode 100644 index 0000000..8689df3 --- /dev/null +++ b/Trampoline.t.cpp @@ -0,0 +1,25 @@ +#define _PTHREAD_ATTR_T +#include + +#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(arg)); + baton->dlopen(baton->library, RTLD_LAZY | RTLD_GLOBAL); + return arg; +} diff --git a/cycript.xml b/cycript.xml new file mode 100644 index 0000000..bf3c3a5 --- /dev/null +++ b/cycript.xml @@ -0,0 +1,12 @@ + + + + com.apple.springboard.debugapplications + + get-task-allow + + task_for_pid-allow + + + + diff --git a/makefile b/makefile index aa0bbba..b4cf765 100644 --- a/makefile +++ b/makefile @@ -29,8 +29,11 @@ code += Cycript.tab.o lex.cy.o 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 @@ -130,9 +133,9 @@ libcycript.$(dll): $(code) $(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) diff --git a/todo.txt b/todo.txt index e6a54bc..512e3c2 100644 --- a/todo.txt +++ b/todo.txt @@ -2,7 +2,6 @@ unicode identifier support (native and \u) 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 -- 2.47.2