在lldb调试中调用c++函数
在lldb调试时,调用oc对象的方法不足为奇,因为msgSend是有原型导出的,oc对象的方法都运行期绑定的,绑定信息都在objc_class中。只要在调试中[receiver sel]之类,lldb就自动完成的整个由SEL通过msgSend路由到receiver的IMP方法并执行的整个过程。但是要调用c++函数则没有这么方便,虽然c++函数(包括成员函数和非成员函数)的链接符号有着函数原型的详细信息,但却不包括类的定义和名字空间的定义,即使lldb翻译出这样一个符号(symbol)QuartzCore`CA::Render::Layer::show(unsigned int, unsigned int),在我们看来就是一个函数原型,但是调试器不这么认为,我们没有办法直接使用上面的符号,除非我们有其他人发布的模块的符号文件,这是不可能的。例如你要调用这个show方法时,大家都会想到将对象指定为CA::Render::Layer*再引用show方法加上参数就搞定。事实上,lldb调试器会抱怨CA名字或结构没有定义,CA::Render没有定义,CA::Render::Layer没有定义,上面那个明明有着我们认为信息齐全的符号根本不能够被你这么轻易用上。
怎么办,我很想利用这个模块设计自带的打印对象信息方法来了解和加深逆向有用的信息,只好试试自己构建调用栈和修改程序计数器地址来模拟出函数的调用。在windbg中可以用这种方法,lldb中应该也没有问题,我是这么想的。
首先我逆向过它的代码,知道这个类没有多继承,只要中断这个对象的其中一个成员方法,在它没有操作栈之前,利用这个this指针(就算是多继承,这时this指针也已经在入口之前经过了正确的适配),到底选用那一个函数好呢?有两个成员函数是对象基本都会调用上的,就是构造和析构,构造中的对象不可用,那么就选在析构之前。
断点在CA::Render::Layer::~Layer()方法,
在断点触发后,构建调用栈,或者利用栈保存寄存器,操作如下
-0x20(%rsp) <-- %rip ; 返回地址,模拟call指令
-0x18(%rsp) <-- %rdx ; 模拟push %rdx
-0x10(%rsp) <-- %rsi ; 模拟push %rsi
-0x8(%rsp) <-- %rdi ; 模拟push %rdi
%rsp <-- %rsp - 0x20 ; 让栈顶指向构建出来的调用栈
%rip <-- &CA::Render::Layer::show ; 模拟jmp
然后执行单步指令step into,使被中断的线程执行CA::Render::Layer::show方法,使用的是CA::Render::Layer::~Layer()方法入口处的this指针。
我将CA::Render::Layer::~Layer()的入口地址,写入到了跳转前栈顶,线程从CA::Render::Layer::show返回就会再次进入我的断点方法,这时再将%rdi,%rsi,%rdx还原,%rsp还原,%rsp <-- %rsp + 0x18,线程在show函数retq时已经将返回地址出栈。让线程继续到原本的轨道上运行。
大至调用的框架就么定下来了。
这能够成功吗?先不要着急,我先来确认一下修改%rip跳转是不是有效。不试不知道,一试吓一跳。修改%rip后,键入s指令到lldb控制台执行,lldb控制台失去了交互控制没有返回,线程也没有执行。只好用XCode的调试单步快捷键F6,XCode的单步F6被执行了,lldb控制台再次返回可交互状态,线程进入了CA::Render::Layer::show函数内的第二条指令,也就是说,调试器执行了两次step into指令(第一次是lldb控制台的s指令,第二次是XCode的单步F6)。然后step out 或者叫finish,函数返回到了调用CA::Render::Layer::~Layer()处的下一条指令。运行和预计的一样,因为我没有构建到调用栈,this指针也被正确使用。我做的行为,让原本要执行的CA::Render::Layer::~Layer()跳转到了CA::Render::Layer::show,自然原本的~Layer()被忽略掉了。这里要注意的成员方法默认是调用者平衡栈,析构函数没有参数,返回处自然就不用平衡栈,虽然CA::Render::Layer::show有两个参数,但是CA::Render::Layer::show自己是不会去平衡栈的,所以栈的平衡没有问题。
不过问题也出来了,直接修改%rip,lldb控制台会工作不正常。出于严谨的态度,我试了一下,我又在~Layer()入口处做了另一次%rip的修改,不过跳转的地址是在~Layer()的结束处,也就是函数内跳转,结果就没有影响到lldb控制台的工作。但是跨函数的跳转就不如人意。
不管怎么样,计划继续实施,这次就加上构建的调用栈。这次又吓了一跳,当我修改完%rsp后,在lldb控制台键入指令修改%rip指向到CA::Render::Layer::show之时,控制台再一次失去了交互。还是上面的方法使用XCode的F6单步一次,控制台再次可以交互,线程进入了CA::Render::Layer::show入口。但之后运行就有问题,并非在我构建的调用栈平衡之处,而是CA::Render::Layer::show体内调用的其它函数对栈操作抛出BAD_ACCESS。从上面的是情况来看直接修改%rsp或%rip,调试器运行有问题,而且在python脚本中,用lldb指令访问寄存器的操作不可取,原因请看我上一篇写到踩过的坑。
因些通过构建调用栈的方法不合适,但是我十分想使用模块中自有的功能方法,由于是模拟器,用的x64处理器,调用约定优先使用寄存器,C++成员函数调用参数约定与非成员函数调用参数约定可以看起一样,(参数约定都是同一顺序,然而32位处理器编译出来第一个参数都是栈,this指针使用寄存器,两种函数之间就有差异)。那么就将计就计,将成员函数摊平为非成员函数,同时lldb支持自定义函数类型,(在vc或windbg中,是不可以在调试器中定义一个函数类型,来转换入口地址的类型的)。于是可以将CA::Render::Layer::show(unsigned int, unsigned int)的映像地址,转换成void(*)(void*, int, int)类型就可以在CA::Render::Layer任何一个成员函数中,通过lldb的表达式或打印等指令去调用CA::Render::Layer::show方法,利用苹果自身的模块自带的功能打印出信息。这种方法在python脚本使用没有问题,作断点命令使用就可以打印出创建过的对象的信息了。再次声明不要在python脚本中使用HandleCommand来访问寄存器,问题的讨论在上一篇,有兴趣请看。
最后是CA::Render::Layer::show打印了什么信息,输出到哪里了,请看下一篇介绍。
在lldb调试中调用c++函数的更多相关文章
- 在lldb调试中调用c++函数 - 如何使用QuartzCore里面的日志消息
承接上一篇,上一篇讲到可以在lldb调试中调用QuartzCore.framework里的CA::Render::Object::show方法来是观察CA::Render模块内的类的信息,但是在lld ...
- C++箴言:避免构造或析构函数中调用虚函数
如果你已经从另外一种语言如C#或者Java转向了C++,你会觉得,避免在类的构造函数或者析构函数中调用虚函数这一原则有点违背直觉.但是在C++中,违反这个原则会给你带来难以预料的后果和无尽的烦恼. 正 ...
- 读书笔记 effective c++ Item 9 绝不要在构造函数或者析构函数中调用虚函数
关于构造函数的一个违反直觉的行为 我会以重复标题开始:你不应该在构造或者析构的过程中调用虚函数,因为这些调用的结果会和你想的不一样.如果你同时是一个java或者c#程序员,那么请着重注意这个条款,因为 ...
- 【校招面试 之 C/C++】第10题 C++不在构造函数和析构函数中调用虚函数
1.不要在构造函数中调用虚函数的原因 在概念上,构造函数的工作是为对象进行初始化.在构造函数完成之前,被构造的对象被认为“未完全生成”.当创建某个派生类的对象时,如果在它的基类的构造函数中调用虚函数, ...
- 【VS开发】MFC中调用C函数模块的解决方案
[VS开发]MFC中调用C函数模块的解决方案 标签(空格分隔): [VS开发] 声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 说明:最近调试基于MFC的程序 ...
- C++构造与析构函数中调用虚函数的问题
前些天想把以前写的内存池算法重写一遍,跨平台是第一目标,当时突发奇想,因为不愿意做成一大堆#if..#end,所以想利用C++的多态性,但是怎么让内存池完好退出却没想到自认为完美的方案.但是一个很偶然 ...
- EC笔记,第二部分:9.不在构造、析构函数中调用虚函数
9.不在构造.析构函数中调用虚函数 1.在构造函数和析构函数中调用虚函数会产生什么结果呢? #; } 上述程序会产生什么样的输出呢? 你一定会以为会输出: cls2 make cls2 delete ...
- 关于在C#中构造函数中调用虚函数的问题
在C#中如果存在类的继承关系,应避免在构造函数中调用虚函数.这是由于C#的运行机制造成的,原因如下: 新建一个类实例时,C#会先初始化该类(对类变量赋值,并将函数记在函数表中),然后再初始化父类.构造 ...
- 如何在C语言中调用Swift函数
在Apple官方的<Using Swift with Cocoa and Objectgive-C>一书中详细地介绍了如何在Objective-C中使用Swift的类以及如何在Swift中 ...
随机推荐
- xpath相关用法及技巧
本节讲解网页解析神器----XPath lxml下载 xpath基本用法 xpath插件 Xpath及XML路径语言,它是一门在XML文档查找信息的语言. 一:lxml下载以及安装 首先需要解决lxm ...
- 解决 Mybatis报错org.apache.ibatis.ognl.NoSuchPropertyException: XXXCriteria$Criterion.noValue
问题 这个noValue一定存在,但是报错. 场景就是存在并发的情况下,尤其是在服务刚刚启动的时候,就会发生这个异常. 但是很不幸,mybatis 3.4.1之前,用的 OGNL都是由这个问题. 分析 ...
- Amazon S3数据存储
从官网下载aws 的unity插件,并做了简单修改(主要用修改PostObject),问题: (一)获取Pool ID 通过服务-Cognito-管理/新建用户池,可以新建或者获取Pool ID (二 ...
- Cannot read property 'forEach' of undefined
在singer-detail组件中,有一个_normalizeSongs()方法,遍历数组 _normalizeSongs(list) { let ret = []; list.forEach(ite ...
- java中的Static、final、Static final各种用法详解
前言 对Static.final.Static final这几个关键词熟悉又陌生?想说却又不知怎么准确说出口?好的,本篇博客文章将简短概要出他们之间的各自的使用,希望各位要是被你的面试官问到了,也能从 ...
- QTCreator增加帮助文档
1.下载QT库的帮助文档,是qch格式的. 2.在QTCreator的->Tools->Options->Help->Add增加下载的qch文件即可.
- 详细讲解CSS中相对定位relative和绝对定位absolute
很多朋友问过我absolute与relative怎么区分,怎么用?我们都知道absolute是绝对 定位,relative是相对定位,但是这个绝对与相对是什么意思呢?绝对是什么地方的绝对,相对又是相对 ...
- 四、docker 仓库(让我们的镜像有处可存)
前言 前面讲完了docker 镜像和容器,以及通过Dockerfile 定制属于我们自己的镜像,那那现在就是需要将我们自己定制的镜像存放到仓库中供他们使用.这一套流程才算是正式走完了.从获取镜像,操作 ...
- 基于SkyWalking的分布式跟踪系统 - 微服务监控
上一篇文章我们搭建了基于SkyWalking分布式跟踪环境,今天聊聊使用SkyWalking监控我们的微服务(DUBBO) 服务案例 假设你有个订单微服务,包含以下组件 MySQL数据库分表分库(2台 ...
- C++等号操作符重载
在新学操作符重载时最令人头疼的可能就是一些堆溢出的问题了,不过呢,只要一步步的写好new 与 delete.绝对不会有类似的问题. 当时我们编译可以通过,但是运行会出错,因为对象s1与s2进行赋值时, ...