Capstone反汇编引擎数据类型及API分析及示例(二)

Author Avatar
kabeor 7月 21, 2019

Capstone反汇编引擎数据类型及API分析及示例(二)

本文由本人首发于先知社区 https://xz.aliyun.com/t/5761

上篇分析了Capstone开放的数据类型,下面就来正式看看API吧
官方开放的API只有二十个左右,但为了能写的更易懂,我将结合实例,分多篇写。
API中作者将capstone缩写为cs,下面我也用这种方式描述

API分析

cs_malloc_t

void* (CAPSTONE_API *cs_malloc_t)(size_t size);

cs的动态内存分配,用于

struct cs_opt_mem {
cs_malloc_t malloc;
cs_calloc_t calloc;
cs_realloc_t realloc;
cs_free_t free;
cs_vsnprintf_t vsnprintf;
} cs_opt_mem;

cs_malloc_t定义于capstone.lib和capstone.dll的cs.c中,

在用户模式下,cs_mem_malloc默认使用系统malloc

Windows driver模式下,cs_malloc_t cs_mem_malloc = cs_winkernel_malloc;
cs_winkernel_malloc定义于\capstone-4.0.1\windows\winkernel_mm.c,

实现代码

void * CAPSTONE_API cs_winkernel_malloc(size_t size)
{
// 长度不能分配为0
NT_ASSERT(size);

// FP; NonPagedPool用于支持 Windows 7
#pragma prefast(suppress : 30030) // 分配可执行的POOL_TYPE内存
size_t number_of_bytes = 0;
CS_WINKERNEL_MEMBLOCK *block = NULL;
// 特定的值能造成溢出
// 如果value中的和超出或低于类型容量,函数将返回NULL。
if (!NT_SUCCESS(RtlSizeTAdd(size, sizeof(CS_WINKERNEL_MEMBLOCK), &number_of_bytes))) {
return NULL;
}
block = (CS_WINKERNEL_MEMBLOCK *)ExAllocatePoolWithTag(
NonPagedPool, number_of_bytes, CS_WINKERNEL_POOL_TAG);
if (!block) {
return NULL;
}
block->size = size;

return block->data;
}

OSX kernel模式下,cs_malloc_t cs_mem_malloc = kern_os_malloc;,这里暂且不探讨。

cs_calloc_t

void* (CAPSTONE_API *cs_calloc_t)(size_t nmemb, size_t size);

cs申请内存并初始化
用于struct cs_opt_mem,定义于cs.c
用户模式: cs_calloc_t cs_mem_calloc = calloc;,使用系统calloc
Windows driver模式: cs_calloc_t cs_mem_calloc = cs_winkernel_calloc;
实现代码

void * CAPSTONE_API cs_winkernel_calloc(size_t n, size_t size)
{
size_t total = n * size;

void *new_ptr = cs_winkernel_malloc(total);
if (!new_ptr) {
return NULL;
}

return RtlFillMemory(new_ptr, total, 0);
}

OSX kernel模式: cs_calloc_t cs_mem_calloc = cs_kern_os_calloc;

直接调用kern_os_malloc了

cs_realloc_t

void* (CAPSTONE_API *cs_realloc_t)(void *ptr, size_t size);

cs重新分配内存
用于struct cs_opt_mem,定义于cs.c
用户模式: cs_realloc_t cs_mem_realloc = realloc;,调用系统realloc
Windows driver模式: cs_realloc_t cs_mem_realloc = cs_winkernel_realloc;

实现代码,可以看出是利用cs_winkernel_malloc重新申请

void * CAPSTONE_API cs_winkernel_realloc(void *ptr, size_t size)
{
void *new_ptr = NULL;
size_t current_size = 0;
size_t smaller_size = 0;

if (!ptr) {
return cs_winkernel_malloc(size);
}

new_ptr = cs_winkernel_malloc(size);
if (!new_ptr) {
return NULL;
}

current_size = CONTAINING_RECORD(ptr, CS_WINKERNEL_MEMBLOCK, data)->size;
smaller_size = (current_size < size) ? current_size : size;
RtlCopyMemory(new_ptr, ptr, smaller_size);
cs_winkernel_free(ptr);

return new_ptr;
}

OSX kernel模式: cs_realloc_t cs_mem_realloc = kern_os_realloc;

cs_free_t

typedef void (CAPSTONE_API *cs_free_t)(void *ptr);

cs释放内存
用于struct cs_opt_mem,定义于cs.c
用户模式: cs_free_t cs_mem_free = free;,调用系统free
Windows driver模式: cs_free_t cs_mem_free = cs_winkernel_free;

实现代码

