Ghidra101再入门(上?)-Ghidra架构介绍

news2025/1/16 17:41:39

Ghidra101再入门(上?)-Ghidra架构介绍

最近有群友问我,说:“用了很多年的IDA,最近想看看Ghidra,这应该怎么进行入门?“这可难到我了。。

我发现,市面上虽然介绍Ghidra怎么用的文章和书籍很多,但是结构化介绍Ghidra本身以及它的架构的文章很少(几乎没有)。对于熟悉IDA的群友,反编译器的使用肯定不存在问题,他们需要的是更加深层的知识。

那我就小写几篇文章(希望不会挖坑不填);这一篇,就从先Ghidra的架构开始说起吧,希望能给已经熟悉或者正在研究静态逆向的朋友们些许帮助。

——————————————————————

这篇文章将讨论下面这些问题,如果有写的不清楚/不对的地方,希望大家告诉我:

  • 为什么要整理这些知识?
  • 什么是反编译?
  • Ghidra反编译器的主要架构是什么样?
  • Ghidra的主要模块有哪些,都是干啥用的?
  • Ghidra这套框架牛逼在哪里?在哪里有很大的缺陷?
  • Ghidra相比于IDA有什么区别/优点/缺点?
  • 为什么Ghidra会设计成这个样子?
  • 其它。。。。?

1、为什么要整理这方面的知识?

学习静态逆向,大家都是从使用IDA/Ghidra这类反编译器的使用开始。然后逐步的深入到各种复杂的场景。

现代静态逆向的工作越来越复杂,对抗越发深入;梳理反编译器架构的知识,学习程序分析技术,在日常工作生活中,一旦反编译器出现什么问题,能够知道如何对反编译器进行修改与优化,多一条解决问题的路子。

可惜,市面上能够供我们学习的反编译框架并不多,反编译软件开发的难度高、工作量大(IDA和Ghidra都已经持续开发了近30年),IDA对外提供的资源又非常的少。幸好Ghidra开源了,给我们提供了一个了解这方面知识的门道。

2、什么是反编译?

所谓"反编译"有广义+狭隘的两层含义;

从广义上,反编译指的是将低级语言、机器语言转换成高级语言一系列技术的集合。技术落地的产品就是"反编译器"。

当然,这篇文章中的反编译更加接地气一点;所谓反编译-~反编译~~反的是什么呢?当然是编译器啊!

所以,从狭隘的角度来看,反编译技术指的是识别编译器优化算法特征,基于程序分析技术编写反向算法,从编译产物中还原被编译器隐藏的高层次抽象。

这种反向算法的集大成者,我们就称为"反编译器"!因此,这篇文章就是参考Ghidra,介绍这一集大成者的主要架构与功能。

如果从正向写代码的角度来看,整个Ghidra对应的应该是IDE(集成开发环境),Ghidra中的反编译器对应的是gcc这类编译器。

3、Ghidra整体架构/主要模块简述

如下图,这是我大概画的Ghidra反编译器主要模块结构。

我画的比较粗糙,一定会有比较多的遗漏的点。先简单介绍一下各个子模块的作用,从上到下,从左到右:

  • Ghidra反编译框架插件:可以在Ghidra的tool目录中找到这些功能,和IDA中的plugin功能类似,Ghidra提供了一套专门的脚本管理页面。
  • Ghidra配套脚本:用于兼容多个平台、headless模式等等的打包后的脚本。
  • Ghidra-Java-Frameword软件主程序:Ghidra的主程序,包含了绝大多数的组件。
  • SoftwareModeling上层二进制抽象:Java代码中对于二进制的抽象,包括了反汇编、中间语言、函数CFG、二进制内存模型等等,逆向工程的主要工作之一,就是从二进制中还原这些上层信息。
  • Ghidra-Decompiler反编译进程:Ghidra的"F5"功能,将中间语言转换为类C的伪代码,他是一个完全独立的模块,与Java主进程通过自定义的”字符流“交互。
  • Ghidra-Decompiler命令行程序:Ghidra的"F5"功能完全独立,因此它可以自己以命令行形式启动并加载二进制,独立的完成反编译的工作。
  • Sleigh-Processor编译器:将Sleigh编译成.sla的支持组件
  • Sleigh-Processor描述模块:Ghidra的核心功能,用于支持x86、arm、mips等数十种指令集架构;GCC、MSVC、GO、Java等多种编译器的"描述模块"。
  • Ghidra开发者套装:包括Sleigh以及Ghidra插件的eclipse-IDE开发支持套件,但是我用IDEA+vscode(喂喂,eclipse都啥时候的老古董了啊)

