From eed4f17489f121ce232ce88b6127b6d4288f867f Mon Sep 17 00:00:00 2001 From: "Jay Freeman (saurik)" Date: Sun, 2 May 2010 09:44:18 +0000 Subject: [PATCH] Got universal inject working. --- Baton.hpp | 2 +- Darwin.mk | 9 ++--- GNUmakefile.in | 6 ++-- Mach/Inject.cpp | 75 +++++++++++++++++---------------------- Trampoline.hpp | 44 +++++++++++++++++++++++ Trampoline.t.cpp | 54 ++++++++++++++++++++++------ makefile | 11 ++++-- todo.txt | 2 ++ trampoline.sh | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 230 insertions(+), 65 deletions(-) create mode 100644 Trampoline.hpp create mode 100755 trampoline.sh diff --git a/Baton.hpp b/Baton.hpp index 5442678..8d8adf5 100644 --- a/Baton.hpp +++ b/Baton.hpp @@ -42,7 +42,7 @@ #include struct Baton { - void (*_pthread_set_self)(pthread_t); + void (*__pthread_set_self)(pthread_t); int (*pthread_create)(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *); int (*pthread_join)(pthread_t, void **); diff --git a/Darwin.mk b/Darwin.mk index e848981..02e2d2a 100644 --- a/Darwin.mk +++ b/Darwin.mk @@ -9,18 +9,15 @@ library += -framework WebKit library += -liconv flags += -I/usr/include/ffi apr_config := /usr/bin/apr-1-config - -ifeq ($(uname_p),i386) -flags += -m32 -endif +flags += -arch i386 -arch x86_64 #-arch armv6 flags += -DCY_ATTACH code += Handler.o inject += Mach/Inject.o Mach/Inject.o: Trampoline.t.hpp Baton.hpp -%.t.hpp: %.t.cpp - $(target)gcc -c -fno-stack-protector -fno-exceptions -Iinclude -o $*.t.o $< $(flags) && { file=($$($(target)otool -l $*.t.o | sed -e 'x; /^1/ { x; /^ *filesize / { s/^.* //; p; }; /^ *fileoff / { s/^.* //; p; }; x; }; x; /^ *cmd LC_SEGMENT$$/ { s/.*/1/; x; }; d;')); od -t x1 -j $${file[0]} -N $${file[1]} $*.t.o | sed -e 's/^[^ ]*//' | tr $$'\n' ' ' | sed -e 's/ */ /g;s/^ *//;s/ $$//;s/ /,/g;s/\([^,][^,]\)/0x\1/g' | sed -e 's/^/static const char $*_[] = {/;s/$$/};/' && echo && echo "/*" && $(target)otool -vVt $*.t.o && echo "*/"; } >$@ && rm -f $*.t.o +%.t.hpp: %.t.cpp trampoline.sh Baton.hpp Trampoline.hpp Darwin.mk + ./trampoline.sh $@ $*.t.dylib $* sed $(target){otool,lipo,nm,gcc} $(flags) -dynamiclib -g0 -fno-stack-protector -fno-exceptions -Iinclude $< -o $*.t.dylib clean:: rm -f Trampoline.t.hpp diff --git a/GNUmakefile.in b/GNUmakefile.in index b193e8f..e6d2ee3 100644 --- a/GNUmakefile.in +++ b/GNUmakefile.in @@ -182,9 +182,9 @@ inject += Mach/Inject.lo Mach/Inject.lo: Trampoline.t.hpp Baton.hpp -%.t.hpp: %.t.cpp - # LC_SEGMENT_64 - $(libtool) --mode=compile $(cxx) -c -fno-stack-protector -fno-exceptions -I$(srcdir)/include -o $*.t.lo $< $(cflags) && { file=($$($(otool) -l .libs/$*.t.o | $(sed) -e 'x; /^1/ { x; /^ *filesize / { s/^.* //; p; }; /^ *fileoff / { s/^.* //; p; }; x; }; x; /^ *cmd LC_SEGMENT/ { s/.*/1/; x; }; d;')); od -t x1 -j $${file[0]} -N $${file[1]} .libs/$*.t.o | $(sed) -e 's/^[^ ]*//' | tr $$'\n' ' ' | $(sed) -e 's/ */ /g;s/^ *//;s/ $$//;s/ /,/g;s/\([^,][^,]\)/0x\1/g' | $(sed) -e 's/^/static const char $*_[] = {/;s/$$/};/' && echo && echo "/*" && $(otool) -vVt .libs/$*.t.o && echo "*/"; } >$@ && rm -f $*.t.lo .libs/$*.t.o +%.t.hpp: %.t.cpp trampoline.sh Baton.hpp Trampoline.hpp + # DOUG: abstract "lipo" and "nm" with configure? + ./trampoline.sh $@ .libs/lib$*.t.dylib $* $(sed) $(otool) lipo nm $(libtool) --mode=compile $(cxx) -dynamiclib -fno-stack-protector -fno-exceptions -I$(srcdir)/include -o lib$*.t.la $< $(cflags) endif endif diff --git a/Mach/Inject.cpp b/Mach/Inject.cpp index 20c2e52..6b14c3a 100644 --- a/Mach/Inject.cpp +++ b/Mach/Inject.cpp @@ -40,9 +40,7 @@ #include #include -extern "C" { -#include -} +#include #include #include @@ -55,18 +53,8 @@ extern "C" { 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); - _assert(value != 0); - if ((name.n_desc & N_ARM_THUMB_DEF) != 0) - value |= 0x00000001; - function = value; -} - void InjectLibrary(pid_t pid) { - // XXX: break this into the build environment + // DOUG: turn this into some kind of -D passed from configure const char *library("/usr/lib/libcycript.dylib"); static const size_t Stack_(8 * 1024); @@ -77,23 +65,7 @@ void InjectLibrary(pid_t pid) { uint8_t *local(reinterpret_cast(apr_palloc(pool, depth))); Baton *baton(reinterpret_cast(local)); - uintptr_t set_self_internal; - uintptr_t set_self_external; - -#if defined(__i386__) || defined(__arm__) - struct nlist nl[3]; - memset(nl, 0, sizeof(nl)); - nl[0].n_un.n_name = (char *) "__pthread_set_self"; - nl[1].n_un.n_name = (char *) "___pthread_set_self"; - nlist("/usr/lib/libSystem.B.dylib", nl); - nlset(set_self_internal, nl, 0); - nlset(set_self_external, nl, 1); -#else - set_self_internal = 0; - set_self_external = 0; -#endif - - baton->_pthread_set_self = reinterpret_cast(reinterpret_cast(&__pthread_set_self) - set_self_external + set_self_internal); + baton->__pthread_set_self = &__pthread_set_self; baton->pthread_create = &pthread_create; baton->pthread_join = &pthread_join; @@ -118,11 +90,6 @@ void InjectLibrary(pid_t pid) { 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)); @@ -130,24 +97,42 @@ void InjectLibrary(pid_t pid) { mach_msg_type_number_t count; size_t push; + Trampoline *trampoline; + #if defined(__arm__) + trampoline = &Trampoline_arm_; arm_thread_state_t state; flavor = ARM_THREAD_STATE; count = ARM_THREAD_STATE_COUNT; push = 0; -#elif defined(__i386__) || defined(__x86_64__) +#elif defined(__i386__) + trampoline = &Trampoline_i386_; i386_thread_state_t state; flavor = i386_THREAD_STATE; count = i386_THREAD_STATE_COUNT; push = 5; +#elif defined(__x86_64__) + trampoline = &Trampoline_x86_64_; + x86_thread_state64_t state; + flavor = x86_THREAD_STATE64; + count = x86_THREAD_STATE64_COUNT; + push = 2; #else #error XXX: implement #endif - uintptr_t frame[push]; + vm_address_t code; + _krncall(vm_allocate(task, &code, trampoline->size_, true)); + vm_write(task, code, reinterpret_cast(trampoline->data_), trampoline->size_); + _krncall(vm_protect(task, code, trampoline->size_, false, VM_PROT_READ | VM_PROT_EXECUTE)); + + printf("_ptss:%p\n", baton->__pthread_set_self); + printf("dlsym:%p\n", baton->dlsym); + printf("code:%zx\n", (size_t) code); + + uint32_t frame[push]; if (sizeof(frame) != 0) memset(frame, 0, sizeof(frame)); - memset(&state, 0, sizeof(state)); mach_msg_type_number_t read(count); @@ -157,18 +142,22 @@ void InjectLibrary(pid_t pid) { #if defined(__arm__) state.r[0] = data; state.sp = stack + Stack_; - state.pc = code; + state.pc = code + trampoline->entry_; if ((state.pc & 0x1) != 0) { state.pc &= ~0x1; state.cpsr |= 0x20; } -#elif defined(__i386__) || defined(__x86_64__) - frame[0] = 0; +#elif defined(__i386__) frame[1] = data; - state.__eip = code; + state.__eip = code + trampoline->entry_; state.__esp = stack + Stack_ - sizeof(frame); +#elif defined(__x86_64__) + frame[0] = 0xdeadbeef; + state.__rdi = data; + state.__rip = code + trampoline->entry_; + state.__rsp = stack + Stack_ - sizeof(frame); #else #error XXX: implement #endif diff --git a/Trampoline.hpp b/Trampoline.hpp new file mode 100644 index 0000000..2f5a5f2 --- /dev/null +++ b/Trampoline.hpp @@ -0,0 +1,44 @@ +/* Cycript - Inlining/Optimizing JavaScript Compiler + * Copyright (C) 2010 Jay Freeman (saurik) +*/ + +/* Modified BSD License {{{ */ +/* + * Redistribution and use in source and binary + * forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the + * above copyright notice, this list of conditions + * and the following disclaimer. + * 2. Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions + * and the following disclaimer in the documentation + * and/or other materials provided with the + * distribution. + * 3. The name of the author may not be used to endorse + * or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/* }}} */ + +struct Trampoline { + const char *data_; + size_t size_; + size_t entry_; +}; diff --git a/Trampoline.t.cpp b/Trampoline.t.cpp index c228259..ad1f515 100644 --- a/Trampoline.t.cpp +++ b/Trampoline.t.cpp @@ -40,27 +40,61 @@ #define _PTHREAD_ATTR_T #include +#include "Standard.hpp" #include "Baton.hpp" -void *Routine(void *); +template +static _finline void dlset(Baton *baton, Type_ &function, const char *name, void *handle = RTLD_DEFAULT) { + function = reinterpret_cast(baton->dlsym(handle, name)); +} + +void *Routine(void *arg) { + Baton *baton(reinterpret_cast(arg)); + + void *(*dlopen)(const char *, int); + dlset(baton, dlopen, "dlopen"); + + void *handle(dlopen(baton->library, RTLD_LAZY | RTLD_LOCAL)); + + void (*CYHandleServer)(pid_t); + dlset(baton, CYHandleServer, "CYHandleServer", handle); + + CYHandleServer(baton->pid); + + return arg; +} + +static void $bzero(void *data, size_t size) { + char *bytes(reinterpret_cast(data)); + for (size_t i(0); i != size; ++i) + bytes[i] = 0; +} extern "C" void Start(Baton *baton) { struct _pthread self; - baton->_pthread_set_self(&self); + $bzero(&self, sizeof(self)); + + // this code comes from _pthread_set_self + self.tsd[0] = &self; + baton->__pthread_set_self(&self); + + int (*pthread_create)(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *); + dlset(baton, pthread_create, "pthread_create"); pthread_t thread; baton->pthread_create(&thread, NULL, &Routine, baton); + int (*pthread_join)(pthread_t, void **); + dlset(baton, pthread_join, "pthread_join"); + void *result; baton->pthread_join(thread, &result); - baton->thread_terminate(baton->mach_thread_self()); -} + mach_port_t (*mach_thread_self)(); + dlset(baton, mach_thread_self, "mach_thread_self"); -void *Routine(void *arg) { - Baton *baton(reinterpret_cast(arg)); - void *handle(baton->dlopen(baton->library, RTLD_LAZY | RTLD_LOCAL)); - void (*HandleServer)(pid_t) = reinterpret_cast(baton->dlsym(handle, "CYHandleServer")); - HandleServer(baton->pid); - return arg; + kern_return_t (*thread_terminate)(thread_act_t); + dlset(baton, thread_terminate, "thread_terminate"); + + baton->thread_terminate(baton->mach_thread_self()); } diff --git a/makefile b/makefile index f939c4b..1de908e 100644 --- a/makefile +++ b/makefile @@ -159,7 +159,14 @@ test: $(deb) if [[ -e test.cy ]]; then cycript test.cy; fi install: cycript $(lib)cycript.$(dll) - cp -p cycript /usr/bin - cp -p $(lib)cycript.$(dll) /usr/lib + sudo cp -p cycript /usr/bin + sudo cp -p $(lib)cycript.$(dll) /usr/lib + # DOUG: this needs to be ported to GNUmakefile + sudo chgrp procmod /usr/bin/cycript + sudo chmod g+s /usr/bin/cycript + +# DOUG: this needs to be ported to GNUmakefile +uninstall: + sudo rm -f /usr/bin/cycript /usr/lib/libcycript.dylib .PHONY: all clean extra package control.tmp diff --git a/todo.txt b/todo.txt index 09ec59e..aab036c 100644 --- a/todo.txt +++ b/todo.txt @@ -29,3 +29,5 @@ I should probably attempt to use the auto_ flag somehow to not do contexts_ push Object_callAsFunction_toCYON should be implemented [NSString stringWithString:""] crashes, on linux, not on mac + +replace procmod g+s with gdb's macosx_get_task_for_pid_rights diff --git a/trampoline.sh b/trampoline.sh new file mode 100755 index 0000000..81ac2ce --- /dev/null +++ b/trampoline.sh @@ -0,0 +1,92 @@ +#!/bin/bash + +shopt -s extglob + +hpp=$1 +object=$2 +name=$3 +sed=$4 +otool=$5 +lipo=$6 +nm=$7 +shift 7 + +#shift 1 +#set /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/gcc-4.2 -I/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.1.3.sdk/usr/include "$@" + +"$@" + +detailed=$(lipo -detailed_info "${object}") + +{ + +echo '#include "Trampoline.hpp"' + +for arch in $(echo "${detailed}" | "${sed}" -e '/^architecture / { s/^architecture //; p; }; d;'); do + offset=$(echo "${detailed}" | "${sed}" -e ' + /^architecture / { x; s/.*/0/; x; }; + /^architecture '${arch}'$/ { x; s/.*/1/; x; }; + x; /^1$/ { x; /^ *offset / { s/^ *offset //; p; }; x; }; x; + d; + ') + + file=($("${otool}" -arch "${arch}" -l "${object}" | "${sed}" -e ' + x; /^1$/ { x; + /^ *fileoff / { s/^.* //; p; }; + /^ *filesize / { s/^.* //; p; }; + x; }; x; + + /^ *cmd LC_SEGMENT/ { x; s/.*/1/; x; }; + + d; + ')) + + fileoff=${file[0]} + filesize=${file[1]} + + echo + echo "static const char ${name}_${arch}_data_[] = {" + + od -v -t x1 -t c -j "$((offset + fileoff))" -N "${filesize}" "${object}" | "${sed}" -e ' + /^[0-7]/ ! { + s@^ @// @; + s/\(....\)/ \1/g; + s@^ // @//@; + s/ *$//; + }; + + /^[0-7]/ { + s/^[^ ]*//; + s/ */ /g; + s/^ *//; + s/ $//; + s/ /,/g; + s/\([^,][^,]\)/0x\1/g; + s/$/,/; + /^,$/ ! { s/^/ /g; p; }; d; + }; + ' + + echo "};" + + echo + entry=$("${nm}" -arch "${arch}" "${object}" | "${sed}" -e '/ _Start$/ { s/ .*//; p; }; d;') + entry=${entry##*(0)} + echo "static size_t ${name}_${arch}_entry_ = 0x${entry:=0};" + + echo + echo "/*" + "${otool}" -vVt -arch "${arch}" "${object}" + echo "*/" + + echo + echo "static Trampoline ${name}_${arch}_ = {" + echo " ${name}_${arch}_data_," + echo " sizeof(${name}_${arch}_data_)," + echo " ${name}_${arch}_entry_," + echo "};" +done + +} >"${hpp}" + +#rm -f "${object}" -- 2.45.2