一个LLDB的Python脚本,断点每处objc_msgSend,从此不用辛辛苦苦的敲 n 或 n -c 了

大神请无视 T_T
整了个LLDBPython脚本,从此不用辛辛苦苦的敲 nn -c 。脚本的功能是:断点在每处objc_msgSend ,然后打印参数。用法:
启动LLDB后,输入以下命令:
command script import /path/to/StepTo_objc_msgSend.py
help
iobjc_msgSend
iobjc_msgSended
iprint_args

脚本在iPhone 5S ARM64测试过,有BUG大家见谅改改就好,别鄙视小弟我哈。


StepTo_objc_msgSend.py

#!/usr/bin/python

import commands
import optparse
import lldb
import shlex
import re
import os

def iobjc_msgSend(debugger, command, result, internal_dict):
	interpreter = lldb.debugger.GetCommandInterpreter()
	returnObject = lldb.SBCommandReturnObject()
	thread = debugger.GetSelectedTarget().GetProcess().GetSelectedThread()
	thread.StepOver()
	while True:
		interpreter.HandleCommand('dis -p -c 10', returnObject)
		disassemble = returnObject.GetOutput();
		p = re.compile(r'->.*')
		m = p.search(disassemble)
		c = m.group(0)
		if 'objc_msgSend' in c :
			print 'objc_msgSend Hited!'
			print disassemble
			break
		else:
			thread.StepOver()

def iobjc_msgSended(debugger, command, result, internal_dict):
	iobjc_msgSend(debugger, command, result, internal_dict)
	interpreter = lldb.debugger.GetCommandInterpreter()
	returnObject = lldb.SBCommandReturnObject()
	thread = debugger.GetSelectedTarget().GetProcess().GetSelectedThread()
	thread.StepOver()
	print 'objc_msgSend Evaluated!'

	interpreter.HandleCommand('dis -s `$pc-0x8` -c 5', returnObject)
	disassemble = returnObject.GetOutput()
	print disassemble

	iprint_args(debugger, command, result, internal_dict)

	interpreter.HandleCommand('po $x0', returnObject)
	ret = returnObject.GetOutput().strip()
	print 'Return Value: %s' % ret


def iprint_args(debugger, command, result, internal_dict):
	interpreter = lldb.debugger.GetCommandInterpreter()
	returnObject = lldb.SBCommandReturnObject()
	interpreter.HandleCommand('po $x0', returnObject)
	arg1 = returnObject.GetOutput().strip()
	interpreter.HandleCommand('p (char *)$x1', returnObject)
	arg2 = returnObject.GetOutput().strip()

	print '-[%s %s]' % (arg1, arg2)

	functionName = '['
	functionName += '%s ' % arg1
	p = re.compile('"(.*)"')
	m = p.search(arg2)
	if m is not None:
		s = m.group(1)
		names = s.split(':')
		print names
		for i in range(len(names) - 1):
			interpreter.HandleCommand('po $x%d' % (i + 2), returnObject)
			value = returnObject.GetOutput().strip()
			name = names[i]
			functionName += ' %s:%s ' % (name, value)
	

	functionName += ']'
	print functionName


def __lldb_init_module(debugger, dict):

	names = ['iobjc_msgSend', 'iobjc_msgSended', 'iprint_args']
	helpTexts = ["Break at next objc_msgSend.", "Evaluate next objc_msgSend.", "Print current objc_msgSend arguments."]
	for i in range(len(names)):
		name = names[i]
		helpText = helpTexts[i]
		debugger.HandleCommand('command script add %s -f %s.%s' % (name, __name__, name))
		print 'The "%s" python command has been installed and is ready for use.' % name
		debugger.HandleCommand('command script add --help "{help}" --function {function} {name}'.format(help=helpText, function=name, name=name))

1 个赞

def getRegisters(frame):
    d = {}

    registerSet = frame.GetRegisters() # Returns an SBValueList.
    for regs in registerSet:
        if 'general purpose registers' in regs.GetName().lower():
            GPRs = regs
            break
    else:
        GPRs = []

    for reg in GPRs:
        d[reg.GetName().lower()] = reg

    return d

debugger.GetSelectedTarget()

process = target.GetProcess()
thread = process.GetSelectedThread()
frame = thread.GetSelectedFrame()

regs = getRegisters(frame)
pc_addr = lldb.SBAddress(regs['pc'].unsigned, target)

inst = target.ReadInstructions(pc_addr, 1)[0]

print(inst)

我个人还是倾向于调API, 实在没办法才HandleCommand

API确实是更好然而lldb的python文档写的太狗屎了。我自己的实现也是handlecommand+正则。

不知道lldb怎么加native插件

是的屎都不如,你只能到处翻他的例子

以op这个需求imo还是fishhook+objc_msgSend更好一点?

不知道不怎么碰这些了

都可以啦, 看个人喜欢, 我是觉得脚本更方便灵活, 我做的话会直接在objc_msgSend下断点, 然后设置callback, 检查lr是不是在某个模块里, 再选择是否输出, 再对$lr下个 OneShot 断点输出返回值, 因为有时候 StepOver 会直接跑飞, 不知道什么原因

bp = lldb.target.BreakpointCreateByName(api)
bp.SetScriptCallbackFunction('module.callback')
bp.SetOneShot(True)

说找不到debugger模块,

断点在这里频率太高了吧?
@Aimer 可以看 这个工具 GitHub - DavidGoldman/InspectiveC: objc_msgSend hook for debugging/inspection purposes.

就我的实践来看, 效率还可以接受, 而且我还是包括objc_msgSend在内上百个API一起断的, 反正也是挂在那里

ios 上么? 其实我最近在搞一个 trace objc_msgSend 的东西, 测了下, objc_msgSend 调用几秒内可以打到几 W 的调用, 并且 InspectiveC 也很慢, 然后就自己搞了个 trace.

我刚才有又试了下 在 objc_msgSend 下了一个不会触发条件断点, 然后点了下界面返回按钮, view 都没有响应. :joy:

当然是ios上,脚本效率肯定有点慢了,看个人怎么取舍了

ty,用过。Xcode8.3+ Boooom

fyi我的意思原本是自动化处理syscall断点或者Tweak

objc_msgSend调用频率太高了…效率太低…

我整理了一下,比较完善的版本,放了在我的Github上了。

1 个赞

这个给力