assembly 从ELF文件中提取指令

vulvrdjw  于 2022-11-24  发布在  其他
关注(0)|答案(1)|浏览(117)

对于我正在编写的一个程序,我需要提取为risc-v arch编译的ELF二进制代码的指令。我尝试提取指令的方法如下:

void dumpCode(FILE *file, Elf32_Phdr *segm, Elf32_Ehdr *header)
{
    char *fileptr;
    struct stat statbuf;
    int *opcode_ptr;
    unsigned int i, vaddr, offset;

    int fd = fileno(file);
    if (fstat(fd, &statbuf)) {
        fprintf(stderr, "[-] Error while stating the file!\n");
        goto fail;
    }

    fileptr = (char *)mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);

    if (MAP_FAILED == fileptr) {
        fprintf(stderr, "[-] Error mapping the file!\n");
        goto fail;
    }

    offset = (0 == segm->p_offset ? header->e_ehsize + header->e_phnum * header->e_phentsize : segm->p_offset); // Mark 1

    opcode_ptr = (int *)(fileptr + offset);

    vaddr = (0 == segm->p_offset ? segm->p_vaddr + header->e_ehsize + header->e_phnum * header->e_phentsize : segm->p_vaddr); // Mark 2

    for (i = 0; i < segm->p_filesz / 4; i++, vaddr += 4) { // Mark 3
        unsigned char *opcode = getOpcode(*opcode_ptr++);
        if (1 == disas(opcode, vaddr)) {
            free(opcode);
            break;
        }
        free(opcode);
    }

    munmap(fileptr, statbuf.st_size);
fail:
    close(fd);
}

为了测试我的函数,我首先编写了一个简单的汇编程序:

.global _start

_start:
    addi a0, x0, 1
    la a1, str
    addi a2, x0, 6
    addi a7, x0, 64
    ecall

    addi a0, x0, 0
    addi a7, x0, 93
    ecall

.data
    str: .ascii "Hello\n"

作为第二个测试文件,我写了一个不同的代码,这次是用C

#include <stdio.h>
#include <math.h>

int main(void)
{
    printf("%.5f", sqrt(2.0));
    return 0;
}

第一个测试文件已使用以下内容进行编译和汇编:riscv32-linux-gnu-as -o test1.o test1.s; riscv32-linux-gnu-ld -o test1 test1.o第二个测试文件已经直接用gcc riscv32-linux-gnu-gcc -o test2 test2.c -lm编译了。返回到 dumpCode 函数,我标记了三行。
1.第一行是文件中放置段的偏移量,如果它是0,如果我没有错,我需要添加(header-〉e_ehsize + header-〉e_phnum * header-〉e_phentsize)字节,以便在正确的位置开始转储。但是当我使用第二个测试文件时,它不起作用。第二个标记遵循与此相同的方法,提取正确的虚拟地址。
1.第三个标记放在for循环中,我用来迭代指令,但是使用(segm-〉p_filesz / 4)来计算二进制中存在的指令的确切数量,它给了我更多的指令。据我所知,额外的数据属于填充,但是我想知道当到达填充部分时,我是否可以停止处理数据。
如何计算需要从elf文件处理的正确字节数?如果该字节数包括填充,我可以忽略它吗?

f0ofjuux

f0ofjuux1#

Source

offset = (0 == segm->p_offset
    ? header->e_ehsize + header->e_phnum * header->e_phentsize
    : segm->p_offset); // Mark 1

这是错误的。文件非常具体:

p_offset
         This member holds the offset from the beginning of the
         file at which the first byte of the segment resides.

数据从segm-〉p_offset开始。没有if或but。
如果您看到0,我怀疑这是因为段没有PT_LOAD标志,这意味着它根本不在文件中(文件中的偏移量没有意义)因为段应该包含ELF头(因此偏移量0没有错误)。
在CPU中,指令和非指令是没有区别的。每4个字节都可能是一条指令。甚至00000000也是一条指令。指令就是程序计数器指向的任何东西。你可以试着找出程序计数器可以指向哪里,但这等同于停机问题,因此是不可能的。

    • 可能**有调试信息或符号说明文件的哪一部分是填充的,但由于CPU不关心,ELF文件的主要部分也不关心。

相关问题