【问题标题】:Disassembler logic for custom opcodes in CC中自定义操作码的反汇编逻辑
【发布时间】:2021-07-08 00:45:29
【问题描述】:

所以我正在构建一个反汇编程序,它将包含十六进制数据的文件转换为汇编语言。

因此,从这种格式中,我可以使用 uint8_t 将文件中的十六进制数据转换为十进制并将它们存储在一个数组中。然后我决定对数组中的最后一个数字进行位移,以获得最后一个函数的指令数;本质上我是在向后解析,因为我不知道开头有多少填充,并且函数中的操作数在函数末尾给出。但后来我意识到这些操作的位大小不同,并且不是完美的 8 位或 16 位界限。所以我被困住了,因为我的数组,使用顶部的例子,基本上是这样的:

uint8_t hex[] = {0x00, 0x03, 0x02, 0x01, 0x42, 0x82, 0x86, 0x04, 0x10, 0x45};

那么任何人都可以帮助我解析逻辑吗?这是我第一次发帖,如果我遗漏了什么,我很抱歉,如果需要,我会提供更多信息或删除

【问题讨论】:

  • 指令大小不同,但操作码都是三位。因此,如果您屏蔽掉一个字节中的其他位,您就知道操作码,并且您可以从中推断出随后的数据值。然后您需要移动和屏蔽适当数量的位(有时从 2 个字节,因为它们未对齐)以获取值。你对每条指令重复这个过程。这与其他反汇编程序没有什么不同。如果您遇到问题,请发布您尝试的代码,人们可能会提供帮助。
  • 只是好奇,这个汇编语言是干什么用的?
  • 嗨@EmanuelP 谢谢你的回复,这正是我想要做的:D 但我被你所说的这一部分困住了“然后你需要改变并掩盖适当数量的位(有时从 2 个字节,因为它们没有对齐)来获取值”。你将如何在字节之间转换?例如,我在数组中的最后一个元素上左右移动 3 位以获得最后一个函数的操作数,然后将数组中的倒数第二个元素向左然后向右移动 5 位以找到操作码,但是这些值跨越数组中的元素。
  • @potatopppcccccc 您可以通过将其与按位或组合在一起来转移到字节。例如,如果您有 b1 需要高 4 位,您可以将它们移到右 4 位。如果您需要 b2 中的低 4 位,则将它们左移,例如(b1 >> 4) | (b2 << 4)。现在您在一个字节中有 8 位表示跨越字节边界的数字。并且您需要注意只包含您需要的那些位,如果需要,可以使用位掩码。
  • 如果您将 uint8_t 数组转换为位数组,而不是移位和屏蔽(我认为这会很复杂) - 它使用更多内存,但您可以更轻松地访问各个位。

标签: c assembly binary disassembly binary-data


【解决方案1】:

如果您将 uint8_t 数组转换为位数组,而不是移位和屏蔽(我认为这会很复杂),它会使用更多的内存,但您可以更轻松地访问各个位。

这是一个执行此操作的示例程序:

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

uint8_t getBits(uint8_t *bits, uint8_t size, uint32_t *index)
{
    uint8_t value = 0;
    *index -= size; // decrement index to the starting point
    for(uint32_t i=0; i<size; i++)
        value = (value<<1) | bits[*index+i];
    return value;
}

