-
-
[原创] IDA遇到浮点数计算时F5解析失败,手动复现计算过程
-
发表于: 2020-7-16 11:33 4715
-
问题背景也是写这篇文章的原因,起因于我之前在论坛发的贴子:求助:大量浮点数运算IDA的F5操作无法解析
总结下贴子中内容,也就是说,IDA在遇到大量浮点数运算时,按Fn+F5解析为伪代码时,是不会把这些运算给解析出来的。而Hopper则会解析为:在伪代码中保留汇编运算。
对于需要完整复现一套算法的逆向工程师来说,这种无法解析的情况是必须迈过去的一道坎,也是一个追求卓越的逆向工程师的必经之路。
参考贴子中大佬给的建议,以下是我个人的解决方法,也算是抛砖引玉,还望各位不吝赐教,欢迎提出更好的解决方法或者改进建议(或许可以编写一套自己的汇编解析方法,来自动生成伪代码)。
我们知道在arm64架构中,存在着x0-x28的通用寄存器,以及v0-v31的浮点型寄存器(如下图,图片来自Xcode的调试信息窗口/iPhone真机调试)。
而大量的浮点数运算,就是靠这些浮点型寄存器完成的。也就是上图中的Floating Point Registers。
正如函数传递通用参数(非浮点数)的调用约定是按x0-x7的顺序来的,传递浮点数时,则是按照v0-v7的顺序来的。因此,首先我们可以根据这个参数传递规则来确认输入的参数。
接着,就是本文的核心了,如何复现函数中的浮点数计算过程。
浮点型寄存器的结构其实很简单,从v0到v31,每一个浮点型寄存器都是16个字节组成的,形象的理解下,就是4个float数,即一个浮点型寄存器=4个float数。
因此,在复现的代码中,我们可以用float数组来模拟浮点型寄存器,比如v0浮点型寄存器,就可以用如下数组定义来表示
其余以此类推。
我们以一条非常简单的指令为例,来看看浮点数运算是怎么被复现的:
即S1 = S3 - S4,这里的S代表什么呢?我们可以从《ios应用逆向与安全》这本书中找到答案,第230页有如下说明:
128位寄存器,Q0-Q31
64位寄存器,D0-D31
32位寄存器,S0-S31
...
或者更直接的,通过动态调试,在调试信息窗口查看浮点型寄存器来进行确认。这样我们就知道了:上面那个S,就是代表1个float,并且是浮点型寄存器的第1个float。因此可复现为以下代码:
如法炮制,对于以下指令:
则可复现为:
当然,除了以上这些非常简单的运算指令,我们还会遇到其他的arm64汇编指令,这种情况我们就可以直接在armDeveloper这个网站搜索指令,了解意思后再进行复现即可。
虽然上面的网站可以直接搜索指令,但国内网络体验并不好,因此也可以参考《ios应用逆向与安全》书中第229页的方法,在arm InfoCenter网站下载官方手册。不过目前官网也给出了以下说明(技术内容被移动到了上面那个体验并不好的armDeveloper网站= =),好在手册还能下载:
一句话总结:浮点数计算由浮点型寄存器完成,因此理解浮点型寄存器的组成结构,用float数组复现对应计算就行!
最后一点建议,arm64汇编并不难,就是对一堆寄存器的简单操作:把内存中的数据载入寄存器->计算->存入内存。正是因为其操作过于简单,也导致了汇编的单调乏味和容易出错的特性,也是后来C、C++等高等语言出现的契机。对于我们搞逆向的来说,耐心点、细心点就问题不大哈哈。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)