]> git.saurik.com Git - cycript.git/blame - Tweak.mm
First test version.
[cycript.git] / Tweak.mm
CommitLineData
c1582939
JF
1/* Cyrker - Remove Execution Server and Disassembler
2 * Copyright (C) 2009 Jay Freeman (saurik)
3*/
4
5/*
6 * Redistribution and use in source and binary
7 * forms, with or without modification, are permitted
8 * provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the
11 * above copyright notice, this list of conditions
12 * and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the
14 * above copyright notice, this list of conditions
15 * and the following disclaimer in the documentation
16 * and/or other materials provided with the
17 * distribution.
18 * 3. The name of the author may not be used to endorse
19 * or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
25 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
33 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36*/
37
38#include <substrate.h>
39
40#include <unistd.h>
41
42#include <CoreFoundation/CoreFoundation.h>
43#include <CoreFoundation/CFLogUtilities.h>
44
45#include <CFNetwork/CFNetwork.h>
46#include <Foundation/Foundation.h>
47
48#include <JavaScriptCore/JSBase.h>
49#include <JavaScriptCore/JSValueRef.h>
50#include <JavaScriptCore/JSObjectRef.h>
51#include <JavaScriptCore/JSContextRef.h>
52#include <JavaScriptCore/JSStringRef.h>
53#include <JavaScriptCore/JSStringRefCF.h>
54
55#include <WebKit/WebScriptObject.h>
56
57#include <sys/types.h>
58#include <sys/socket.h>
59#include <netinet/in.h>
60
61/* XXX: bad _assert */
62#define _assert(test) do { \
63 if ((test)) break; \
64 CFLog(kCFLogLevelNotice, CFSTR("_assert(%u)"), __LINE__); \
65} while (false)
66
67#define _trace() do { \
68 CFLog(kCFLogLevelNotice, CFSTR("_trace(%u)"), __LINE__); \
69} while (false)
70
71static JSContextRef Context_;
72static JSClassRef joc_;
73static JSObjectRef Array_;
74static JSStringRef length_;
75static Class NSCFBoolean_;
76
77struct Client {
78 CFHTTPMessageRef message_;
79 CFSocketRef socket_;
80};
81
82@interface NSObject (Cyrver)
83- (NSString *) cy$toJSON;
84@end
85
86@implementation NSObject (Cyrver)
87- (NSString *) cy$toJSON {
88 return [NSString stringWithFormat:@"<%@>", [self description]];
89} @end
90
91@implementation WebUndefined (Cyrver)
92- (NSString *) cy$toJSON {
93 return @"undefined";
94} @end
95
96@implementation NSArray (Cyrver)
97- (NSString *) cy$toJSON {
98 NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
99 [json appendString:@"["];
100
101 bool comma(false);
102 for (NSObject *object in self) {
103 if (comma)
104 [json appendString:@","];
105 else
106 comma = true;
107 [json appendString:[object cy$toJSON]];
108 }
109
110 [json appendString:@"]"];
111 return json;
112} @end
113
114@implementation NSNumber (Cyrver)
115- (NSString *) cy$toJSON {
116 return [self class] != NSCFBoolean_ ? [self stringValue] : [self boolValue] ? @"true" : @"false";
117} @end
118
119@implementation NSString (Cyrver)
120- (NSString *) cy$toJSON {
121 CFMutableStringRef json(CFStringCreateMutableCopy(kCFAllocatorDefault, 0, (CFStringRef) self));
122
123 /* XXX: I can't believe there isn't a safe helper function for this... */
124 CFStringFindAndReplace(json, CFSTR("\\"), CFSTR("\\\\"), CFRangeMake(0, CFStringGetLength(json)), 0);
125 CFStringFindAndReplace(json, CFSTR("\""), CFSTR("\\\""), CFRangeMake(0, CFStringGetLength(json)), 0);
126 CFStringFindAndReplace(json, CFSTR("\t"), CFSTR("\\t"), CFRangeMake(0, CFStringGetLength(json)), 0);
127 CFStringFindAndReplace(json, CFSTR("\r"), CFSTR("\\r"), CFRangeMake(0, CFStringGetLength(json)), 0);
128 CFStringFindAndReplace(json, CFSTR("\n"), CFSTR("\\n"), CFRangeMake(0, CFStringGetLength(json)), 0);
129
130 CFStringInsert(json, 0, CFSTR("\""));
131 CFStringAppend(json, CFSTR("\""));
132
133 return (NSString *) json;
134} @end
135
136@interface CY$JSArray : NSArray {
137 JSObjectRef object_;
138 JSContextRef context_;
139}
140
141- (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context;
142
143- (NSUInteger) count;
144- (id) objectAtIndex:(NSUInteger)index;
145
146@end
147
148static id JSObjectToNSObject(JSContextRef ctx, JSObjectRef object) {
149 if (JSValueIsObjectOfClass(ctx, object, joc_))
150 return reinterpret_cast<id>(JSObjectGetPrivate(object));
151 else if (JSValueIsInstanceOfConstructor(ctx, object, Array_, NULL))
152 return [[[CY$JSArray alloc] initWithJSObject:object inContext:ctx] autorelease];
153 else
154 return @"Hello";
155 //return [[[CY$JSObject alloc] initWithJSObject:object inContext:ctx] autorelease];
156}
157
158static CFTypeRef JSValueToCFTypeCopy(JSContextRef ctx, JSValueRef value) {
159 JSType type(JSValueGetType(ctx, value));
160
161 switch (type) {
162 case kJSTypeUndefined:
163 return [WebUndefined undefined];
164 break;
165
166 case kJSTypeNull:
167 return nil;
168 break;
169
170 case kJSTypeBoolean:
171 return CFRetain(JSValueToBoolean(ctx, value) ? kCFBooleanTrue : kCFBooleanFalse);
172 break;
173
174 case kJSTypeNumber: {
175 double number(JSValueToNumber(ctx, value, NULL));
176 return CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &number);
177 } break;
178
179 case kJSTypeString: {
180 JSStringRef string(JSValueToStringCopy(ctx, value, NULL));
181 CFStringRef object(JSStringCopyCFString(kCFAllocatorDefault, string));
182 JSStringRelease(string);
183 return object;
184 } break;
185
186 case kJSTypeObject:
187 return CFRetain((CFTypeRef) JSObjectToNSObject(ctx, (JSObjectRef) value));
188 break;
189
190 default:
191 _assert(false);
192 return NULL;
193 break;
194 }
195}
196
197static id JSValueToNSObject(JSContextRef ctx, JSValueRef value) {
198 id object((id) JSValueToCFTypeCopy(ctx, value));
199 return object == nil ? nil : [object autorelease];
200}
201
202@implementation CY$JSArray
203
204- (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context {
205 if ((self = [super init]) != nil) {
206 object_ = object;
207 context_ = context;
208 } return self;
209}
210
211- (NSUInteger) count {
212 return JSValueToNumber(context_, JSObjectGetProperty(context_, object_, length_, NULL), NULL);
213}
214
215- (id) objectAtIndex:(NSUInteger)index {
216 JSValueRef value(JSObjectGetPropertyAtIndex(context_, object_, index, NULL));
217 id object(JSValueToNSObject(context_, value));
218 return object == nil ? [NSNull null] : object;
219}
220
221@end
222
223static CFStringRef JSValueToJSONCopy(JSContextRef ctx, JSValueRef value) {
224 id object(JSValueToNSObject(ctx, value));
225 return (CFStringRef) [(object == nil ? @"null" : [object cy$toJSON]) retain];
226}
227
228static void OnData(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) {
229 switch (type) {
230 case kCFSocketDataCallBack:
231 CFDataRef data(reinterpret_cast<CFDataRef>(value));
232 Client *client(reinterpret_cast<Client *>(info));
233
234 if (client->message_ == NULL)
235 client->message_ = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, TRUE);
236
237 if (!CFHTTPMessageAppendBytes(client->message_, CFDataGetBytePtr(data), CFDataGetLength(data)))
238 CFLog(kCFLogLevelError, CFSTR("CFHTTPMessageAppendBytes()"));
239 else if (CFHTTPMessageIsHeaderComplete(client->message_)) {
240 CFURLRef url(CFHTTPMessageCopyRequestURL(client->message_));
241 Boolean absolute;
242 CFStringRef path(CFURLCopyStrictPath(url, &absolute));
243 CFRelease(client->message_);
244
245 CFStringRef code(CFURLCreateStringByReplacingPercentEscapes(kCFAllocatorDefault, path, CFSTR("")));
246 CFRelease(path);
247
248 JSStringRef script(JSStringCreateWithCFString(code));
249 CFRelease(code);
250
251 JSValueRef result(JSEvaluateScript(Context_, script, NULL, NULL, 0, NULL));
252 JSStringRelease(script);
253
254 CFHTTPMessageRef response(CFHTTPMessageCreateResponse(kCFAllocatorDefault, 200, NULL, kCFHTTPVersion1_1));
255 CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Type"), CFSTR("application/json; charset=utf-8"));
256
257 CFStringRef json(JSValueToJSONCopy(Context_, result));
258 CFDataRef body(CFStringCreateExternalRepresentation(kCFAllocatorDefault, json, kCFStringEncodingUTF8, NULL));
259 CFRelease(json);
260
261 CFStringRef length(CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"), CFDataGetLength(body)));
262 CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), length);
263 CFRelease(length);
264
265 CFHTTPMessageSetBody(response, body);
266 CFRelease(body);
267
268 CFDataRef serialized(CFHTTPMessageCopySerializedMessage(response));
269 CFRelease(response);
270
271 CFSocketSendData(socket, NULL, serialized, 0);
272 CFRelease(serialized);
273
274 CFRelease(url);
275 }
276 break;
277 }
278}
279
280static void OnAccept(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) {
281 switch (type) {
282 case kCFSocketAcceptCallBack:
283 Client *client(new Client());
284
285 client->message_ = NULL;
286
287 CFSocketContext context;
288 context.version = 0;
289 context.info = client;
290 context.retain = NULL;
291 context.release = NULL;
292 context.copyDescription = NULL;
293
294 client->socket_ = CFSocketCreateWithNative(kCFAllocatorDefault, *reinterpret_cast<const CFSocketNativeHandle *>(value), kCFSocketDataCallBack, &OnData, &context);
295
296 CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, client->socket_, 0), kCFRunLoopDefaultMode);
297 break;
298 }
299}
300
301static JSValueRef joc_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) {
302 return NULL;
303}
304
305static JSValueRef obc_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) {
306 NSString *name([(NSString *) JSStringCopyCFString(kCFAllocatorDefault, propertyName) autorelease]);
307 if (Class _class = NSClassFromString(name))
308 return JSObjectMake(ctx, joc_, _class);
309 return NULL;
310}
311
312extern "C" void TweakInitialize() {
313 NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
314
315 NSCFBoolean_ = objc_getClass("NSCFBoolean");
316
317 pid_t pid(getpid());
318
319 struct sockaddr_in address;
320 address.sin_len = sizeof(address);
321 address.sin_family = AF_INET;
322 address.sin_addr.s_addr = INADDR_ANY;
323 address.sin_port = htons(10000 + pid);
324
325 CFDataRef data(CFDataCreate(kCFAllocatorDefault, reinterpret_cast<UInt8 *>(&address), sizeof(address)));
326
327 CFSocketSignature signature;
328 signature.protocolFamily = AF_INET;
329 signature.socketType = SOCK_STREAM;
330 signature.protocol = IPPROTO_TCP;
331 signature.address = data;
332
333 CFSocketRef socket(CFSocketCreateWithSocketSignature(kCFAllocatorDefault, &signature, kCFSocketAcceptCallBack, &OnAccept, NULL));
334 CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0), kCFRunLoopDefaultMode);
335
336 JSClassDefinition definition;
337
338 definition = kJSClassDefinitionEmpty;
339 definition.getProperty = &obc_getProperty;
340 JSClassRef obc(JSClassCreate(&definition));
341
342 definition = kJSClassDefinitionEmpty;
343 definition.getProperty = &joc_getProperty;
344 joc_ = JSClassCreate(&definition);
345
346 Context_ = JSGlobalContextCreate(obc);
347
348 JSObjectRef global(JSContextGetGlobalObject(Context_));
349
350 length_ = JSStringCreateWithUTF8CString("length");
351
352 JSStringRef name(JSStringCreateWithUTF8CString("Array"));
353 Array_ = JSValueToObject(Context_, JSObjectGetProperty(Context_, global, name, NULL), NULL);
354 JSStringRelease(name);
355
356 [pool release];
357}