]> git.saurik.com Git - cycript.git/blob - Tweak.mm
2dc974a6aff7f6f5c1ac72341bbc6fe3e9e97427
[cycript.git] / Tweak.mm
1 /* Cyrker - Remove Execution Server and Disassembler
2 * Copyright (C) 2009 Jay Freeman (saurik)
3 */
4
5 /* Modified BSD License {{{ */
6 /*
7 * Redistribution and use in source and binary
8 * forms, with or without modification, are permitted
9 * provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the
12 * above copyright notice, this list of conditions
13 * and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the
15 * above copyright notice, this list of conditions
16 * and the following disclaimer in the documentation
17 * and/or other materials provided with the
18 * distribution.
19 * 3. The name of the author may not be used to endorse
20 * or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
25 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
34 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38 /* }}} */
39
40 #include <substrate.h>
41
42 #include <unistd.h>
43
44 #include <CoreFoundation/CoreFoundation.h>
45 #include <CoreFoundation/CFLogUtilities.h>
46
47 #include <CFNetwork/CFNetwork.h>
48 #include <Foundation/Foundation.h>
49
50 #include <JavaScriptCore/JSBase.h>
51 #include <JavaScriptCore/JSValueRef.h>
52 #include <JavaScriptCore/JSObjectRef.h>
53 #include <JavaScriptCore/JSContextRef.h>
54 #include <JavaScriptCore/JSStringRef.h>
55 #include <JavaScriptCore/JSStringRefCF.h>
56
57 #include <WebKit/WebScriptObject.h>
58
59 #include <sys/types.h>
60 #include <sys/socket.h>
61 #include <netinet/in.h>
62
63 /* XXX: bad _assert */
64 #define _assert(test) do { \
65 if ((test)) break; \
66 CFLog(kCFLogLevelNotice, CFSTR("_assert(%s):%u"), #test, __LINE__); \
67 throw; \
68 } while (false)
69
70 #define _trace() do { \
71 CFLog(kCFLogLevelNotice, CFSTR("_trace():%u"), __LINE__); \
72 } while (false)
73
74 static JSContextRef context_;
75 static JSClassRef joc_;
76 static JSObjectRef Array_;
77 static JSStringRef name_;
78 static JSStringRef message_;
79 static JSStringRef length_;
80 static Class NSCFBoolean_;
81
82 struct Client {
83 CFHTTPMessageRef message_;
84 CFSocketRef socket_;
85 };
86
87 @interface NSObject (Cyrver)
88 - (NSString *) cy$toJSON;
89 // XXX: - (JSValueRef) cy$JSValueInContext:(JSContextRef)context;
90 @end
91
92 @implementation NSObject (Cyrver)
93
94 - (NSString *) cy$toJSON {
95 return [self description];
96 }
97
98 @end
99
100 @implementation WebUndefined (Cyrver)
101
102 - (NSString *) cy$toJSON {
103 return @"undefined";
104 }
105
106 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
107 return JSValueMakeUndefined(context);
108 }
109
110 @end
111
112 @implementation NSArray (Cyrver)
113
114 - (NSString *) cy$toJSON {
115 NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
116 [json appendString:@"["];
117
118 bool comma(false);
119 for (id object in self) {
120 if (comma)
121 [json appendString:@","];
122 else
123 comma = true;
124 [json appendString:[object cy$toJSON]];
125 }
126
127 [json appendString:@"]"];
128 return json;
129 }
130
131 @end
132
133 @implementation NSDictionary (Cyrver)
134
135 - (NSString *) cy$toJSON {
136 NSMutableString *json([[[NSMutableString alloc] init] autorelease]);
137 [json appendString:@"({"];
138
139 bool comma(false);
140 for (id key in self) {
141 if (comma)
142 [json appendString:@","];
143 else
144 comma = true;
145 [json appendString:[key cy$toJSON]];
146 [json appendString:@":"];
147 NSObject *object([self objectForKey:key]);
148 [json appendString:[object cy$toJSON]];
149 }
150
151 [json appendString:@"})"];
152 return json;
153 }
154
155 @end
156
157 @implementation NSNumber (Cyrver)
158
159 - (NSString *) cy$toJSON {
160 return [self class] != NSCFBoolean_ ? [self stringValue] : [self boolValue] ? @"true" : @"false";
161 }
162
163 - (JSValueRef) cy$JSValueInContext:(JSContextRef)context {
164 return [self class] != NSCFBoolean_ ? JSValueMakeNumber(context, [self doubleValue]) : JSValueMakeBoolean(context, [self boolValue]);
165 }
166
167 @end
168
169 @implementation NSString (Cyrver)
170
171 - (NSString *) cy$toJSON {
172 CFMutableStringRef json(CFStringCreateMutableCopy(kCFAllocatorDefault, 0, (CFStringRef) self));
173
174 CFStringFindAndReplace(json, CFSTR("\\"), CFSTR("\\\\"), CFRangeMake(0, CFStringGetLength(json)), 0);
175 CFStringFindAndReplace(json, CFSTR("\""), CFSTR("\\\""), CFRangeMake(0, CFStringGetLength(json)), 0);
176 CFStringFindAndReplace(json, CFSTR("\t"), CFSTR("\\t"), CFRangeMake(0, CFStringGetLength(json)), 0);
177 CFStringFindAndReplace(json, CFSTR("\r"), CFSTR("\\r"), CFRangeMake(0, CFStringGetLength(json)), 0);
178 CFStringFindAndReplace(json, CFSTR("\n"), CFSTR("\\n"), CFRangeMake(0, CFStringGetLength(json)), 0);
179
180 CFStringInsert(json, 0, CFSTR("\""));
181 CFStringAppend(json, CFSTR("\""));
182
183 return [reinterpret_cast<const NSString *>(json) autorelease];
184 }
185
186 @end
187
188 @interface CY$JSObject : NSDictionary {
189 JSObjectRef object_;
190 JSContextRef context_;
191 }
192
193 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context;
194
195 - (NSUInteger) count;
196 - (id) objectForKey:(id)key;
197 - (NSEnumerator *) keyEnumerator;
198 - (void) setObject:(id)object forKey:(id)key;
199 - (void) removeObjectForKey:(id)key;
200
201 @end
202
203 @interface CY$JSArray : NSArray {
204 JSObjectRef object_;
205 JSContextRef context_;
206 }
207
208 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context;
209
210 - (NSUInteger) count;
211 - (id) objectAtIndex:(NSUInteger)index;
212
213 @end
214
215 JSContextRef JSGetContext() {
216 return context_;
217 }
218
219 id JSObjectToNSObject(JSContextRef ctx, JSObjectRef object) {
220 if (JSValueIsObjectOfClass(ctx, object, joc_))
221 return reinterpret_cast<id>(JSObjectGetPrivate(object));
222 // XXX: exception
223 else if (JSValueIsInstanceOfConstructor(ctx, object, Array_, NULL))
224 return [[[CY$JSArray alloc] initWithJSObject:object inContext:ctx] autorelease];
225 else
226 return [[[CY$JSObject alloc] initWithJSObject:object inContext:ctx] autorelease];
227 }
228
229 CFStringRef CYCopyCFString(JSStringRef value) {
230 return JSStringCopyCFString(kCFAllocatorDefault, value);
231 }
232
233 void CYThrow(JSContextRef ctx, JSValueRef value);
234
235 CFStringRef CYCopyCFString(JSContextRef ctx, JSValueRef value) {
236 JSValueRef exception(NULL);
237 JSStringRef string(JSValueToStringCopy(ctx, value, &exception));
238 CYThrow(context_, exception);
239 CFStringRef object(CYCopyCFString(string));
240 JSStringRelease(string);
241 return object;
242 }
243
244 NSString *CYCastNSString(JSStringRef value) {
245 return [reinterpret_cast<const NSString *>(CYCopyCFString(value)) autorelease];
246 }
247
248 CFTypeRef CYCopyCFType(JSContextRef ctx, JSValueRef value) {
249 JSType type(JSValueGetType(ctx, value));
250
251 switch (type) {
252 case kJSTypeUndefined:
253 return CFRetain([WebUndefined undefined]);
254 break;
255
256 case kJSTypeNull:
257 return nil;
258 break;
259
260 case kJSTypeBoolean:
261 return CFRetain(JSValueToBoolean(ctx, value) ? kCFBooleanTrue : kCFBooleanFalse);
262 break;
263
264 case kJSTypeNumber: {
265 JSValueRef exception(NULL);
266 double number(JSValueToNumber(ctx, value, &exception));
267 CYThrow(context_, exception);
268 return CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &number);
269 } break;
270
271 case kJSTypeString:
272 return CYCopyCFString(context_, value);
273 break;
274
275 case kJSTypeObject:
276 return CFRetain((CFTypeRef) JSObjectToNSObject(ctx, (JSObjectRef) value));
277 break;
278
279 default:
280 _assert(false);
281 break;
282 }
283 }
284
285 NSArray *CYCastNSArray(JSPropertyNameArrayRef names) {
286 size_t size(JSPropertyNameArrayGetCount(names));
287 NSMutableArray *array([NSMutableArray arrayWithCapacity:size]);
288 for (size_t index(0); index != size; ++index)
289 [array addObject:CYCastNSString(JSPropertyNameArrayGetNameAtIndex(names, index))];
290 return array;
291 }
292
293 id CYCastNSObject(JSContextRef ctx, JSValueRef value) {
294 const NSObject *object(reinterpret_cast<const NSObject *>(CYCopyCFType(ctx, value)));
295 return object == nil ? nil : [object autorelease];
296 }
297
298 void CYThrow(JSContextRef ctx, JSValueRef value) {
299 if (value == NULL)
300 return;
301 @throw CYCastNSObject(ctx, value);
302 }
303
304 JSValueRef CYCastJSValue(JSContextRef ctx, id value) {
305 return [value cy$JSValueInContext:ctx];
306 }
307
308 JSStringRef CYCopyJSString(JSContextRef ctx, id value) {
309 return JSStringCreateWithCFString(reinterpret_cast<CFStringRef>([value description]));
310 }
311
312 @implementation CY$JSObject
313
314 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context {
315 if ((self = [super init]) != nil) {
316 object_ = object;
317 context_ = context;
318 } return self;
319 }
320
321 - (NSUInteger) count {
322 JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context_, object_));
323 size_t size(JSPropertyNameArrayGetCount(names));
324 JSPropertyNameArrayRelease(names);
325 return size;
326 }
327
328 - (id) objectForKey:(id)key {
329 JSValueRef exception(NULL);
330 JSStringRef string(CYCopyJSString(context_, key));
331 JSValueRef value(JSObjectGetProperty(context_, object_, string, &exception));
332 JSStringRelease(string);
333 CYThrow(context_, exception);
334 return CYCastNSObject(context_, value);
335 }
336
337 - (NSEnumerator *) keyEnumerator {
338 JSPropertyNameArrayRef names(JSObjectCopyPropertyNames(context_, object_));
339 NSEnumerator *enumerator([CYCastNSArray(names) objectEnumerator]);
340 JSPropertyNameArrayRelease(names);
341 return enumerator;
342 }
343
344 - (void) setObject:(id)object forKey:(id)key {
345 JSValueRef exception(NULL);
346 JSStringRef string(CYCopyJSString(context_, key));
347 JSObjectSetProperty(context_, object_, string, CYCastJSValue(context_, object), kJSPropertyAttributeNone, &exception);
348 JSStringRelease(string);
349 CYThrow(context_, exception);
350 }
351
352 - (void) removeObjectForKey:(id)key {
353 JSValueRef exception(NULL);
354 JSStringRef string(CYCopyJSString(context_, key));
355 // XXX: this returns a bool
356 JSObjectDeleteProperty(context_, object_, string, &exception);
357 JSStringRelease(string);
358 CYThrow(context_, exception);
359 }
360
361 @end
362
363 @implementation CY$JSArray
364
365 - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context {
366 if ((self = [super init]) != nil) {
367 object_ = object;
368 context_ = context;
369 } return self;
370 }
371
372 - (NSUInteger) count {
373 JSValueRef exception(NULL);
374 JSValueRef value(JSObjectGetProperty(context_, object_, length_, &exception));
375 CYThrow(context_, exception);
376 double number(JSValueToNumber(context_, value, &exception));
377 CYThrow(context_, exception);
378 return number;
379 }
380
381 - (id) objectAtIndex:(NSUInteger)index {
382 JSValueRef exception(NULL);
383 JSValueRef value(JSObjectGetPropertyAtIndex(context_, object_, index, &exception));
384 CYThrow(context_, exception);
385 id object(CYCastNSObject(context_, value));
386 return object == nil ? [NSNull null] : object;
387 }
388
389 @end
390
391 CFStringRef JSValueToJSONCopy(JSContextRef ctx, JSValueRef value) {
392 id object(CYCastNSObject(ctx, value));
393 return reinterpret_cast<CFStringRef>([(object == nil ? @"null" : [object cy$toJSON]) retain]);
394 }
395
396 static void OnData(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) {
397 switch (type) {
398 case kCFSocketDataCallBack:
399 CFDataRef data(reinterpret_cast<CFDataRef>(value));
400 Client *client(reinterpret_cast<Client *>(info));
401
402 if (client->message_ == NULL)
403 client->message_ = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, TRUE);
404
405 if (!CFHTTPMessageAppendBytes(client->message_, CFDataGetBytePtr(data), CFDataGetLength(data)))
406 CFLog(kCFLogLevelError, CFSTR("CFHTTPMessageAppendBytes()"));
407 else if (CFHTTPMessageIsHeaderComplete(client->message_)) {
408 CFURLRef url(CFHTTPMessageCopyRequestURL(client->message_));
409 Boolean absolute;
410 CFStringRef path(CFURLCopyStrictPath(url, &absolute));
411 CFRelease(client->message_);
412
413 CFStringRef code(CFURLCreateStringByReplacingPercentEscapes(kCFAllocatorDefault, path, CFSTR("")));
414 CFRelease(path);
415
416 JSStringRef script(JSStringCreateWithCFString(code));
417 CFRelease(code);
418
419 JSValueRef result(JSEvaluateScript(JSGetContext(), script, NULL, NULL, 0, NULL));
420 JSStringRelease(script);
421
422 CFHTTPMessageRef response(CFHTTPMessageCreateResponse(kCFAllocatorDefault, 200, NULL, kCFHTTPVersion1_1));
423 CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Type"), CFSTR("application/json; charset=utf-8"));
424
425 CFStringRef json(JSValueToJSONCopy(JSGetContext(), result));
426 CFDataRef body(CFStringCreateExternalRepresentation(kCFAllocatorDefault, json, kCFStringEncodingUTF8, NULL));
427 CFRelease(json);
428
429 CFStringRef length(CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"), CFDataGetLength(body)));
430 CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), length);
431 CFRelease(length);
432
433 CFHTTPMessageSetBody(response, body);
434 CFRelease(body);
435
436 CFDataRef serialized(CFHTTPMessageCopySerializedMessage(response));
437 CFRelease(response);
438
439 CFSocketSendData(socket, NULL, serialized, 0);
440 CFRelease(serialized);
441
442 CFRelease(url);
443 }
444 break;
445 }
446 }
447
448 static void OnAccept(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) {
449 switch (type) {
450 case kCFSocketAcceptCallBack:
451 Client *client(new Client());
452
453 client->message_ = NULL;
454
455 CFSocketContext context;
456 context.version = 0;
457 context.info = client;
458 context.retain = NULL;
459 context.release = NULL;
460 context.copyDescription = NULL;
461
462 client->socket_ = CFSocketCreateWithNative(kCFAllocatorDefault, *reinterpret_cast<const CFSocketNativeHandle *>(value), kCFSocketDataCallBack, &OnData, &context);
463
464 CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, client->socket_, 0), kCFRunLoopDefaultMode);
465 break;
466 }
467 }
468
469 static JSValueRef joc_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) {
470 return NULL;
471 }
472
473 static JSValueRef obc_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) {
474 NSString *name([(NSString *) JSStringCopyCFString(kCFAllocatorDefault, propertyName) autorelease]);
475 if (Class _class = NSClassFromString(name))
476 return JSObjectMake(ctx, joc_, _class);
477 return NULL;
478 }
479
480 MSInitialize {
481 NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]);
482
483 NSCFBoolean_ = objc_getClass("NSCFBoolean");
484
485 pid_t pid(getpid());
486
487 struct sockaddr_in address;
488 address.sin_len = sizeof(address);
489 address.sin_family = AF_INET;
490 address.sin_addr.s_addr = INADDR_ANY;
491 address.sin_port = htons(10000 + pid);
492
493 CFDataRef data(CFDataCreate(kCFAllocatorDefault, reinterpret_cast<UInt8 *>(&address), sizeof(address)));
494
495 CFSocketSignature signature;
496 signature.protocolFamily = AF_INET;
497 signature.socketType = SOCK_STREAM;
498 signature.protocol = IPPROTO_TCP;
499 signature.address = data;
500
501 CFSocketRef socket(CFSocketCreateWithSocketSignature(kCFAllocatorDefault, &signature, kCFSocketAcceptCallBack, &OnAccept, NULL));
502 CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0), kCFRunLoopDefaultMode);
503
504 JSClassDefinition definition;
505
506 definition = kJSClassDefinitionEmpty;
507 definition.getProperty = &obc_getProperty;
508 JSClassRef obc(JSClassCreate(&definition));
509
510 definition = kJSClassDefinitionEmpty;
511 definition.getProperty = &joc_getProperty;
512 joc_ = JSClassCreate(&definition);
513
514 context_ = JSGlobalContextCreate(obc);
515
516 JSObjectRef global(JSContextGetGlobalObject(JSGetContext()));
517
518 name_ = JSStringCreateWithUTF8CString("name");
519 message_ = JSStringCreateWithUTF8CString("message");
520 length_ = JSStringCreateWithUTF8CString("length");
521
522 JSStringRef name(JSStringCreateWithUTF8CString("Array"));
523 JSValueRef exception(NULL);
524 JSValueRef value(JSObjectGetProperty(JSGetContext(), global, name, &exception));
525 CYThrow(context_, exception);
526 JSStringRelease(name);
527 Array_ = JSValueToObject(JSGetContext(), value, &exception);
528 CYThrow(context_, exception);
529
530 [pool release];
531 }