编译器【免费下载链接】movfuscatorThe single instruction C compiler项目地址https://gitcode.com/gh_mirrors/mo/movfuscator点击查看免费下载本篇围绕仓库 poc/README.md 展开系统讲解 M/o/Vfuscator 1.0PoC 版本如何把 BrainF*k下称 BF源程序翻译成纯mov指令的 32 位 x86 程序并逐条对照 poc/movfuscator.c 源码解析-mmio、-nojmp、-cell16、-O各编译开关的底层实现。读完本篇你可以独立完成编译 BF → 生成 NASM 汇编 → 汇编链接为 mov-only 可执行文件的完整流程并理解只用一条指令实现图灵完备计算在寄存器、数据表与控制流层面的具体落地方式。1. 背景PoC 目录与 M/o/Vfuscator 1.0/2.0 的分工整个 movfuscator 仓库包含两代实现M/o/Vfuscator 1.0PoC位于 poc/ 目录是一个把 BF 程序编译成 mov 指令的编译器原作者说明其最适合与 Jeffry Johnston 的 BFBASICBASIC → BF 编译器配合使用构成一条 BASIC → BF → mov 的完整工具链M/o/Vfuscator 2.0仓库主体根 README.md 所述是完整的单指令 C 编译器movcc以 LCC 为前端。根 README 的 History 一节明确指出The original M/o/Vfuscator (M/o/Vfuscator 1.0) compiles programs from the esoteric language BrainF*!... It is still available as a proof of concept in the poc directory。其理论灵感来自 Stephen Dolan 的论文《mov is Turing-complete》该论文证明了 x86 的mov指令集合具备图灵完备性PoC 则是对这一定理的工程化验证。PoC 目录内容如下路径内容poc/movfuscator.c1.0 编译器本体单文件 C 程序poc/examples/四个已编译好的示例可执行文件32 位 ELFpoc/examples/README示例说明各自的编译来源与使用的开关poc/examples/demo.sh实时观察 MMIO 输出的小脚本poc/crackme/两个找密钥挑战程序用 2.0 编译2. 总体架构一个读标准输入、写标准输出的汇编生成器poc/README.md 给出的核心用法只有两行命令gcc movfuscator.c -o movfuscator # 编译编译器本体 ./movfuscator example.bf example.asm # 把 BF 翻译成 mov 汇编也就是说poc/movfuscator.c 本身就是一个 C 程序它从标准输入逐字符读取 BF 源码向标准输出打印 NASM 格式汇编。从源码结构看其处理流程分为三段main函数poc/movfuscator.c#L60-L643发射固定数据段.data/.bssBF 内存模型所需的全部数据表发射前导代码_start按开关决定是否插入 MMIO 映射或 SIGILL 信号处理的前置调用主翻译循环while (!feof(stdin))逐字符扫描 BF 源码对 - . , [ ]分别生成对应的 mov 序列poc/movfuscator.c#L337-L633最后补上程序回边。三个关键容量常量定义在文件头部#define ID_STACK_SIZE 8192 // BF 循环嵌套深度上限 #define BF_DATA_SIZE (128*1024) // BF 数据指针可寻址的内存单元数 #define MMIO_SIZE (128*1024) // MMIO 映射区大小必须 BF_DATA_SIZE数据段中BF 的单元格由宏c_s声明——每个单元格附带两个索引表条目d_前缀存单元格地址s_前缀是标志 → 目标地址的跳板表这是后续所有 mov 序列做条件寻址的基础poc/movfuscator.c#L107-L111%macro c_s 1 %1: dd 0 d_%1: dd 0 s_%1: dd d_%1, %1 %endmacro.bss段则放置三块128K的 BF 内存m/s/z和一块256*256的比较表epoc/movfuscator.c#L245-L249。3. 只用 mov 实现算术与逻辑查找表 内存比较这是整个 PoC 最核心的机制。加法不是add而是查表。编译器在数据段生成四张 256 项/65536 项的查找表poc/movfuscator.c#L145-L171incb: ; 8 位加一表: incb[i] (i1) 0xff %assign y 1 %rep 256 db y0xff %assign y y1 %endrep incw: ; 16 位加一表: incw[i] (i1) 0xffff %assign y 1 %rep 256*256 dw y0xffff %assign y y1 %endrep ; decb / decw 类似生成减一表于是 BF 的指令被翻译成读出单元值 → 用值作索引取incb→ 写回全程只有 movpoc/movfuscator.c#L433-L461mov eax, [on] mov ebx, [s_mseax] mov edx, [dp] mov eax, 0 mov al, [ebxedx] mov al, [incbeax] ; “加法” 查表 mov [ebxedx], al/移动数据指针dp同理只是改用incw/decw对 16 位指针值做查表增/减poc/movfuscator.c#L493-L535。比较运算使用e表64K 字节。生成的eq宏把取两个数比较变成在内存的同一张表里分别写入 0 和 4再读回其中一个字节——两地址相同则读到 4真不同则读到 0假poc/movfuscator.c#L173-L191%macro eq 3 mov eax, 0 mov edx, 0 mov ax, [%2] mov byte [eeax], 0 mov byte [e%3], 4 mov dl, [eeax] mov [%1], edx %endmacro ; neq 宏把 0/4 对调逻辑运算or/and/not则直接走三张小常数表o、a、no_0: dd 0,4、a_0: dd 0,0、n: dd 4,0poc/movfuscator.c#L129-L140以左操作数、右操作数为下标各查一次表即可。可以看到布尔值编码为4 真0 假初始标志on: dd 4。BF 的[]循环在没有跳转指令的前提下是这样翻译的编译器在 C 端维护一个 ID 栈上限 8192对应 BF 嵌套循环深度为每个[分配一个自增编号id发射的代码不跳过循环体而是更新一组线程状态——on执行开关、id当前循环号、otother tag——使[处根据单元值为 0 与否改写这些标志]处再按对称逻辑复位[见 poc/movfuscator.c#L545-L587]见 poc/movfuscator.c#L589-L629。从源码结构看整个 BF 程序因此被拉直成一个单一直线无限循环所有分支都退化为对标志位与单元格的 mov 读写[ ]语义靠标志状态维持而不再有任何控制转移指令。4. I/O 的两种实现int 0x80 与 MMIO 映射4.1 默认模式保留一条 int 0x80不传开关时.输出与,输入使用 32 位系统调用poc/movfuscator.c#L370-L383mov eax, 4 ; sys_write mov ebx, 1 ; stdout mov ecx, c mov edx, 1 int 0x80此时程序含int 0x80与末尾一条jmp严格意义已不是纯 mov——poc/examples/factor 就是按这种方式编译的。4.2 -mmio用内存映射文件替代系统调用加-mmio后编译器在_start处发射一段前导代码poc/movfuscator.c#L257-L313用常规调用建立两幅内存映射 I/O 区open(/dev/stdin, O_RDONLY)与open(/dev/stdout, O_RDWR)fd 分别存入fdi/fdo对输出文件lseek(fdout, MMIO_SIZE-1, SEEK_SET)再write一个字节——把文件撑到 128K对两个 fd 各做一次mmap(0, MMIO_SIZE, PROT_READ/PROT_WRITE, MAP_SHARED, fd, 0)得到只读映射src与可写映射dst。之后 BF 的,就是mov cl, [srcsri]从映射区读一字节.就是mov [dstdsi], cl写映射区配合incw表推进读写下标sri/dsipoc/movfuscator.c#L354-L408。这就是 README 中-mmio 允许用 mov 替代 int 0x80的含义。关键限制mmap只映射真实文件管道/终端不可用所以 I/O 流必须由文件承载——运行-mmio程序时必须写成./example sample_in sample_out而不能直接连终端详见第 7 节。5. 程序回边一条 jmp 还是故障 movBF 是一个无限循环的虚拟机程序末尾必须回到loop:标号。poc/README.md 给出的两条路默认发射jmp looppoc/movfuscator.c#L638-L640-nojmp改为发射一条会触发 SIGILL 的 movmov cs, ax ; ax 恒为 0写段寄存器立即触发非法指令异常编译器会先通过sigaction注册 SIGILL 处理器处理器指向loop本身poc/movfuscator.c#L315-L322extern sigaction mov dword [esp], 4 ; sigill mov dword [esp4], sa mov dword [esp8], 0 call sigaction并且每轮循环开头会先执行mov esp, [dsp]恢复栈指针抵消信号处理帧对esp的扰动poc/movfuscator.c#L329-L335。从源码结构看指令流因异常被打断、由信号处理函数把控制权送回循环起点用异常机制替代了控制转移这正是--no-mov-loop思路在 1.0 中的前身。-mov开关等价于同时打开-mmio -nojmppoc/movfuscator.c#L86-L89得到真正只有 mov 指令的程序体。6. 使用方法从 BF 到 mov-only 可执行文件的完整流程以下内容完整继承自 poc/README.md 的 Usage 一节并补充依赖前提第一步编译编译器本体gcc movfuscator.c -o movfuscator第二步把 BF 翻译成 mov 汇编./movfuscator example.bf example.asm第三步用 NASM 32 位链接器生成可执行文件nasm -felf example.asm -o example.o ld -melf_i386 example.o -o example完整流水线从 BASIC 到 mov-only 可执行文件./bfbasic.sh example.bas example.bf # BFBASIC: BASIC - BF ./movfuscator example.bf example.asm nasm -felf example.asm -o example.o ld -melf_i386 example.o -o example ./example适用前提与限制目标平台必须是 32 位 Linuxld -melf_i386生成 i386 ELF需要自备 NASM 与 binutils完整流水线中的bfbasic.shBFBASIC 包装脚本不在本仓库内需按原文档 References 提及的 BFBASIC 项目自行准备仓库未附带 BF 源码样例README 引用了外部 BF 程序集作为练习来源此处按规范不附链接若希望得到-mov级别的纯 mov 程序第三步运行前请确保 I/O 由文件承载。7. 编译开关详解文档说明 源码行为poc/README.md 列出的 Flags 全表如下并逐条对照源码开关文档说明源码行为-mmioUse memory mapped I/O. 用 mov 替代 int 0x80 做 I/O但要求 I/O 流由文件承载发射src/dst/sri/dsi等数据L216-L232与第 4.2 节的open/lseek/write/mmap前导-nojmp用一条故障 mov 替代唯一的 jmp 来实现程序循环注册 SIGILL 信号处理L315-L322程序尾部发射mov cs, axL635-L637循环开头恢复esp-mov只用 mov 指令等价于-mmio -nojmp同时置位两个开关L86-L89-cell16使用 16 位内存单元格所有读/写单元格的指令从al/cl/dl升级为ax/cx/dx加法查表改用incw/decw见第 3 节-O启用优化扫描 BF 源码中连续的 - 把重复次数记入repL343-L351生成时共享同一段取址设置、仅重复查表 mov如 L441-L446避免为同一指令重复发射完整序列关于-O值得说明它的优化不是把 100 个折叠成一次乘法而是去冗余——对...这样的连续序列只发射一次指针/标志取址代码随后重复发射查表 mov使输出显著变短、变快。8. 示例程序集examples/ 与 demo.shpoc/examples/ 提供了四个已编译的 32 位 ELF 示例均为ELF 32-bit LSB executable其 README 说明了各自的编译路径与开关组合恰好覆盖了第 7 节全部开关场景示例编译来源使用的开关结果nibblesC 经 M/o/Vfuscator2.0编译—非 1.0 产物贪吃蛇游戏w a s d控制移动末尾的 jmp 与外部调用尚未 mov 化factorBF 经 M/o/Vfuscator1.0无保留int 0x80与末尾唯一一条jmp的基线示例primesBF 经 M/o/Vfuscator1.0-nojmp移除末尾 jmp 的示例99_bottlesBASIC 经 BFBASIC → BF → M/o/Vfuscator1.0-mmio-nojmp只含 mov 指令的完整流水线成果输出走 MMIO要求文件承载的 I/O 流运行方式摘自 poc/examples/README./example # 普通示例直接运行 # -mmio 编译的程序必须由文件承载 I/O ./example sample_in sample_out # 或用仓库自带脚本边跑边看输出 ./demo.sh exampledemo.sh 的实现很简短touch出sample_in/sample_out两个空文件把示例进程在文件重定向下放到后台运行然后watch -t -n .1 tail -20 sample_out | sed ...每 0.1 秒刷新一次输出文件尾部最后pkill结束示例。它本质上是给 MMIO 程序提供了终端实时查看的体验补偿——因为-mmio程序无法直接写终端。9. 附带挑战poc/crackmepoc/crackme/ 还包含两个挑战程序目标都是找出密钥crackme1一个约 15 行的 C 语言 crackme使用 M/o/Vfuscator 2.0 编译crackme2与 crackme1 结构相同但换了密钥并将strcmp改为常数时间比较用于防止针对 crackme1 的某些最简求解思路。这两个程序虽然由 2.0 编译但与 PoC 目录放在一起为读者提供了一个动手逆向mov-only 程序的真实靶子由于汇编体只有 mov传统基于控制流与运算指令模式的静态分析手段基本失效。10. 小结与延伸阅读poc/movfuscator.c 用不到 700 行 C 代码完整演示了mov 图灵完备的工程化路径查表做算术、内存写入-读回做比较、标志位拉直控制流、MMIO 映射做 I/O、故障 mov 信号处理做回边与仓库主体的 2.0movccLCC 前端的完整 C 编译器相比1.0 的价值在于概念验证与最小实现——根 README.md 的 History 一节即以此定位 poc 目录1.0 的 BF 数据区上限为 128KBBF_DATA_SIZE、循环嵌套上限 8192ID_STACK_SIZE使用时需自行确保 BF 程序规模在范围内参考资料原作者为 chris domasxoreaxeaxeax理论依据为 Stephen Dolan 的论文《mov is Turing-complete》BASIC → BF 前端建议使用 Jeffry Johnston 的 BFBASIC原文档 References 一节列出按输出规范此处不附外部链接。赞分享编译器【免费下载链接】movfuscatorThe single instruction C compiler项目地址https://gitcode.com/gh_mirrors/mo/movfuscator点击查看免费下载相关推荐如何快速本地部署Wan2.1-Fun-1.3B-InPAI视频生成环境配置与权重下载一步到位指南 如何快速本地部署Wan2.1 Fun 1.3B InPAI视频生成环境配置与权重下载一步到位指南 想要在本地电脑上体验强大的AI视频生成功能吗Wan2M/o/Vfuscator 完全实战指南一个只输出 mov 指令的单指令 C 编译器如何工作M/o/Vfuscator 完全实战指南一个只输出 mov 指令的单指令 C 编译器如何工作 本篇以 README https://link.gitcode.编译器Skidfuscator核心功能深度解析第三代控制流混淆技术揭秘Skidfuscator核心功能深度解析第三代控制流混淆技术揭秘 你是否正在寻找一款真正能保护Java代码安全的生产级混淆工具Skidfuscator作为基应用安全开发工具上一篇Lap数据库迁移机制详解v0.1到v0.2如何无痛自动升级下一篇RT-Thread 在珠海泰为 TAE32F5300_EVAL_BOARD 开发板上的 BSP 移植与快速上手指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考