From 4f500fdf328c85708f0232447828da1c3ef0c4cd Mon Sep 17 00:00:00 2001 From: longv2go Date: Mon, 7 Nov 2016 17:29:34 +0800 Subject: [PATCH 1/2] Add pivars command --- commands/FBClassDump.py | 112 +++++++++++++++++++++++++++--------- fblldbobjcruntimehelpers.py | 2 +- 2 files changed, 87 insertions(+), 27 deletions(-) diff --git a/commands/FBClassDump.py b/commands/FBClassDump.py index 3615dd0..81b1ed3 100644 --- a/commands/FBClassDump.py +++ b/commands/FBClassDump.py @@ -8,7 +8,8 @@ def lldbcommands(): return [ FBPrintMethods(), FBPrintProperties(), - FBPrintBlock() + FBPrintBlock(), + FBPrintIvars() ] class FBPrintMethods(fb.FBCommand): @@ -22,7 +23,7 @@ def options(self): return [ fb.FBCommandArgument(short='-a', long='--address', arg='showaddr', help='Print the implementation address of the method', default=False, boolean=True), fb.FBCommandArgument(short='-i', long='--instance', arg='insmethod', help='Print the instance methods', default=False, boolean=True), - fb.FBCommandArgument(short='-c', long='--class', arg='clsmethod', help='Print the class methods', default=False, boolean=True), + fb.FBCommandArgument(short='-c', long='--class', arg='clsmethod', help='Print the class methods', default=False, boolean=True), fb.FBCommandArgument(short='-n', long='--name', arg='clsname', help='Take the argument as class name', default=False, boolean=True) ] @@ -48,10 +49,9 @@ def run(self, arguments, options): class FBPrintProperties(fb.FBCommand): - def name(self): return 'pproperties' - + def description(self): return "Print the properties of an instance or Class" @@ -65,9 +65,28 @@ def args(self): def run(self, arguments, options): cls = getClassFromArgument(arguments[0], options.clsname) - printProperties(cls) +class FBPrintIvars(fb.FBCommand): + def name(self): + return 'pivars' + + def description(self): + return 'Print the Ivars of an instance of Class' + + def options(self): + return [ + fb.FBCommandArgument(short='-n', long='--name', arg='clsname', help='Take the argument as class name', default=False, boolean=True) + ] + + def args(self): + return [ fb.FBCommandArgument(arg='instance or class', type='instance or Class', help='an Objective-C Class.') ] + + def run(self, arguments, options): + cls = getClassFromArgument(arguments[0], options.clsname) + printIvars(cls) + + class FBPrintBlock(fb.FBCommand): def name(self): return 'pblock' @@ -110,9 +129,9 @@ def run(self, arguments, options): }; struct Block_literal_1 real = *((__bridge struct Block_literal_1 *)$block); NSMutableDictionary *dict = (id)[NSMutableDictionary dictionary]; - + [dict setObject:(id)[NSNumber numberWithLong:(long)real.invoke] forKey:@"invoke"]; - + if (real.flags & BLOCK_HAS_SIGNATURE) { char *signature; if (real.flags & BLOCK_HAS_COPY_DISPOSE) { @@ -130,10 +149,10 @@ def run(self, arguments, options): char *type = (char *)[sig getArgumentTypeAtIndex:i]; [types addObject:(id)[NSString stringWithUTF8String:type]]; } - + [dict setObject:types forKey:@"signature"]; } - + RETURN(dict); """ command = string.Template(tmpString).substitute(block=block) @@ -142,16 +161,16 @@ def run(self, arguments, options): signature = json['signature'] if not signature: print 'Imp: ' + hex(json['invoke']) - return + return sigStr = '{} ^('.format(decode(signature[0])) # the block`s implementation always take the block as it`s first argument, so we ignore it sigStr += ', '.join([decode(m) for m in signature[2:]]) sigStr += ');' - + print 'Imp: ' + hex(json['invoke']) + ' Signature: ' + sigStr -# helpers +# helpers def isClassObject(arg): return runtimeHelpers.class_isMetaClass(runtimeHelpers.object_getClass(arg)) @@ -188,6 +207,11 @@ def printProperties(cls, showvalue=False): for p in props: print p.prettyPrintString() +def printIvars(cls): + ivars = getIvars(cls) + for var in ivars: + print var.prettyPrintString() + def decode(code): encodeMap = { 'c': 'char', @@ -234,16 +258,16 @@ def getMethods(klass): unsigned int outCount; Method *methods = (Method *)class_copyMethodList((Class)$cls, &outCount); NSMutableArray *result = (id)[NSMutableArray array]; - + for (int i = 0; i < outCount; i++) { NSMutableDictionary *m = (id)[NSMutableDictionary dictionary]; SEL name = (SEL)method_getName(methods[i]); [m setObject:(id)NSStringFromSelector(name) forKey:@"name"]; - + char * encoding = (char *)method_getTypeEncoding(methods[i]); [m setObject:(id)[NSString stringWithUTF8String:encoding] forKey:@"type_encoding"]; - + NSMutableArray *types = (id)[NSMutableArray array]; NSInteger args = (NSInteger)method_getNumberOfArguments(methods[i]); for (int idx = 0; idx < args; idx++) { @@ -251,13 +275,13 @@ def getMethods(klass): [types addObject:(id)[NSString stringWithUTF8String:type]]; } [m setObject:types forKey:@"parameters_type"]; - + char *ret_type = (char *)method_copyReturnType(methods[i]); [m setObject:(id)[NSString stringWithUTF8String:ret_type] forKey:@"return_type"]; - + long imp = (long)method_getImplementation(methods[i]); [m setObject:[NSNumber numberWithLongLong:imp] forKey:@"implementation"]; - + [result addObject:m]; } RETURN(result); @@ -300,23 +324,23 @@ def getProperties(klass): objc_property_t *props = (objc_property_t *)class_copyPropertyList((Class)$cls, &count); for (int i = 0; i < count; i++) { NSMutableDictionary *dict = (id)[NSMutableDictionary dictionary]; - + char *name = (char *)property_getName(props[i]); - [dict setObject:(id)[NSString stringWithUTF8String:name] forKey:@"name"]; - + [dict setObject:(id)[NSString stringWithUTF8String:name] forKey:(id _Nonnull)@"name"]; + char *attrstr = (char *)property_getAttributes(props[i]); - [dict setObject:(id)[NSString stringWithUTF8String:attrstr] forKey:@"attributes_string"]; - + [dict setObject:(id)[NSString stringWithUTF8String:attrstr] forKey:(id _Nonnull)@"attributes_string"]; + NSMutableDictionary *attrsDict = (id)[NSMutableDictionary dictionary]; unsigned int pcount; objc_property_attribute_t *attrs = (objc_property_attribute_t *)property_copyAttributeList(props[i], &pcount); for (int i = 0; i < pcount; i++) { NSString *name = (id)[NSString stringWithUTF8String:(char *)attrs[i].name]; NSString *value = (id)[NSString stringWithUTF8String:(char *)attrs[i].value]; - [attrsDict setObject:value forKey:name]; + [attrsDict setObject:(id _Nonnull)value forKey:(id _Nonnull)name]; } - [dict setObject:attrsDict forKey:@"attributes"]; - + [dict setObject:attrsDict forKey:(id _Nonnull)@"attributes"]; + [result addObject:dict]; } RETURN(result); @@ -358,3 +382,39 @@ def prettyPrintString(self): attrs.append("setter={}".format(self.attributes['S'])) return "@property ({}) {} {};".format(", ".join(attrs), decode(self.attributes['T']), self.name) + +def getIvars(klass): + tmpString = """ + NSMutableArray *result = (id)[NSMutableArray array]; + unsigned int count; + + Ivar *vars = (Ivar *)class_copyIvarList((Class)$cls, &count); + if (vars) { + for (int i = 0; i < count; i++) { + NSMutableDictionary *dict = (id)[NSMutableDictionary dictionary]; + + char *name = (char *)ivar_getName(vars[i]); + [dict setObject:(id _Nonnull)[NSString stringWithUTF8String:name] forKey:(id _Nonnull)@"name"]; + NSString *encoding = [NSString stringWithUTF8String:(char *)ivar_getTypeEncoding(vars[i])]; + [dict setObject:(id _Nonnull)encoding forKey:(id _Nonnull)@"encoding"]; + NSNumber *offset = [NSNumber numberWithLong:ivar_getOffset(vars[i])]; + [dict setObject:(id _Nonnull)offset forKey:(id _Nonnull)@"offset"]; + + [result addObject:dict]; + } + } + RETURN(result); + """ + command = string.Template(tmpString).substitute(cls=klass) + json = fb.evaluate(command) + return [Ivar(m) for m in json] + +class Ivar: + def __init__(self, json): + self.name = json['name'] + self.offset = json['offset'] + self.encoding = json['encoding'] + + def prettyPrintString(self): + return "[" + hex(self.offset) + "] " + decode(self.encoding) + " " + self.name + diff --git a/fblldbobjcruntimehelpers.py b/fblldbobjcruntimehelpers.py index 43373bc..11faf02 100644 --- a/fblldbobjcruntimehelpers.py +++ b/fblldbobjcruntimehelpers.py @@ -18,7 +18,7 @@ def objc_getClass(className): return value def object_getClass(object): - command = '(void*)object_getClass({})'.format(object) + command = '(void*)object_getClass((id){})'.format(object) value = fb.evaluateExpression(command) return value From 57b55f777f8ab80407eae62bf2e3db5071112881 Mon Sep 17 00:00:00 2001 From: longv2go Date: Tue, 8 Nov 2016 11:23:50 +0800 Subject: [PATCH 2/2] pivars command add -r option --- commands/FBClassDump.py | 53 +++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/commands/FBClassDump.py b/commands/FBClassDump.py index 81b1ed3..6834398 100644 --- a/commands/FBClassDump.py +++ b/commands/FBClassDump.py @@ -76,15 +76,35 @@ def description(self): def options(self): return [ - fb.FBCommandArgument(short='-n', long='--name', arg='clsname', help='Take the argument as class name', default=False, boolean=True) + fb.FBCommandArgument(short='-n', long='--name', arg='clsname', help='Take the argument as class name', default=False, boolean=True), + fb.FBCommandArgument(short='-r', long='--recursive', arg='recursive', help='Print the ancestor`s ivars', default=False, boolean=True) ] def args(self): - return [ fb.FBCommandArgument(arg='instance or class', type='instance or Class', help='an Objective-C Class.') ] + return [ fb.FBCommandArgument(arg='instance or class, if argument is a instance object then print the values', type='instance or Class', help='an Objective-C Class.') ] def run(self, arguments, options): cls = getClassFromArgument(arguments[0], options.clsname) - printIvars(cls) + + ins = None + if not options.clsname: + ins = arguments[0] + + if options.recursive: + cls = int(cls, 16) + classes = [] + while cls: + classes.append(cls) + superClass = runtimeHelpers.class_getSuperclass(cls) + cls = int(superClass, 16) + + classes.reverse() + for c in classes: + print runtimeHelpers.class_getName(c) + ":" + printIvars(c, ins, 4) + print "" + else: + printIvars(cls, ins) class FBPrintBlock(fb.FBCommand): @@ -207,10 +227,10 @@ def printProperties(cls, showvalue=False): for p in props: print p.prettyPrintString() -def printIvars(cls): - ivars = getIvars(cls) +def printIvars(cls, instance=None, indent=0): + ivars = getIvars(cls, instance) for var in ivars: - print var.prettyPrintString() + print var.prettyPrintString(indent) def decode(code): encodeMap = { @@ -383,10 +403,11 @@ def prettyPrintString(self): return "@property ({}) {} {};".format(", ".join(attrs), decode(self.attributes['T']), self.name) -def getIvars(klass): +def getIvars(klass, instance=None): tmpString = """ NSMutableArray *result = (id)[NSMutableArray array]; unsigned int count; + id object = (id)$obj; Ivar *vars = (Ivar *)class_copyIvarList((Class)$cls, &count); if (vars) { @@ -400,12 +421,20 @@ def getIvars(klass): NSNumber *offset = [NSNumber numberWithLong:ivar_getOffset(vars[i])]; [dict setObject:(id _Nonnull)offset forKey:(id _Nonnull)@"offset"]; + if (object) { + id value = object_getIvar((id)object, vars[i]); + [dict setObject:(id _Nonnull)[NSNumber numberWithLong:(long)value] forKey:(id _Nonnull)@"value"]; + } + [result addObject:dict]; } } RETURN(result); """ - command = string.Template(tmpString).substitute(cls=klass) + if instance is None: + instance = 0 + + command = string.Template(tmpString).substitute(obj=instance, cls=klass) json = fb.evaluate(command) return [Ivar(m) for m in json] @@ -414,7 +443,11 @@ def __init__(self, json): self.name = json['name'] self.offset = json['offset'] self.encoding = json['encoding'] + self.value = json.get('value') - def prettyPrintString(self): - return "[" + hex(self.offset) + "] " + decode(self.encoding) + " " + self.name + def prettyPrintString(self, indent=0): + ret = "[" + hex(self.offset) + "] " + decode(self.encoding) + " " + self.name + if self.value: + ret = ret + " ->" + hex(self.value) + return " "*indent + ret