通过读取 ADRP 和 ADD 指令值获取函数地址

逆向工程 艾达 部件 手臂 ios 男子气概
2021-07-06 22:07:27

你好逆向工程师,

我正在分析一个胖 Macho-O 二进制文件,它有一个 ADRP 和一个 ADD 指令。我说的是这些说明:

__text:00000001002E050C                 ADRP            X8, #some_function@PAGE
__text:00000001002E0510                 ADD             X8, X8, #some_function@PAGEOFF

ADRP 指令具有字节“08 00 00 90”。

ADD 指令的字节为“08 61 0D 91” 如何从 2 条指令中获取值?这是我计算 some_function 地址的程序:它应该对 21 位偏移进行符号扩展,将其左移 12,然后将其添加到 PC 并清除低 12 位。然后我应该从 ADD 指令中获取最后 12 位,并将其添加到该值中。

    int instr = 0x90000008;
    //int instr = 0x80000090;
    int value = 0x1fffff & instr;
    int mask = 0x100000;
    if(mask & instr)
    {
            value += 0xffe00000;
    }
    printf("value : %08x\n", value);
    value = value << 12;
    printf("value : %08x\n", value);
    int instr2 = 0x910d6108;
    //int instr2 = 0x08610d91;
    value += (instr2 & 0xfff); //get the last 12 bits from instr2
    printf("value : %08x\n", value);

执行指令后,值00000001002E0358应该在X8中,因为那是我们要计算的函数的地址。我的程序的输出是:

value : 00000008
value : 00008000
value : 00008108

我究竟做错了什么?

结论:我读错了 ARM 手册。ARM 的官方 AArch64 手册是您应该使用的手册。

最终代码:

    const int tab32[32] = {
     0,  9,  1, 10, 13, 21,  2, 29,
    11, 14, 16, 18, 22, 25,  3, 30,
     8, 12, 20, 28, 15, 17, 24,  7,
    19, 27, 23,  6, 26,  5,  4, 31};

    int log2_32 (uint32_t value)
    {
        value |= value >> 1;
        value |= value >> 2;
        value |= value >> 4;
        value |= value >> 8;
        value |= value >> 16;
        return tab32[(uint32_t)(value*0x07C4ACDD) >> 27];
    }

    uint64_t get_page_address_64(uint64_t addr, uint32_t pagesize)
    {
            int bits_page_offset;
            bits_page_offset = log2_32(pagesize);
            return (addr >> (bits_page_offset - 1)) << (bits_page_offset - 1);
    }

    uint64_t get_adrp_add_va(unsigned char *adrp_loc, uint64_t va){
        uint32_t instr, instr2, immlo, immhi;
        int32_t value;
        int64_t value_64;
        //imm12 64 bits if sf = 1, else 32 bits
        uint64_t imm12;
        instr = *(uint32_t *)adrp_loc;
        immlo = (0x60000000 & instr) >> 29;
        immhi = (0xffffe0 & instr) >> 3;
        value = (immlo | immhi) << 12;
        //sign extend value to 64 bits
        value_64 = value;
        //get the imm value from add instruction
        instr2 = *(uint32_t *)(adrp_loc + 4);
        imm12 = (instr2 & 0x3ffc00) >> 10;
        if(instr2 & 0xc00000)
        {
                imm12 <<= 12;

        }
        return get_page_address_64(va, PAGE_SIZE) + value_64 + imm12;
    }
1个回答

对于第一条指令 ( 0x90000008),它匹配以下 PC 相对寻址指令的操作码。

pc 相对寻址操作码

0x90000008 = 0b10010000000000000000000000001000所以我们有 op=1 (ADRP)、immlo=0、immhi=0 和 Rd=8 (X8)。该指令解码为ADRP X8, #0这将获取指令指针所在的当前页面,添加 0<<12,然后存储在寄存器中,X8以便您拥有

X8 = page_address_of(0x00000001002E050C) + 0<<12 = 0x00000001002E0000

下一条指令0x910d6108与 ADD/SUBTRACT 立即数指令匹配。

添加/子操作码

0x910d6108 = 0b10010001000011010110000100001000 所以我们有 sf=1(64 位变体)、op=0(添加)、S=0(非饱和)、shift=0(LSL #0)、imm12=0x358、Rn=8(X8)、Rd =8 (X8)。

它解码后ADD X8, X8, #0x358会将 0x358 添加到 X8,因此您将拥有

X8 = X8 + 0x358 = 0x00000001002E0358