As we've mentioned in the last post, Protection against Message Forward in Objective-C, there're at least two tools for tracing the calling sequence of the methods,
However, they just cannot handle it in the scene below,
@interface ProtectedClass : NSObject { @public NSString * _password; } @property (nonatomic, getter=password, setter=setPassword:) NSString * password; @end /// ...omited... ProtectedClass * obj = [[ProtectedClass alloc] init]; obj->_password = @"喵咕咪~"; // directly access, undectectable in BigBang or ANYMethodLog [obj setPassword:@"喵"]; // BigBang or ANYMethodLog dectectable /// ...omited...
Because it's not necessarily to call getter or setter in Objective-C when access or change an ivar. Since Objective-C is just a superset of C, so the object (or instance) in Objective-C acts pretty much like the struct in C. You can directly access its member if you have the memory address. Let's check out what happens when compiling.
Here is our code, written in Objective-C, and it's probably quite often to be seen in your projects.
And when Xcode generates the assemble code, (noticing the line 78 and 79)
They've been transformed to objc_storeStrong
and objc_msgSend
respectively. For the latter one, i.e, objc_msgSend
, we're pretty familiar with it. (If not, you may refer to Objective-C 消息发送与转发机制原理) As for the former one, objc_storeStrong
, well, it's actually quite simple. According to the clang document,
void objc_storeStrong(id *object, id value) { id oldValue = *object; value = [value retain]; *object = value; [oldValue release]; }
That's straightforward. And in simple words, objc_storeStrong
stores the object value
to the address object
points to. There's nothing related to Objective-C's message mechanism, just simple memory accessing. This code explains why BigBang or ANYMethodLog failed to detect such changes. (In fact, Objective-C's KVO mechanism would also failed.)
But luckily, there's a objc_storeStrong
function, we could hook objc_storeStrong
and search the address object
belongs to which instance, and then we can get notified if any ivar is changed directly. And here we use fishhook.
// directly acccess rebind_symbols((struct rebinding []){ {"objc_storeStrong", (void *)meow_objc_storeStrong, (void **)&meow_orig_objc_storeStrong}, {"objc_storeWeak", (void *)meow_objc_storeWeak, (void **)&meow_orig_objc_storeWeak} }, 2); // add ivar in runtime rebind_symbols((struct rebinding []){ {"class_addIvar", (void *)meow_class_addIvar, (void **)&meow_orig_class_addIvar}, {"class_addProperty", (void *)meow_class_addProperty, (void **)&meow_orig_class_addProperty} }, 2);
These would be enough for monitoring ivar changes for a given class. And then, we need every new instance of the given class, so we need to hook -[aClass init]
and class_createInstance
.
// hook class_createInstance rebind_symbols((struct rebinding []){ {"class_createInstance", (void *)meow_class_createInstance, (void **)&meow_orig_class_createInstance} }, 1); // hook [aClass init] Method initMethod = class_getInstanceMethod(aClass, @selector(init)); IMP meow_orig_init = method_getImplementation(initMethod); IMP meow_init = imp_implementationWithBlock(^(id object, SEL sel){ id obj = ((id(*)(id, SEL))(meow_orig_init))(object, sel); // hold weak reference to that object __weak id weakObject = obj; NSLog(@"[INFO] new object of %s. range <%p, %p>", class_getName(self._class), obj, (long long *)(__bridge void *)(obj) + self.classSize); [self.objects addObject:weakObject]; return obj; }); class_replaceMethod(self._class, @selector(init), meow_init, "@@:@");
However, some instance may be already inited, thus why we need choose, a function from cycript.
- (void)updateObjects { NSArray * objects = [choose choose:self._class]; for (id object in objects) { NSLog(@"[INFO] object:%@", object); [Meow printAllIvar:object]; // hold weak reference to those objects __weak id weakObject = object; [self.objects addObject:weakObject]; } }
Now, we've the ability to capture (almost, since you can memcpy
an instance) every single new instance and all existed instances of given class. And once you have the instance, you can do things like class_copyIvarList
, object_getIvar
, ivar_getName
and so on and so forth. (As a matter of fact, that's basically the omitted codes of Meow
)
This tool is on my GitHub, Meow.
找到了,谢啦
大神,你的choose.h文件是自己封装的吗?
引用了自己的项目,在BlueCocoa/choose