Apple高级调试与逆向工程

(十三)SB示例 升级版的lookup

2020-03-10  本文已影响0人  收纳箱

1. 升级版的lookup

1.1 创建自动化脚本

这个项目的starter目录中包含了两个Python脚本,这将使您在创建LLDB脚本内容时的生活更轻松。具体如下:

把这两个文件都放在本章的starter文件夹中,并将它们粘贴到~/lldb/目录中。进入到~/.lldbinit文件并添加以下代码行:

command script import ~/lldb/lldbinit.py
创建lookup命令
 ~> lldb
(lldb) reload_script

如果没有错误的话,我们就可以开始了。

(lldb) __generate_script lookup
Opening "/Users/ycpeng/lldb/lookup.py"...
(lldb) reload_script
(lldb) lookup
Hello! the lookup command is working!
实现lookup命令

前面我们看到lookup命令背后的基础相当简单。“秘密”就是使用SBTargetfindGlobalFunctions。在那之后,只需要进行格式化输出。

我们将继续使用Allocator项目。打开项目,在iPhone模拟器上构建并运行。运行之后,暂停应用程序并启动LLDB。

这个FindGlobalFunctions需要哪些参数?

(lldb) script help(lldb.SBTarget.FindGlobalFunctions)
Help on function FindGlobalFunctions in module lldb:

FindGlobalFunctions(self, name, max_matches, matchtype)
    FindGlobalFunctions(SBTarget self, str const * name, uint32_t max_matches, lldb::MatchType matchtype) -> SBSymbolContextList

因为它是一个Python类,所以可以忽略第一个自参数。名为name的str参数将是我们查询字符串。最大匹配将指定您想要的最大匹配数量。如果使用0,它将返回所有可用的匹配项。matchType参数是一个lldb Python枚举,可以指定执行不同类型的搜索,例如regexnon-regex

因为regex搜索实际上是唯一的方法,所以我们将使用LLDB枚举值lldb.eMatchTypeRegex

其他枚举值可以在这里找到:https://lldb.llvm.org/python_reference/_lldb%27-module.html#eMatchTypeRegex

是时候在lookup.py脚本中实现它了。打开~/lldb/lookup.py。在handle_command末尾找到以下代码:

# Uncomment if you are expecting at least one argument
# clean_command = shlex.split(args[0])[0]
result.AppendMessage('Hello! the lookup command is working!')

替换为

#1
clean_command = shlex.split(args[0])[0]
#2
target = debugger.GetSelectedTarget()
#3
contextlist = target.FindGlobalFunctions(clean_command, 0, lldb.eMatchTypeRegex)
#4
result.AppendMessage(str(contextlist))
  1. 获取传递给脚本的命令的干净版本。
  2. 通过SBDebugger获取SBTarget的实例。
  3. 使用FindGlobalFunctions API,传入clean_command。提供的0表示结果数没有上限,并使用eMatchTypeRegex匹配类型进行正则表达式搜索。
  4. contextlist转换为Python str,然后将其附加到SBCommandReturnObject
(lldb) lookup DSObjectiveCObject
     Module: file = "/Users/ycpeng/Library/Developer/Xcode/DerivedData/Allocator-fnbctbueesgtzzfgksqcimloegwm/Build/Products/Debug-iphonesimulator/Allocator.app/Allocator", arch = "x86_64"
CompileUnit: id = {0x00000000}, file = "/Users/ycpeng/Desktop/Books/Apple_Debugging_and_Reverse_Engineering_v3.0/25-Script Bridging with SBValue & Language Contexts/starter/Allocator/Allocator/DSObjectiveCObject.m", language = "unknown"
   Function: id = {0x100000268}, name = "-[DSObjectiveCObject setLastName:]", range = [0x0000000100002150-0x0000000100002181)
   FuncType: id = {0x100000268}, byte-size = 0, decl = DSObjectiveCObject.h:36, compiler_type = "void (NSString *)"
     Symbol: id = {0x000000db}, range = [0x0000000100002150-0x0000000100002190), name="-[DSObjectiveCObject setLastName:]"
...

得到的输出比image lookup -rn DSObjectiveCObject更糟糕。我们用script研究一下这个执行结果。

(lldb) script k = lldb.target.FindGlobalFunctions('DSObjectiveCObject', 0, lldb.eMatchTypeRegex)

我们可以去查看SBSymbolContextList的文档,或直接查看它的所有方法。