void CAPSTONE_API cs_winkernel_free(void *ptr)
{
if (ptr) {
ExFreePoolWithTag(CONTAINING_RECORD(ptr, CS_WINKERNEL_MEMBLOCK, data), CS_WINKERNEL_POOL_TAG);
}
}

OSX kernel模式: cs_free_t cs_mem_free = kern_os_free;

cs_vsnprintf_t

int (CAPSTONE_API *cs_vsnprintf_t)(char *str, size_t size, const char *format, va_list ap);

按size大小输出到字符串str中

用户模式:

值得注意的是,如果系统为wince,将使用_vsnprintf函数
vsnprintf ()和_vsnprintf()对于驱动程序都是可用的,但是它们有一些不同。
在需要返回值和设置空终止符时应使用vsnprintf()

vsnprintf定义在stdio.h

Windows driver模式: cs_vsnprintf_t cs_vsnprintf = cs_winkernel_vsnprintf;

代码实现

int CAPSTONE_API cs_winkernel_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
{
int result = _vsnprintf(buffer, count, format, argptr);

// _vsnprintf()在字符串被截断时返回-1,在整个字符串被存储但“buffer”末尾没有“\0”时返回“count”。在这两种情况下,都需要手动添加空终止符。
if (result == -1 || (size_t)result == count) {
buffer[count - 1] = '\0';
}

if (result == -1) {
// 在返回-1时,函数必须获取并返回一些本来要写入的字符。因此,通过重试使用temp buffer进行相同的转换,这个缓冲区就可能足够大来完成格式化,并且获得很多本应写入的字符。
char* tmp = cs_winkernel_malloc(0x1000);
if (!tmp) {
return result;
}

result = _vsnprintf(tmp, 0x1000, format, argptr);
NT_ASSERT(result != -1);
cs_winkernel_free(tmp);
}

return result;
}

OSX kernel模式: cs_vsnprintf_t cs_vsnprintf = vsnprintf;,使用默认vsnprintf

cs_skipdata_cb_t

size_t (CAPSTONE_API cs_skipdata_cb_t)(const uint8_t code, size_t code_size, size_t offset, void *user_data);

SKIPDATA选项的用户自定义回调函数。

code:包含要分解的代码的输入缓冲区。和传递给cs_disasm()的缓冲区相同。
code_size:上面的code缓冲区的大小(以字节为单位)。
offset:上面提到的输入缓冲区code中当前检查字节的位置。
user_data:用户数据通过cs_opt_skipdata结构中的@user_data字段传递给cs_option()。
return:返回要跳过的字节数,或者0表示立即停止反汇编。

cs_skipdata_cb_t在struct cs_opt_skipdata中调用,下面来看一个例子
分析写在注释中

#include <stdio.h>
#include <stdlib.h>

#include "platform.h"
#include "capstone.h"

struct platform {
cs_arch arch;
cs_mode mode;
unsigned char* code;
size_t size;
const char* comment;
cs_opt_type opt_type;
cs_opt_value opt_value;
cs_opt_type opt_skipdata;
size_t skipdata;
};

static void print_string_hex(unsigned char* str, size_t len) //输出机器码
{
unsigned char* c;

printf("Code: ");
for (c = str; c < str + len; c++) {
printf("0x%02x ", *c & 0xff);
}
printf("\n");
}

