]> git.saurik.com Git - cycript.git/blob - Tweak.mm
First test version.
[cycript.git] / Tweak.mm
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
71 static JSContextRef Context_;
72 static JSClassRef joc_;
73 static JSObjectRef Array_;
74 static JSStringRef length_;
75 static Class NSCFBoolean_;
76
77 struct 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
148 static 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
158 static 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
197 static 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
223 static CFStringRef JSValueToJSONCopy(JSContextRef ctx, JSValueRef value) {
224 id object(JSValueToNSObject(ctx, value));
225 return (CFStringRef) [(object == nil ? @"null" : [object cy$toJSON]) retain];
226 }
227
228 static 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
280 static 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
301 static JSValueRef joc_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) {
302 return NULL;
303 }
304
305 static 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
312 extern "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 }