下面的重心放在三个最重要的组件:Ghidra-Framework主程序、Decompiler反编译器、Sleigh-Processor指令集架构描述配置。

4、Ghidra-Framework软件主程序:

这个组件是Ghidra的主程序,使用Java编写,也就是我们反编译时所使用的Ghidra那一整套界面(Ghidra啥时候找个产品经理和设计师,把你那破UI修一下啊)

主程序的功能十分的复杂,这里只说两个关键模块:

4-1、Analysis分析器:

与IDA不同,Ghidra虽然负责载入可执行文件,但是对于函数识别、反汇编、栈平衡、指令等等解析与处理,都需要分析器完成(IDA会自动的完成这部分工作)。

可以说,Analysis模块除了不负责"F5",其它啥功能都要做;

但对于反编译器,核心的Analysis有那么几个:

  • 匹配查找函数头(function start search):通过编译器的配置以及特定的二进制信息查找函数起始位置,配置文件可以在Processor中的xxxxpattern.xml中找到
  • 递归下降反编译(Symbol/Disassemble/Subroutine):从已知的函数头执行递归下降的反编译,已知的函数头通过匹配查找、符号信息、start函数入口等等方法获取。
  • No-Return函数处理(No-Return Function):通过规则识别并传播标记no-return函数。
  • 数据引用(Reference):反编译过程中对于数据地址、字符串、常量的引用信息

图片描述

4-2、SoftwareModeeling二进制抽象:

这个模块是上层java进程中的二进制抽象,所谓"二进制的抽象"指的是从二进制中还原出高层的信息,包括:二进制内存结构、函数、导入表、导出表、反汇编指令、中间语言、数据与交叉引用等等。

因此反编译器的界面和写代码时的IDE很相似(个人看法)。

5、Sleigh-Processor指令集架构描述

这个模块是Ghidra最牛逼的东西,它继承了反编译技术奠基者Cifuentes, C博士和M. Van Emmerik博士的QUBT框架。

在反编译器开发的过程总,最麻烦的事情之一,就是对不同指令集架构、编译器、ISA的适配;并且这个工作还没有讨巧的办法,就是纯纯的苦力活。

例如,在IDA中如果需要支持一个新的指令集,就必须编写一个叫做processor的模块。而在Ghidra中"不需要写代码"通过这一套描述语言实现了多架构的支持功能。

5-1、binary-rewrite技术简述

在上个世纪末,操作系统、指令集、ISA并没有现在这么统一。
那时候的程序员也遇到了多架构的麻烦:给特定平台开发的程序换个平台就用没法用了,需要重新写。

学术界就提出了"Binary-rewrite"技术,通过一套系统将程序从一个平台转换为另一个平台(例如将C语言编写的程序,转换到JVM上运行)
UQBT框架实现了将二进制转换为中间语言,再将中间语言重编译成其它平台的能力。

 

为了降低从二进制到中间语言的转换成本,UQBT框架提供了一种叫做"通用描述语言Semantic Syntax Language (SSL)"的描述类型语言
通过写对指令集、操作系统、调用约定的描述配置,不用写代码,就可以实现从二进制到中间语言的转换。

5-2、Ghidra-Sleigh描述语言概述

同样,Ghidra这套描述语言的原型来自"Binary-rewrite"技术中的SSL类描述语言。

介绍Sleigh编写与适配指令集的文章已经很多了,这里我也就简单的介绍一下,有需要可以去看Ghidra的官方文档。

总得来说,一套完整的Processor描述应该由这么几部分组成:

  • 语言总描述(.ldefs):描述这是什么架构、大小端、编译器等等
  • ISA描述(.slaspec/.sinc/.pspec):最主要的模块,描述指令集、寄存器、中间语言对应关系等等复杂的情况
  • 编译器/调用约定描述(.cspec):主要是描述不同编译器的调用约定,比如x86就有MSVC、GCC、Go等很多种可能的编译器,不同编译器都有单独的配置。
  • 函数匹配(xxxpattern.xml):二进制形式的函数匹配,一般是用于匹配函数头。

5-3、对比IDA-Processor,Sleigh的优势是什么?

这是个很有意思的问题,我也不是编译领域的专家,如果说的不对麻烦大佬们指教指教。

在最初的binary-rewrite的QUBT论文中,作者提出了"相比于编写Processor代码,使用SSL描述语言工作量能减少一个数量级"的观点。

但是,在我把两边都尝试过之后,发现其实对于不熟悉的新手,两边的学习成本都很高。

IDA需要了解清楚Processor框架中,并且IDA的资料很少。还有一点,IDA缺少很多通用能力,比如,IDA似乎没有提供通用间接跳转处理的代码,恢复switch间接跳转的代码是非常难写的。

而Ghidra-Sleigh等于重新学习了一种语言,不过由于它和指令集架构很像,所以没有那么困难。并且Ghidra的反编译做了很多的工作,开发者不需要考虑别的,只需要”描述清楚“,剩下的交给反编译就行了。

所以,我个人觉得,在Processor编写中并不能减少什么工作量,还是一个体力工作。Sleigh的优势在于他的格式很规范,谁写都一样,因此写问题也很容易修。并且反编译器承担了很多麻烦的工作,我们只需要按部就班的"描述"就好了。

6、Decompiler反编译器

如果对反编译的实现细节有兴趣,可以先看看官方文档,(或者等我下一篇文章,但是不知道什么时候填好坑)

Ghidra提供了和IDA-F5同样的将中间语言"lift"到类C语言伪代码的功能。

但是和IDA的架构不同,Ghidra的反编译器是一个完全独立的进程,并且是一个完全独立的模块。而IDA中反编译模块是集成在主程序中。

并且Ghidra反编译器在设计时就是"架构无关"的,无论是什么指令集(哪怕是VMP),只要写好中间语言p-code的转换功能,Ghidra都能把他翻译成类C的伪代码。你看看隔壁IDA,一个指令集的F5支持都能卖个好几万。

6-1、完全独立反编译器的架构缺陷

完全独立的反编译器/进程,意思是反编译器可以独立编译并运行,上层Java应用通过开启进程并使用字符流与反编译进程进行交互。

但是,这种做法引入了很多的架构问题(我个人看法啊):

  • 进程间通信会有很多额外的开销。
  • 主Java程序和反编译模块
  • 主Java程序和和反编译模块需要同时维护两套"二进制抽象";无法互相复用。
  • 每当反编译需要上层的数据(比如我手动修改了函数签名),只能通过进程间通信获得,这使得类似功能开发十分的麻烦。
  • 主Java功能和native反编译功能同时实现了一些功能类似的分析,无法重用代码。
  • 无法展示反编译结果与汇编/中间语言的对应关系。
  • Ghidra的字符流好奇怪,比较难看懂。

可以说,所有的麻烦,都是因为两边完全独立导致没法共享资源导致的。

6-2、从架构上对比IDA与Ghidra反编译的F5能力

个人感觉,无论是从反编译的效率还是准确率来说,毫无疑问,IDA就是这个领域的"行业标准"

但是如果需要重头支持一套新的指令集,那我一定选择Ghidra,毕竟IDA没有F5给我用。

大家都知道,IDA的反编译中层优化使用了8层转换,每向上一层中间语言就能缩减一部分,从下到上依次为:

  • MMAT_GENERATED:类似于汇编
  • MMAT_PREOPTIMIZED:在basic-block上构造def+use+kill关系
  • MMAT_LOCOPT:在basic-block完成局部优化,构造CFG,处理基于规则的窥孔优化
  • MMAT_CALLS:基于调用约定识别函数调用、参数、返回值
  • MMAT_GLBOPT1:将call的各种block进行合并,全局优化,全局死代码消除
  • MMAT_GLBOPT2:全局优化,全局常量传播与条件优化
  • MMAT_GLBOPT3:全局窥孔优化
  • MMAT_LVARS:局部变量还原

