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. HDU 6112 今夕何夕 (预处理 枚举)

    中文题意都看的懂啦~ 思路很简单,就是通过前一天推出当天是星期几,直接枚举所有2017-9999年的每一天就好了.ㄟ( ▔, ▔ )ㄏ 代码: #include <cstdio> #def ...

  2. echarts折线图动态改变数据时的一个bug

    echarts折线图中当增加dataZoom,修改start大于0的时候,会出现折线混乱,变成竖直的线,绘制有问题. 解决方法,在dataZoom中增加filterMode: 'empty' http ...

  3. 电脑扫描不出u盘的解决办法

    现象:u盘已插上但是设备和驱动器里却找不到 解决办法: 首先记下u盘名称,然后 我的电脑-右键-管理-设备管理器,找到u盘,卸载设备后重新插入u盘即可

  4. 徐明星系列之徐明星创办的OK资本成为RnF金融有限公司的锚定投资者

    12月17日,由区块链专家徐明星创办的OK集团的投资部门OK资本宣布,它将成为RnF金融有限公司的锚定投资者.OK集团成立于2012年,创始人徐明星是前豆丁网CTO,从豆丁网离职后,徐明星创办了OK集 ...

  5. instruments无法连接,设备查询不到,找不到工程,查询不到对应app

    这种问题真是让人捉急,一定要沐浴更衣,怀着一颗虔诚的心. 1.拔掉设备(iPhone/iPad),关掉设备.(长按电源键) 2.关闭Xcode和Instruments 3.重启设备(iPhone/iP ...

  6. 【redis】redis的过期策略

    redis过期策略 在使用redis做缓存的时候,我们常常会设置过期时间.那么redis是如何清理这些过期的数据呢? 答案是: 定期删除 + 惰性删除 定期删除: redis每100ms就会随机抽查删 ...

  7. 四、pymysql模块、索引和慢查询

    目录 一.pymysql模块 (一)如何使用 (二)sql注入问题 二.索引 (一)主键索引 (二)唯一索引 (三)普通索引 (四)联合索引 (五)不会命中索引的情况 (六)explain (七)索引 ...

  8. 三、进程和线程、协程在python中的使用

    三.进程和线程.协程在python中的使用 1.多进程一般使用multiprocessing库,来利用多核CPU,主要是用在CPU密集型的程序上,当然生产者消费者这种也可以使用.多进程的优势就是一个子 ...

  9. 设计模式C++描述----19.命令(Command)模式

    一. 举例说明 我们知道,在多线程程序中,多个用户都给系统发 Read 和 Write 命令.这里有几点需要说明: 1. 首先明确一点,所有的这些 Read 和 Write 命令都是调用一个库函数. ...

  10. Scrapy 之settings配置

    BOT_NAME 默认: 'scrapybot' 当您使用 startproject 命令创建项目时其也被自动赋值. ROBOTSTXT_OBEY = False 是否遵守rebotes.txt协议 ...