前言
iOS逆向时经常会遇到参数为block类型,本文介绍一个lldb script,可快速打印出Objective-C方法中block参数的类型。
zblock <block-address> : print oc block signature, parameter -d for disassemble
I lldb打印block参数签名
1.1 install
cd ~
git clone git@github.com:zhangkn/zlldb.git
然后在 ~/.lldbinit 文件中添加下行内容:command script import ~/zlldb/main.py
1.2 使用
- 如果是逆向工作的话,没有代码,那可以断点到 objc_msgSend这行
- 执行命令 zblock 0x100588080 (block的地址传给 zblock命令),然后block的参数就出来了。
- 根据每一行的
type encoding
对照[苹果文档ocrtTypeEncodings](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
)即可知道block的参数都有什么。
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html由于Xcode11内置的lldb script开始默认Python3版本,facebook的chisel还有些支持问题(可能现在解决了)。zlldb就是把我自己常用的几个命令放到了这里,支持Python3(也就是最新版Xcode)。除了zblock外,还有几个简单的命令,大家可以参考README。
Objective-C type encodings
Objective-C method encodings
1.3 Python script
zblock_print_block_signature
def zblock_print_block_signature(debugger, target, process, block_address):
pointer_size = 8 if zblock_arch_for_target_is_64bit(target) else 4
# print("pointer size = {0}".format(pointer_size))
# print("block address = %x"%(block_address))
flags_address = block_address + pointer_size # The `flags` integer is after a pointer in the struct
flags_error = lldb.SBError()
flags = process.ReadUnsignedFromMemory(flags_address, 4, flags_error)
if not flags_error.Success():
print("Could not retrieve the block flags")
return
block_has_signature = ((flags & (1 << 30)) != 0) # BLOCK_HAS_SIGNATURE = (1 << 30)
block_has_copy_dispose_helpers = ((flags & (1 << 25)) != 0) # BLOCK_HAS_COPY_DISPOSE = (1 << 25)
if not block_has_signature:
print("The block does not have a signature")
return
block_descriptor_address = block_address + 2 * 4 + 2 * pointer_size # The block descriptor struct pointer is after 2 pointers and 2 int in the struct
block_descriptor_error = lldb.SBError()
block_descriptor = process.ReadPointerFromMemory(block_descriptor_address, block_descriptor_error)
if not block_descriptor_error.Success():
print("Could not read the block descriptor struct")
return
signature_address = block_descriptor + 2 * pointer_size # The signature is after 2 unsigned int in the descriptor struct
if block_has_copy_dispose_helpers:
signature_address += 2 * pointer_size # If there are a copy and dispose function pointers the signature
signature_pointer_error = lldb.SBError()
signature_pointer = process.ReadPointerFromMemory(signature_address, signature_pointer_error)
signature_error = lldb.SBError()
signature = process.ReadCStringFromMemory(signature_pointer, 255, signature_error)
if not signature_error.Success():
print("Could not retrieve the signature")
return
print("Signature Address: 0x%x" %(signature_address))
print("Signature String: %s" %(signature))
escaped_signature = signature.replace('"', '\\"')
method_signature_cmd = 'po [NSMethodSignature signatureWithObjCTypes:"' + escaped_signature + '"]'
debugger.HandleCommand(method_signature_cmd)
docurl = 'https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html'
print('Type Encodings Ref: %s' % (docurl))
def zblock_disass_block_invoke_function(debugger, target, process, block_address, instruction_count):
pointer_size = 8 if zblock_arch_for_target_is_64bit(target) else 4
invoke_function_address = block_address + pointer_size + 2 * 4 # The `invoke` function is after one pointer and 2 int in the struct
print("Invoke address: 0x%x" % (invoke_function_address))
invoke_function_error = lldb.SBError()
invoke_function_pointer = process.ReadPointerFromMemory(invoke_function_address, invoke_function_error)
if not invoke_function_error.Success():
print("Could not retrieve the block invoke function pointer")
return
disass_cmd = "disassemble --start-address " + str(invoke_function_pointer) + " -c " + str(instruction_count)
debugger.HandleCommand(disass_cmd)
def zblock_arch_for_target_is_64bit(target):
arch_64 = ['arm64', 'x86_64']
arch = target.GetTriple().split('-')[0]
return arch in arch_64
def cmd_zblock(debugger, command, result, internal_dict):
cmd_args = shlex.split(command)
usage = "usage: %prog arg1 [--disass -d] [--number-instructions -n]"
parser = optparse.OptionParser(prog='zblock', usage=usage)
parser.add_option('-d', '--disass', action='store_true', dest='disass', default=False)
parser.add_option('-n', '--number-instructions', dest='numberinstructions', default=20)
try:
(options, args) = parser.parse_args(cmd_args)
except:
print("error parse parameter")
return
if len(args) == 0:
print("You need to specify the name of a variable or an address")
return
number_instructions = options.numberinstructions
should_disass = options.disass
target = debugger.GetSelectedTarget()
process = target.GetProcess()
thread = process.GetSelectedThread()
frame = thread.GetSelectedFrame()
variable_arg = args[0]
address = int(variable_arg,0)
if address == 0:
print("invalid address")
return
print("Block address: 0x%x" % (address))
zblock_print_block_signature(debugger, target, process, address)
if should_disass:
zblock_disass_block_invoke_function(debugger, target, process, address, number_instructions)
see also
公号:iOS逆向
Chisel is a collection of LLDB commands to assist debugging iOS apps.