繁体   English   中英

ARM逆向工程ROM转储

[英]ARM reverse engineering ROM dump

我使用的是旧的嵌入式系统,具有ARM cpu内核,我转储了ROM,用binwalk解压缩,并加载到IDA pro上。 我发现在某个例行程序中,BX不带LR,而是带有通用寄存器(R2,R3等),在伪代码中,它就像这样的“ memory [0xCC1232](a3,v4)”一样。 我以为这就像ARM编译器构建C指针函数一样。 可以吗 无论如何,我的主要问题是,如何仅通过ROM转储找到该子例程(memoryx ..)?因为该地址是ram地址,所以在没有ram dump的情况下,我找不到该子例程的引用?那个值写到这个特定的地址,但这几乎是不可能的,谢谢

取决于编译器设置和不使用lr定位bx的核心是很常见的,如果不需要,因为lr的较早的核心pop(通常是ldmia)不适用于在arm和thumb模式之间切换,因此编译器将生成pop像r3然后bx r3。

extern unsigned int more_fun ( void );
unsigned int fun ( void )
{
    return(more_fun()+1);
}

00000000 <fun>:
   0:   b510        push    {r4, lr}
   2:   f7ff fffe   bl  0 <more_fun>
   6:   3001        adds    r0, #1
   8:   bc10        pop {r4}
   a:   bc02        pop {r1}
   c:   4708        bx  r1
   e:   46c0        nop         ; (mov r8, r8)

vs

00000000 <fun>:
   0:   b508        push    {r3, lr}
   2:   f7ff fffe   bl  0 <more_fun>
   6:   3001        adds    r0, #1
   8:   bd08        pop {r3, pc}
   a:   bf00        nop

r4 vs r3因为虚拟寄存器在这里无关紧要,编译器会这样做很有趣,但是它们只是用来使堆栈对齐,而不是保留寄存器。

这当然是拇指模式,手臂模式

00000000 <fun>:
   0:   e92d4010    push    {r4, lr}
   4:   ebfffffe    bl  0 <more_fun>
   8:   e8bd4010    pop {r4, lr}
   c:   e2800001    add r0, r0, #1
  10:   e12fff1e    bx  lr

您可能希望看到lr与bx lr一起使用

或较新的版本,但gcc至少默认为拇指模式,您必须强制它生成像这样的手臂代码

00000000 <fun>:
   0:   e92d4010    push    {r4, lr}
   4:   ebfffffe    bl  0 <more_fun>
   8:   e2800001    add r0, r0, #1
   c:   e8bd8010    pop {r4, pc}

至于查找ram中的内容,您需要做更多的工作,这可能是不可能的。 如果这是一个嵌入式系统,并且所有内容都在非易失性存储器(闪存/ ROM /等)中,则在某些情况下(如果代码在ram中),则在分支之前将其复制或解压缩或以其他方式存储到ram中。 另一方面,您可能已经分解了一些代码,可以在ram中调用代码,但是ram中的代码可能来自下载,基本上是一些最终不在板上/芯片上的外部源。 就像具有代码的引导程序,该引导程序可以通过uart下载程序然后将其分支到该程序一样,这并不意味着该代码可以正常使用,并且当然意味着您无法预测或知道该代码将是什么,更不用说找到并反汇编它了。

如果我下载到0x20000000,可以用手使用not lr的bx启动到这样的代码中,并且以某种方式知道这是拇指代码,而不是arm,那么我需要以某种方式将地址或1加上or bx到那个地址,有时您这样做有寄存器的,有时没有。

您还将看到链接器使用bx修补远处的东西或更改模式:

unsigned int more_fun ( void )
{
    return(3);
}

链接器会为您添加蹦床:

00000000 <fun>:
   0:   e92d4010    push    {r4, lr}
   4:   eb000003    bl  18 <__more_fun_from_arm>
   8:   e8bd4010    pop {r4, lr}
   c:   e2800001    add r0, r0, #1
  10:   e12fff1e    bx  lr

00000014 <more_fun>:
  14:   2003        movs    r0, #3
  16:   4770        bx  lr

00000018 <__more_fun_from_arm>:
  18:   e59fc000    ldr r12, [pc]   ; 20 <__more_fun_from_arm+0x8>
  1c:   e12fff1c    bx  r12
  20:   00000015    andeq   r0, r0, r5, lsl r0
  24:   00000000    andeq   r0, r0, r0

在这种情况下使用r12。

通常情况下,不拆卸和分析代码就无法确定实际的跳转地址。 但是,可能有几种技巧。

  1. 通常,ROM的一部分会在非常早的启动时复制到RAM。 尝试在初始化中找到该复制例程。 如果幸运的话,您将能够获得感兴趣的RAM位置的初始内容。如果非常幸运的话,该位置不会在运行时中重写,而是实际数据。 (见下文)
  2. 尝试先反汇编部分代码。 在大多数情况下,地址是从ROM中的某个表中获取的,然后存储在局部变量中,并且该表的地址在那里。
  3. 在大多数情况下,子例程代码主体位于ROM的开头(从入口点开始)到BX LR结束。 您可以通过扫描ROM来查找所有或大多数子例程入口点,因为怀疑下一个子例程的入口点位于上一个“ BX LR”的旁边。 要确定是否间接调用了特定的子例程,请尝试搜索其子地址是否存在于ROM中,或者是否装有“ MOVW” +“ MOVT”对。 这使您知道可以在哪里使用它。
  4. 在某些情况下,子例程地址作为参数传递给子例程调用。 因此,请尝试搜索是否有任何调用者在代码中引用某个地址。
  5. 可能有过程表,看起来像是长字指向ROM代码区(可能与null交错)。 尝试找到它们,然后确定可以使用哪种情况。

从GNAT的运行时获取的示例RAM初始化代码:

        movw    r0,#:lower16:__data_start ; __data_start points to RAM
        movt    r0,#:upper16:__data_start ; (often it is start of RAM)
        movw    r1,#:lower16:__data_words
        movw    r2,#:lower16:__data_load  ; __data_load points to ROM
        movt    r2,#:upper16:__data_load  ; right after code and readonly data
        cbz     r1,1f
0:      ldr     r4,[r2],#4
        str     r4,[r0],#4
        subs    r1,r1,#1
        bne     0b
1:

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM