我正在开发嵌入式系统Coldfire ROM。当前,我正在尝试对其进行逆向工程,以获取有关其结构的更多深入知识。
ROM代码质量似乎很差,我看到很多冗余和无效代码,好像代码是在非常低的优化水平下编译的。
我告诉你,因为我不知道这个事实是否与我要提出的问题有某种联系。
不经常检查代码,而是进入单个多字节指令内部的跳转。我是这项技术的新手,所以我想知道是否有人可以让我正确地理解这一点,所以我认为这很陌生。
以下是我所指的示例:
我具有从ROM:00005474开始的功能

[...]
.ROM:00005490 30 07             movew %d7,%d0
.ROM:00005492 4a 80             tstl %d0
.ROM:00005494 66 0c             bnes 0x0000000c :12 
.ROM:00005496 70 00             moveq #0,%d0
.ROM:00005498 60 00 00 a8       braw 0x000000b2
.ROM:0000549c 4e b9 00 00 36 f8 jsr 0x000036f8  : the jump points inside here
.ROM:000054a2 32 06             movew %d6,%d1
.ROM:000054a4 48 c1             extl %d1
.ROM:000054a6 20 01             movel %d1,%d0
[...]


在ROM的另一个区域中,我具有从ROM:00012016开始的另一个功能

>
[...]
.ROM:000120c0 72 00             moveq #0,%d1
.ROM:000120c2 12 00             moveb %d0,%d1
.ROM:000120c4 20 3c 00 00 00 ff movel #255,%d0
.ROM:000120ca b2 80             cmpl %d0,%d1
.ROM:000120cc 66 00 01 92       bnew 0x00012260
.ROM:000120d0 4e b9 00 00 54 9e jsr 0x0000549e :here the jump I do not understand
.ROM:000120d6 72 00             moveq #0,%d1
.ROM:000120d8 12 00             moveb %d0,%d1
.ROM:000120da 20 3c 00 00 00 ff movel #255,%d0
[...]


如果我尝试跟随跳转并开始从地址ROM:0000549e开始反汇编该函数,则会得到翻译,导致以下解释。我知道它是可执行的,但在此操作中并不能使您大开眼界。

[...]
.ROM:0000549e 00 00 36 f8                      orib #-8,%d0
.ROM:000054a2 32 06                            movew %d6,%d1
.ROM:000054a4 48 c1                            extl %d1
.ROM:000054a6 20 01                            movel %d1,%d0
[...]


在该技术背后,有一些我应该知道的老方法。为什么这个ROM开发人员应该使用这种奇怪的技术?减少代码大小?如果是这样,则它不适合其余的代码,这些代码非常冗余并且其中包含将永远不会执行的无效代码!

EDIT.20190214
在字节0xc000处,开始时,代码块扩展到0x40000,其第一个功能在其开始部分似乎不完整。
怪异现象似乎是从这一点开始的,并扩展到了固件第二部分开始的字节0x40000。
在此代码块中,大约有200个不同维度的函数,并且此块中的所有函数中都不存在具有奇怪的绝对地址的JSR。
该块中的功能似乎可以无缝地交互,但是偶尔会出现地址不一致的情况。

评论

我忘了说第二个函数跳转到的JSR @ .ROM:0000549c的地址是有效的函数起始地址。该地址对应于一个函数的序言,第一个函数(包含代码的那个函数)不执行。

您确定地址正确吗?例如,您有“ 00005494 66 0c bnes 0x00000012”,但目标地址应为5494 + 0c = 54a0而不是0012。

感谢您的评论。实际上,代码是直接从我关注的ROM中“按原样”获取的。因为您提出了这个疑问,所以我看了您作为示例的部分。ROM:00005494 66 0c bnes 0x00000012。这是一个相对跳转,0x0c或12(如果愿意)是跳转的偏移量。因此,** 0x5494 + 0x02 + 0x0c = 0x54a2 **这是在条件为真时CPU接下来将执行的指令。 (请注意,在提取JUMP指令后,向PC添加了2)在我看来,在这种情况下一切正常。我有什么想念的吗?

我明白你的意思。不知何故,当我从反汇编程序复制粘贴时,已生成“ 0x”。我将解决此错字。谢谢。

可以在0x000036f8处添加代码吗?

#1 楼

它看起来确实像某些微控制器中使用的大小优化,尽管我还没有看到它在子例程调用中使用。特别是,用于HC08和HC12的CodeWarrior编译器使用它来优化短分支。
从Freescale / NXP的S12(X)生成工具参考手册(HC(S)12后端优化部分):

