在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中 ...
随机推荐
- python方法是什么?
python方法是什么? 方法用来描述对象所具有的行为. 在类中定义的方法可以粗略分为四大类:公有方法.私有方法.静态方法.类方法. 公有方法.私有方法一般所指属于对象的实例方法, 私有方法的名字以两 ...
- Windows突破远程连接最大数去掉限制登录
当对方设置最大连接数 超过限制时 可以用这个命令 win+r 输入 mstsc /v:192.168.18.131:3389 /console windows server 2003 sp2 以 ...
- 6. SSH远程管理服务实战
1. SSH基本概念? ssh是一个应用层安全协议 2.SSH主要的功能是? 实现远程登录, 数据传输过程中进行加密. 钉钉(澡堂模式) 远程登录: ssh telnet 3.SSH与Telnet之间 ...
- 手动部署 Docker+Grafana+Prometheus系统监控之Redis
监控规划图 使用Docker 创建两台Redis docker run -d --name redis1 redis docker run -d --name redis2 redis 查看redis ...
- ASP.NET Core 3.0 : 二十八. 在Docker中的部署以及docker-compose的使用
本文简要说一下ASP.NET Core 在Docker中部署以及docker-compose的使用 (ASP.NET Core 系列目录). 系统环境为CentOS 8 . 打个广告,求职中.. 一 ...
- net core WebApi——公用库April.Util公开及发布
前言 在之前鼓捣过一次基础工程April.WebApi后,就考虑把常用的类库打包做成一个公共类库,这样既方便维护也方便后续做快速开发使用,仓库地址:April.Util_github,April.Ut ...
- MATLAB实例:聚类初始化方法与数据归一化方法
MATLAB实例:聚类初始化方法与数据归一化方法 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 1. 聚类初始化方法:init_methods.m f ...
- Asp.Net Core 单元测试正确姿势
背景 ASP.NET Core 支持依赖关系注入 (DI) 软件设计模式,并且默认注入了很多服务,具体可以参考 官方文档, 相信只要使用过依赖注入框架的同学,都会对此有不同深入的理解,在此无需赘言. ...
- CSP2019知识点整理
也算是接下来二十天的复习计划吧 仅止于联赛难度左右 基础算法 字符串 char[] cstring memset() 输入无& gets(), fgets(stdin, ,); strcmp, ...
- 使用float设置经典的网站前端结构
float浮动是能使得标签脱离文档流,此处脱离文档流,是指此便签后面的,没有脱离文档流的标签将此标签当作透明,按正常来布局. float脱离文档流,是受到父级范围限制的,在父级范围内脱离文档流,脱离文 ...