(lldb) gdocumentation SBSymbolContextList
(lldb) script dir(lldb.SBSymbolContextList)
['Append', 'Clear', 'GetContextAtIndex', 'GetDescription', 'GetSize', 'IsValid', '__bool__', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__module__', '__ne__', '__new__', '__nonzero__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__swig_destroy__', '__swig_getmethods__', '__swig_setmethods__', '__weakref__', 'blocks', 'compile_units', 'functions', 'get_block_array', 'get_compile_unit_array', 'get_function_array', 'get_line_entry_array', 'get_module_array', 'get_symbol_array', 'line_entries', 'modules', 'symbols']

我们可以发现两个重要的方法__iter____getitem__,说明我们可以遍历和使用索引访问它。

//等效于script k.__getitem__(0)
(lldb) script k[0]
<lldb.SBSymbolContext; proxy of <Swig Object of type 'lldb::SBSymbolContext *' at 0x10898b6c0> >

(lldb) script print(k[0])
     Module: file = "/Users/ycpeng/Library/Developer/Xcode/DerivedData/Allocator-fnbctbueesgtzzfgksqcimloegwm/Build/Products/Debug-iphonesimulator/Allocator.app/Allocator", arch = "x86_64"
CompileUnit: id = {0x00000000}, file = "/Users/ycpeng/Desktop/Books/Apple_Debugging_and_Reverse_Engineering_v3.0/25-Script Bridging with SBValue & Language Contexts/starter/Allocator/Allocator/DSObjectiveCObject.m", language = "objective-c"
   Function: id = {0x100000268}, name = "-[DSObjectiveCObject setLastName:]", range = [0x0000000100002150-0x0000000100002181)
   FuncType: id = {0x100000268}, byte-size = 0, decl = DSObjectiveCObject.h:36, compiler_type = "void (NSString *)"
     Symbol: id = {0x000000db}, range = [0x0000000100002150-0x0000000100002190), name="-[DSObjectiveCObject setLastName:]"

我们关心的是方法名,如何快速拿到它呢?

(lldb) script print(k[0].symbol.name)
-[DSObjectiveCObject setLastName:]
(lldb) script print(k[0].function.name)
-[DSObjectiveCObject setLastName:]

下面我们将

#3
contextlist = target.FindGlobalFunctions(clean_command, 0, lldb.eMatchTypeRegex)
#4
result.AppendMessage(str(contextlist))

替换为

contextlist = target.FindGlobalFunctions(clean_command, 0, lldb.eMatchTypeRegex)
output = ''
for context in contextlist:
    output += context.symbol.name + '\n\n'
result.AppendMessage(output)

并在LLDB中实验一下

(lldb) reload_script
(lldb) lookup DSObjectiveCObject
-[DSObjectiveCObject setLastName:]

-[DSObjectiveCObject .cxx_destruct]

-[DSObjectiveCObject setFirstName:]

-[DSObjectiveCObject eyeColor]

-[DSObjectiveCObject init]

-[DSObjectiveCObject lastName]

-[DSObjectiveCObject setEyeColor:]

-[DSObjectiveCObject firstName]

输出更加干净了。

现在我想看看这些函数在进程中的位置。我想将一个特定的模块所有函数打印出来,最上面是模块名和命中数量和分割的*

返回lookup.py文件,创建两个新函数。

首先在lookup.py脚本中的handle_command函数下面实现generateModuleDictionary函数:

def generateModuleDictionary(contextlist):
    mdict = {}
    for context in contextlist: 
        #1
        key = context.module.file.fullpath
        #2
        if not key in mdict:
            mdict[key] = []
        #3
        mdict[key].append(context)
    return mdict
  1. SBSymbolContext中,通过SBModuleSBFileSpecfullPath获取Python字符串,并将其分配给一个名为key的变量。获取完整路径(而不是SBFileSpecbasename属性)很重要,因为可能有多个具有相同basename的模块。
  2. mdict变量将保存找到的所有符号的列表,并按模块拆分。此字典中的键将是模块名称,值将是在该模块中找到的符号数组。检查字典是否已经包含此模块的列表。如果没有,将为此模块键添加一个空白列表。
  3. SBSymbolContext实例添加到此模块的相应列表中。

这个函数下面继续完成generateOutput

def generateOutput(mdict, options, target):
    #1
    output = ''
    separator = '*' * 60 + '\n'
    #2
    for key in mdict:
        #3
        count = len(mdict[key])
        firstItem = mdict[key][0]
        #4
        moduleName = firstItem.module.file.basename
        output += '{0}{1} hits in {2}\n{0}'.format(separator, count, moduleName)
        #5
        for context in mdict[key]:
            query = ''
            query += context.symbol.name
            query += '\n\n'
            output += query
    return output
  1. 输出变量将是包含最终传递给SBCommandReturnObject的所有内容的返回字符串。
  2. 遍历mdict字典中找到的所有键。
  3. 这将获取命中数量和第一个对象(下一步从里面取出模块名)。
  4. 获取模块名。
  5. 遍历Python列表中的所有SBSymbolContext项,并将名称添加到输出变量中。

扩展handle_command函数中的代码,以便可以使用刚刚创建的两个新方法。找到:

output = ''
for context in contextlist:
    output += context.symbol.name + '\n\n'

替换为:

mdict = generateModuleDictionary(contextlist)
output = generateOutput(mdict, options, target)

试一下:

(lldb) reload_script
(lldb) lookup DSObjectiveCObject
************************************************************
8 hits in Allocator
************************************************************
-[DSObjectiveCObject setLastName:]

-[DSObjectiveCObject .cxx_destruct]

-[DSObjectiveCObject setFirstName:]

-[DSObjectiveCObject eyeColor]

-[DSObjectiveCObject init]

-[DSObjectiveCObject lastName]

-[DSObjectiveCObject setEyeColor:]

-[DSObjectiveCObject firstName]

我们现在可以查看OC中有两个参数的所有方法:

(lldb) lookup initWith(\\w+\:){2,2}\]

