hopper的逆向代码功能并不如想象中那么好,尤其是在逆向c++代码时。对于从ObjC进入iOS开发又不太清楚运行时的人员来说,hopper可以将反汇编码输出成[obj selector:what]这样的ObjC式的函数调用,一定会很惊叹。其实ObjC式函数调用的关键就是枢纽函数的msg_send(c style)以及枢纽机制(ObjC对象消息机制)中的分派机制(消息分派)的消息@selector。msg_send是c风格的函数,只要参照其传参设定(前面文章以经介绍过,《gcc在x64体系中如何传递参数,linux,mac,iOS适用》)。对象消息@selector是一个SEL类型,其定义是const char* const, 而且在映像的数据段必须有对应的字符串。这样一来,只要有这个SEL消息的字符串,就可以写一个类似格式转换输出的脚本。的确,用hopper来逆向ObjC的函数可以大大简化反汇编码的阅读(调用了哪些函数这一点上),例如一个ObjC式的函数调用,都那么重复那些步骤而且是许多步,一屏也看不了几个调用。

但c++的情况就不一样。下面我选了一个函数,因为hopper的简单分析失效了。

我选用的是cocoa的iOS模拟器x64版本里的QuartzCore.framework的一个函数,CA::Layer::set_bit。下面是hopper的逆向伪代码:

就这样一瞥,效果很不错,但是却是错误的。

首先是传参的解析,这个一错,就是开始错了,后面就大错。这比直接去分析反汇编码还冤枉。
其次是没有办法分辨静态成员函数还是成员函数。
第三就是不清楚成员函数指针的调用。

下面是我对hopper加的批注:

先是逆向伪代码:

再是反汇编代码:

<20161002 补充>

hopper将rdi至r8五个寄存器往函数原型的五个参数上硬套。

实际上CA::Layer::set_bit是一个成员函数,rdi是对象指针,rsi才是函数原型的第一个参数,一共使用了4个寄存数作为传参,最后一个参数是一个函数指针,没有使用r9,而是使用了堆栈两个cpu字长的长度作为最后一个参数。

</20161002>

现在开始逐一分析。
首先hopper无视了this指针这个参数。为什么ObjC的函数它又可以解析出this,严格来说self不是this指针,它是llvm编译器约定在c风格的函数msg_send定义的第一个参数,以及msg_send_stret定义的第二个参数,也就是说ObjC的函数调用实质是一个c风格的函数调用。而c++的成员函数调用又有另外的约定,所以hopper失效了。
hopper以c风格函数的传参约定套用在成员函数CA::Layer::set_bit定义的参数序列,这是错误的开端。可以参看反汇编码,寄存器%rsi是传送到了-0x40(%rbp)的内存单元中,而这个%rsi才是c++成员函数的第一个参数,而对应于hopper逆向代码的arg0。然而hopper错误套用约定,hopper看到的参数向左shift了一个身位,所以它将%rsi看成了成员函数的第二个参数,var_40=arg1。
所以hopper正确的逆向应该从这样开始:

