From f7f5a88b37426db8a71fb21adb29e55ae84e70c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=91=A3=E5=AE=8F=E6=98=8C?= Date: Mon, 23 Apr 2018 13:36:06 +0800 Subject: [PATCH 1/4] fix #198 pblock & pproperties type erro --- commands/FBClassDump.py | 4 ++-- fblldbbase.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/commands/FBClassDump.py b/commands/FBClassDump.py index 3615dd0..6ed3f81 100644 --- a/commands/FBClassDump.py +++ b/commands/FBClassDump.py @@ -111,7 +111,7 @@ 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"]; + [dict setValue:(id)[NSNumber numberWithLong:(long)real.invoke] forKey:@"invoke"]; if (real.flags & BLOCK_HAS_SIGNATURE) { char *signature; @@ -131,7 +131,7 @@ def run(self, arguments, options): [types addObject:(id)[NSString stringWithUTF8String:type]]; } - [dict setObject:types forKey:@"signature"]; + [dict setValue:types forKey:@"signature"]; } RETURN(dict); diff --git a/fblldbbase.py b/fblldbbase.py index 3a8f5b1..fd0bb8b 100755 --- a/fblldbbase.py +++ b/fblldbbase.py @@ -150,7 +150,8 @@ def evaluateCStringExpression(expression, printErrors=True): if (!IS_JSON_OBJ(ret)) {\ (void)[NSException raise:@"Invalid RETURN argument" format:@""];\ }\ - NSDictionary *__dict = @{@"return":ret};\ + NSMutableDictionary *__dict = (id)[NSMutableDictionary dictionary];\ + [__dict setValue:(id)ret forKey:@"return"];\ NSData *__data = (id)[NSJSONSerialization dataWithJSONObject:__dict options:0 error:NULL];\ NSString *__str = (id)[[NSString alloc] initWithData:__data encoding:4];\ (char *)[__str UTF8String];}) From e3f84a67b90eb06af20ee607dedc8a45192b865e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=91=A3=E5=AE=8F=E6=98=8C?= Date: Tue, 24 Apr 2018 11:09:21 +0800 Subject: [PATCH 2/4] Add print capture variables for pblock before: Imp: 0x10a451d40 Signature: void ^(); after: Imp: 0x10a451d40 Signature: void ^() Variables : { }; --- commands/FBClassDump.py | 105 +++++++++++++++++++++++++++++++++++++--- fblldbbase.py | 10 ++-- 2 files changed, 104 insertions(+), 11 deletions(-) diff --git a/commands/FBClassDump.py b/commands/FBClassDump.py index 6ed3f81..07ea279 100644 --- a/commands/FBClassDump.py +++ b/commands/FBClassDump.py @@ -139,17 +139,108 @@ def run(self, arguments, options): command = string.Template(tmpString).substitute(block=block) json = fb.evaluate(command) + variables_json = self.getBlockVariables(block) + if variables_json is not None: + json.update(variables_json) + + variablesStrs = [] + for i in range(10): + varKey = 'variables['+str(i)+']' + if varKey in json: + variablesStrs.append(json[varKey]) + variablesStr = '\n'.join(variablesStrs) + signature = json['signature'] if not signature: - print 'Imp: ' + hex(json['invoke']) + print 'Imp: ' + hex(json['invoke']) + ' Variables : {\n'+variablesStr+'\n};' 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 += ');' + sigStr += ')' - print 'Imp: ' + hex(json['invoke']) + ' Signature: ' + sigStr + print 'Imp: ' + hex(json['invoke']) + ' Signature: ' + sigStr + ' Variables : {\n'+variablesStr+'\n};' + + def getBlockVariables(self, block, min_var_count=1, max_var_count=20): + ''' + no __Block_byref_xxx + ''' + + # http://clang.llvm.org/docs/Block-ABI-Apple.html + tmpString = """ + #define BLOCK_VARIABLES_COUNT ($variables_count) + enum { + BLOCK_HAS_COPY_DISPOSE = (1 << 25), + BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code + BLOCK_IS_GLOBAL = (1 << 28), + BLOCK_HAS_STRET = (1 << 29), // IFF BLOCK_HAS_SIGNATURE + BLOCK_HAS_SIGNATURE = (1 << 30), + }; + struct Block_literal_1 { + void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock + int flags; + int reserved; + void (*invoke)(void *, ...); + struct Block_descriptor_1 { + unsigned long int reserved; // NULL + unsigned long int size; // sizeof(struct Block_literal_1) + // optional helper functions + void (*copy_helper)(void *dst, void *src); // IFF (1<<25) + void (*dispose_helper)(void *src); // IFF (1<<25) + // required ABI.2010.3.16 + const char *signature; // IFF (1<<30) + } *descriptor; + // imported variables + Class *variables[BLOCK_VARIABLES_COUNT]; + }; + struct Block_literal_1 real = *((__bridge struct Block_literal_1 *)$block); + NSMutableDictionary *dict = (id)[NSMutableDictionary dictionary]; + + // Get the list of classes and look for testPointerClass + NSInteger numClasses = objc_getClassList(NULL, 0); + Class *classesList = (Class*)malloc(sizeof(Class) * numClasses); + numClasses = objc_getClassList(classesList, numClasses); + + Class **blockVariables = real.variables; + for (int i = 0; i < BLOCK_VARIABLES_COUNT; i++) { + Class *obj = (Class*)blockVariables[i]; + if (obj == NULL) { + break; + } + + Class testPointerClass = (Class)(*obj); + BOOL isClass = NO; + for (int i = 0; i < numClasses; i++) + { + if (classesList[i] == testPointerClass) + { + isClass = YES; + break; + } + } + if (!isClass) { + break; + } + + NSString *key = [NSString stringWithFormat:@"variables[%d]", i]; + NSString *value = [NSString stringWithFormat:@"%@", obj]; + [dict setValue:value forKey:key]; + } + + free(classesList); + + RETURN(dict); + """ + last_json = None + for i in range(min_var_count, max_var_count): + command = string.Template(tmpString).substitute(block=block, variables_count=i) + json = fb.evaluate(command, printErrors=False) + if json is not None: + last_json = json + else: + break + return last_json # helpers def isClassObject(arg): @@ -302,10 +393,10 @@ def getProperties(klass): NSMutableDictionary *dict = (id)[NSMutableDictionary dictionary]; char *name = (char *)property_getName(props[i]); - [dict setObject:(id)[NSString stringWithUTF8String:name] forKey:@"name"]; + [dict setValue:(id)[NSString stringWithUTF8String:name] forKey:@"name"]; char *attrstr = (char *)property_getAttributes(props[i]); - [dict setObject:(id)[NSString stringWithUTF8String:attrstr] forKey:@"attributes_string"]; + [dict setValue:(id)[NSString stringWithUTF8String:attrstr] forKey:@"attributes_string"]; NSMutableDictionary *attrsDict = (id)[NSMutableDictionary dictionary]; unsigned int pcount; @@ -313,9 +404,9 @@ def getProperties(klass): 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 setValue:value forKey:name]; } - [dict setObject:attrsDict forKey:@"attributes"]; + [dict setValue:attrsDict forKey:@"attributes"]; [result addObject:dict]; } diff --git a/fblldbbase.py b/fblldbbase.py index fd0bb8b..4de48b7 100755 --- a/fblldbbase.py +++ b/fblldbbase.py @@ -168,21 +168,23 @@ def check_expr(expr): # Example: # >>> fblldbbase.evaluate('NSString *str = @"hello world"; RETURN(@{@"key": str});') # {u'key': u'hello world'} -def evaluate(expr): +def evaluate(expr, printErrors=True): if not check_expr(expr): raise Exception("Invalid Expression, the last expression not include a RETURN family marco") command = "({" + RETURN_MACRO + '\n' + expr + "})" - ret = evaluateExpressionValue(command, printErrors=True) + ret = evaluateExpressionValue(command, printErrors=printErrors) if not ret.GetError().Success(): - print ret.GetError() + if printErrors: + print ret.GetError() return None else: process = lldb.debugger.GetSelectedTarget().GetProcess() error = lldb.SBError() ret = process.ReadCStringFromMemory(int(ret.GetValue(), 16), 2**20, error) if not error.Success(): - print error + if printErrors: + print error return None else: ret = json.loads(ret) From e07b8b9f39d954eb3ae3d5dfd29f42987034e806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=91=A3=E5=AE=8F=E6=98=8C?= Date: Tue, 24 Apr 2018 13:10:59 +0800 Subject: [PATCH 3/4] fix pblock error --- commands/FBClassDump.py | 9 ++++++--- fblldbbase.py | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/commands/FBClassDump.py b/commands/FBClassDump.py index 07ea279..70bcd3d 100644 --- a/commands/FBClassDump.py +++ b/commands/FBClassDump.py @@ -198,9 +198,9 @@ def getBlockVariables(self, block, min_var_count=1, max_var_count=20): NSMutableDictionary *dict = (id)[NSMutableDictionary dictionary]; // Get the list of classes and look for testPointerClass - NSInteger numClasses = objc_getClassList(NULL, 0); + NSInteger numClasses = (NSInteger)objc_getClassList(NULL, 0); Class *classesList = (Class*)malloc(sizeof(Class) * numClasses); - numClasses = objc_getClassList(classesList, numClasses); + numClasses = (NSInteger)objc_getClassList(classesList, numClasses); Class **blockVariables = real.variables; for (int i = 0; i < BLOCK_VARIABLES_COUNT; i++) { @@ -219,9 +219,12 @@ def getBlockVariables(self, block, min_var_count=1, max_var_count=20): break; } } + // __Block_byref_xxx may break this + /* if (!isClass) { - break; + break; } + */ NSString *key = [NSString stringWithFormat:@"variables[%d]", i]; NSString *value = [NSString stringWithFormat:@"%@", obj]; diff --git a/fblldbbase.py b/fblldbbase.py index 4de48b7..6811b4e 100755 --- a/fblldbbase.py +++ b/fblldbbase.py @@ -144,8 +144,8 @@ def evaluateCStringExpression(expression, printErrors=True): RETURN_MACRO = """ #define IS_JSON_OBJ(obj)\ (obj != nil && ((bool)[NSJSONSerialization isValidJSONObject:obj] ||\ - (bool)[obj isKindOfClass:[NSString class]] ||\ - (bool)[obj isKindOfClass:[NSNumber class]])) + (bool)[obj isKindOfClass:(Class)[NSString class]] ||\ + (bool)[obj isKindOfClass:(Class)[NSNumber class]])) #define RETURN(ret) ({\ if (!IS_JSON_OBJ(ret)) {\ (void)[NSException raise:@"Invalid RETURN argument" format:@""];\ From a31c1f45d7180309f9b8707bf5758c5dc051518d Mon Sep 17 00:00:00 2001 From: dhcdht Date: Tue, 22 May 2018 08:06:20 +0800 Subject: [PATCH 4/4] remove min_var_count and add comments --- commands/FBClassDump.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/commands/FBClassDump.py b/commands/FBClassDump.py index 70bcd3d..f81e4ee 100644 --- a/commands/FBClassDump.py +++ b/commands/FBClassDump.py @@ -139,12 +139,14 @@ def run(self, arguments, options): command = string.Template(tmpString).substitute(block=block) json = fb.evaluate(command) - variables_json = self.getBlockVariables(block) + # We assume that the maximum number of variables captured by the block is 10 + max_var_count = 10 + variables_json = self.getBlockVariables(block, max_var_count) if variables_json is not None: json.update(variables_json) variablesStrs = [] - for i in range(10): + for i in range(max_var_count): varKey = 'variables['+str(i)+']' if varKey in json: variablesStrs.append(json[varKey]) @@ -162,9 +164,12 @@ def run(self, arguments, options): print 'Imp: ' + hex(json['invoke']) + ' Signature: ' + sigStr + ' Variables : {\n'+variablesStr+'\n};' - def getBlockVariables(self, block, min_var_count=1, max_var_count=20): + def getBlockVariables(self, block, max_var_count): ''' no __Block_byref_xxx + We must check the block's captured variables one by one here. + no reason, but it works. + We assume that the maximum number of variables captured by the block is max_var_count ''' # http://clang.llvm.org/docs/Block-ABI-Apple.html @@ -236,7 +241,7 @@ def getBlockVariables(self, block, min_var_count=1, max_var_count=20): RETURN(dict); """ last_json = None - for i in range(min_var_count, max_var_count): + for i in range(1, max_var_count): command = string.Template(tmpString).substitute(block=block, variables_count=i) json = fb.evaluate(command, printErrors=False) if json is not None: