王柳 发表于 2022-6-23 20:11:26

Capstone反汇编(二)

文章目录



[*]前言
[*]一 cs_option API 分析
[*]

[*]1.1 cs_option
[*]1.2 cs_opt_type
[*]1.3 cs_opt_value
[*]1.4 例程演示

[*]二、 SKIPDATA mode
[*]

[*]2.1 数据结构
[*]2.2 例程演示
[*]2.3 改变助记符例程

[*]总结
[*]参考资料

前言

Capstone反汇编(一)
上篇文章只是简单分析了官方给出的一个案例用到的API,接下来分析其它的API和数据结构
一 cs_option API 分析

1.1 cs_option

反编译引擎的运行时选项
cs_option(csh ud, cs_opt_type type, size_t value) 1.2 cs_opt_type

type是cs_option函数的第二个参数,是反汇编引擎运行时枚举选项
// 反汇编引擎的运行时选项
typedef enum cs_opt_type {
        CS_OPT_INVALID = 0,        // <未指定选项
        CS_OPT_SYNTAX,        // 汇编输出语法
        CS_OPT_DETAIL,        // 将指令结构分解为细节
        CS_OPT_MODE,        // 在运行时更改引擎的模式
        CS_OPT_MEM,        // 用户自定义动态内存相关函数
        CS_OPT_SKIPDATA, // 反汇编时跳过数据,然后引擎处于 SKIPDATA 模式。
        CS_OPT_SKIPDATA_SETUP, // 为 SKIPDATA 选项设置用户自定义函数
        CS_OPT_MNEMONIC, // 自定义指令助记符
        CS_OPT_UNSIGNED, // 以无符号形式打印立即操作数
} cs_opt_type; 看一下结果反汇编出来的语法变成了AT&T语法
https://img-blog.csdnimg.cn/d3f3d32c0cf1472a8f50cb0705aa652b.png
其中:
/// Runtime option value (associated with option type above)
typedef enum cs_opt_value {
        CS_OPT_OFF = 0,///< Turn OFF an option - default for CS_OPT_DETAIL, CS_OPT_SKIPDATA, CS_OPT_UNSIGNED.
        CS_OPT_ON = 3, ///< Turn ON an option (CS_OPT_DETAIL, CS_OPT_SKIPDATA).
        CS_OPT_SYNTAX_DEFAULT = 0, ///< Default asm syntax (CS_OPT_SYNTAX).
        CS_OPT_SYNTAX_INTEL, ///< X86 Intel asm syntax - default on X86 (CS_OPT_SYNTAX).
        CS_OPT_SYNTAX_ATT,   ///< X86 ATT asm syntax (CS_OPT_SYNTAX).
        CS_OPT_SYNTAX_NOREGNAME, ///< Prints register name with only number (CS_OPT_SYNTAX)
        CS_OPT_SYNTAX_MASM, ///< X86 Intel Masm syntax (CS_OPT_SYNTAX).
        CS_OPT_SYNTAX_MOTOROLA, ///< MOS65XX use $ as hex prefix
} cs_opt_value; // 以AT&T语法显示
cs_option(handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT); 二、 SKIPDATA mode

默认情况下,Capstone 在遇到 a broken instruction 时会停止反汇编,大多数时候,原因是这是输入中混合的数据,Capstone 不理解这个“weird” code。
通常,建议您确定下一个代码在哪里,然后从该位置继续反汇编。 但是,在某些情况下,您只想让 Capstone 自动跳过一些数据,直到找到合法指令,然后从那里继续。 因此,为此目的引入了 SKIPDATA 模式。
简单点来说就是当输入的二进制代码中有不合法的指令,Capstone遇到这个不合法指令便会停止反汇编,不会继续反汇编后面的数据,所以引入了SKIPDATA 模式,跳过这些不合法的指令,并用一个助记符替代,然后继续反汇编后面的数据。
2.1 数据结构

cs_opt_skipdata
#include <stdio.h>
#include <string.h>
#include <inttypes.h>

#include <capstone/capstone.h>

#define CODE "\x55\x48\x8b\x05\xb8\x13\x00\x00"


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");
}


int main(void)
{
        csh handle;
        cs_insn *insn;
        size_t count;

        if (cs_open(CS_ARCH_X86, CS_MODE_64, &handle) != CS_ERR_OK)
                return -1;

          // 以AT&T语法显示
    cs_option(handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT);

        print_string_hex(CODE, strlen(CODE));
        printf("Disasm:\n");

        count = cs_disasm(handle, CODE, sizeof(CODE)-1, 0x1000, 0, &insn);
        if (count > 0) {
                size_t j;
                for (j = 0; j < count; j++) {
                        printf("0x%"PRIx64":\t%s\t\t%s: ", insn.address, insn.mnemonic,
                                        insn.op_str);

                        for(int i = 0 ; i < insn.size; i++)
                        {
                                printf("0x%02x ", insn.bytes);
                        }
                        printf("\n");
                }

                cs_free(insn, count);
        } else
                printf("ERROR: Failed to disassemble given code!\n");

        cs_close(&handle);

    return 0;
} (2)cs_skipdata_cb_t
insn.bytes是指令的机器码,即对应的二进制
insn.size是该机器指令的长度 (3)打开 SKIPDATA mode
要让Capstone跳过一些(未知的)数据量,直到下一个合法指令,只需使用cs_option()打开选项CS_OPT_SKIPDATA(默认关闭),如下所示:
for(int i = 0 ; i < insn.size; i++)
{
        printf("0x%02x ", insn.bytes);
} 2.2 例程演示

例程采用官方的例程:
使用SKIPDATA mode 之前:
//用户自定义设置 SKIPDATA 选项
typedef struct cs_opt_skipdata {
        // Capstone 将要跳过的数据视为特殊的“指令”
        // 用户可以在这里为该指令的“助记符”指定字符串。
        // 默认情况下(如果 @mnemonic 为 NULL),Capstone 使用“.byte”。
        const char *mnemonic;

        // Capstone hits 数据时调用的用户自定义回调函数
        // 如果此回调的返回值为正数 (>0), Capstone 将准确跳过该字节数并继续
        // 否则,如果回调返回 0,Capstone 停止反汇编并立即从 cs_disasm() 返回
        // 注意:如果此回调指针为 NULL,Capstone 将根据体系结构跳过一些字节,如下所示:
        /// Arm:   2 bytes (Thumb mode) or 4 bytes.
        /// Arm64:   4 bytes.
        /// Mips:    4 bytes.
        /// M680x:   1 byte.
        /// PowerPC: 4 bytes.
        /// Sparc:   4 bytes.
        /// SystemZ: 2 bytes.
        /// X86:   1 bytes.
        /// XCore:   2 bytes.
        /// EVM:   1 bytes.
        /// RISCV:   4 bytes.
        /// WASM:    1 bytes.
        /// MOS65XX: 1 bytes.
        /// BPF:   8 bytes.
        cs_skipdata_cb_t callback;         // default value is NULL

        // 用户自定义数据将被传递给callback函数指针
        void *user_data;
} cs_opt_skipdata; https://img-blog.csdnimg.cn/2ce7d538e6374a93b2c1b527352c7926.png
Capstone 在遇到 a broken instruction 时会停止反汇编了。
使用SKIPDATA mode 之后:
/**
        SKIPDATA 选项的用户定义回调函数
@code: 包含要反汇编的代码的输入缓冲区。
      这与传递给 cs_disasm() 的缓冲区相同。
@code_size: 上述 code 缓冲区的大小(以字节为单位)。
@offset: 当前检查字节在上述输入缓冲区 code 中的位置。
@user_data: 用户数据通过 cs_opt_skipdata 结构中的 user_data 字段传递给 cs_option()。

@return: 返回要跳过的字节数,或 0 立即停止反汇编。
*/
typedef size_t (CAPSTONE_API *cs_skipdata_cb_t)(const uint8_t *code, size_t code_size, size_t offset, void *user_data); Capstone 在遇到 a broken instruction 时,自动跳过一些数据,用.byte代替,直到找到合法指令,然后从那里继续反汇编。
Capstone 跳过 1 个字节的数据并继续从输入流中的下一个字节进行反汇编。 在这种情况下,实际上 Capstone 将跳过数据视为指令 ID 为零的特殊指令,助记符为“.byte”,操作数字符串为其跳过的字节序列的十六进制代码。
https://img-blog.csdnimg.cn/0067c0c9d61b4f28b2ca4dcae2ec0c9d.png
2.3 改变助记符例程

如上所述,Capstone 认为数据将被跳过具有默认助记符“.byte”的指令。 要更改此助记符,请使用带有 CS_OPT_SKIPDATA_SETUP 的 cs_option(),如下所示:
csh handle;
cs_open(CS_ARCH_X86, CS_MODE_64, &handle);

// turn on SKIPDATA mode
cs_option(handle, CS_OPT_SKIPDATA, CS_OPT_ON); /* Capstone Disassembler Engine */

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

#include <capstone/platform.h>
#include <capstone/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 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"

        struct platform platforms[] = {
                {
                        CS_ARCH_X86,
                        CS_MODE_64,
                        (unsigned char*)RANDOM_CODE,
                        sizeof(RANDOM_CODE) - 1,
                },

        };

        csh handle;
        uint64_t address = 0x1000;
        cs_insn *insn;
        cs_err err;
        int i;
        size_t count;

        for (i = 0; i < sizeof(platforms)/sizeof(platforms); i++) {

                err = cs_open(platforms.arch, platforms.mode, &handle);
                if (err) {
                        printf("Failed on cs_open() with error returned: %u\n", err);
                        abort();
                }

                if (platforms.opt_type)
                        cs_option(handle, platforms.opt_type, platforms.opt_value);

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

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

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

                        // print out the next offset, after the last insn
                        printf("0x%" PRIx64 ":\n", insn.address + insn.size);

                        // free memory allocated by cs_disasm()
                        cs_free(insn, count);
                } else {
                        printf("****************\n");
                        print_string_hex(platforms.code, platforms.size);
                        printf("ERROR: Failed to disasm given code!\n");
                        abort();
                }

                printf("\n");

                cs_close(&handle);
        }
}

int main()
{
        test();

        return 0;
} https://img-blog.csdnimg.cn/e7eb7e47790c45ffa9d26ae7e4bd1ed0.png
从结果可以看到助记符由.byte变成db了。
总结

本文章主要讲解了cs_option函数和Capstone的SKIPDATA mode。
参考资料

https://xz.aliyun.com/t/5772
https://xz.aliyun.com/t/5753
http://www.capstone-engine.org/skipdata.html

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: Capstone反汇编(二)