10.4 认识Capstone反汇编引擎

news2025/1/21 9:29:02

Capstone 是一款开源的反汇编框架,目前该引擎支持的CPU架构包括x86、x64、ARM、MIPS、POWERPC、SPARC等,Capstone 的特点是快速、轻量级、易于使用,它可以良好地处理各种类型的指令,支持将指令转换成AT&T汇编语法或Intel汇编语法等多种格式。Capstone的库可以集成到许多不同的应用程序和工具中,因此被广泛应用于反汇编、逆向工程、漏洞分析和入侵检测等领域,著名的比如IDA Pro、Ghidra、Hopper Disassembler等调试器都在使用该引擎。

  • 官方网站:http://www.capstone-engine.org/

读者可自行下载符合条件的版本,这里笔者选择的是capstone-4.0.2-win32版本,下载并解压这个版本,当读者解压后以后即可在项目中引用该引擎,Capstone引擎的配置非常容易,仅仅需要配置引用目录及库目录即可,配置完成如下图所示;

实现反汇编的第一步则是打开一个可执行文件,通常在引擎内可调用cs_open()函数实现打开,当打开成功时则该函数会返回一个句柄(handle)用来进行后续的反汇编操作,函数的原型通常如下:

cs_err cs_open
(
    cs_arch arch, 
    cs_mode mode, 
    csh *handle
)

其中,各参数的含义如下:

  • arch:指定要打开的CPU架构,例如CS_ARCH_X86表示x86架构,CS_ARCH_ARM表示ARM架构等。
  • mode:指定CPU的模式,例如CS_MODE_32表示32位模式,CS_MODE_64表示64位模式等。
  • handle:一个指针,用于返回打开成功后的句柄handle。

如上所示,函数返回值为cs_err类型,表示函数执行的状态或错误码,它是一个枚举类型,当函数执行成功时返回的数值为CS_ERR_OK,其次函数的第一个参数是指定CPU架构为x86,第二个参数是指定模式为32位模式,最后一个参数用来返回(handle)句柄。

当一个进程被打开后,则下一步可以通过调用cs_disasm()函数来实现对打开文件的反汇编,cs_disasm函数是Capstone反汇编框架中的一个函数,用于对指定的二进制数据进行反汇编,返回解码后的指令信息。函数原型通常如下:

size_t cs_disasm
(
    csh handle,
    const uint8_t *code,
    size_t code_size,
    uint64_t address,
    size_t count,
    cs_insn *insn
);

其中,各参数的含义如下:

  • handle:反汇编器句柄,表示使用哪一个Capstone实例来执行反汇编操作。
  • code:待反汇编的二进制数据的指针,可以是一个地址。
  • code_size:待反汇编的数据的长度,以字节为单位。
  • address:指定待反汇编数据的地址,通常为起始地址。
  • count:指定要反汇编的指令数,如果为0,则会一直反汇编至遇到code_size终止。
  • insn:指向用于保存反汇编结果的cs_insn结构体对象指针,在函数调用结束后存储反汇编结果。

函数返回值为size_t类型,代表解码的指令数量。在cs_disasm()函数中,我们通过将待反汇编的数据以及其它必要的参数传递给该函数,然后使用cs_insn结构体对象来存储反汇编结果。通过该函数,我们可以获取指令的指令助记符、指令操作数、寻址模式、使用的寄存器等信息。

当读者理解了这两个API接口后,那么反汇编实现将变得很容易实现,我们来看一下官方针对反汇编实现的一种方式,我们自行封装一个DisassembleCode()函数,该函数传入机器码字符串以及该字符串的长度则会输出该字符串的反汇编代码片段,这段代码的实现如下所示;

#include <stdio.h>
#include <inttypes.h>
#include <capstone/capstone.h>

#pragma comment(lib,"capstone32.lib")

// 反汇编字符串
void DisassembleCode(char *start_offset, int size)
{
    csh handle;
    cs_insn *insn;
    size_t count;

    char *buffer = "\x55\x8b\xec\x81\xec\x24\x03\x00\x00\x6a\x17\x90\x90\x90";

    // 打开句柄
    if (cs_open(CS_ARCH_X86, CS_MODE_32, &handle) != CS_ERR_OK)
    {
        return;
    }

    // 反汇编代码,地址从0x1000开始,返回总条数
    count = cs_disasm(handle, (unsigned char *)start_offset, size, 0x1000, 0, &insn);

    if (count > 0)
    {
        size_t index;
        for (index = 0; index < count; index++)
        {
            for (int x = 0; x < insn[index].size; x++)
            {
                printf("机器码: %d -> %02X \n", x, insn[index].bytes[x]);
            }

            printf("地址: 0x%"PRIx64" | 长度: %d 反汇编: %s %s \n", insn[index].address, insn[index].size, insn[index].mnemonic, insn[index].op_str);
        }

        cs_free(insn, count);
    }
    else
    {
        printf("反汇编返回长度为空 \n");
    }

    cs_close(&handle);
}

int main(int argc, char *argv[])
{
    char *buffer = "\x55\x8b\xec\x81\xec\x24\x03\x00\x00\x6a\x17\x90\x90\x90";
    DisassembleCode(buffer, 14);

    system("pause");
    return 0;
}

运行上述代码片段,则可看到如下图所示的输出效果;

上述代码虽然实现了反汇编但并无法保存结果,对于一个通用程序来说,我们当然是希望这写反汇编代码能够存储到一个特殊的容器内,当需要使用是可以随时调出来,此处我们通过定义一个MyStruct并将所需反汇编指令通过ptr.push_back(location)放入到一个全局容器内进行存储,当读者调用DisassembleCode(buffer, 14)函数是则会返回std::vector<MyStruct> ptr,并在主函数内通过循环输出这个容器,改进后的代码将会更加易于使用;

#include <iostream>
#include <vector>
#include <inttypes.h>
#include <capstone/capstone.h>

#pragma comment(lib,"capstone32.lib")

using namespace std;

typedef struct
{
    int OpCodeSize;
    int OpStringSize;
    unsigned long long Address;
    unsigned char OpCode[16];
    char OpString[256];
}MyStruct;

static void print_string_hex(unsigned char *str, size_t len)
{
    unsigned char *c;
    for (c = str; c < str + len; c++)
    {
        printf("0x%02x ", *c & 0xff);
    }
    printf("\n");
}

// 反汇编字符串
std::vector<MyStruct> DisassembleCode(char *start_offset, int size)
{
    std::vector<MyStruct> ptr = {};

    csh handle;
    cs_insn *insn;
    size_t count;

    // 打开句柄
    if (cs_open(CS_ARCH_X86, CS_MODE_32, &handle) != CS_ERR_OK)
    {
        return{};
    }

    // 反汇编代码,地址从0x1000开始,返回总条数
    count = cs_disasm(handle, (unsigned char *)start_offset, size, 0x0, 0, &insn);

    if (count > 0)
    {
        size_t index;

        // 循环反汇编代码
        for (index = 0; index < count; index++)
        {
            // 清空
            MyStruct location;
            memset(&location, 0, sizeof(MyStruct));

            // 循环拷贝机器码
            for (int x = 0; x < insn[index].size; x++)
            {
                location.OpCode[x] = insn[index].bytes[x];
            }

            // 拷贝地址长度
            location.Address = insn[index].address;
            location.OpCodeSize = insn[index].size;

            // 拷贝反汇编指令
            strcpy_s(location.OpString, insn[index].mnemonic);
            strcat_s(location.OpString, " ");
            strcat_s(location.OpString, insn[index].op_str);

            // 得到反汇编长度
            location.OpStringSize = strlen(location.OpString);

            ptr.push_back(location);
        }
        cs_free(insn, count);
    }
    else
    {
        return{};
    }
    cs_close(&handle);
    return ptr;
}

int main(int argc, char *argv[])
{
    char *buffer = "\x55\x8b\xec\x81\xec\x24\x03\x00\x00\x6a\x17\x90\x90\x90";

    // 反汇编并返回容器
    std::vector<MyStruct> ptr = DisassembleCode(buffer, 14);

    // 循环整个容器
    for (int x = 0; x < ptr.size(); x++)
    {
        // 输出地址
        printf("%08X | ", ptr[x].Address);
        printf("%03d | ", ptr[x].OpStringSize);

        // 输出反汇编
        for (int z = 0; z < ptr[x].OpStringSize; z++)
        {
            printf("%c", ptr[x].OpString[z]);
        }
        printf("\n");

        // 输出机器码
        for (int y = 0; y < ptr[x].OpCodeSize; y++)
        {
            printf("%02X ", ptr[x].OpCode[y]);
        }

        printf("\n");
        // print_string_hex(ptr[x].OpCode, ptr[x].OpCodeSize);
    }

    system("pause");
    return 0;
}

运行后输出效果图如下图所示;

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/b277703.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

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

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

相关文章

C++:模板进阶与继承

模板进阶与继承 模板进阶1.非类型的模板参数2.模板的特化2.1特化的概念2.2函数模板特化2.3类模板特化2.4全特化和偏特化2.4.1全特化2.4.2偏特化 3.模板的分离编译3.1同文件分离3.2不同文件下分离 继承1.继承的概念和定义1.1继承的概念1.2继承的定义1.2.1定义格式1.2.2继承关系和…

哈希应用之位图

文章目录 1.位图概念2.面试题引入3.代码解决[配注释]4.位图应用4.1找到100亿个整数里只出现一次的整数4.2找两个分别有100亿个整数的文件的交集[只有1G内存]1.法一[使用于数据量<42亿]2.法二[适用于数据量大>42亿]3.在一个有100亿个int的文件中找到出现次数不超过2次的所…

自动驾驶技术的基础知识

自动驾驶技术是现代汽车工业中的一项革命性发展&#xff0c;它正在改变着我们对交通和出行的理解。本文将介绍自动驾驶技术的基础知识&#xff0c;包括其概念、历史发展、分类以及关键技术要素。 1. 自动驾驶概念 自动驾驶是一种先进的交通技术&#xff0c;它允许汽车在没有人…

字符集、编码格式的理解

计算机中只能存储二进制01&#xff0c; 要想存储字符&#xff0c;就要有一个字符与编码的映射关系&#xff0c;这个关系就是字符集。 字符集就是字符与编码的映射关系* 字符集的发展历程&#xff1a; 因为计算机是欧美先发明的&#xff0c;他们的语言就26个字母&#xff0c;所…

MyBatis-plus使用

1 基础介绍 MyBatis-Plus (opens new window)&#xff08;简称 MP&#xff09;是一个 MyBatis (opens new window)的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 它已经封装好了一些crud方法&#xff0c;我们不需要再写…

速学数据结构 | 手把手教你会单链表的构建方式

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《初阶数据结构》《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 &#x1f4cb; 前言1. 什么是链表1.1 链表的物理结构1.2 链表的种类 2. 链表的实现一. SList.h 单链表的声明3.…

10. 激光雷达到车身坐标系外参的标定方法(lidar2car)

目录 0. 论文及代码1. 标定原理2. 拟合平面3. 标定roll/pitch/height4. 标定yaw4.1 理解从B_spline拟合的轨迹中得到vehicle航向 5. 精度 0. 论文及代码 参考论文&#xff1a;SensorX2car: Sensors-to-car calibration for autonomous driving in road scenarios 参考代码&…

kafka初体验基础认知部署

kafka 基础介绍 Apache Kafka是一个分布式流处理平台&#xff0c;最初由LinkedIn开发并于2011年开源。它主要用于解决大规模数据的实时流式处理和数据管道问题。 Kafka是一个分布式的发布-订阅消息系统&#xff0c;可以快速地处理高吞吐量的数据流&#xff0c;并将数据实时地分…

医院PACS系统源码 PACS系统源码

医用软件中的影像归档与传输系统软件&#xff08;Picture Archiving and Communication System&#xff0c;简称PACS&#xff09;是一种用于存储、管理和传输医学影像数据的系统。其主要功能包括&#xff1a; 影像存储&#xff1a;PACS可以将医学影像数据以数字化的形式存储在服…