int main()
{
    // sample program
    uint8_t array[] = {0x00,0x03,0x02,0x01,0x42,0x82,0x86,0x04,0x10,0x45};
    
    // program with zero padding
    // uint8_t array[] = {0xE8,0x39,0x06,0xA0,0xC4,0x16,0x82,0x90,0x4A,0x08,0x41};
    
    uint32_t array_size = sizeof(array)/sizeof(*array); // 10 bytes
    uint32_t bits_size = 8*array_size; // 80 bytes
    uint8_t* bits = malloc(bits_size);
    
    for(uint32_t a=0;a<array_size;a++)
        for(uint32_t b=0;b<8;b++)
            bits[a*8+b] = (array[a] >> (7-b)) & 1;
    
    puts("Binary program file:");
    for(uint32_t i=0;i<bits_size;i++)
        printf("%s%d",(i%8?"":" "),bits[i]);
    puts("");
    
    enum                    {  MOV,  CAL,  RET,  REF,  ADD,  PRINT,  NOT,  EQU};
    uint8_t params[]      = {    2,    1,    0,    2,    2,      1,    1,    1};
    const char *opcodes[] = {"MOV","CAL","RET","REF","ADD","PRINT","NOT","EQU"};

    enum                    {  VAL,  REG,  STK,  PTR};
    uint8_t value_size[]  = {    8,    3,    5,    5};
    const char *types[]   = {"VAL","REG","STK","PTR"};

    uint32_t index = bits_size; // start at end
    
    // minimum program size is function(3) + opcode(3) + size(5)
    // if there are less than that number of bits then it must be padding
    while(index>10)
    {
        uint8_t size = getBits(bits,5,&index);
        printf("\nsize=%d\n",size);
        if (size > 0)
        {
            for(int o=0; o<size; o++)
            {
                uint8_t opcode = getBits(bits,3,&index);
                printf("opcode=%s",opcodes[opcode]);
                
                for(int p=0; p<params[opcode]; p++)
                {
                    printf("%c ",p?',':':');
                    
                    uint8_t type = getBits(bits,2,&index);
                    printf("type=%s ",types[type]);
                    
                    uint8_t value = getBits(bits,value_size[type],&index);
                    printf("value=%d",value);
                }
                
                puts("");
            }
        
            uint8_t function = getBits(bits,3,&index);
            printf("function=%d\n",function);
        }
    }
    return 0;
}

试试https://onlinegdb.com/S1qVStz8d

getBits() 的工作原理:

您从原始值中创建一个单独的数字数组,然后从其中一次取一个位以创建一个新值 - getBits() 是我为此编写的函数。

要了解它是如何工作的,想象一下它是如何以 10 为底的:321 被放入数组 {3,2,1} 中,您可以将其转回一个值:

value = 0;
value = value*10 + digits[0];
value = value*10 + digits[1];
value = value*10 + digits[2];

给出(((0)*10+3)*10+2)*10+1,即321

如果将5(二进制101)放入数组{1,0,1},您可以将其转回一个值:

value = 0;
value = value*2 + bits[0];
value = value*2 + bits[1];
value = value*2 + bits[2];

这给出了(((0)*2+1)*2+0)*2+1,即5(二进制101

确实有效。一个体面的编译器会将*2 优化为&lt;&lt;1,将+ 优化为|,但你可以自己做(我就是这么做的):

value = 0;
value = (value<<1) | bits[0];
value = (value<<1) | bits[1];
value = (value<<1) | bits[2];

生成相同的二进制文件00000101

这只是一个可读性问题 - 对于十进制,您希望看到 value*10+x,但对于二进制,您希望看到移位 / 之类的位运算,而不是乘法 / 加法之类的数学运算。

然后,如果你使用一个带有一个大小和一个指向数组末尾的索引的循环,你会得到:

uint8_t value = 0;
index -= size; // decrement index to the starting point
for(uint32_t i=0; i<size; i++)
    value = (value<<1) | bits[index+i];

但是,当然,如果它是一个函数,那么 index 需要是一个指针,并且你需要在任何地方取消引用它:

uint8_t getBits(uint8_t *bits, uint8_t size, uint32_t *index)
{
    uint8_t value = 0;
    *index -= size; // decrement index to the starting point
    for(uint32_t i=0; i<size; i++)
        value = (value<<1) | bits[*index+i];
    return value;
}

【讨论】:

  • 感谢您的帮助,您能解释一下 getBits 的工作原理吗?我对此有点困惑,尤其是 value = (value
  • @potatopppcccccc 看看我对答案所做的编辑。让我知道这是否足以解释它 - 如果不是,我会进一步分解它。
  • 很抱歉回复晚了,但这很好地解释了它。非常感谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-06
  • 1970-01-01
  • 1970-01-01
  • 2022-01-08
  • 2012-06-04
  • 1970-01-01
相关资源
最近更新 更多