在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中 ...
随机推荐
- vue 详情跳转至列表页 实现列表页缓存
甲爸爸提了一个需求,希望公众号内的商城能够像app一样,从商品详情页跳转至列表页及其他列表页时,可以实现列表页缓存(数据不刷新.位置固定到之前点的商品的位置) 本来想着scrollBehavior应该 ...
- webpack 打包 todolist 应用
写在前面的话: 一直想着手动配置webpack实现应用,正好最近这段时间比较空闲,就写了一个通过webpack打包实现todolist的简单应用.本文内容包括:通过webpack打包css,html ...
- Python编程系列---使用字典实现路由静态路由
def index(): print('Index Page....') def bbs(): print('BBS Page....') def login(): print('Login Page ...
- C#刷遍Leetcode面试题系列连载(5):No.593 - 有效的正方形
上一篇 LeetCode 面试题中,我们分析了一道难度为 Easy 的数学题 - 自除数,提供了两种方法.今天我们来分析一道难度为 Medium 的面试题. 今天要给大家分析的面试题是 LeetCod ...
- day10作业(函数实现注册''')
在猜年龄的基础上编写登录.注册方法,并且把猜年龄游戏分函数处理,如 登录函数 注册函数 猜年龄函数 选择奖品函数 '''在猜年龄的基础上编写登录.注册方法,并且把猜年龄游戏分函数处理,如 2. 登录函 ...
- django-URL之include函数(五)
三种格式:(1)incude(module,namespace=None) from django.urls import path,include from book import urls url ...
- 获取用户地理位置.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- Okhttp 请求流程梳理
最近在看 Okhttp 的源码.不得不说源码设计的很巧妙,从中能学到很多.其实网上关于 Okhttp 的文章已经很多了,自己也看了很多.但是俗话说得好,好记性不如烂笔头,当你动手的时候,你会发现你在看 ...
- 运营的Python指南 - Python 操作Excel
这是一份写给运营人员的Python指南.本文主要讲述如何使用Python操作Excel.完成Excel的创建,查询和修改操作. 相关代码请参考 https://github.com/RustFishe ...
- 【Linux】【自学笔记】Linux下面docker安装mysql
写在前面: 捣腾继续,之前把一个SpringBoot的程序安装在docker上面,参考链接:https://www.cnblogs.com/aki-stones/p/2019-11-01-note.h ...