但是Ghidra不同,Ghidra将所有的优化代码全部塞到了coreation.cc一个文件里面。并且使用了类似于“不动点算法”的工作原理,不断的优化直到中间语言无法优化下去位置(不再改变)

我按照个人的经验,将Ghidra的反编译器中端从里到外、从前到后整理,依次为:

  • action-base:通过流敏感技术,将机器码翻译成中间语言,识别函数,处理间接跳转等等。
  • action-stackstall:抽象操作栈信息,恢复栈变量
  • Varnode:还原高层变量信息
  • action-rule:窥孔优化,通过预置的规则对语义进行等价还原
  • typerecovery:还原类型信息
  • struct: 还原控制流信息

Ghidra这种不断循环的算法,存在很大的性能问题,对于超大的函数往往会反编译超时。并且这种将优化代码全部塞到coreation.cc的做法。。。使得代码层次十分混乱。

这一篇文章就到这吧,后面是反编译器设计的内容了,再写下去太长了,留到以后的文章吧。

7、之后的内容?

如果有时间填坑?

后面将会详细说说Ghidra-decompiler反编译器F5组件的实现细节以及架构。

以及当反编译器出现问题的时候(比如花指令等混淆),如何进行修复。

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1081291.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ASEMI整流桥GBU810参数,GBU810封装

编辑-Z GBU810参数描述: 型号:GBU810 最大直流反向电压VR:1000V 最大工作峰值反向电压VRWM:700V 最大平均正向电流IF:8A 非重复正向浪涌电流IFSM:200A 操作和储存温度范围TJ ,TSTG:-55 t…

Ubuntu18.04下载安装基于使用QT的pcl1.13+vtk8.2,以及卸载

一、QVTKWidget、QVTKWidget2、QVTKOpenGLWidget、QVTKOpenGLNativeWidget 区别 1.Qt版本 Qt5.4以前版本:QVTKWidget2/QVTKWidget。 Qt5.4以后版本:QVTKOpenGLWidget/QVTKOpenGLWidget。 2.VTK版本(Qt版本为5.4之后) 在VTK8.2以前的版本:QVT…

企业如何使用CRM客户管理系统全面了解客户

B2B业务由于决策链长,涉及的部门和人员多,购买周期短则2、3个月,长则一年半载的原因一直被大家痛呼难做。B2B业务要求企业去认识客户,更要深入地了解客户。基于这种需求,使用CRM客户管理系统是企业全面了解客户的重要手…

C++入门之命名空间详解

一、为什么要使用命名空间 命名空间的功能就是区分不同的代码段,避免使用不同代码时带来变量名冲突的问题。 在写C语言代码时,常常回面临命名冲突的问题。例如: 可以成功运行。 但是如果要使用 time.h 头文件时,就会与库发生冲突…

C++primer 第二章 变量和基本类型

昨天思考了一下,感觉明白了。于是报名了软考,还有挑战z杯,想着四级还要不要报,毕竟我也不是有天赋的人,就只能努力去做个努力的人。加油!!! 不知道未来怎么样,那就走好现在吧!!&…

Tableau:商业智能(BI)工具

Tableau入门 1、Tableau概述2、Tableau DesktopTableau保存文件类型和文件夹 1、Tableau概述 Tableau 成立于 2003 年,Tableau于2019年被 Salesforce 收购,是斯坦福大学一个计算机科学项目的成果,该项目旨在改善分析流程并让人们能够通过可视…

重新定义公共厕所,智慧公厕最新解决方案与推广路径

随着科技的进步,现代城市管理的智慧化解决方案在不断挑战传统的管理方式,而在智慧城市领域有一个热点的物联网应用解决方案——智慧公厕。智慧公厕不仅仅是公共厕所的升级版,它也是城市文明,高效,环保和科技的体现。本…