短BRA优化(-OnB = a禁用它)
BRN的操作码替换一个字节上的分支。
CPS的操作码替换了两个字节以上的分支
,清单10.19短BRA优化示例
int q(void) {
if (f()) {
    return 1;
  } else {
    return 0;
  }
}

通过这种优化产生的代码:
0000 160000 JSR f
0003 044403 TBEQ D,3 ;abs = 0009
0006 C601 LDAB #1
0008 21C7 BRN -57 ;abs = FFD1
000A 87 CLRA
000B 3D RTS

使用-OnB=a(禁用短BRA优化)选项,编译器会产生一个
更多字节:
0000 160000 JSR f
0003 044404 TBEQ D,4 ;abs = 000A
0006 C601 LDAB #1
0008 2001 BRA 1 ;abs = 000B
000A C7 CLRB
000B 87 CLRA
000C 3D RTS

分支优化器将第二个示例中的BRA 1替换为操作码
“ BRN”,0x21。然后,解码器将具有CLRB的BRN加入一个BRN。实际上,
解码器编写如下内容:
0008 21 “BRA 1”
000A C7 CLRB

第二个代码中的CLRB在第一个列表中消失,消失在
BRN指令的偏移量中。使用BRA 2也可以进行相同类型的优化。然后取一个CPS #的操作码。
注意解码器列表中的BRNCPS通常是此优化的结果。如果是,则在操作码后隐藏一两个附加的机器指令。
编译器将此代码作为SKIP1SKIP2伪操作码写入列表文件。

我没有发现提及用于ColdFire的此类优化,并且由于您提到的代码看起来并未优化这可能不是这里发生的事情。我有一个理论是,重叠的指令序列来自编译器的标准库,而不是开发人员的代码,因此它已针对大小进行了优化,甚至在汇编中手动编写。
另一个选择是,您正在查看正在分解的数据,这可能会产生各种奇怪的效果。例如,它可能是嵌入在代码中的跳转表。
编辑是的,000120c0上的内容确实类似于某种表。例如,当转换为单词数组时,它变为:
CODE:000120C0  dc.w 00, 00, 3C, 0, $FF, $B280, 00, 2, EB9
CODE:000120C0  dc.w 0, 9E, 00, 00, 3C, 0, $FF

所以它可能根本不是代码。我怀疑这可能是初始任务表或类似任务。

评论


谢谢你的回答。即使不是我要找的答案,我也很喜欢这个解释。不幸的是,所有设想的方案都不适合我的情况。我觉得排除了编译器大小的优化,因为周围的代码是多余的。我认为,出于相同的原因,它不是经过大小优化的库,而是按原样包含。最后,我认为它甚至都不是跳转表,因为JSR地址是有效的,并且因为附近没有PC相对指令。

–亚历山德罗
19年2月10日在12:39

附带的问题,我也在尝试猜测这个ROM相对是什么;我的意思是将哪个框架或操作系统用作构建它的模板。它的任务描述符非常小,只有几个字段:堆栈已满,当前堆栈,当前PC广告和一些位字段标志。任务数量似乎在编译时是固定的。你能建议什么吗?我已经测试了MQX,MQX lite,免费的RTOS,BRTOS和其他一些产品。 ROM建于2008年。

–亚历山德罗
19年2月10日在12:41

可能是本地出产的东西。请查看最后的修改。

–伊戈尔·斯科钦斯基♦
19年2月10日在15:18

$ 203C,$ b280,$ 4EB9,-这对我来说很像代码。唯一的问题是绝对跳转似乎要指向错误的地址(或者反汇编地址本身是错误的)。

–布鲁斯·雅培(Bruce Abbott)
19年2月10日在21:14

您好,再次感谢您花费时间为我写东西。您的评论很恰当,我想我会在您的位置做同样的评论。但是,从0x12016开始并包含0x120d0的代码块实际上是一个函数。我在原始问题上添加了该功能的开始,以向您展示如何得出该结论。 ROM周围的其他代码片段也引用了该函数,但是在此阶段,我无法告诉您哪个是正确的调用流程。最后,我选择了这两个代码片段来证实我的问题,但它们并不是唯一的。

–亚历山德罗
19年2月11日在8:27

#2 楼

现在,您已经向我们展示了一些代码,一种模式就开始出现。

jsr     unk_A75E       ; This function does not exist.
...
jsr     (loc_576A+2).l  ; ...points in the middle of an instruction
...
jsr     (loc_88C+4).l   ; ...points in the middle of an instruction


跳转到一条指令的中间有时可以使生成速度稍微快一些以及更紧凑的代码(例如,在向寄存器加载两个可能值之一时消除分支)。