int CA::Layer::set_bit(uint32_t arg1, uint32_t arg2, uint32_t arg3, bool arg4, void(*arg5)(*)) {
assert(this == arg0);
var_40 = arg1;

这样它逆向输出的代码才有可能不误导人。
你看出了不同了吗,我再补一下hopper的错误生成作对比

int CA::Layer::set_bit(uint32_t arg0, uint32_t arg1, uint32_t arg2, bool arg3, void(*arg4)(*)) {
var_40 = arg1;

这在实际开发和调试中,都足以冤枉大量工作时间。

通过调整,hopper失效的问题就是解决了吗?
没有,hopper逆向还是失效了。错误就在对成员函数指针的解析。逆向的样本CA::Layer::set_bit最后一个参数是一个成员函数指针,用于回调用的。但在hopper的逆向代码中,这个参数并没有被使用过,因为hopper失灵了。
或许在msvc平台下,hopper将成员函数指针看作是void*可能会擦边正确,但是在*nix和bsd体系的平台上就不一定。因为*nix和bsd是由GNU_C标准的编译器编译的,也就是GNU_C中成员函数指针的规则并非一个单纯的函数入口地址,前面的文章我也有介绍过。我们来回看反汇编码,这个样本函数并没有使用r9来传递arg5,然而却使用了两个堆栈单元来传参,这样一来参数个数就多出来了。如果这时你还不清楚GNU_C编译器下的成员函数指针是什么一回事,就比较难以解释这个函数了。详细请参看我前面的文章,《反汇编带看清成员函数指针的本尊(gcc@x64平台)》。这里的两个堆栈单元其实就是最后一个参数(成员函数指针),而且不是一个单纯的地址指针,而是一个sizeof(void*)*2大小的对象。没错成员函数指针是一个对象。在使用var_50和var_58的地方,其实就是在回调这个成员函数指针的引用。
但是hopper的逆向还是错了(rcx)(rdi),hopper将成员函数指针当作普通函数指针来解释了。清楚的人都明白,rdi就不是成员函数定义输入参数,修正后应该是rdi->(rcx)(var_38)。

还有就是hopper并没有正确分析出这个样本函数的返回类型,用了一个int来充当。其实从返回部分的反汇编码可以看到,并没有对rax作是任何操作,也就是说返回void。

第二个问题和上面的问题一样,但反应在逆向代码体中对其它成员函数的调用。CA::Transaction::lock()这个并非静态成员函数,必须要有一个调用的对象,然而是hopper没有明确生成关于调用对象的指向。
r12 = CA::Transaction::ensure_compat();
CA::Transaction::lock();
应该为:
rdi = r12 = CA::Transaction::ensure_compat();
((CA::Transaction*)r12)->lock();

第三个问题,不清楚成员函数指针的调用,这一点在第一个问题谈及成员函数指针参数时讲了。

最后我贴上我逆向出来的代码:

hopper反汇编工具的逆向伪代码功能并不理想的更多相关文章

  1. 反汇编工具 objdump的使用简介

    arm-linux-objdump -D led.elf > led_elf.dis objdump是gcc工具链中的反汇编工具,作用是由编译链接好的elf格式的可执行程序反过来得到反汇编代码 ...

  2. 【转】iOS开发工具系列(按功能分)

    http://www.cocoachina.com/newbie/basic/2014/0417/8187.html 这是我们多篇iOS开发工具系列篇中的一篇,此前的文章比如:那些不能错过的Xcode ...

  3. 安卓反汇编工具arm-eabi-objdump

    安卓反汇编工具 在Arm平台系统自带的反编译工具在android/prebuild/linux-/toolchail/arm-abil-/bin目录下的arm_eabi-objdump进行反汇编 ar ...

  4. 痞子衡嵌入式:开启NXP-MCUBootUtility工具的HAB加密功能 - CST(中英双语)

    1 Reason for enabling HAB encryption function 为什么要开启HAB加密功能 NXP-MCUBootUtility is a tool designed fo ...

  5. 痞子衡嵌入式:开启NXP-MCUBootUtility工具的HAB签名功能 - CST(中英双语)

    1 Reason for enabling HAB signature function 为什么要开启HAB签名功能 NXP-MCUBootUtility is a tool designed for ...

  6. Python交互K线工具 K线核心功能+指标切换

    Python交互K线工具 K线核心功能+指标切换 aiqtt团队量化研究,用vn.py回测和研究策略.基于vnpy开源代码,刚开始接触pyqt,开发界面还是很痛苦,找了很多案例参考,但并不能完全满足我 ...

  7. 微信小程序开发——开发者工具中素材管理功能使用的注意事项

    为什么使用“素材管理”: 微信小程序环境中本地资源图片是无法通过 WXSS 获取的,可以使用网络图片,或者 base64,或者使用<image/>标签.. 当然,如果不想这么麻烦,你可能会 ...

  8. 学汇编的时候可以拿IDA之类的反汇编工具辅助学习,再用gdb或者IDA动态调试,跟踪每条指令的 执行结果。都不难

    作者:潘安仁链接:https://www.zhihu.com/question/40720890/answer/87926792来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...

  9. 自制反汇编工具使用实例 其二(使用xmm寄存器初始化对象,以及空的成员函数指针)

    在反汇编代码中,当看到xmm寄存器,第一反应是将要进行浮点操作或访问,但是更加多的情况是在使用xmm寄存器初始化局部对象. 下面是自制反汇编工具翻译出来的代码: // -[CALayer setAll ...

随机推荐

  1. NServiceBus+RabbitMQ开发分布式应用

    前言      NServiceBus提供了8种传输管道组件,分别是Learning.MSMQ.Azure Service Bus.Azure Service Bus (Legacy).Azure S ...

  2. java学习5-面向对象(下)

    final修饰符: final用于修饰类.变量和方法. final修饰变量时,一旦获得了初始值就不可改变 1.抽象方法和抽象类 抽象方法与抽象类的规则: a.抽象方法和抽象类必须使用abstract修 ...

  3. jmeter-控制业务比例

    方式一: 多线程组 缺点:由于各事务相应时间一般不一致,故只能粗略的控制业务占比 实例:待补充

  4. .NET Core3.0 日志 logging

    多年的经验,日志记录是软件开发的重要组成部分.没有日志记录机制的系统不是完善的系统.在开发阶段可以通过debug附件进程进行交互调试,可以检测到一些问题,但是在上线之后,日志的记录起到至关重要的作用. ...

  5. travis-ci + php + casperjs 持续集成

    .travis.yml 文件添加内容: sudo: required language: php php: - 5.5 before_script: - npm install -g casperjs ...

  6. vue+webpack+element-ui项目打包优化速度与app.js、vendor.js打包后文件过大

    从开通博客到现在也没写什么东西,最近几天一直在研究vue+webpack+element-ui项目打包速度优化,想把这几天的成果记录下来,可能对前端牛人来说我这技术比较菜,但还是希望给有需要的朋友提供 ...

  7. MyBatis(2)-- MyBatis配置mybatis-config.xml

    一.properties属性 1.可以在mybatis-config.xml中使用property子元素配置 <properties resource="jdbc.properties ...

  8. Java基础(十七)日志(Log)

    1.日志的概念 在调试有问题的代码时,经常需要插入一些System.out.println方法来观察程序运行的操作过程.但是,一旦发现了问题并且解决了问题,就需要将这些System.out.print ...

  9. SpringBoot整合SSM(代码实现Demo)

    SpringBoot整合SSM 如图所示: 一.数据准备: 数据库文件:数据库名:saas-export,表名:ss_company 创建表语句: DROP TABLE IF EXISTS ss_co ...

  10. 条款03:尽肯使用const

    定义常量 define 是一个Compile-Time的概念,它的生命周期止于编译器期,它存在与程序的代码段,在实际程序中它只是一个常数.一个命令中的参数.并没有实际的存在 const常量存在于程序的 ...