1.4 为lookup添加选项

配置generateOptionParser

def generateOptionParser():
    usage = "usage: %prog [options] code_to_query"
    parser = optparse.OptionParser(usage=usage, prog="lookup")
    parser.add_option("-l", "--load_address",
                      action="store_true",
                      default=False,
                      dest="load_address",
                      help="Show the load addresses for a particular hit")
    parser.add_option("-s", "--module_summary",
                      action="store_true",
                      default=False,
                      dest="module_summary",
                      help="Only show the amount of queries in the module")
    return parser

您将首先实现--load_address。在generateOutput函数中,找到在SBSymbolContext上迭代的for循环,并替换为:

for context in mdict[key]:
            query = ''
            
            #1
            if options.load_address:
                #2
                start = context.symbol.addr.GetLoadAddress(target)
                end = context.symbol.end_addr.GetLoadAddress(target)
                #3
                startHex = '0x' + format(start, '012x')
                endHex = '0x' + format(end, '012x')
                query += '[{}-{}]\n'.format(startHex, endHex)

            query += context.symbol.name
            query += '\n\n'
            output += query
  1. 判断是否需要加载地址信息。
  2. 符号的addrend_addr中通过GetLoadAddress获取到加载地址。
  3. 使用Python format函数对使用Python long表示开始和结束地址进行格式化。

实验一下:

(lldb) reload_script
(lldb) lookup -l DSObjectiveCObject
************************************************************
8 hits in Allocator
************************************************************
[0x0001056de150-0x0001056de190]
-[DSObjectiveCObject setLastName:]

[0x0001056de190-0x0001056de1e9]
-[DSObjectiveCObject .cxx_destruct]

[0x0001056de0f0-0x0001056de130]
-[DSObjectiveCObject setFirstName:]

[0x0001056de070-0x0001056de090]
-[DSObjectiveCObject eyeColor]

[0x0001056ddf60-0x0001056de070]
-[DSObjectiveCObject init]

[0x0001056de130-0x0001056de150]
-[DSObjectiveCObject lastName]

[0x0001056de090-0x0001056de0d0]
-[DSObjectiveCObject setEyeColor:]

[0x0001056de0d0-0x0001056de0f0]
-[DSObjectiveCObject firstName]

然后在generateOutputmoduleName = firstItem.module.file.basename后面添加:

if options.module_summary:
    output += '{} hits in {}\n'.format(count, moduleName)
    continue

实验一下:

(lldb) reload_script
(lldb) lookup -s viewWillAppear
2 hits in Allocator
1 hits in DocumentManager
54 hits in UIKitCore
4 hits in ShareSheet
6 hits in AVKit
2 hits in WebKit
1 hits in PDFKit
1 hits in GLKit
10 hits in MapKit
27 hits in ContactsUI
5 hits in OnBoardingKit
2 hits in AccessibilityUIUtilities
20 hits in Preferences
上一篇下一篇

猜你喜欢

热点阅读