echarts实现圆柱体 渐变柱体

const weatherIcons [ { lable: ‘寿险’, id: 2, img: require(/assets/images/customerModule/title-action.png) }, { lable: ‘重疾’, id: 3, img: require(/assets/images/customerModule/title-action.png) }, { lable: ‘医疗’, id: 4, img: require(/assets/images/…

区块链跨链技术

区块链跨链技术 背景 近年来,随着区块链技术的不断发展,区块链的应用场景逐渐从最初的加密货币领域扩展到金融、物流、医疗、公共服务等各个领域。随着区块链的应用场景不断增多,区块链的“数据孤岛”问题日益突出,不同场景下的…

yolov8剪枝实践

本文使用的剪枝库是torch-pruning ,实验了该库的三个剪枝算法GroupNormPruner、BNScalePruner和GrowingRegPruner。 安装使用 安装依赖库 pip install torch-pruning 把 https://github.com/VainF/Torch-Pruning/blob/master/examples/yolov8/yolov8_pruning.py&…

Mac系统清理工具BuhoCleaner

BuhoCleaner是一款在Mac电脑上运行的清洁软件。它的界面简洁,易于使用,能够快速扫描Mac电脑上的垃圾文件、重复文件、大型文件等,帮助用户清理不需要的文件,释放磁盘空间。 该软件的主要功能包括: 垃圾文件清理&…

哈希桶封装unordered set和map

目录 进一步实现哈希桶 引入 keyofValue 迭代器 insert返回值 operator[ ] key不能修改 模拟实现 keyofValue 代码 迭代器 谁在前 普通迭代器转换为const迭代器 const *this 问题 代码 insert和erase const迭代器转换为普通迭代器 key不能修改 完整版代码 …

前端axios发送请求,在请求头添加参数

1.在封装接口传参时,定义形参,params是正常传参,name则是我想要在请求头传参 export function getCurlList (params, name) {return request({url: ********,method: get,params,name}) } 2.接口调用 const res await getCurlList(params,…

电力智能运维管理平台:提升电力行业运营效率与安全

随着电力行业的不断发展,电力系统的运维管理逐渐成为关注的焦点。如何在保证供电稳定的同时,提高运营效率,降低运营成本,是电力行业面临的挑战。电力智能运维管理平台,正是在这一背景下应运而生的一种解决方案。 力…

insightface的预训练权重buffalo_sc.zip下载

想要下载 https://github.com/deepinsight/insightface里的权重找了半天,网络时而卡掉,所以分享 一下终于下载好了,存在百度网盘里,分享给大家。 链接:https://pan.baidu.com/s/1PKp3pPzFg8hrbqACUfHO2A?pwdamtf 提…

数据转换为excel模板下载

一、引入依赖 <dependency><groupId>org.jxls</groupId><artifactId>jxls-poi</artifactId><version>2.12.0</version></dependency> 二、准备解析的数据封装 package com.dst.modules.business.after.sale.parts.sparepa…

Java学习复杂的对象数组操作

Java学习复杂的对象数组操作 定义一个长度为3的数组&#xff0c;数组存储1~3名学生对象作为初始数据&#xff0c;学生对象的学号&#xff0c;姓名各不相同。 学生的属性&#xff1a;学号&#xff0c;姓名&#xff0c;年龄。 要求1&#xff1a;再次添加一个学生对象&#xff0…

什么是React的虚拟DOM(Virtual DOM)?它的作用是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

nginx服务---2

如何统计连接数&#xff0c;以及根据域名配置虚拟主机 cd /usr/local/nginx/conf vim nginx.conf server {listen 80;server_name www.abc.com;charset utf-8;access_log logs/www.abc.com;error_log logs/www.abc.error.log;location / {root /var/www/html/zzr;in…

Pytorvh之Vision Transformer图像分类

文章目录 前言一、Transformer1.Transformer概览2.Self-Attention3.Multi-head Attention4.Position-wise Feed-Forward Networks(位置前馈网络)5.残差连接和层归一化6.Positional Encodings(位置编码) 二、Vision Transformer1.Vision Transformer概览2.Embedding层结构&#…