]> git.saurik.com Git - cycript.git/commitdiff
Implemented Mach injection: Cycript into any process.
authorJay Freeman (saurik) <saurik@saurik.com>
Sat, 31 Oct 2009 10:23:51 +0000 (10:23 +0000)
committerJay Freeman (saurik) <saurik@saurik.com>
Sat, 31 Oct 2009 10:23:51 +0000 (10:23 +0000)
Baton.hpp [new file with mode: 0644]
Connector.cpp
Console.cpp
Darwin-arm.mk
Exception.hpp
Mach/Inject.cpp [new file with mode: 0644]
Trampoline.t.cpp [new file with mode: 0644]
cycript.xml [new file with mode: 0644]
makefile
todo.txt

diff --git a/Baton.hpp b/Baton.hpp
new file mode 100644 (file)
index 0000000..45f0ec9
--- /dev/null
+++ b/Baton.hpp
@@ -0,0 +1,12 @@
+#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[];
+};
index dc0d4cbcaac9a5b455e4742add7438d3ae38b1b2..9d49a05d652b5bc5ba1bbefab7d87625f8eebcd9 100644 (file)
@@ -111,6 +111,8 @@ static void Unlink() {
 }
 
 MSInitialize {
+    CFLog(kCFLogLevelError, CFSTR("CY:Notice: library loaded"));
+
     _aprcall(apr_initialize());
 
     CYServer *server(new CYServer());
index ef3de637a7e63dee7342635ce3495da05fc7dae5..569157a6dcc65bbfd49f46b8b238a0a0ac730dc0 100644 (file)
@@ -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;
+    }
+}
index c1858fe7fa96a402b7e5d316042083092ca48921..10e4289ff4a25ea999810af81e8d15b31c85c375 100644 (file)
@@ -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,$^) \
index a3be1a8db0459ac2be55d0e85624eedd122bf5eb..28283457fc6ced1e671b5373f11cb51958d3f04b 100644 (file)
@@ -42,6 +42,7 @@
 
 #include <JavaScriptCore/JSBase.h>
 
+#include <apr_pools.h>
 #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 (file)
index 0000000..ca1f07c
--- /dev/null
@@ -0,0 +1,102 @@
+#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));
+}
diff --git a/Trampoline.t.cpp b/Trampoline.t.cpp
new file mode 100644 (file)
index 0000000..8689df3
--- /dev/null
@@ -0,0 +1,25 @@
+#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;
+}
diff --git a/cycript.xml b/cycript.xml
new file mode 100644 (file)
index 0000000..bf3c3a5
--- /dev/null
@@ -0,0 +1,12 @@
+<!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>
+
index aa0bbbae6dd91289e9a409473cd2784fa7270548..b4cf765d1e71439cc1fc7933ecb58d6b39dd2e6e 100644 (file)
--- 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)
 
index e6a54bc04ba80e8159f1982e79936a9eaa74f2d1..512e3c27d807a820af696b87770640c45c9740ef 100644 (file)
--- 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