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

2019-07-27 约 914 字 预计阅读 5 分钟

声明:本文 【Capstone反汇编引擎数据类型及API分析及示例(二)】 由作者 kabeor 于 2019-07-27 09:34:00 首发 先知社区 曾经 浏览数 24 次

感谢 kabeor 的辛苦付出!

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,望支持

关键词:[‘安全技术’, ‘二进制安全’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now