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 許可協議。轉載請註明出處!
文章出處:https://www.cnblogs.com/LyShark/p/17744285.html
本博客所有文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!