8051架构速览:内部RAM、特殊功能寄存器(SFR)、程序存储器映射、中断系统

做固件逆向,说白了就是跟芯片的“五脏六腑”打交道。8051这玩意儿虽然老,但它的架构设计非常经典。你搞懂了它的内存布局和中断机制,反汇编出来的代码就不再是天书了。

我个人习惯,拿到一个8051的固件,第一件事不是急着反汇编,而是先看它的存储器映射。这就像你去一个陌生的城市,先看地图一样。今天我们就来把这张“地图”画清楚。

2.1 内部RAM:那128个字节的“战场”

8051的内部RAM只有128字节,地址从0x00到0x7F。你别看它小,这里可是程序运行的“主战场”。我把它分成三块来讲:

  • 工作寄存器区(0x00-0x1F):共32字节,分成4组(Bank0~Bank3),每组8个寄存器(R0~R7)。CPU当前用哪一组,由PSW寄存器里的RS1、RS0两位决定。反汇编时看到MOV R0, #0x55,你得知道这个R0到底映射到哪个物理地址。
  • 位寻址区(0x20-0x2F):共16字节,128位。每个位都有独立的地址(0x00~0x7F)。这是8051的独门绝技,很多控制逻辑都靠它实现。我在逆向一个老式空调控制器时,发现所有按键状态都映射在这个区域,用位操作指令直接读写,效率极高。
  • 通用RAM区(0x30-0x7F):剩下的80字节,给用户随便用。堆栈通常也设在这里。

避坑指南:我曾经在分析一个通信协议栈时,发现堆栈指针SP被初始化到了0x60。结果程序跑着跑着就死机了。后来一查,是堆栈向下增长时,把0x5F处的关键数据给覆盖了。记住,8051的堆栈是向上增长的(压栈时SP先加1),而且默认复位后SP=0x07,刚好在工作寄存器区。很多新手工程师忘了重新设置SP,导致寄存器数据被堆栈冲掉。

2.2 特殊功能寄存器(SFR):控制芯片的“开关面板”

SFR的地址范围是0x80到0xFF,一共128个字节。但8051实际只用了其中一部分。这些寄存器控制着定时器、串口、I/O口、中断等所有外设。反汇编时,看到直接地址操作(比如MOV 0x90, #0xFF),你就要立刻反应过来——这是在操作P1口(0x90是P1的地址)。

我整理了一张常用SFR的速查表,做逆向时经常用到:

地址 符号 功能描述 可位寻址
0x80 P0 端口0 是
0x81 SP 堆栈指针 否
0x87 PCON 电源控制寄存器 否
0x88 TCON 定时器控制寄存器 是
0x89 TMOD 定时器模式寄存器 否
0x8A TL0 定时器0低字节 否
0x8B TL1 定时器1低字节 否
0x8C TH0 定时器0高字节 否
0x8D TH1 定时器1高字节 否
0x90 P1 端口1 是
0x98 SCON 串口控制寄存器 是
0x99 SBUF 串口数据缓冲器 否
0xA0 P2 端口2 是
0xA8 IE 中断使能寄存器 是
0xB0 P3 端口3 是
0xB8 IP 中断优先级寄存器 是
0xD0 PSW 程序状态字 是
0xE0 ACC 累加器 是
0xF0 B B寄存器 是

小技巧:SFR中地址能被8整除的(如0x80、0x88、0x90...),都支持位寻址。反汇编时看到SETB 0x97,你查一下表就知道这是在置位P1.7。这个规律能帮你快速定位代码在操作哪个外设。

2.3 程序存储器映射:代码到底在哪儿跑?

8051采用哈佛架构,程序存储器和数据存储器是分开的。程序存储器(ROM/Flash)的地址空间是64KB(0x0000~0xFFFF)。但有几个特殊地址你得记住:

  • 0x0000:复位入口。CPU上电或复位后,第一条指令从这里开始取。
  • 0x0003:外部中断0(INT0)的中断服务程序入口。
  • 0x000B:定时器0溢出中断入口。
  • 0x0013:外部中断1(INT1)入口。
  • 0x001B:定时器1溢出中断入口。
  • 0x0023:串口中断入口。

每个中断入口之间只间隔8个字节。你想想看,8个字节能写几条指令?通常放一条LJMP指令跳转到真正的中断处理函数。反汇编时,看到0x0003处是LJMP 0x0150,你就知道外部中断0的处理函数在0x0150。

注意:有些芯片厂商(比如STC)会扩展程序存储器,或者把ISP引导码放在高地址区。我遇到过一款芯片,复位后先跳到0x1C00执行引导程序,然后再跳回0x0000。如果你直接反汇编0x0000开始的代码,会发现逻辑不完整——因为引导程序初始化了一些关键寄存器。

2.4 中断系统:CPU的“紧急通道”

8051有5个中断源(标准版),每个都有固定的入口地址和优先级。中断系统由两个核心寄存器控制:IE(中断使能)和IP(中断优先级)。

IE寄存器的位定义如下:

位 符号 功能
EA IE.7 全局中断使能(总开关)
ES IE.4 串口中断使能
ET1 IE.3 定时器1中断使能
EX1 IE.2 外部中断1使能
ET0 IE.1 定时器0中断使能
EX0 IE.0 外部中断0使能

反汇编时,看到SETB 0xA8(IE的地址),你就知道这是在开中断。如果看到CLR 0xA8,那就是关中断。我经常通过扫描这些指令来定位中断相关的代码。

中断优先级默认是:INT0 > 定时器0 > INT1 > 定时器1 > 串口。但你可以通过IP寄存器调整。每个中断有两个优先级:高(1)和低(0)。如果两个同优先级的中断同时触发,硬件会按自然优先级顺序响应。

实战经验:我在逆向一个工业控制器的固件时,发现它的定时器0中断优先级被设成了高,而外部中断0是低。但奇怪的是,外部中断0的处理函数里竟然调用了延时函数。这导致定时器0频繁打断外部中断的处理,最终造成数据丢失。嗯,这明显是一个设计缺陷。通过反汇编,我很快就定位到了问题所在。

2.5 架构总览图

下面这张图是我自己画的8051架构总览,把上面讲的内容串起来。你盯着看几分钟,应该就能建立起整体印象。

8051架构总览图 8051 架构总览图 CPU 核心 内部 RAM 0x00-0x7F (128B) 工作寄存器区 (0x00-0x1F) 位寻址区 (0x20-0x2F) 通用 RAM (0x30-0x7F) SFR 0x80-0xFF (128B) P0, P1, P2, P3 TCON, TMOD, IE, IP ACC, B, PSW, SP 程序存储器 0x0000-0xFFFF (64KB) 0x0000: 复位入口 0x0003: INT0 中断入口 0x000B: 定时器0中断入口 中断系统 5个中断源 IE: 中断使能 IP: 中断优先级 两级优先级 说明:CPU通过内部总线和地址总线访问RAM、SFR和程序存储器,中断系统可以随时打断CPU的当前执行流。

这张图里,CPU核心在中间,左边是内部RAM,右边是SFR,下面是程序存储器和中断系统。箭头表示CPU可以访问这些模块。你反汇编时看到的每一条指令,最终都会落到这些模块的某个地址上。

嗯,8051的架构其实不复杂,但细节很多。我个人觉得,搞懂内部RAM的分区和SFR的地址映射,是逆向分析的第一步。下次你拿到一个固件,先别急着反汇编,打开它的存储器映射表,对照着看代码,你会发现很多规律。