• ARM ELF函数重定位


    ARM ELF的函数重定位与x86是一致的,但由于汇编指令不同,再鼓捣一遍。

    示例代码:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main () {
            puts ("Hello world");
            sleep (1);
            FILE *fp = fopen ("1.c", "r");
            fclose (fp);
            exit (0);
    }
    

    通过 readelf -r 可以查看ELF中所有需要重定位的函数,我们以fopen()函数为例,分析其重定位过程。

    $ arm-linux-androideabi-readelf -r elf_2
    
    Relocation section '.rel.plt' at offset 0x278 contains 7 entries:
     Offset     Info    Type            Sym.Value  Sym. Name
    00009ff4  00000516 R_ARM_JUMP_SLOT   00000000   fopen
    

    首先main()函数中,通过 bl 82f4调用fopen(),82f4是一个16进制表示的地址,位于.plt节。

    $ arm-linux-androideabi-objdump -d elf_2
    000083c8 <main>:
    ...
        8404:       ebffffba        bl      82f4 <fopen@plt>
    
     Disassembly of section .plt:
     
     000082f4 <fopen@plt>:
        82f4:       e28fc600        add     ip, pc, #0, 12 @由于ARM三级流水,PC = 0x82f4 + 0x8
        82f8:       e28cca01        add     ip, ip, #4096
        82fc:       e5bcfcf8        ldr     pc, [ip, #3320]! @ip + 0xcf8 = 0x9ff4
    

    以上三条指令执行完,从0x9ff4位置取值给pc,完成间接寻址的跳转。看一下0x9ff4处内容:

    (gdb) p/x *0x9ff4
    $1 = 0x82b0
    

    程序跳转到0x82b0位置:

    Disassembly of section .plt:
    
    000082b0 <__libc_init@plt-0x14>:
        82b0:       e52de004        push    {lr}            ; (str lr, [sp, #-4]!)
        82b4:       e59fe004        ldr     lr, [pc, #4]    ; 82c0 <__libc_init@plt-0x4>
        82b8:       e08fe00e        add     lr, pc, lr
        82bc:       e5bef008        ldr     pc, [lr, #8]!
        82c0:       00001d18        andeq   r1, r0, r8, lsl sp
    

    可以看到,这是.plt节的开始位置,IDA帮助我们做了一些显示的优化,所以其汇编结果与objdump看到的不同,它假装替我们完成了GOT的重定位过程,实际并非如此:

    @ida的显示结果
    .got:00009FF4 fopen_ptr       DCD __imp_fopen
    

    下面解析一下.plt节开头的这几条指令:

    @ 1. stack <- lr

    @ 2. lr <- 0x1d18

    @ 3. lr <- 0x82c0 + 0x1d18 = 0x9fd8

    @ 4. pc <- [0x9fd8 + 0x8], lr <- 0x9fd8 + 0x8 = 0x9fe0

    发现程序最终从0x9fe0地址处取值,并间接寻址将其作为地址跳转过去执行。使用gdb发现,此处静态值为0x0。显然这块地址内容,要由程序运行时动态补充的,否则这条指令将产生0地址访问异常。

    (gdb) p/x *0x82c0
    $1 = 0x1d18
    (gdb) p/x *0x9fe0
    $2 = 0x0
    (gdb) 
    

    原来GOT的前3项,是为系统预留的(GOT[0][1][2]),其中GOT[1]中是ELF中所有动态库构成的链表的指针,GOT[2]是_dl_runtime_resolve函数指针。这个函数将具体完成函数的重定向过程,并将结果反馈到GOT表中。

    因此上面静态分析时,GOT这3个表项是没有值的,它们由加载器动态填充。

    以前画的x86的图,同样适应 ARM:

     

  • 相关阅读:
    开启sentry权限控制hue
    hive_server2的权限控制
    自带的simple认证
    tableau备份
    tableau分布式添加节点
    升级tableau版本
    tableau日常管理
    mavn Nexus Repository Manager漏洞
    第3章:打造命令行工具
    基于从库+binlog方式恢复数据
  • 原文地址:https://www.cnblogs.com/gm-201705/p/9863947.html
Copyright © 2020-2023  润新知