YoloV8训练自己的模型 Pycharm Remote Development

参考视频&#xff1a;https://www.youtube.com/watch?vm9fH9OWn8YM YOLO官方网站&#xff1a;GitHub - ultralytics/ultralytics: NEW - YOLOv8 &#x1f680; in PyTorch > ONNX > OpenVINO > CoreML > TFLite 在本地的pycharm上面建立一个项目 使用scp把代码传…

星球作业(第十一期)Android中Binder简述

Binder 什么是binder&#xff1f;简述下它的工作过程和使用场景。 什么是Binder&#xff1f; Binder是Android中的一个类&#xff0c;实现了IBinder接口&#xff1b; 从IPC的角度来说&#xff0c;Binder是Android中的一种通讯方式&#xff1b; 从Android Framework角度来说&a…

一文搞清楚Java中常见的IO模型

什么是IO 首先&#xff0c;我们要清楚什么是IO&#xff0c;根据冯诺依曼结构&#xff0c;计算机结构分为5部分&#xff1a;运算器、控制器、存储器、输入设备和输出设备。 输入设备和输出设备都属于外设&#xff0c;网卡、硬盘这种既可以属于输入设备也可以属于输出设备。 输入…

深度学习-卷积神经网络-ResNET

文章目录 前言1.resnet2.作者3.精度&#xff08;TOP-5&#xff09;4.论文一览5.竞赛排名6.网络退化7.残差8.残差 1.作者 前言 本文来自B站&#xff1a; ResNet深度残差网络 1.resnet 2.作者 3.精度&#xff08;TOP-5&#xff09; 4.论文一览 5.竞赛排名 6.网络退化 ResNet解…

拆解常见的6类爆款标题写作技巧!

究竟是先写好文章再拟标题还是先确定标题再写文章呢&#xff1f;很多写稿小白都会有这样的疑惑。 在“人人皆可新媒体”的时代&#xff0c;公众号推文类型琳琅满目&#xff0c;每个人都可以建立自己的公众号&#xff0c;写出自己想写的文章。 但怎样起标题、起什么样的标题&a…

MyCat安装文档

JDK安装 JDK具体安装步骤如下&#xff1a; 1. 上传安装包 使用FinalShell自带的上传工具将jdk的二进制发布包上传到Linux 由于上述在进行文件上传时&#xff0c;选择的上传目录为根目录 /&#xff0c;上传完毕后&#xff0c;我们执行指令 cd / 切换到根目录下&#xff0c;查…

STM32 10个工程篇:1.IAP远程升级(六)

在IAP远程升级的最后一篇博客里&#xff0c;笔者想概括性地梳理总结IAP程序设计中值得注意的问题&#xff0c;诚然市面上或者工作后存在不同版本的IAP下位机和上位机软件&#xff0c;也存在不同定义的报文格式&#xff0c;甚至对于相似的知识点不同教程又有着完全不同的解读&am…

You Know What is C++嵌套类

C嵌套类 一、嵌套类1.嵌套类和访问权限2.作用域3.访问控制 一、嵌套类 在一个类的内部定义另一个类&#xff0c;我们称之为嵌套类&#xff0c;或者嵌套类型。引入嵌套类&#xff0c;因为外围类需要使用嵌套类对象作为底层实现&#xff0c;并且该嵌套类只用于外围类的实现&…

黑马JVM总结(二十七)

&#xff08;1&#xff09;synchronized代码块 synchronized代码块的底层原理&#xff0c;它是给一个对象进行一个加锁操作&#xff0c;它是如何保证如果你出现了synchronized代码块中出现了问题&#xff0c;它需要给这个对象有一个正确的解锁操作呢&#xff0c;加锁解锁是成对…

为什么网络安全明明缺口很大,却看起来招聘很少呢?

2023 年我国网络空间安全人才数量缺口超过了 140 万&#xff0c;就业人数却只有 10 多万&#xff0c;缺口高达了 93%。这里就有人会问了&#xff1a; 1、网络安全行业为什么这么缺人&#xff1f; 2、明明人才那么稀缺&#xff0c;为什么招聘时招安全的人员却没有那么多呢&…