10.4 認識Capstone反彙編引擎

来源:https://www.cnblogs.com/LyShark/archive/2023/10/06/17744285.html
-Advertisement-
Play Games

Capstone 是一款開源的反彙編框架,目前該引擎支持的CPU架構包括x86、x64、ARM、MIPS、POWERPC、SPARC等,Capstone 的特點是快速、輕量級、易於使用,它可以良好地處理各種類型的指令,支持將指令轉換成AT&T彙編語法或Intel彙編語法等多種格式。Capstone的... ...


Capstone 是一款開源的反彙編框架,目前該引擎支持的CPU架構包括x86、x64、ARM、MIPS、POWERPC、SPARC等,Capstone 的特點是快速、輕量級、易於使用,它可以良好地處理各種類型的指令,支持將指令轉換成AT&T彙編語法或Intel彙編語法等多種格式。Capstone的庫可以集成到許多不同的應用程式和工具中,因此被廣泛應用於反彙編、逆向工程、漏洞分析和入侵檢測等領域,著名的比如IDA Pro、Ghidra、Hopper Disassembler等調試器都在使用該引擎。

讀者可自行下載符合條件的版本,這裡筆者選擇的是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 許可協議。轉載請註明出處!

文章作者:lyshark (王瑞)
文章出處:https://www.cnblogs.com/LyShark/p/17744285.html
本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在上圖中我們想在點擊bi-pen的時候獲取到td綁定的id,常用 這是一種常見的方式來訪問一個元素的祖父節點。這種寫法在簡單的情況下是有效的,但在某些情況下可能不夠靈活或可維護。所以我們考慮使用 closest 方法: Element.closest() 方法允許你查找最接近當前元素的祖先元素,滿足 ...
  • 原理比較簡單就不上圖片了 你也許聽說過在行元素中使用vertical-align: middle; 可以實現居中對其,但實際使用上,常常沒有作用。 其實行元素有四條線分別是: 頂線 中線 基線 底線 預設行元素是基線對齊的(兩個元素的基線在同一高度) 下麵代碼相當於圖片的基線和文字的中線對齊(圖片的 ...
  • 10 月 5 日 - 6 日,ViteConf 2023 線上舉行,Vue 和 Vite 的創建者尤雨溪發表了題為《The State of Vite》 的演講,他分享了 Vite 的現狀與未來展望,本文就來看一看 Vite 現在怎麼樣了,以及未來的路將怎麼走! Vite 版本發佈情況 首先,來快速 ...
  • 元組用於在單個變數中存儲多個項目。 mytuple = ("apple", "banana", "cherry") 元組是 Python 中的 4 種內置數據類型之一,用於存儲數據集合,另外還有列表、集合和字典,它們都具有不同的特性和用途。元組是有序且不可更改的集合。元組使用圓括弧表示。 示例,創建 ...
  • 二:Java 基礎知識 一、標識符和關鍵字 1. 標識符 1. 在java語言中,用來標誌類名、對象名、變數名、方法名、類型名、數組名、包名的有效字元序列,稱為“標識符”; 2. 標識符由字母、數字、下劃線、美元符號組成,且第一個字元不能是數字; 3. java語言區分大小寫; 4. 標誌符命名規則 ...
  • (1)前言:總結三次題目集的知識點、題量、難度等情況 第一次的作業基本就是熟悉一下java基本的語法,這部分Java就算需要自學也並不算難,並且本身就有了一些c語言的基礎,作業的問題主要是我們對於輸入輸出和判斷迴圈語句的使用,總體的難度很低。第二次的作業就是類的使用以及私有化和相關封裝。第三次是是除 ...
  • Dart語言是純面向對象的編程語言,就算是函數(對象的成員函數一般稱為方法)也是對象,它也有類型,那麼函數也可以作為其他函數的參數,或者賦值給其他變數。除此之外,Dart中的函數還有什麼特別之處、它有什麼規則和約束…… ...
  • 相信小伙伴們在日常的開發中,調試代碼時,免不了經常修改代碼,這個時候,為了驗證效果,必須要重啟 Spring Boot 應用。 頻繁地重啟應用,導致開發效率降低,加班隨之而來。有沒有什麼辦法,能讓 Spring Boot 項目熱部署呢,從而不用每次都手點。答案是肯定的。 第一步:添加 spring- ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...