krakenlake/vmon

GitHub: krakenlake/vmon

一个用纯 RISC-V 汇编编写的机器码监视器,提供内存转储、反汇编、汇编、断点管理等调试功能,可通过 UART 与目标系统交互。

Stars: 57 | Forks: 4

# VMON - RISC-V 机器码监视器 VMON 是一个用于 RISC-V 系统的小型机器码监视器,带有 UART 通信,完全用 RISC-V 汇编语言编写。 ![Screenshot 2025-05-16 at 00 02 59](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/89b4613819114857.png) ## 功能 - 十六进制和 ASCII 内存转储 - 带十六进制/十进制输出的反汇编器 - 带十六进制/十进制/二进制输入的汇编器 - 支持 RV64GC 指令(为 RV128 做好准备) - 支持部分伪指令 - 十六进制/十进制/二进制转换 - 内存区域搜索 - 内存区域复制 - 异常捕获 - 可为 RV32/RV64/RV128 目标构建 - 可在 QEMU 或 RISC-V 硬件上运行 - 可在 M 模式或 S 模式下运行 - 可裸机运行或从外部调用 - 通过 UART 进行终端 I/O - 编译时可配置包含的命令集 - 编译时可配置支持的 ISA 和扩展 - 可选择是否包含 RISC-V 示例代码构建可执行文件 ## 需求 - 用于构建的 riscv32/riscv64 GNU 工具链(取决于目标) - Make 用于构建可执行文件 - QEMU 或 RISC-V 硬件用于运行 ## 构建 - 在 Makefile 中选择一个 `TARGET`(或设置一个新的) - 查看 `config/config..h` 并根据需要定义/取消定义 - 查看 `src/include/vmon/UART.h` 中的目标 UART 设置 - 执行 `make` ## 配置选项 ### 命令 你可以使用 `WITH_CMD_...` 定义来配置特定版本的 VMON 应包含的命令集。这对于内存非常小的系统很有用——你可以配置你的构建只包含你需要的命令,而省略像 `i` 或 `h` 这样包含大量 ASCII 数据的命令。 ### 寄存器名称 如果你想在 VMON 中使用 ABI 寄存器名称,请定义 `ABI_REGISTER_NAMES`,否则将使用 `x0`-`x31`。 ### 测试代码 使用 `WITH_TESTCODE_...` 定义来配置可执行文件中包含的测试代码。 ### 支持的指令 使用 `DISASS_...` 定义来指定你将能够使用 VMON 进行汇编/反汇编的指令集。 关于 RVC 的说明:由于支持 RVC 指令的汇编/反汇编会使问题变得更加复杂,支持 RVC 的可执行文件会相当大。因此,对于支持 RVC 且内存较小的系统,有两个选项: 1. 添加 RVC 支持,能够汇编/反汇编 RVC 指令,但可执行文件较大 2. 不添加 RVC 支持,仅使用非 RVC 指令(手写汇编代码会更大),但可执行文件较小 所以,请明智地选择。 ## 运行 - 执行 `make run` 在 QEMU 上运行 ## 使用 VMON ### 命令行 VMON 理解以下命令: #### a 汇编输入(按 ENTER 停止)[仍在测试中,请谨慎使用]。 #### b 列出所有断点(最多支持 8 个断点)。 #### bc 清除给定地址处的断点。 #### br 重置(清除)所有断点。 #### bs 在给定地址设置断点。如果已达到最大断点数,断点将不会被设置。只接受有效的指令地址(4 字节对齐,如果启用了 RVC 则为 2 字节对齐)。 如果给定的断点地址对 VMON 不可读写,当执行 `g` 命令时将导致异常,因为 `g` 命令会尝试通过保存所有断点地址处的原始指令并用 `ebreak` 指令覆盖它们来激活所有断点。VMON 返回命令行时将恢复原始指令。 #### c 复制内存内容。当两个区域重叠时也能正确工作。 #### d [] [] 从 反汇编到 [仍在测试中,请谨慎使用]。 如果未给出 ,默认打印 16 条指令。 如果未给出地址,转储将从之前使用的最后一个地址开始。 #### f #### fb 在内存中从 查找 。 #### fh <16bit_value> 在内存中从 查找 <16bit_value>。 #### fw <32bit_value> 在内存中从 查找 <32bit_value>。 #### g 跳转到 。 恢复进入时保存的寄存器并执行 `j `)。 断点将被激活。 #### h 打印命令行帮助信息。 #### i 打印一些内部信息。 #### m [] [] 从 内存转储。 如果未给出 ,默认打印 16 行(128 字节)。 如果未给出地址,转储将从之前使用的最后一个地址继续。 #### p [] [] [...] 写入(poke),... #### pw <32bit_value0> [<32bit_value1>] [<32bit_value2>] [...] 写入(poke)<32bit_value0> 到 ,<32bit_value1> 到 ,... (按小端序) #### r 转储进入时保存的寄存器。 #### s 设置寄存器的值。对于浮点寄存器,有效的位模式将被直接采用,例如要将浮点寄存器设置为值 "42.0",你实际上需要输入 "s ft0 0x42280000",因为 0x42280000 是该值的 IEEE 754 表示。 #### x 退出 VMON。 恢复进入时保存的寄存器和 pc。 断点将不会被激活。 #### ? 以十六进制、十进制和二进制表示打印 。 如果 以 "0x" 开头,它将被解释为十六进制。 如果以 "0b" 开头,它将被解释为二进制。 否则,它将被解释为十进制。 #### 数值 **所有地址和值都接受十六进制("0x...")、二进制("0b...")或十进制(无前缀)。** ### 汇编输入 `a` 命令切换到汇编输入模式。将打印给定的内存地址,可以在提示符处输入指令。如果指令语法有效,指令字将被汇编并存储在指定的内存地址处,当前地址将增加指令的大小,然后可以输入下一条指令。 #### 寄存器名称 VMON 接受为指令和寄存器定义的 RISC-V 语法。寄存器可以用 `x0`-`x31` 或它们的 ABI 名称 `zero`、`ra`、`sp`、... `t6` 来指让。VMON 接受哪种风格的寄存器名称在编译时通过在 `config.h` 中设置 `ABI_REGISTER_NAMES` 来配置。 #### 跳转地址 分支指令和 `JAL` 的目标地址被编码为指令中的偏移量,但为方便起见,在此期望以绝对地址输入,例如: Screenshot 2025-05-21 at 16 36 56 `JALR` 的偏移量期望为相对偏移量: Screenshot 2025-05-21 at 16 39 00 ### 异常 VMON 安装了一个陷阱处理程序来捕获异常。异常会被打印出来: ![Screenshot 2025-05-16 at 09 36 18](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/e7d29fbdaa114900.png) VMON 只能在启动时启用陷阱处理程序(如果可能,默认启用),前提是可执行文件包含陷阱处理程序,目标平台实现了 Zicsr 扩展,并且可执行文件在 M 模式下运行。 ### 栈处理 如果 VMON 在 M 模式下运行,它将在启动时设置自己的栈。 否则,将使用调用者的传入 `sp`。 情况下,所有整数和浮点寄存器都将在进入时保存到栈上并在退出时恢复。可以使用 `r` 命令打印保存的寄存器,并使用 `s` 命令进行修改。 ## 已知问题 请参阅 [issues 页面](https://github.com/krakenlake/vmon/issues)。 ## 历史 Steve Wozniak 于 1976 年编写了 [WOZ 监视器(又名 WOZMON)](https://github.com/jefftranter/6502/blob/master/asm/wozmon/wozmon.s)。早期的 PET/CBM 机型附带机器码监视器。我花了几个小时在 C64 上找出正确的监视器入口地址,只是(很久以后)才意识到它实际上没有(至少在 ROM 中没有,后来出现了 [SMON](https://www.c64-wiki.com/wiki/SMON) 等)。然后,在 80 年代末,8 位时代以及从机电层面到应用层面完全控制和理解你的家用电脑的时代已经结束。 快进到 2023 年,Brouce Hoult 以某种方式向 [r/RISCV](https://www.reddit.com/r/RISCV/comments/1446c0i/comment/jnft8wa/) 发起挑战,要将 WOZMON 移植到 RISC-V 汇编。我查看了原始代码,虽然我非常欣赏这类巫术并且实际上想接受这个挑战,但我决定不这样做。时代已经变了,今天不再需要将任何东西塞进 256 字节,虽然我开始这个项目只是"为了学习 RISC-V",但我希望代码是可维护的,意味着易于理解、结构更好、更可靠一些,并且易于被他人扩展,这样它在未来可能仍然对某些人有用。
标签:Findomain, GNU工具链, M-mode, QEMU, RISC-V, Ruby on Rails, RV128, RV32, RV64, RV64GC, S-mode, UART通信, 云资产清单, 内存转储, 内联执行, 反汇编器, 安全报告生成, 嵌入式开发, 底层开发, 快速连接, 机器代码监控, 汇编器, 汇编语言, 裸机编程, 身份验证强制, 逆向工程