static void test()
{

#define X86_CODE32 "\x8d\x4c\x32\x08\x01\xd8\x81\xc6\x34\x12\x00\x00\x00\x91\x92" //测试用机器码

#define RANDOM_CODE "\xed\x00\x00\x00\x00\x1a\x5a\x0f\x1f\xff\xc2\x09\x80\x00\x00\x00\x07\xf7\xeb\x2a\xff\xff\x7f\x57\xe3\x01\xff\xff\x7f\x57\xeb\x00\xf0\x00\x00\x24\xb2\x4f\x00\x78"

cs_opt_skipdata skipdata = {
// 把默认 "data" 描述符从 ".byte" 重命名为 "db"
"db",
};

struct platform platforms[2] = { //以默认描述符和自定义描述符两种方式建立一个数组
{
CS_ARCH_X86,
CS_MODE_32,
(unsigned char*)X86_CODE32,
sizeof(X86_CODE32) - 1,
"X86 32 (Intel syntax) - Skip data",
},
{
CS_ARCH_X86,
CS_MODE_32,
(unsigned char*)X86_CODE32,
sizeof(X86_CODE32) - 1,
"X86 32 (Intel syntax) - Skip data with custom mnemonic",
CS_OPT_INVALID,
CS_OPT_OFF,
CS_OPT_SKIPDATA_SETUP,
(size_t)& skipdata,
},

};

csh handle; //建立capstone句柄
uint64_t address = 0x1000; //设置起始地址
cs_insn* insn; //具体信息结构体
cs_err err; //错误枚举
int i;
size_t count; //成功反汇编行数

for (i = 0; i < sizeof(platforms) / sizeof(platforms[0]); i++) {
printf("****************\n");
printf("Platform: %s\n", platforms[i].comment);
err = cs_open(platforms[i].arch, platforms[i].mode, &handle); //错误检查
if (err) {
printf("Failed on cs_open() with error returned: %u\n", err);
abort();
}

if (platforms[i].opt_type)
cs_option(handle, platforms[i].opt_type, platforms[i].opt_value);

// 打开SKIPDATA 模式
cs_option(handle, CS_OPT_SKIPDATA, CS_OPT_ON);
cs_option(handle, platforms[i].opt_skipdata, platforms[i].skipdata);

count = cs_disasm(handle, platforms[i].code, platforms[i].size, address, 0, &insn);
if (count) {
size_t j;

print_string_hex(platforms[i].code, platforms[i].size);
printf("Disasm:\n");

for (j = 0; j < count; j++) { //输出汇编
printf("0x%" PRIx64 ":\t%s\t\t%s\n",
insn[j].address, insn[j].mnemonic, insn[j].op_str);
}

// 最后一行代码后打印偏移
printf("0x%" PRIx64 ":\n", insn[j - 1].address + insn[j - 1].size);

// 释放cs_disasm()申请的内存
cs_free(insn, count);
}
else {
printf("****************\n");
printf("Platform: %s\n", platforms[i].comment);
print_string_hex(platforms[i].code, platforms[i].size);
printf("ERROR: Failed to disasm given code!\n");
abort();
}

printf("\n");

cs_close(&handle);
}
}

int main()
{
test();

return 0;
}

运行结果如下,可以看出,默认的 .byte数据类型被改为db描述符

cs_version

unsigned int CAPSTONE_API cs_version(int *major, int *minor);

用来输出capstone版本号
参数
major: API主版本
minor: API次版本
return: 返回主次版本的16进制,如4.0版本返回 0x0400

通过分析源码发现


该版本定义于cs.c中,编译后不可更改,不接受自定义版本

示例1:

#include <stdio.h>
#include <stdlib.h>

#include "platform.h"
#include "capstone.h"

static int test()
{
return cs_version(NULL, NULL);
}

int main()
{
int version = test();
printf("%X", version);
return 0;
}

输出

示例2,强行修改版本:

#include <stdio.h>
#include <stdlib.h>

#include "platform.h"
#include "capstone.h"

static int test()
{
int ma[] = { 5 };
int mi[] = { 6 };

return cs_version(ma, mi);
}

int main()
{
int version = test();
printf("%X", version);
return 0;
}

输出:

可以看到并不能改变

cs_support

bool CAPSTONE_API cs_support(int query);

用来检查capstone库是否支持参数输入的架构或处于某编译选项
通过查看源码得知,共有四种查询参数

bool CAPSTONE_API cs_support(int query)
{
if (query == CS_ARCH_ALL)
return all_arch == ((1 << CS_ARCH_ARM) | (1 << CS_ARCH_ARM64) |
(1 << CS_ARCH_MIPS) | (1 << CS_ARCH_X86) |
(1 << CS_ARCH_PPC) | (1 << CS_ARCH_SPARC) |
(1 << CS_ARCH_SYSZ) | (1 << CS_ARCH_XCORE) |
(1 << CS_ARCH_M68K) | (1 << CS_ARCH_TMS320C64X) |
(1 << CS_ARCH_M680X) | (1 << CS_ARCH_EVM));

if ((unsigned int)query < CS_ARCH_MAX)
return all_arch & (1 << query);

if (query == CS_SUPPORT_DIET) {
#ifdef CAPSTONE_DIET
return true;
#else
return false;
#endif
}

if (query == CS_SUPPORT_X86_REDUCE) {
#if defined(CAPSTONE_HAS_X86) && defined(CAPSTONE_X86_REDUCE)
return true;
#else
return false;
#endif
}

// unsupported query
return false;
}

示例1(CS_ARCH_ALL,检查是否支持所有架构):

示例2(CSARCH*,检查是否支持指定架构)

示例3(检查是否处于DIET编译模式):

示例4(检查是否处于X86_REDUCE编译模式):
)

From https://kabeor.github.io/Capstone反汇编引擎数据类型及API分析及示例(二)//) bye

This blog is under a CC BY-NC-SA 4.0 Unported License
本文链接:https://kabeor.github.io/Capstone反汇编引擎数据类型及API分析及示例(二)/