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

Capstone反汇编引擎数据类型及API分析及示例(一)
上篇分析了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编译模式):
)

下篇将介绍更多有用的API,望支持

点击收藏 | 0 关注 | 1
登录 后跟帖