但是,在此例程中,每个jsr指令似乎都指向无效代码,没有明显的原因。这些地址是绝对立即数,并且您的CPU没有MMU,因此这些地址必须有效。但是,这些位置的代码可能与您想像的不一样。

您可能假设ROM从地址0开始。如果不是这种情况,则使用pc相关代码(分支指令等)。 )仍会指向“正确”地址,但绝对跳转不会。另一种可能性是,ROM在上电时确实从地址0开始,但后来被特殊硬件交换为RAM和/或移至更高的地址。也可以将代码从ROM复制到RAM(可能在其他地址)以执行代码。这样做有多种可能的原因:-


RAM的循环时间可能比ROM短,因此复制到RAM的代码将执行得更快,从而需要更少(或不需要)等待
由于代码执行从位置0开始,所以ROM在启动时必须位于位置0。但是中断向量也出现在低内存区域中,如果中断向量位于ROM中,则无法在运行时进行更改。
绝对短寻址(只能访问存储器的前32k和后32k)比长寻址要快一点,因此可能需要在该区域具有RAM来存储频繁访问的变量。

您应该分析从位置0开始的代码,验证它是否做一些重要的事情,例如加载堆栈指针,清除RAM和初始化I / O硬件。然后寻找将可执行代码从ROM复制到RAM的任何内容,以及可能更改内存映射的I / O操作。由此您应该能够弄清楚这些jsr指令所指向的位置真正是什么。

您不能确切地说出该代码用于哪个CPU,坦率地说,我不会打扰浏览所有Coldfire V2数据表,以了解它们可以做什么。但是您应该这样做,因为它可能具有与您的问题相关的功能。为了全面理解,您还应该跟踪电路以确定I / O引脚等的位置和功能。进行逆向工程时,每一点信息都会有所帮助!

评论


感谢您的详细回答。我非常感谢您为帮助我而付出的努力。我尝试听从您的建议。在接下来的2条评论中,一些信息整合了比赛状态。

–亚历山德罗
19年2月13日在10:23



SoC是MCF5282;我很确定我使用BDM接口下载的ROM从0x0开始。那是因为我从那个地址开始下载了它,因为在那个地址我找到了中断向量表。我确定运行时的内存映射如下:ROM:0-7ffff,RAM:80000-8ffff,IO:40000000。值得一提的是,我有两个中断向量副本,一个从0开始,另一个从40000开始。值得一提的是,这两个副本是由指向ISR中第一个ISR的嵌入式绝对地址耦合的由第二个表指向。

–亚历山德罗
19年2月13日在10:24

复位向量指向的地址实际上是启动设备启动功能。正如我的评论之一所说,我发现只有一个功能可以将数据从ROM转移到RAM。我不能确定它是唯一的,但是该设备只有512KiB ROM和只有64KiB RAM,我想这个事实不鼓励使用RAM来获取代码。另外,我对ROM内部的以下模式“ 4eb90008”(JSR 0x0008xxxx)进行了快速研究,但没有发现任何现象。我还验证了“ JSR(Ax)”代码中的存在,并且仅在我确定为来自“ stdlib”(printf链)的函数中发现了一些功能

–亚历山德罗
19年2月13日在10:24

最后,我检查了所有转储的地址空间,以查找“ movc xx,cr_ [45]”,是否有任何办法可以移动RAM基址或FLASH基址。没什么值得一提的。在初始化功能期间,它会按照预期将Flash基地址固定为0x0,将RAM基地址固定为0x80000的时间。此外,VBR(向量基址寄存器)设置为0x0。

–亚历山德罗
19年2月13日在10:25

“ SoC是MCF5282;所以您的'SOC'实际上是具有内部ROM和RAM的MCU。“我有两个中断向量副本...绝对地址指向第二张表所指向的ISR中的第一张ISR。” -您可以向我们展示这些矢量吗?ROM中0(sp)和4(pc)位置的矢量是什么,您可以向我们展示PC矢量指向的第一行代码吗?

–布鲁斯·雅培(Bruce Abbott)
19年2月13日在11:57

#3 楼

这些现象可以通过在闪存上编写代码的技术来解释。代码分为两部分,这使开发人员可以在两个不同的时刻在闪存中编写代码。从MCF5282和MCF5216 ColdFire微控制器用户手册,修订版3,第6-19页中指出:因此,单个擦除页实际上是2 KB(0xc00 = 24页)。具有怪异的JSR目标的代码很可能源自先前版本中保留的垃圾,而不会通过将当前代码写入较低内存而被擦除。
在IgorSkochinsky和BruceAbbott的帮助下详细阐述了此答案。