首页
社区
课程
招聘
[原创] QBDI 源码阅读笔记
发表于: 2025-7-21 00:58 12755

[原创] QBDI 源码阅读笔记

2025-7-21 00:58
12755

用的 Ubuntu 编译的 Linux 版本, android 版本也编译了, 不过为了方便调试还是用的 Linux, 两者编译有些许不同, 原理和代码一样.

长文预警! 长文预警! 长文预警!

感觉前后文联系很强, 写的比较细, 几乎是每个地方都刨根问底, 如果跳着看, 注意回看前文, 有些前面写了, 我就没在后面重复.

建议一个屏幕打开源码, 一个屏幕读文章. 这样可以自己亲身体会一下.

作者也是小白, 难免疏忽, 如有错误还请您指出.

617K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6c8b7V1c8u0i4K6u0r3f1f1u0p5d9b7`.`.

QBDI 实现了一个自己的 ir, 然后将要插桩的二进制指令与插桩代码统一翻译成自己的 ir, 然后借助 llvm 的组件转成对应架构二进制指令, 天才的构思. 接下来让我们一点点揭开它神秘的面纱.

跑一下 build/examples/ 中的可执行程序, 看看效果

可以正常跑, 好了现在开始阅读 example 目录下的代码, 首先看下 CMakeLists.txt, examples/CMakeLists.txt:

添加了 mnemonicFilter modules_c modules_cpp thedude_c thedude_cpp cryptolock_cpp cryptolock_c 可执行文件, 然后再看 examples/c/CMakeLists.txt:

创建 fibonacci_c 可执行文件和 tracer_preload_c 共享库. 斐波那契数列真是老演员了, 我们从这里开始看起, 上边的 c 语言示例同样讲解的这个代码, 我觉得官方文档写的真好, 我无论怎么讲都显得冗余, 但为了完整性, 我还是简单概括下:

进入 src/Engine/VM_C.cpp 中查看代码:

可以看到代码很简单, 就是处理参数, 返回了一个 VM 类的实例, 跟进去 VM 类看一下, 来到了 include/QBDI/VM.h 下, 整个文件都是这个类的定义, 支持了很多的方法, 因为太长所以只截了几张图:

我们看一下它的构造函数:

将参数传入到了 Engine 类中, 我们同样找一下 Engine 类初始化函数:

可以看到, 很复杂, 初始化了很多类, 我们暂时不分析, 继续往下看.

可以看到是在调用 VM 类中的 getGPRState 方法, 再跟入 engine->getGPRState 来到 src/Engine/Engine.cpp 中:

返回了 Engine 类 中的 curGPRState 属性, 让我们跟踪一下 curGPRState:

在 include/QBDI/arch/X86_64/State.h 中找到了 GPRState 结构体的定义:

可以看到在 include/QBDI/arch/ 下同样有其他架构的一些支持, 根据我们编译时运行的脚本 GPRState 等有不同的实现.

在 src/Engine/Engine.cpp 与 src/Engine/Engine.h 中实现了 Engine 类, 而 VM 类中的 engine 属性就是 Engine 类的实例. 我们不着急分析这些类, 而是分析这个斐波那契示例实现的过程, 慢慢的就明白这些类扮演了什么样的角色.

我按执行顺序从上往下贴的, 可以看到这段是为虚拟栈分配了内存空间, 并且再内存大小上做了对齐处理, 然后将虚拟寄存器中的栈指针, 帧指针赋值. (虚拟栈存在堆上, 虚拟寄存器存在寄存器中)

然后来到 src/Engine/VM_C.cpp 文件来看实现:

与上边的 qbdi_getGPRState 类似, 还是调用 VM 类中的方法, 我们接着往下看, 跟入 addCodeCB 方法:

一点点看, 首先是 True::unique(), 跟进去:

发现就是一个模板工具类, 用来快速创建 unique_ptr 只能指针(这个指针的使用与 rust 中的所有权转移类似, 一个 std::unique_ptr 对象在任何时刻只能拥有一个指向动态分配对象的指针. 它不允许拷贝构造或赋值, 但可以通过 std::move 将所有权从一个 std::unique_ptr 转移到另一个), 现在我们疑惑的是 AutoUnique 这个模板类在 True::unique 调用时 typename T, typename U 是什么, 为此我们还要跟入 True 类一探究竟:

在跟入 AutoClone<PatchCondition, True>:

看到这, 我们可以知道 T 类型是 PatchCondition, U 类型是 True, 而 AutoClone AutoUnique 模板类相当于插入继承链, 本来是 True 类继承 PatchCondition 类变为了 True 继承 AutoClone 继承 AutoUnique 继承 PatchCondition. 而 True::unique(address) 创建的也就是一个类型为 True 的智能指针, 而翻回头来 InstrRuleBasicCBK::unique 也是类似的实现:

好了现在我们可以回过头来跟入 engine->addInstrRule 函数了:

把插桩规则的个数 instrRulesCounter 作为 id 返回, 并规定了最大个数 id < EVENTID_VM_MASK, 再看 this->clearCache(rule->affectedRange()), 先跟入 rule->affectedRange(如果跳转错误, 也可以搜索 InstrRuleBasicCBK::affectedRange):

InstrRuleBasicCBK 的 condition 属性就是 True::unique(), 而 True 类继承自 PatchCondition, 在跟入 condition->affectedRange, 就到了 PatchCondition 类中:

Range 与 RangeSet 太长了, 说下作用, 不在贴出代码, 感兴趣可以到 include/QBDI/Range.h 中自行查看:

回到 Engine::addInstrRule 函数中来看剩余这部分:

std::make_pair 创建了一个 std::pair 对象, 将 id 和 rule 组合在一起. std::upper_bound 是一个标准库函数, 用于在有序范围内找到第一个不满足给定条件的元素. 而 instrRules 是std::vector<std::pair<uint32_t, std::unique_ptr<InstrRule>>> 类型, 所以这段代码就是将 rule 插入到 instrRules 中第一个优先级小于或等于 rule 的优先级的位置. 使得 instrRules 中的规则始终按照优先级从高到低.

总结一下 qbdi_addCodeCB 函数就是创建插桩规则然后按输入的优先级存起来. 其中插桩规则保存了回调函数, 插桩位置, 用户参数...具体可以看 InstrRuleBasicCBK 类的构造函数.

二话不说跟进 addMnemonicCB:

可以看到和 qbdi_addCodeCB 函数实现唯一的差别就是 True::unique() 变为了 MnemonicIs::unique(mnemonic). mnemonic 就是咱们传进来的出发回调的特定指令, 在斐波那契这个示例中就是 "CALL*". 而 MnemonicIs 类与 True 类似:

看到这结合上面不难猜测 True 和 MnemonicIs 类都是用来判断回调条件的, 而这个 test 虚函数就是判断的统一调用. 所以我们在来看看 MnemonicIs::test:

我们先来看看第一个参数的类型 Patch 类, 跟入来到了 src/Patch/Patch.h 中, 代码不长, 将头文件注释:

在跟进 llvmcpu.getInstOpcodeName 看一眼:

看样子是在返回反汇编指令, 行, 这里先打住, 乱猜也是浪费时间, 接下往下看.

从上到下一步步跟入, execBroker 是 Engine 类的一个属性 ExecBroker *execBroker, 也是 ExecBroker 类的实例, 我们继续跟入 execBroker->addInstrumentedModuleFromAddr 函数:

看函数的名字就很容易知道在做什么, 写出注释. 首先先看看 getCurrentProcessMaps 函数, 来到了 src/Utility/Memory_linux.cpp 中, 还是会根据你编译的架构确定函数实现, 在同级目录下同样有其他架构的实现:

闲话少说来看看 getCurrentProcessMaps 函数的实现:

也就是通过 Linux 系统调用 getpid() 获取当前进程的 pid, 然后通过读取 /proc/[pid]/maps 文件获取内存映射, 然后将每条映射存进 std::vector<MemoryMap> maps 中.

在来看 addInstrumentedModule(回看一下上面的 ExecBroker::addInstrumentedModuleFromAddr 函数):

代码很简单就是将找到 name 对应的内存映射, 然后将映射的地址 保存到 ExecBroker 类的 instrumented 属性中. (我真要控制你了, 又调用 getCurrentProcessMaps() 遍历一遍)

好了, 现在再看这个函数感觉一切都明了了(再自己回顾一遍):

头文件的注释:

再来看具体实现:

用 va_start(ap, argNum) 处理可变参数, 参数存在 ap 中(随机提问, 在汇编层是如何实现可变参数的? 事实上只要知道可变参数的起始地址, 知道参数个数, 调用约定就可以顺藤摸瓜一点点找过去), 跟进 callV 函数:

将可变参数传入列表中在调用 callA, 继续跟入 callA:

首先检测栈指针, 然后调用 simulateCallA(看注释的意思是处理参数, QBDI_GPR_GET 在上边贴过源码, 忘记的话可以自己看一下), 跟进去:

根据目标架构的调用约定, 将参数加载到寄存器或栈中, 不同的架构有不同的处理方式, FRAME_LENGTH 决定了栈帧可以容纳的最大参数数量(一般来说, 在各个架构的调用约定中, 参数先通过寄存器传参, 然后再通过栈传参). 回过头再来看看 run:

首先看看设置的回调函数 stopCallback:

再跟入 addCodeAddrCB 中:

似曾相识的感觉有没有, 注册回调函数?!(忘记的朋友可以翻一翻上边). 有了上边的经验, 我们直接看 AddressIs:

如果我没记错的话 AddressIs::unique(address) 中的 address 应该是 42, 上边 #define FAKE_RET_ADDR 42 定义的. 带着疑问, 我们回到 VM::run, 跟入 bool ret = engine->run(start, stop)(等下, 我要再帮你回忆一下, start 是我们要插桩的函数的地址, stop 是注册回调返回的回调 id):

一个很长的函数, 让我们一点点看吧, 首先是 QBDI_REQUIRE_ABORT(not running, "Cannot run an already running Engine") 确保引擎当前没有在运行. 然后是 rword currentPC = start, 将 PC 指向了目标函数地址. 然后 curGPRState = gprState.get() 获取当前通用寄存器, curFPRState = fprState.get() 获取当前浮点寄存器, gprState 与 fprState 都是 Engine 类的属性:

GPRState 结构体的定义可以在前面找到, FPRState 与 GPRState 类似, 同样在 include/QBDI/arch/X86_64/State.h 中, 每个架构有不同的实现:

接着往下看:

还记得 execBroker, Engine 中存的 ExecBroker 实例, 我们用这个 ExecBroker 类记录了插桩的作用范围, 这里就是在检查目标函数地址是否在指定范围中, 不在的话直接返回. 再接着:

将 running 属性设置 true, 表示正在运行, 然后进入一个 do-while 大循环, 直到当前 PC 指针 currentPC 为 传进来的回调函数 id stop. 好了, 是时候进入主逻辑, 也就是这个 do-while 大循环, 先是 VMAction action = CONTINUE, 哦, 状态机? 我们跟进这个枚举看一看:

接着往下看, 一个大的 if-else:

看注释很容易理解, if 中处理了 currentPC 不在插桩范围内的情况, else 是执行, 我们先看 else 掌握执行的主要逻辑, 再来看 if 中的逻辑. 首先是 VMEvent event = VMEvent::SEQUENCE_ENTRY, 还是跟入枚举对象:

再回过头来看这段:

在 ARM 架构时, 区分 ARM 模式(32 位指令集)和 Thumb 模式(16 位指令集). 在往下看:

检查是否有待处理的缓存刷新操作, 更新缓存的 gprState 与 fprState 为当前值. 首先明确 blockManager 属性是 std::unique_ptr<ExecBlockManager> 类型, 也就是 ExecBlockManager 类的智能指针. 再看一下 isFlushPending:

没什么好说的, 再看一下 flushCommit:

我们需要关注两个属性 regions 与 codeBlockMap, 首先是 std::vector<ExecRegion> regions, 看 ExecRegion 类(比较短就贴出了):

再来看 std::map<rword, ExecBlock *> codeBlockMap, 也就是 ExecBlock 类, 非常的长, 在 src/ExecBlock/ExecBlock.h 文件中, 可以自行查看完整的实现:

它通过两块连续内存(代码块 codeBlock + 数据块 dataBlock)存储和运行插桩后的基本块, 同时提供执行控制、内存管理、记录等功能.

我们同样再找一下 ExecBlockManager 类的构造:

有个印象就行, 直到大概初始化了什么.

在回过来往下看:

看注释像是在检查能不能用缓存, 我们第一次执行到这, 肯定没有, 不过我们还是分析一下, 首先让我们先看看参数 currentPC 现在的值是 strat 也就是目标函数的地址, 然后 curCPUMode 是 Engine 类的 CPUMode curCPUMode 属性, 让我们看一下 CPUMode 是什么:

还是在 include/QBDI/arch/X86_64/State.h 中的一个枚举, 代表了 cpu 状态

在看看 currentSequence 的类型 SeqLoc:

看样子是一个表示位置的结构体, 好了现在跟入 blockManager->getProgrammedExecBlock:

又是一个很长的函数, 先跟入 searchRegion:

一个二分查找, 找到 address 所在的块的下标返回(regions 类型上面有讲过, 可以回看), 这样的话 regions 这个 vector 中存储的块的执行地址就是按循序排列的.

再回到 getProgrammedExecBlock, 往下看, 如果找到了就进入 if 逻辑, 否则返回一个 nullptr, 我们看一下 if 逻辑:

根据注释, 我们大概知道它的逻辑, 但是为了弄清楚还是关注几个点. 首先跟入 getExecRegionKey:

将地址在 arm 结构时做特殊处理, 不再赘述. 再看一眼 region.sequenceCache 的类型: std::map<rword, SeqLoc> sequenceCache, 一个地址与 SeqLoc 结构体(前面贴出过代码)的映射.

往下看, 看到 region.blocks[seqLoc->second.blockIdx]->selectSeq(seqLoc->second.seqID) 我们就大概明白 SeqLoc 结构体了, 它记录了指令序列的起始 seqStart 与结束 seqEnd 的地址, 以及所在 blocks 的索引 blockIdx, 自身的 id seqID, 所在块结束的地址 bbEnd. (还是贴出来, 方便你看):

可以再看下 selectSeq 函数:

看一下 seqRegistry 的类型: std::vector<SeqInfo> seqRegistry, 也就是说 seqID 实际上是 seqRegistry 中的下标, 看一下 SeqInfo:

再看一下 instRegistry 的类型: std::vector<InstInfo>, 还是一个 vector, 看 InstInfo:

好了, 现在可以理一理了, SeqLoc 记录了指令序列的位置以及 seqID, 根据 seqID 可以作为 seqRegistry 的下标找到 SeqInfo, 姑且称之为序列详情(根据这句代码 currentInst = seqRegistry[currentSeq].startInstID), 而用 SeqInfo 中的 startInstID 与 endInstID, 可以作为 instRegistry 属性的下标找到 InstInfo, 姑且称之为指令详情(根据这句代码 static_cast<rword>(instRegistry[currentInst].offset). 每个结构体还额外记录了很多信息. 而 ExecBlock::selectSeq 函数就是将当前块(ExecBlock 的实例)的一些属性设置成 seqID 对应的, 包括 currentSeq 当前序列, currentInst 当前指令, context->hostState.selector 上下文中的选择子, context->hostState.executeFlags 上下文中的执行标志.

回到 if 逻辑, 继续看 const auto instLoc = region.instCache.find(target), 找到 instCache 属性的类型 std::map<rword, InstLoc>, 看 InstLoc:

简直就是 sequenceCache 的翻版, 不在多说, 来看 block->getSeqID 函数:

多少有点循环链表的意思了, block->getSeqStart 也是类似:

再看 block->getInstMetadata:

使用了 instMetadata 属性, 我们来看看它的类型 std::vector<InstMetadata> instMetadata, 跟入 InstMetadata 类:

InstMetadata 存储了指令的很多信息.

再来看看 block->splitSequence:

构造 SeqInfo 实例, 存入 seqRegistry 中. 到此我们弄清楚了 getProgrammedExecBlock 的全部流程, 在回看一遍, 自己理一理.

回到 Engine::run 接着往下看:

这里就是处理缓存中没有时的逻辑, 整体看一下, 从名字也大概知道, handleNewBasicBlock(currentPC) 是生成新块, event |= BASIC_BLOCK_NEW 记录事件, blockManager->getProgrammedExecBlock 是刚刚看过的读取当前块. 所以我们重点看 handleNewBasicBlock:

先进入 patch:

通读一遍后, 我们可能已经知道它在做什么了, 还是补充说明几个点. llvm 部分我们不在刨根问底, 因为这篇写的已经够多了, llvm 的源码阅读放在别的文章.

先从 execBroker->getInstrumentedRange().getElementRange(start) 开始, execBroker 是我们的老朋友了(在上面获取内存 map, 设置插桩范围时就是它), execBroker->getInstrumentedRange:

instrumented 这个属性就存储了插桩的范围, 不记得的回看一下之前的分析, 在看 getElementRange:

返回了当前的 Range 用于计算块大小等, 再来看 llvmcpu.getInstruction:

LLVMCPU 这个类对 QBDI 需要的 llvm 中的功能做了封装, 在 src/Engine/LLVMCPU.h 中:

disassembler 属性类型为 std::unique_ptr<llvm::MCDisassembler>, 让我介绍一下:

而 disassembler->getInstruction 是 llvm api, 我们简单看一下头文件的注释:

再来看看 showInst:

asmPrinter 属性的类型是 std::unique_ptr<llvm::MCInstPrinter>, 让我介绍一下:

MSTI 属性类型是 std::unique_ptr<llvm::MCSubtargetInfo>, 让我介绍一下:

看看 asmPrinter->printInst 的注释:

回到 Engine::patch 我们接着看, patchRuleAssembly 是 Engine 类的属性, 是 PatchRuleAssembly 类的实例, 在上面 Engine 类构造函数中有: patchRuleAssembly = std::make_unique<PatchRuleAssembly>(options), 再来看 patchRuleAssembly->generate:

还记得这个函数的参数是什么吗? inst 是 llvm::MCInst 的实例, 存储解析出的机械指令, address 是指令地址, instSize 指令大小, llvmcpu llvm 方法封装, patchList 储存返回的地址.

看上去逻辑很明了. 用传进来的参数构建 Patch 实例, 然后用已有的 patch 规则处理这个实例, 然后处理可以合并的 patch. 但是已有的补丁规则 patchRules 是哪来的, 顶着头顶上的问号, 我们找一找, patchRules 是 PatchRuleAssembly 的属性, 我们找到 PatchRuleAssembly 的构造函数:

跟入 getDefaultPatchRules, 一个巨无霸函数:

这个函数位于 src/Patch/X86_64/PatchRuleAssembly_X86_64.cpp 中, 对于不同的架构有不同的实现:

这个函数我们要仔细分析一下, 先来看 PatchRule 类, 在 src/Patch/PatchRule.h 中:

我们看看 canBeApplied 方法实现:

和前面对上了, 还记得前面的注册回调函数的 True 和 MnemonicIs 类吗, 由各种子类实现 test 虚函数, 然后由 canBeApplied 调用.

apply 有些复杂, 留在后再调用时在分析. 回到 getDefaultPatchRules, 再来看 conv_unique 方法:

模板处理可变参数, 这个应该很经典, 那么 conv_unique 函数就是将传入的未知格式参数压入 vector, 然后返回 vector(随机提问: 模板处理可变参数在汇编层的实现?)

再往下看, 我们看到了各种 GetPCOffset::unique SubstituteWithTemp::unique ModifyInstruction::unique SetOpcode::unique.......他们就是 QBDI 的 ir. 我们随便找一个规则解读一下:

这是对 JMP *[RIP + IMM] 的转换, 用 QBDI 的 ir 去实现. rules 是 std::vector<PatchRule> 类型, 也就是说 emplace_back 进去的会被用来构造 PatchRule, 还记 PatchRule 的构造吗:

第一个是判断知否执行的条件, 第二个是执行的 ir. 先看条件:

从名字上也很容易看出来: 指令是 llvm::X86::JMP64m 并且使用 PC 寄存器. 我们直接看 OpIs 类:

patch.metadata.inst.getOpcode() 是 llvm 的 api, 一看就懂, 不在赘述. OpIs 类就是用来判断汇编指令的, 也就是操作码. 再看 UseReg:

UseReg 类检查指令是否使用了指定的寄存器. 再看 And 类:

就是将传入的条件们包装起来, 然后在 test 实现中, 使用 std::all_of 要所有的条件都返回 true 才是 true,

条件实现都在 src/Patch/PatchCondition.h src/Patch/PatchCondition.cpp 中. 条件判断的类都继承了 PatchCondition 类, 包括上面回调函数的也是, PatchCondition 类在上面贴出了, 不在重复, 子类都要实现 test 这个核心的虚函数.

然后是生成 QBDI 的 ir, 围绕着这个注释讲:

首先是 Temp(0) := RIP + Constant(0) 对应 GetPCOffset::unique(Temp(0), Constant(0)), 先看看 Temp:

可以看到就是包装了一层, 像 Constant, Operand 等也是类似, 都是包装一层赋予一定含义, 我们不在意义查看, Reg 类略有不同, 贴出注释:

再跟进 GetPCOffset:

首先我们关注的是它的父类 PatchGenerator 类:

不出意外, 应该是每个 QBDI ir 生成类都是 PatchGenerator 的子类, 并且都位于 src/Patch/PatchGenerator.h src/Patch/PatchGenerator.cpp 中, generate 应该是生成 ir 的核心虚方法.

接下来看一下 GetPCOffset::generate 的实现:

根据是传入的是常量还是操作数, 由不一样的调用:

可以看到最终都是调用 LoadImm::unique, 我们先不着急跟进去, 先看看 patch.metadata.endAddress:

获取指令结束地址, patch.metadata.inst 是 llvm::MCInst 的实例, 还记得吗, 咱们之前调用 llvm api 解析出来的. op 是操作数索引, patch.metadata.inst.getOperand(op).getImm() 就是调用 llvm api 获取索引对应的 操作数.

现在跟进 LoadImm, 来到 src/Patch/RelocatableInst.h 中:

同样先看看父类 RelocatableInst:

看名字是用来构建重定位指令.

现在看看 LoadImm 的两个虚方法, 在 src/Patch/X86_64/RelocatableInst_X86_64.cpp 文件中, 同样是根据架构改变:

看到着我们大概就知道了, 这个就是用来赋值的, mov 指令, 也就是组成了 Temp(0) := RIP + Constant(0), 即如下伪代码:

我们跟入 mov64ri, mov64ri32, mov32ri:

可以看到是在构造对应的 llvm::MCInst 实例返回. 好了这里我们全部理清了.

接下来看看下一条的实现 JMP *[RIP + IMM] --> MOV Temp(1), [Temp(0) + IMM]:

首先是 ModifyInstruction:

来看看 generate 方法:

与 And 类似, 调用传入的 transforms 中每个实例的 transform 方法, 将当前汇编指令的信息传入.

然后看看传入的 transforms 从 SubstituteWithTemp开始, 在 src/Patch/InstTransform.h中:

它的父类 InstTransform:

与之前的类似. 让我们来看看 SubstituteWithTemp::transform

放到我们这个例子中, 也就是将 JMP *[RIP + IMM] 中的 RIP 换成 Temp(0) 成了 JMP *[Temp(0) + IMM].

也就是说为之前这个 Temp(0) 临时对象分配一个真实的寄存器, TempManager 类也就是用来管理临时寄存器的以及这种映射关系的, 篇幅原因, 我们不在细看, 并不复杂, 感兴趣的朋友可以自行查看.

再回头看:

跟入 SetOpcode 类:

这个简单粗暴, 直接将汇编指令种类替换, 即由 JMP *[Temp(0) + IMM] 变为 MOV *[Temp(0) + IMM].

再来看:

跟进 AddOperand:

猜也猜到它的作用了, 向指令中插入新的操作数, 来看 transform 的实现:

即将 MOV *[Temp(0) + IMM] 变为 MOV Temp(1), [Temp(0) + IMM].

最后是 DataBlock[Offset(RIP)] := Temp(1):

我们跟进 WriteTemp:

查看 generate 方法的实现:

生成可重定位指令, 将临时值写入指定位置, 因为我们是 OffsetType, 所以跟进 StoreDataBlock, 其他的感兴趣可以自行查看:

来看看 reloc 方法, 来到了 src/Patch/X86_64/RelocatableInst_X86_64.cpp 中, 不同的架构实现不同:

mov64mr 与 mov32mr 位于 src/Patch/X86_64/Layer2_X86_64.cpp, 同样因架构而异:

这个 StoreDataBlock 类的作用是: 生成将寄存器值存储到数据块的 llvm::MCInst 指令​​. 这里就是将 Temp(1) 寄存器的值覆盖原 PC 值.

ok, 回到 getDefaultPatchRules 函数, 其他的规则也是类似, 不在一一分析. 我们走了太远, 已经快要忘记来时的路, 现在让我们回到 Engine::patch 方法. 在它的 do-while 循环中, 不断调用 patchRuleAssembly->generate, 用的规则将汇编指令转成 QBDI 自己的 ir, 然后, QBDI 的 ir 再到 llvm::MCInst. 在根据是否改变 PC 分割出基本块, 如果改变了那就是一个基本块, 这样我们将结果存入 basicBlock 中返回, 反之继续. 回到了 Engine::handleNewBasicBlock 方法(再贴一遍):

然后进入 blockManager->preWriteBasicBlock:

就是在找一个大小合适缓冲区位置, 然后返回要存的 patch 数量. 进入 instrument 方法:

代码也很好懂, 就是从刚刚生成好的基本块中取出每个指令 patch, 然后调用我们设置的插桩规则 instrRules (在设置回调函数那里写入的, 可以搜索 InstrRuleBasicCBK::unique). 在我们这个例子中 rule 的类型为 InstrRuleBasicCBK, 取出每条插桩规则看看能不能插桩 rule->tryInstrument. 我们看一下 InstrRuleBasicCBK 类中的 tryInstrument 方法:

先调用 canBeApplied 判断是否可以插桩:

调用 condition->test. 上面已经讲过不在赘述. 然后看 instrument(patch, patchGen, breakToHost, position, priority, tag) 的参数, patch 是汇编指令对应的 ir, patchGen, breakToHost, position, priority, tag 都是 InstrRuleBasicCBK 类属性, InstrRuleBasicCBK 类在上面贴出来过, 不在重复, 让我们去看看 InstrRuleBasicCBK 的构造函数:

patchGen 看样子是将我们传入的回调函数和自定义参数的 patch, position 是插桩位置(指令前或者指令后), breakToHost 传入的固定值 true, priority 传入的优先级, tag 与 position 相关.

看看 getCallbackGenerator 方法的实现:

看一眼 GetConstant:

好吧, 这些代码应该比较熟悉了, 概括一下就是 MOV REG64 temp, IMM64 cst, 这个注释.

再看 GetInstId:

再来看看 InstId:

也就是将执行块的 id execBlock->getNextInstID() 当作立即数, 用 mov 放入 Temp(0) 寄存器中.

回来再来看 instrument 函数:

还是整体浏览一遍, 然后找不懂得点查看实现, 首先是 RelocatableInst::UniquePtrVec instru, 这个是最终插桩代码, 我们看看它的属性 using UniquePtrVec = std::vector<std::unique_ptr<RelocatableInst>> 可重定位的指令集合, 将 patchGen 中的先放进来:

看看 append 函数:

让我们理一理: generate 方法会对应一条或者多条指令, 以 RelocatableInst 数组的形式返回, RelocatableInst 类的 reloc 会返回 llvm::MCInst 实例.

然后看这里:

根据 position 处理插桩位置, 是在指令前还是指令后.

然后是这里:

GetConstant(Temp(0), Constant(address)).generate(patch, tempManager) 在前面讲过, mov Temp(0), address, 将插桩地址赋给 Temp(0) 寄存器, 再看后面这个 append: tempManager.getRegForTemp(0) 前面讲过, 为临时寄存器配分一个实际寄存器. 看看 SaveReg:

这个 StoreDataBlock 咱们再前面也讲过, 概括就是这个注释 MOV MEM64 DataBlock[offset], REG64 reg, 看完这段咱们应该懂了 SaveReg(tempManager.getRegForTemp(0), Offset(Reg(REG_PC))) 就是将 MOV MEM64 DataBlock[PC], Temp(0), 将寄存器 Temp(0) 中的值写入数据块偏移为 Reg(REG_PC) 的值. 也就是将寄存器 Temp(0) 中的值写入 PC.

再回到 InstrRule::instrument, 往下看 tempManager.generateSaveRestoreInstructions:

LoadReg 与 SaveReg 相反, 代码非常相似, 从数据块加载值到对应寄存器, 而这个函数是用来生成寄存器的保存和恢复的指令, 它根据指定的数量 unrestoredRegNum, 决定哪些寄存器需要保存但不需要恢复, 哪些寄存器需要保存和恢复. 生成的指令分别存储在 saveInst 和 restoreInst 列表中, 未恢复的寄存器存储在 unrestoredReg 列表中.

再看 prepend 函数:

没什么好说的, 最后看 patch.addInstsPatch 函数:

按优先级存入 instsPatchs 属性中. 好了 InstrRule::instrument 函数就是将我回调函数与自定义参数处理好, 记录到 patch 的属性中.

现在回到 Engine::instrument 函数(在贴一次):

看最后的 patch.finalizeInstsPatch:

看一下 RelocTag 类:

在看一下 TargetPrologue 类:

RelocTag 与 TargetPrologue 类不在多说.

Patch::finalizeInstsPatch 方法是将插桩代码的指令从 instsPatchs 属性中读取真的插入到 patch 中, patch 对应了一条汇编指令, 由多指令实现.

回到 Engine::handleNewBasicBlock(再贴一次):

看最后的 blockManager->writeBasicBlock(std::move(basicBlock), patchEnd) 将指令写入缓存(这里我们插入到指令是插到 patch, 而 blockManager->preWriteBasicBlock 预分配的空间是按 patch 个数, 所以预分配的空间不会因为插入插桩代码和不足):

浏览器一遍函数. 这里构建了指令序列 SeqLoc, 对应了一条 patch 中包含的一条或者多条指令的范围. 存入 sequenceCache instCache 缓存, 和前面串起来了. 我们也理一理, region 中包含很多 block, 然后 block 包含很多 seq, seq 中包含很多 inst.

然后看这里:

跟入 writeSequence:

代码也很读, 根据注释, 我们大体懂了做什么, 我们跟入 writePatch

先判断是否已经填满一个块, 然后调用 applyRelocatedInst:

读完后, 让我们关注 llvmcpu.writeInstruction(inst->reloc(this, llvmcpu), stream, getCurrentPC()), 这里调用了 reloc 返回 llvm::MCInst 实例, 跟入 writeInstruction:

这里调用了 llvm 的 api assembler->getEmitter().encodeInstruction 将 llvm::MCValue 转为机器码, 存入 CB. 回头看一下 writeCodeByte:

将机器码写如 codeBlock 中.

好了, 终于读完了 Engine::handleNewBasicBlock 方法, 我们回到 Engine::run 方法, 走了太远了, 我们再贴一下它的代码, 并且注释一下, 帮你恢复死去的记忆:

有没有感觉已经很明了了, 我们再看几个函数 signalEvent:

再来看 curExecBlock->execute 执行函数:

先看 run 函数中的 qbdi_runCodeBlock 如何执行的:

找到 src/ExecBlock/X86_64/linux_X86_64.s 中的 __qbdi_runCodeBlock, 还是不同架构实现不同:

这里调用汇编进入我们之前用 llvm 写的机器码.

还记得上边我们如何插入插桩指令的吗? 我们有三条步, 回调函数地址赋值给 hostState.callback, 将用户自定义参数赋值给 hostState.data, 将执行块 id 赋值给 hostState.origin. 然后执行完一段后, 会跳回来检查是不是填好了回调 hostState.callback, 如果有就执行:

好了, 差不多就写到这, 自己再回顾一遍 Engine::run 函数. 想一想都做了什么...

整体看下来, QBDI 的代码写的非常好, 规范, 工整, 像一个艺术品.

我们的 QBDI 之旅告一段落, 等等, 我们是不是还忘了什么: 收尾, 某个被跳过的 if...好吧, 已经写了足够多了, 大行不顾细谨, 不在纠结了.

mkdir build
cd build
../cmake/config/config-linux-X86_64.sh
# cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..
# ln -s compile_commands.json ..
# 要安装到系统目录, 这样头文件可以直接导入
sudo ninja install
mkdir build
cd build
../cmake/config/config-linux-X86_64.sh
# cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..
# ln -s compile_commands.json ..
# 要安装到系统目录, 这样头文件可以直接导入
sudo ninja install
# 编译 examples/ 程序
cd build
# 编译 debug 版可以配合 gdb 调试
cmake -DQBDI_EXAMPLES=ON -DCMAKE_BUILD_TYPE=Debug -DQBDI_LOG_DEBUG=ON ..
ninja
# 编译 examples/ 程序
cd build
# 编译 debug 版可以配合 gdb 调试
cmake -DQBDI_EXAMPLES=ON -DCMAKE_BUILD_TYPE=Debug -DQBDI_LOG_DEBUG=ON ..
ninja
mkdir QBDIPreload && cd QBDIPreload
# build/package/qbdi-preload-template 脚本中的命令, 要在编译时执行 sudo ninja install 才能复制过来
cp /usr/local/share/qbdipreloadX86_64/qbdi_preload_template/* .
mkdir build && cd build
cmake ..
make
mkdir QBDIPreload && cd QBDIPreload
# build/package/qbdi-preload-template 脚本中的命令, 要在编译时执行 sudo ninja install 才能复制过来
cp /usr/local/share/qbdipreloadX86_64/qbdi_preload_template/* .
mkdir build && cd build
cmake ..
make
// 斐波那契数列
int fibonacci(int n) {
    if(n <= 2)
        return 1;
    return fibonacci(n-1) + fibonacci(n-2);
}
// 斐波那契数列
int fibonacci(int n) {
    if(n <= 2)
        return 1;
    return fibonacci(n-1) + fibonacci(n-2);
}
#include "QBDI.h"
 
// 初始化虚拟机
VMInstanceRef vm;
qbdi_initVM(&vm, NULL, NULL, 0);
 
// 虚拟机状态的指针
GPRState *state;
state = qbdi_getGPRState(vm);
assert(state != NULL);
 
// 请求一个虚拟堆栈, 虚拟机不使用你进程中的常规堆栈, QBDI 需要自己的堆栈
uint8_t *fakestack;
static const uint32_t STACK_SIZE = 0x100000; // 1MB
bool res = qbdi_allocateVirtualStack(state, STACK_SIZE, &fakestack);
assert(res == true);
#include "QBDI.h"
 
// 初始化虚拟机
VMInstanceRef vm;
qbdi_initVM(&vm, NULL, NULL, 0);
 
// 虚拟机状态的指针
GPRState *state;
state = qbdi_getGPRState(vm);
assert(state != NULL);
 
// 请求一个虚拟堆栈, 虚拟机不使用你进程中的常规堆栈, QBDI 需要自己的堆栈
uint8_t *fakestack;
static const uint32_t STACK_SIZE = 0x100000; // 1MB
bool res = qbdi_allocateVirtualStack(state, STACK_SIZE, &fakestack);
assert(res == true);
// 获取当前指令的地址和反汇编, 并将其打印出来,
 
// 回调函数遵循 InstCallback 的函数签名
VMAction showInstruction(VMInstanceRef vm, GPRState *gprState,
                        FPRState *fprState, void *data) {
// 获取指令的分析信息, 包括指令地址和反汇编代码
const InstAnalysis *instAnalysis = qbdi_getInstAnalysis(
    vm, QBDI_ANALYSIS_INSTRUCTION | QBDI_ANALYSIS_DISASSEMBLY);
 
// 打印指令的地址和反汇编代码
printf("0x%" PRIRWORD ": %s\n", instAnalysis->address,
        instAnalysis->disassembly);
 
return QBDI_CONTINUE;
}
// 通过传参控制回调是在指令前(QBDI_PREINST)或指令后(QBDI_POSTINST)被调用.
uint32_t cid = qbdi_addCodeCB(vm, QBDI_PREINST, showInstruction, NULL, 0);
assert(cid != QBDI_INVALID_EVENTID);
 
// 计算递归调用次数
 
// QBDI 回调函数都允许用户传递一个自定义参数 data, 其类型为 void *
VMAction countIteration(VMInstanceRef vm, GPRState *gprState,
                        FPRState *fprState, void *data) {
(*((int *)data))++;
 
return QBDI_CONTINUE;
}
int iterationCount = 0;
// qbdi_addMnemonicCB 可以用于注册一个回调函数, 该回调函数仅在遇到特定指令时被调用
qbdi_addMnemonicCB(vm, "CALL*", QBDI_PREINST, countIteration, &iterationCount, 0);
// 获取当前指令的地址和反汇编, 并将其打印出来,
 
// 回调函数遵循 InstCallback 的函数签名
VMAction showInstruction(VMInstanceRef vm, GPRState *gprState,
                        FPRState *fprState, void *data) {
// 获取指令的分析信息, 包括指令地址和反汇编代码
const InstAnalysis *instAnalysis = qbdi_getInstAnalysis(
    vm, QBDI_ANALYSIS_INSTRUCTION | QBDI_ANALYSIS_DISASSEMBLY);
 
// 打印指令的地址和反汇编代码
printf("0x%" PRIRWORD ": %s\n", instAnalysis->address,
        instAnalysis->disassembly);
 
return QBDI_CONTINUE;
}
// 通过传参控制回调是在指令前(QBDI_PREINST)或指令后(QBDI_POSTINST)被调用.
uint32_t cid = qbdi_addCodeCB(vm, QBDI_PREINST, showInstruction, NULL, 0);
assert(cid != QBDI_INVALID_EVENTID);
 
// 计算递归调用次数
 
// QBDI 回调函数都允许用户传递一个自定义参数 data, 其类型为 void *
VMAction countIteration(VMInstanceRef vm, GPRState *gprState,
                        FPRState *fprState, void *data) {
(*((int *)data))++;
 
return QBDI_CONTINUE;
}
int iterationCount = 0;
// qbdi_addMnemonicCB 可以用于注册一个回调函数, 该回调函数仅在遇到特定指令时被调用
qbdi_addMnemonicCB(vm, "CALL*", QBDI_PREINST, countIteration, &iterationCount, 0);
// QBDI 的插桩代码需要一个生效的地址范围, 如果超出此范围, QBDI 不再处理, 即插桩代码不再发生作用.
bool res = qbdi_addInstrumentedModuleFromAddr(vm, (rword)&fibonacci);
assert(res == true);
 
// qbdi_call() 函数最终运行插桩, 用 retvalue 接收返回值, 并通过 QBDI 调用目标函数, 1 是参数个数, n 是要传入参数
rword retvalue;
res = qbdi_call(vm, &retvalue, (rword)fibonacci, 1, n);
assert(res == true);
// QBDI 的插桩代码需要一个生效的地址范围, 如果超出此范围, QBDI 不再处理, 即插桩代码不再发生作用.
bool res = qbdi_addInstrumentedModuleFromAddr(vm, (rword)&fibonacci);
assert(res == true);
 
// qbdi_call() 函数最终运行插桩, 用 retvalue 接收返回值, 并通过 QBDI 调用目标函数, 1 是参数个数, n 是要传入参数
rword retvalue;
res = qbdi_call(vm, &retvalue, (rword)fibonacci, 1, n);
assert(res == true);
/*! 创建并初始化一个虚拟机实例.
 *
 * @param[out] instance 创建的虚拟机实例.
 * @param[in]  cpu      一个 C 字符串, 指定要使用的 CPU 模型名称.
 *                      如果为 NULL, 则使用默认架构的 CPU 模型
 *                      (更多细节请参考 LLVM 文档).
 * @param[in]  mattrs   一个以 NULL 结尾的 C 字符串数组, 指定 CPU 模型的属性.
 *                      如果为 NULL, 则不指定额外的特性.
 * @param[in]  opts     要在虚拟机中启用的选项.
 */
QBDI_EXPORT void qbdi_initVM(VMInstanceRef *instance, const char *cpu,
                             const char **mattrs, Options opts);
/*! 创建并初始化一个虚拟机实例.
 *
 * @param[out] instance 创建的虚拟机实例.
 * @param[in]  cpu      一个 C 字符串, 指定要使用的 CPU 模型名称.
 *                      如果为 NULL, 则使用默认架构的 CPU 模型
 *                      (更多细节请参考 LLVM 文档).
 * @param[in]  mattrs   一个以 NULL 结尾的 C 字符串数组, 指定 CPU 模型的属性.
 *                      如果为 NULL, 则不指定额外的特性.
 * @param[in]  opts     要在虚拟机中启用的选项.
 */
QBDI_EXPORT void qbdi_initVM(VMInstanceRef *instance, const char *cpu,
                             const char **mattrs, Options opts);
#define QBDI_REQUIRE_ACTION(req, ac)           \
  if (!(req)) {                                \
    QBDI_ERROR("Assertion Failed : {}", #req); \
    ac;                                        \
  }
 
void qbdi_initVM(VMInstanceRef *instance, const char *cpu, const char **mattrs,
                 Options opts) {
  QBDI_REQUIRE_ACTION(instance, return);
 
  *instance = nullptr;
  std::string cpuStr = "";
  std::vector<std::string> mattrsStr;
 
  if (cpu != nullptr) {
    cpuStr += cpu;
  }
 
  if (mattrs != nullptr) {
    for (unsigned i = 0; mattrs[i] != nullptr; i++) {
      mattrsStr.emplace_back(mattrs[i]);
    }
  }
 
  *instance = static_cast<VMInstanceRef>(new VM(cpuStr, mattrsStr, opts));
}
#define QBDI_REQUIRE_ACTION(req, ac)           \
  if (!(req)) {                                \
    QBDI_ERROR("Assertion Failed : {}", #req); \
    ac;                                        \
  }
 
void qbdi_initVM(VMInstanceRef *instance, const char *cpu, const char **mattrs,
                 Options opts) {
  QBDI_REQUIRE_ACTION(instance, return);
 
  *instance = nullptr;
  std::string cpuStr = "";
  std::vector<std::string> mattrsStr;
 
  if (cpu != nullptr) {
    cpuStr += cpu;
  }
 
  if (mattrs != nullptr) {
    for (unsigned i = 0; mattrs[i] != nullptr; i++) {
      mattrsStr.emplace_back(mattrs[i]);
    }
  }
 
  *instance = static_cast<VMInstanceRef>(new VM(cpuStr, mattrsStr, opts));
}
VM::VM(const std::string &cpu, const std::vector<std::string> &mattrs,
       Options opts)
    : memoryLoggingLevel(0), memCBID(0),
      memReadGateCBID(VMError::INVALID_EVENTID),
      memWriteGateCBID(VMError::INVALID_EVENTID) {
#if defined(_QBDI_ASAN_ENABLED_)
  opts |= Options::OPT_DISABLE_FPR;
#endif
  engine = std::make_unique<Engine>(cpu, mattrs, opts, this);
  memCBInfos = std::make_unique<std::vector<std::pair<uint32_t, MemCBInfo>>>();
  instrCBInfos = std::make_unique<
      std::vector<std::pair<uint32_t, std::unique_ptr<InstrCBInfo>>>>();
}
VM::VM(const std::string &cpu, const std::vector<std::string> &mattrs,
       Options opts)
    : memoryLoggingLevel(0), memCBID(0),
      memReadGateCBID(VMError::INVALID_EVENTID),
      memWriteGateCBID(VMError::INVALID_EVENTID) {
#if defined(_QBDI_ASAN_ENABLED_)
  opts |= Options::OPT_DISABLE_FPR;
#endif
  engine = std::make_unique<Engine>(cpu, mattrs, opts, this);
  memCBInfos = std::make_unique<std::vector<std::pair<uint32_t, MemCBInfo>>>();
  instrCBInfos = std::make_unique<
      std::vector<std::pair<uint32_t, std::unique_ptr<InstrCBInfo>>>>();
}
Engine::Engine(const std::string &_cpu, const std::vector<std::string> &_mattrs,
               Options opts, VMInstanceRef vminstance)
    : vminstance(vminstance), instrRulesCounter(0), vmCallbacksCounter(0),
      curCPUMode(CPUMode::DEFAULT), options(opts), eventMask(VMEvent::NO_EVENT),
      running(false) {
 
  llvmCPUs = std::make_unique<LLVMCPUs>(_cpu, _mattrs, opts);
  blockManager = std::make_unique<ExecBlockManager>(*llvmCPUs, vminstance);
  execBroker = blockManager->getExecBroker();
 
  // Get Patch rules Assembly for this architecture
  patchRuleAssembly = std::make_unique<PatchRuleAssembly>(options);
 
  gprState = std::make_unique<GPRState>();
  fprState = std::make_unique<FPRState>();
  curGPRState = gprState.get();
  curFPRState = fprState.get();
 
  initGPRState();
  initFPRState();
 
  curExecBlock = nullptr;
}
Engine::Engine(const std::string &_cpu, const std::vector<std::string> &_mattrs,
               Options opts, VMInstanceRef vminstance)
    : vminstance(vminstance), instrRulesCounter(0), vmCallbacksCounter(0),
      curCPUMode(CPUMode::DEFAULT), options(opts), eventMask(VMEvent::NO_EVENT),
      running(false) {
 
  llvmCPUs = std::make_unique<LLVMCPUs>(_cpu, _mattrs, opts);
  blockManager = std::make_unique<ExecBlockManager>(*llvmCPUs, vminstance);
  execBroker = blockManager->getExecBroker();
 
  // Get Patch rules Assembly for this architecture
  patchRuleAssembly = std::make_unique<PatchRuleAssembly>(options);
 
  gprState = std::make_unique<GPRState>();
  fprState = std::make_unique<FPRState>();
  curGPRState = gprState.get();
  curFPRState = fprState.get();
 
  initGPRState();
  initFPRState();
 
  curExecBlock = nullptr;
}
GPRState *qbdi_getGPRState(VMInstanceRef instance) {
  QBDI_REQUIRE_ACTION(instance, return nullptr);
  return static_cast<VM *>(instance)->getGPRState();
}
 
GPRState *VM::getGPRState() const { return engine->getGPRState(); }
GPRState *qbdi_getGPRState(VMInstanceRef instance) {
  QBDI_REQUIRE_ACTION(instance, return nullptr);
  return static_cast<VM *>(instance)->getGPRState();
}
 
GPRState *VM::getGPRState() const { return engine->getGPRState(); }
GPRState *Engine::getGPRState() const { return curGPRState; }
GPRState *Engine::getGPRState() const { return curGPRState; }
GPRState *curGPRState;
 
/*! X86_64 General Purpose Register context.
 */ // SPHINX_X86_64_GPRSTATE_BEGIN
typedef struct QBDI_ALIGNED(8) {
  rword rax;
  rword rbx;
  rword rcx;
  rword rdx;
  rword rsi;
  rword rdi;
  rword r8;
  rword r9;
  rword r10;
  rword r11;
  rword r12;
  rword r13;
  rword r14;
  rword r15;
  rword rbp;
  rword rsp;
  rword rip;
  rword eflags;
  // Only backup and restore with OPT_ENABLE_FS_GS
  rword fs;
  rword gs;
} GPRState;
// SPHINX_X86_64_GPRSTATE_END
GPRState *curGPRState;
 
/*! X86_64 General Purpose Register context.
 */ // SPHINX_X86_64_GPRSTATE_BEGIN
typedef struct QBDI_ALIGNED(8) {
  rword rax;
  rword rbx;
  rword rcx;
  rword rdx;
  rword rsi;
  rword rdi;
  rword r8;
  rword r9;
  rword r10;
  rword r11;
  rword r12;
  rword r13;
  rword r14;
  rword r15;
  rword rbp;
  rword rsp;
  rword rip;
  rword eflags;
  // Only backup and restore with OPT_ENABLE_FS_GS
  rword fs;
  rword gs;
} GPRState;
// SPHINX_X86_64_GPRSTATE_END
bool qbdi_allocateVirtualStack(GPRState *ctx, uint32_t stackSize,
                               uint8_t **stack) {
  return allocateVirtualStack(ctx, stackSize, stack);
}
 
bool allocateVirtualStack(GPRState *ctx, uint32_t stackSize, uint8_t **stack) {
  (*stack) = static_cast<uint8_t *>(alignedAlloc(stackSize, 16));
  if (*stack == nullptr) {
    return false;
  }
 
  QBDI_GPR_SET(ctx, REG_SP, reinterpret_cast<QBDI::rword>(*stack) + stackSize);
  QBDI_GPR_SET(ctx, REG_BP, QBDI_GPR_GET(ctx, REG_SP));
 
  return true;
}
 
// 分配对齐的内存
void *alignedAlloc(size_t size, size_t align) {
  void *allocated = nullptr;  // 初始化分配的内存指针为 nullptr
  // 对齐大小必须是 2 的幂
  if ((align == 0) || ((align & (align - 1)) != 0)) {
    return nullptr;  // 如果对齐大小不是 2 的幂, 返回 nullptr
  }
#if defined(QBDI_PLATFORM_LINUX) || defined(QBDI_PLATFORM_ANDROID) || \
    defined(QBDI_PLATFORM_OSX)
  // 在 Linux、Android 和 macOS 上使用 posix_memalign 分配对齐内存
  int ret = posix_memalign(&allocated, align, size);  // 尝试分配对齐内存
  if (ret != 0) {
    return nullptr;  // 如果分配失败, 返回 nullptr
  }
#elif defined(QBDI_PLATFORM_WINDOWS)
  // 在 Windows 上使用 _aligned_malloc 分配对齐内存
  allocated = _aligned_malloc(size, align);  // 尝试分配对齐内存
#endif
  return allocated;  // 返回分配的内存指针
}
 
#ifdef __cplusplus
#define QBDI_GPR_GET(state, i) (reinterpret_cast<const QBDI::rword *>(state)[i])
#define QBDI_GPR_SET(state, i, v) \
  (reinterpret_cast<QBDI::rword *>(state)[i] = v)
#else
#define QBDI_GPR_GET(state, i) (((rword *)state)[i])
#define QBDI_GPR_SET(state, i, v) (((rword *)state)[i] = v)
#endif
bool qbdi_allocateVirtualStack(GPRState *ctx, uint32_t stackSize,
                               uint8_t **stack) {
  return allocateVirtualStack(ctx, stackSize, stack);
}
 
bool allocateVirtualStack(GPRState *ctx, uint32_t stackSize, uint8_t **stack) {
  (*stack) = static_cast<uint8_t *>(alignedAlloc(stackSize, 16));
  if (*stack == nullptr) {
    return false;
  }
 
  QBDI_GPR_SET(ctx, REG_SP, reinterpret_cast<QBDI::rword>(*stack) + stackSize);
  QBDI_GPR_SET(ctx, REG_BP, QBDI_GPR_GET(ctx, REG_SP));
 
  return true;
}
 
// 分配对齐的内存
void *alignedAlloc(size_t size, size_t align) {
  void *allocated = nullptr;  // 初始化分配的内存指针为 nullptr
  // 对齐大小必须是 2 的幂
  if ((align == 0) || ((align & (align - 1)) != 0)) {
    return nullptr;  // 如果对齐大小不是 2 的幂, 返回 nullptr
  }
#if defined(QBDI_PLATFORM_LINUX) || defined(QBDI_PLATFORM_ANDROID) || \
    defined(QBDI_PLATFORM_OSX)
  // 在 Linux、Android 和 macOS 上使用 posix_memalign 分配对齐内存
  int ret = posix_memalign(&allocated, align, size);  // 尝试分配对齐内存
  if (ret != 0) {
    return nullptr;  // 如果分配失败, 返回 nullptr
  }
#elif defined(QBDI_PLATFORM_WINDOWS)
  // 在 Windows 上使用 _aligned_malloc 分配对齐内存
  allocated = _aligned_malloc(size, align);  // 尝试分配对齐内存
#endif
  return allocated;  // 返回分配的内存指针
}
 
#ifdef __cplusplus
#define QBDI_GPR_GET(state, i) (reinterpret_cast<const QBDI::rword *>(state)[i])
#define QBDI_GPR_SET(state, i, v) \
  (reinterpret_cast<QBDI::rword *>(state)[i] = v)
#else
#define QBDI_GPR_GET(state, i) (((rword *)state)[i])
#define QBDI_GPR_SET(state, i, v) (((rword *)state)[i] = v)
#endif
/*! 注册特定指令事件的回调事件.
 
 * @param[in] instance  虚拟机实例.
 * @param[in] pos       事件回调的相对位置
 *                      (QBDI_PREINST / QBDI_POSTINST).
 * @param[in] cbk       回调函数的指针.
 * @param[in] data      传递给回调函数的用户自定义数据.
 * @param[in] priority  回调的优先级.
 
 * @return 注册的插桩的 ID(如果失败则返回 QBDI_INVALID_EVENTID).
 */
QBDI_EXPORT uint32_t qbdi_addCodeCB(VMInstanceRef instance, InstPosition pos,
                                    InstCallback cbk, void *data, int priority);
/*! 注册特定指令事件的回调事件.
 
 * @param[in] instance  虚拟机实例.
 * @param[in] pos       事件回调的相对位置
 *                      (QBDI_PREINST / QBDI_POSTINST).
 * @param[in] cbk       回调函数的指针.
 * @param[in] data      传递给回调函数的用户自定义数据.
 * @param[in] priority  回调的优先级.
 
 * @return 注册的插桩的 ID(如果失败则返回 QBDI_INVALID_EVENTID).
 */
QBDI_EXPORT uint32_t qbdi_addCodeCB(VMInstanceRef instance, InstPosition pos,
                                    InstCallback cbk, void *data, int priority);
uint32_t qbdi_addCodeCB(VMInstanceRef instance, InstPosition pos,
                        InstCallback cbk, void *data, int priority) {
  QBDI_REQUIRE_ACTION(instance, return VMError::INVALID_EVENTID);
  return static_cast<VM *>(instance)->addCodeCB(pos, cbk, data, priority);
}
uint32_t qbdi_addCodeCB(VMInstanceRef instance, InstPosition pos,
                        InstCallback cbk, void *data, int priority) {
  QBDI_REQUIRE_ACTION(instance, return VMError::INVALID_EVENTID);
  return static_cast<VM *>(instance)->addCodeCB(pos, cbk, data, priority);
}
uint32_t VM::addCodeCB(InstPosition pos, InstCallback cbk, void *data,
                       int priority) {
  QBDI_REQUIRE_ACTION(cbk != nullptr, return VMError::INVALID_EVENTID);
  return engine->addInstrRule(InstrRuleBasicCBK::unique(
      True::unique(), cbk, data, pos, true, priority,
      (pos == PREINST) ? RelocTagPreInstStdCBK : RelocTagPostInstStdCBK));
}
uint32_t VM::addCodeCB(InstPosition pos, InstCallback cbk, void *data,
                       int priority) {
  QBDI_REQUIRE_ACTION(cbk != nullptr, return VMError::INVALID_EVENTID);
  return engine->addInstrRule(InstrRuleBasicCBK::unique(
      True::unique(), cbk, data, pos, true, priority,
      (pos == PREINST) ? RelocTagPreInstStdCBK : RelocTagPostInstStdCBK));
}
// helper to create a object as a unique_ptr
template <typename T, typename U>
class AutoUnique : public T {
public:
  template <typename... Args>
  AutoUnique(Args &&...args) : T(std::forward<Args>(args)...) {}
 
  template <typename... Args>
  inline static std::unique_ptr<T> unique(Args &&...args) {
    return std::make_unique<U>(std::forward<Args>(args)...);
  };
};
// helper to create a object as a unique_ptr
template <typename T, typename U>
class AutoUnique : public T {
public:
  template <typename... Args>
  AutoUnique(Args &&...args) : T(std::forward<Args>(args)...) {}
 
  template <typename... Args>
  inline static std::unique_ptr<T> unique(Args &&...args) {
    return std::make_unique<U>(std::forward<Args>(args)...);
  };
};
class True : public AutoClone<PatchCondition, True> {
public:
  /*! Return true.
   */
  True() {}
 
  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override {
    return true;
  }
};
class True : public AutoClone<PatchCondition, True> {
public:
  /*! Return true.
   */
  True() {}
 
  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override {
    return true;
  }
};
// helper to clone object, when the copy is possible
template <typename T, typename U>
class AutoClone : public AutoUnique<T, U> {
public:
  template <typename... Args>
  AutoClone(Args &&...args) : AutoUnique<T, U>(std::forward<Args>(args)...) {}
 
  inline std::unique_ptr<T> clone() const override {
    return std::make_unique<U>(*static_cast<const U *>(this));
  };
};
// helper to clone object, when the copy is possible
template <typename T, typename U>
class AutoClone : public AutoUnique<T, U> {
public:
  template <typename... Args>
  AutoClone(Args &&...args) : AutoUnique<T, U>(std::forward<Args>(args)...) {}
 
  inline std::unique_ptr<T> clone() const override {
    return std::make_unique<U>(*static_cast<const U *>(this));
  };
};
class InstrRuleBasicCBK : public AutoUnique<InstrRule, InstrRuleBasicCBK> {
 
  PatchConditionUniquePtr condition;
  PatchGeneratorUniquePtrVec patchGen;
  InstPosition position;
  bool breakToHost;
  RelocatableInstTag tag;
  InstCallback cbk;
  void *data;
 
public:
  /*! 创建新的插桩规则, 包含条件判断、生成器列表、插桩位置和主机中断请求.
   *
   * @param[in] condition    用于判断该PatchRule是否适用的PatchCondition条件
   * @param[in] cbk          待执行的回调函数
   * @param[in] data         传递给回调函数的数据指针
   * @param[in] position     枚举值, 指示插桩应位于指令之前还是之后
   * @param[in] breakToHost  布尔值, 决定该插桩是否应以主机中断结束(例如在回调场景)
   * @param[in] priority     回调函数的优先级
   * @param[in] tag          回调函数的标签
   */
  InstrRuleBasicCBK(PatchConditionUniquePtr &&condition, InstCallback cbk,
                    void *data, InstPosition position, bool breakToHost,
                    int priority = PRIORITY_DEFAULT,
                    RelocatableInstTag tag = RelocTagInvalid);
 
  ~InstrRuleBasicCBK() override;
 
  std::unique_ptr<InstrRule> clone() const override;
 
  inline InstPosition getPosition() const { return position; }
 
  RangeSet<rword> affectedRange() const override;
 
  /*! 通过评估当前上下文中的规则条件, 确定本规则是否适用.
   *
   * @param[in] patch    包含当前上下文的补丁对象
   * @param[in] llvmcpu  LLVMCPU对象
   *
   * @return 若插桩条件在当前补丁上评估为真, 则返回true
   */
  bool canBeApplied(const Patch &patch, const LLVMCPU &llvmcpu) const;
 
  bool changeDataPtr(void *data) override;
 
  inline bool tryInstrument(Patch &patch,
                            const LLVMCPU &llvmcpu) const override {
    if (canBeApplied(patch, llvmcpu)) {
      instrument(patch, patchGen, breakToHost, position, priority, tag);
      return true;
    }
    return false;
  }
};
class InstrRuleBasicCBK : public AutoUnique<InstrRule, InstrRuleBasicCBK> {
 
  PatchConditionUniquePtr condition;
  PatchGeneratorUniquePtrVec patchGen;
  InstPosition position;
  bool breakToHost;
  RelocatableInstTag tag;
  InstCallback cbk;
  void *data;
 
public:
  /*! 创建新的插桩规则, 包含条件判断、生成器列表、插桩位置和主机中断请求.
   *
   * @param[in] condition    用于判断该PatchRule是否适用的PatchCondition条件
   * @param[in] cbk          待执行的回调函数
   * @param[in] data         传递给回调函数的数据指针
   * @param[in] position     枚举值, 指示插桩应位于指令之前还是之后
   * @param[in] breakToHost  布尔值, 决定该插桩是否应以主机中断结束(例如在回调场景)
   * @param[in] priority     回调函数的优先级
   * @param[in] tag          回调函数的标签
   */
  InstrRuleBasicCBK(PatchConditionUniquePtr &&condition, InstCallback cbk,
                    void *data, InstPosition position, bool breakToHost,
                    int priority = PRIORITY_DEFAULT,
                    RelocatableInstTag tag = RelocTagInvalid);
 
  ~InstrRuleBasicCBK() override;
 
  std::unique_ptr<InstrRule> clone() const override;
 
  inline InstPosition getPosition() const { return position; }
 
  RangeSet<rword> affectedRange() const override;
 
  /*! 通过评估当前上下文中的规则条件, 确定本规则是否适用.
   *
   * @param[in] patch    包含当前上下文的补丁对象
   * @param[in] llvmcpu  LLVMCPU对象
   *
   * @return 若插桩条件在当前补丁上评估为真, 则返回true
   */
  bool canBeApplied(const Patch &patch, const LLVMCPU &llvmcpu) const;
 
  bool changeDataPtr(void *data) override;
 
  inline bool tryInstrument(Patch &patch,
                            const LLVMCPU &llvmcpu) const override {
    if (canBeApplied(patch, llvmcpu)) {
      instrument(patch, patchGen, breakToHost, position, priority, tag);
      return true;
    }
    return false;
  }
};
uint32_t Engine::addInstrRule(std::unique_ptr<InstrRule> &&rule) {
  uint32_t id = instrRulesCounter++;
  QBDI_REQUIRE_ACTION(id < EVENTID_VM_MASK, return VMError::INVALID_EVENTID);
 
  this->clearCache(rule->affectedRange());
 
  auto v = std::make_pair(id, std::move(rule));
 
  // insert rule in instrRules and keep the priority order
  auto it = std::upper_bound(instrRules.begin(), instrRules.end(), v,
                             [](const decltype(instrRules)::value_type &a,
                                const decltype(instrRules)::value_type &b) {
                               return a.second->getPriority() >
                                      b.second->getPriority();
                             });
  instrRules.insert(it, std::move(v));
 
  return id;
}
uint32_t Engine::addInstrRule(std::unique_ptr<InstrRule> &&rule) {
  uint32_t id = instrRulesCounter++;
  QBDI_REQUIRE_ACTION(id < EVENTID_VM_MASK, return VMError::INVALID_EVENTID);
 
  this->clearCache(rule->affectedRange());
 
  auto v = std::make_pair(id, std::move(rule));
 
  // insert rule in instrRules and keep the priority order
  auto it = std::upper_bound(instrRules.begin(), instrRules.end(), v,
                             [](const decltype(instrRules)::value_type &a,
                                const decltype(instrRules)::value_type &b) {
                               return a.second->getPriority() >
                                      b.second->getPriority();
                             });
  instrRules.insert(it, std::move(v));
 
  return id;
}
RangeSet<rword> InstrRuleBasicCBK::affectedRange() const {
  return condition->affectedRange();
}
RangeSet<rword> InstrRuleBasicCBK::affectedRange() const {
  return condition->affectedRange();
}
class PatchCondition {
public:
  using UniquePtr = std::unique_ptr<PatchCondition>;
  using UniquePtrVec = std::vector<std::unique_ptr<PatchCondition>>;
 
  virtual UniquePtr clone() const = 0;
 
  virtual bool test(const Patch &patch, const LLVMCPU &llvmcpu) const = 0;
 
  virtual RangeSet<rword> affectedRange() const {
    RangeSet<rword> r;
    r.add(Range<rword>(0, (rword)-1));
    return r;
  }
 
  virtual ~PatchCondition() = default;
};
class PatchCondition {
public:
  using UniquePtr = std::unique_ptr<PatchCondition>;
  using UniquePtrVec = std::vector<std::unique_ptr<PatchCondition>>;
 
  virtual UniquePtr clone() const = 0;
 
  virtual bool test(const Patch &patch, const LLVMCPU &llvmcpu) const = 0;
 
  virtual RangeSet<rword> affectedRange() const {
    RangeSet<rword> r;
    r.add(Range<rword>(0, (rword)-1));
    return r;
  }
 
  virtual ~PatchCondition() = default;
};
auto v = std::make_pair(id, std::move(rule));
 
// insert rule in instrRules and keep the priority order
auto it = std::upper_bound(instrRules.begin(), instrRules.end(), v,
                            [](const decltype(instrRules)::value_type &a,
                            const decltype(instrRules)::value_type &b) {
                            return a.second->getPriority() >
                                    b.second->getPriority();
                            });
instrRules.insert(it, std::move(v));
auto v = std::make_pair(id, std::move(rule));
 
// insert rule in instrRules and keep the priority order
auto it = std::upper_bound(instrRules.begin(), instrRules.end(), v,
                            [](const decltype(instrRules)::value_type &a,
                            const decltype(instrRules)::value_type &b) {
                            return a.second->getPriority() >
                                    b.second->getPriority();
                            });
instrRules.insert(it, std::move(v));
uint32_t qbdi_addMnemonicCB(VMInstanceRef instance, const char *mnemonic,
                            InstPosition pos, InstCallback cbk, void *data,
                            int priority) {
  QBDI_REQUIRE_ACTION(instance, return VMError::INVALID_EVENTID);
  return static_cast<VM *>(instance)->addMnemonicCB(mnemonic, pos, cbk, data,
                                                    priority);
}
uint32_t qbdi_addMnemonicCB(VMInstanceRef instance, const char *mnemonic,
                            InstPosition pos, InstCallback cbk, void *data,
                            int priority) {
  QBDI_REQUIRE_ACTION(instance, return VMError::INVALID_EVENTID);
  return static_cast<VM *>(instance)->addMnemonicCB(mnemonic, pos, cbk, data,
                                                    priority);
}
uint32_t VM::addMnemonicCB(const char *mnemonic, InstPosition pos,
                           InstCallback cbk, void *data, int priority) {
  QBDI_REQUIRE_ACTION(mnemonic != nullptr, return VMError::INVALID_EVENTID);
  QBDI_REQUIRE_ACTION(cbk != nullptr, return VMError::INVALID_EVENTID);
  return engine->addInstrRule(InstrRuleBasicCBK::unique(
      MnemonicIs::unique(mnemonic), cbk, data, pos, true, priority,
      (pos == PREINST) ? RelocTagPreInstStdCBK : RelocTagPostInstStdCBK));
}
uint32_t VM::addMnemonicCB(const char *mnemonic, InstPosition pos,
                           InstCallback cbk, void *data, int priority) {
  QBDI_REQUIRE_ACTION(mnemonic != nullptr, return VMError::INVALID_EVENTID);
  QBDI_REQUIRE_ACTION(cbk != nullptr, return VMError::INVALID_EVENTID);
  return engine->addInstrRule(InstrRuleBasicCBK::unique(
      MnemonicIs::unique(mnemonic), cbk, data, pos, true, priority,
      (pos == PREINST) ? RelocTagPreInstStdCBK : RelocTagPostInstStdCBK));
}
class MnemonicIs : public AutoClone<PatchCondition, MnemonicIs> {
  std::string mnemonic;
 
public:
  /*! Return true if the mnemonic of the current instruction is equal to
   * Mnemonic.
   *
   * @param[in] mnemonic   A null terminated instruction mnemonic (using LLVM
   * style)
   */
  MnemonicIs(const char *mnemonic) : mnemonic(mnemonic) {}
 
  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override;
};
class MnemonicIs : public AutoClone<PatchCondition, MnemonicIs> {
  std::string mnemonic;
 
public:
  /*! Return true if the mnemonic of the current instruction is equal to
   * Mnemonic.
   *
   * @param[in] mnemonic   A null terminated instruction mnemonic (using LLVM
   * style)
   */
  MnemonicIs(const char *mnemonic) : mnemonic(mnemonic) {}
 
  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override;
};
bool MnemonicIs::test(const Patch &patch, const LLVMCPU &llvmcpu) const {
  return QBDI::String::startsWith(
      mnemonic.c_str(), llvmcpu.getInstOpcodeName(patch.metadata.inst));
}
 
bool startsWith(const char *prefix, const char *str) {
  // 确保prefix和str都不是nullptr
  QBDI_REQUIRE_ACTION(prefix != nullptr, return false);
  QBDI_REQUIRE_ACTION(str != nullptr, return false);
 
  // 遍历prefix和str, 逐字符比较
  while (*prefix && *str) {
    // 通配符匹配: 如果prefix中的当前字符是'*', 则跳过直到str中的字符与prefix中的下一个字符匹配
    if (*prefix == '*') {
      // 如果prefix的下一个字符(忽略大小写)与str的当前字符匹配, 则移动prefix和str的指针
      if (toupper(*(prefix + 1)) == toupper(*str++)) {
        prefix++;
      }
      // 继续检查下一个字符
      continue;
    }
    // 比较当前字符(忽略大小写)
    if (toupper(*prefix++) != toupper(*str++)) {
      // 如果字符不匹配, 返回false
      return false;
    }
  }
  // 检查prefix是否还有未匹配的字符(除了通配符'*'之外)
  if (*prefix && *(prefix + 1)) {
    // 如果prefix还有未匹配的字符, 则返回false
    return false;
  }
  // 检查str的下一个字符是否是数字、下划线或字符串结束符
  // 如果不是, 则返回false
  if (!((*str >= '0' && *str <= '9') || *str == '_' || !*str)) {
    return false;
  }
  // 如果所有检查都通过, 则返回true
  return true;
}
bool MnemonicIs::test(const Patch &patch, const LLVMCPU &llvmcpu) const {
  return QBDI::String::startsWith(
      mnemonic.c_str(), llvmcpu.getInstOpcodeName(patch.metadata.inst));
}
 
bool startsWith(const char *prefix, const char *str) {
  // 确保prefix和str都不是nullptr
  QBDI_REQUIRE_ACTION(prefix != nullptr, return false);
  QBDI_REQUIRE_ACTION(str != nullptr, return false);
 
  // 遍历prefix和str, 逐字符比较
  while (*prefix && *str) {
    // 通配符匹配: 如果prefix中的当前字符是'*', 则跳过直到str中的字符与prefix中的下一个字符匹配
    if (*prefix == '*') {
      // 如果prefix的下一个字符(忽略大小写)与str的当前字符匹配, 则移动prefix和str的指针
      if (toupper(*(prefix + 1)) == toupper(*str++)) {
        prefix++;
      }
      // 继续检查下一个字符
      continue;
    }
    // 比较当前字符(忽略大小写)
    if (toupper(*prefix++) != toupper(*str++)) {
      // 如果字符不匹配, 返回false
      return false;
    }
  }
  // 检查prefix是否还有未匹配的字符(除了通配符'*'之外)
  if (*prefix && *(prefix + 1)) {
    // 如果prefix还有未匹配的字符, 则返回false
    return false;
  }
  // 检查str的下一个字符是否是数字、下划线或字符串结束符
  // 如果不是, 则返回false
  if (!((*str >= '0' && *str <= '9') || *str == '_' || !*str)) {
    return false;
  }
  // 如果所有检查都通过, 则返回true
  return true;
}
namespace llvm {
class MCInst;
}
 
namespace QBDI {
class LLVMCPU;
class RelocatableInst;
 
// 指令补丁结构
struct InstrPatch {
  InstPosition position;
  int priority;
  std::vector<std::unique_ptr<RelocatableInst>> insts;
};
 
// 指令补丁的类
class Patch {
private:
  std::vector<InstrPatch> instsPatchs;
 
public:
  InstMetadata metadata;
  std::vector<std::unique_ptr<RelocatableInst>> insts;
  // 由 PatchRule 生成的标志
  std::vector<std::pair<unsigned int, int>> patchGenFlags;
  int patchGenFlagsOffset = 0;
  // 用于在 ExecBlockManager 中注册的指令回调
  std::vector<std::unique_ptr<InstCbLambda>> userInstCB;
  // 指令使用的寄存器
  std::array<RegisterUsage, NUM_GPR> regUsage;
  std::map<RegLLVM, RegisterUsage> regUsageExtra;
  // 本补丁使用的临时寄存器
  std::set<RegLLVM> tempReg;
  const LLVMCPU *llvmcpu;
  bool finalize = false;
 
  using Vec = std::vector<Patch>;
 
  Patch(const llvm::MCInst &inst, rword address, uint32_t instSize,
        const LLVMCPU &llvmcpu);
 
  Patch(Patch &&);
  Patch &operator=(Patch &&);
 
  ~Patch();
 
  void setModifyPC(bool modifyPC);
 
  // 在指令列表的末尾追加一个可重定位指令
  void append(std::unique_ptr<RelocatableInst> &&r);
  // 在指令列表的末尾追加多个可重定位指令
  void append(std::vector<std::unique_ptr<RelocatableInst>> v);
 
  // 在指令列表的开头插入一个可重定位指令
  void prepend(std::unique_ptr<RelocatableInst> &&r);
  // 在指令列表的开头插入多个可重定位指令
  void prepend(std::vector<std::unique_ptr<RelocatableInst>> v);
 
  // 在指定位置插入多个可重定位指令
  void insertAt(unsigned position,
                std::vector<std::unique_ptr<RelocatableInst>> v);
 
  void addInstsPatch(InstPosition position, int priority,
                     std::vector<std::unique_ptr<RelocatableInst>> v);
 
  void finalizeInstsPatch();
};
 
} // namespace QBDI
namespace llvm {
class MCInst;
}
 
namespace QBDI {
class LLVMCPU;
class RelocatableInst;
 
// 指令补丁结构
struct InstrPatch {
  InstPosition position;
  int priority;
  std::vector<std::unique_ptr<RelocatableInst>> insts;
};
 
// 指令补丁的类
class Patch {
private:
  std::vector<InstrPatch> instsPatchs;
 
public:
  InstMetadata metadata;
  std::vector<std::unique_ptr<RelocatableInst>> insts;
  // 由 PatchRule 生成的标志
  std::vector<std::pair<unsigned int, int>> patchGenFlags;
  int patchGenFlagsOffset = 0;
  // 用于在 ExecBlockManager 中注册的指令回调
  std::vector<std::unique_ptr<InstCbLambda>> userInstCB;
  // 指令使用的寄存器
  std::array<RegisterUsage, NUM_GPR> regUsage;
  std::map<RegLLVM, RegisterUsage> regUsageExtra;
  // 本补丁使用的临时寄存器
  std::set<RegLLVM> tempReg;
  const LLVMCPU *llvmcpu;
  bool finalize = false;
 
  using Vec = std::vector<Patch>;
 
  Patch(const llvm::MCInst &inst, rword address, uint32_t instSize,
        const LLVMCPU &llvmcpu);
 
  Patch(Patch &&);
  Patch &operator=(Patch &&);
 
  ~Patch();
 
  void setModifyPC(bool modifyPC);
 
  // 在指令列表的末尾追加一个可重定位指令
  void append(std::unique_ptr<RelocatableInst> &&r);
  // 在指令列表的末尾追加多个可重定位指令
  void append(std::vector<std::unique_ptr<RelocatableInst>> v);
 
  // 在指令列表的开头插入一个可重定位指令
  void prepend(std::unique_ptr<RelocatableInst> &&r);
  // 在指令列表的开头插入多个可重定位指令
  void prepend(std::vector<std::unique_ptr<RelocatableInst>> v);
 
  // 在指定位置插入多个可重定位指令
  void insertAt(unsigned position,
                std::vector<std::unique_ptr<RelocatableInst>> v);
 
  void addInstsPatch(InstPosition position, int priority,
                     std::vector<std::unique_ptr<RelocatableInst>> v);
 
  void finalizeInstsPatch();
};
 
} // namespace QBDI
const char *LLVMCPU::getInstOpcodeName(const llvm::MCInst &inst) const {
  return getInstOpcodeName(inst.getOpcode());
}
 
const char *LLVMCPU::getInstOpcodeName(unsigned opcode) const {
  /* llvm::StringRef can return a no null terminate pointer
   * However, in the case of opcode name, that seems to be always the case
   * see <ARM|AArch64|X86>InstrNameData
   */
  return MCII->getName(opcode).data();
}
const char *LLVMCPU::getInstOpcodeName(const llvm::MCInst &inst) const {
  return getInstOpcodeName(inst.getOpcode());
}
 
const char *LLVMCPU::getInstOpcodeName(unsigned opcode) const {
  /* llvm::StringRef can return a no null terminate pointer
   * However, in the case of opcode name, that seems to be always the case
   * see <ARM|AArch64|X86>InstrNameData
   */
  return MCII->getName(opcode).data();
}
bool qbdi_addInstrumentedModuleFromAddr(VMInstanceRef instance, rword addr) {
  QBDI_REQUIRE_ACTION(instance, return false);
  return static_cast<VM *>(instance)->addInstrumentedModuleFromAddr(addr);
}
 
bool VM::addInstrumentedModuleFromAddr(rword addr) {
  return engine->addInstrumentedModuleFromAddr(addr);
}
 
bool Engine::addInstrumentedModuleFromAddr(rword addr) {
  return execBroker->addInstrumentedModuleFromAddr(addr);
}
bool qbdi_addInstrumentedModuleFromAddr(VMInstanceRef instance, rword addr) {
  QBDI_REQUIRE_ACTION(instance, return false);
  return static_cast<VM *>(instance)->addInstrumentedModuleFromAddr(addr);
}
 
bool VM::addInstrumentedModuleFromAddr(rword addr) {
  return engine->addInstrumentedModuleFromAddr(addr);
}
 
bool Engine::addInstrumentedModuleFromAddr(rword addr) {
  return execBroker->addInstrumentedModuleFromAddr(addr);
}
bool ExecBroker::addInstrumentedModuleFromAddr(rword addr) {
  // 遍历当前进程的内存映射
  for (const MemoryMap &m : getCurrentProcessMaps()) {
    // 检查当前内存映射是否包含指定地址
    if (m.range.contains(addr)) {
      // 如果内存映射有名称(即是一个已知的模块)
      if (not m.name.empty()) {
        // 尝试将该模块添加到已监控的模块列表中
        return addInstrumentedModule(m.name);
      } else if (m.permission & QBDI::PF_EXEC) {
        // 如果内存映射没有名称, 但具有执行权限
        // 则将该内存范围添加到已监控的范围中
        addInstrumentedRange(m.range);
        return true;
      } else {
        // 如果内存映射没有名称且没有执行权限, 则返回false
        return false;
      }
    }
  }
  // 如果没有找到包含指定地址的内存映射, 返回false
  return false;
}
bool ExecBroker::addInstrumentedModuleFromAddr(rword addr) {
  // 遍历当前进程的内存映射
  for (const MemoryMap &m : getCurrentProcessMaps()) {
    // 检查当前内存映射是否包含指定地址
    if (m.range.contains(addr)) {
      // 如果内存映射有名称(即是一个已知的模块)
      if (not m.name.empty()) {
        // 尝试将该模块添加到已监控的模块列表中
        return addInstrumentedModule(m.name);
      } else if (m.permission & QBDI::PF_EXEC) {
        // 如果内存映射没有名称, 但具有执行权限
        // 则将该内存范围添加到已监控的范围中
        addInstrumentedRange(m.range);
        return true;
      } else {
        // 如果内存映射没有名称且没有执行权限, 则返回false
        return false;
      }
    }
  }
  // 如果没有找到包含指定地址的内存映射, 返回false
  return false;
}
QBDI_EXPORT std::vector<MemoryMap>
getCurrentProcessMaps(bool full_path = false);
 
std::vector<MemoryMap> getCurrentProcessMaps(bool full_path) {
  return getRemoteProcessMaps(getpid(), full_path);
}
 
// 获取指定进程的内存映射
std::vector<MemoryMap> getRemoteProcessMaps(QBDI::rword pid, bool full_path) {
  static const int BUFFER_SIZE = 256; // 定义缓冲区大小
  char line[BUFFER_SIZE] = {0}; // 用于存储读取的每一行数据
  FILE *mapfile = nullptr; // 文件指针, 用于打开 /proc/[pid]/maps 文件
  std::vector<MemoryMap> maps; // 用于存储解析后的内存映射信息
 
  // 构造 /proc/[pid]/maps 文件的路径
  snprintf(line, BUFFER_SIZE, "/proc/%llu/maps", (unsigned long long)pid);
  // 打开文件
  mapfile = fopen(line, "r");
  // 打印调试信息
  QBDI_DEBUG("Querying memory maps from {}", line);
  // 确保文件打开成功
  QBDI_REQUIRE_ACTION(mapfile != nullptr, return maps);
 
  // 处理 /proc/[pid]/maps 文件中的每一行
  while (fgets(line, BUFFER_SIZE, mapfile) != nullptr) {
    char *ptr = nullptr; // 用于指向当前解析的位置
    MemoryMap m; // 创建一个 MemoryMap 对象, 用于存储当前行的解析结果
 
    // 去掉行尾的换行符
    if ((ptr = strchr(line, '\n')) != nullptr) {
      *ptr = '\0';
    }
    ptr = line; // 从行首开始解析
    QBDI_DEBUG("Parsing line: {}", line);
 
    // 解析内存范围
    m.range.setStart(strtoul(ptr, &ptr, 16)); // 解析起始地址
    ptr++; // 跳过 '-'
    m.range.setEnd(strtoul(ptr, &ptr, 16)); // 解析结束地址
 
    // 跳过空格
    while (isspace(*ptr))
      ptr++;
 
    // 解析权限标志
    m.permission = QBDI::PF_NONE; // 初始化权限标志
    if (*ptr == 'r') // 读权限
      m.permission |= QBDI::PF_READ;
    ptr++;
    if (*ptr == 'w') // 写权限
      m.permission |= QBDI::PF_WRITE;
    ptr++;
    if (*ptr == 'x') // 执行权限
      m.permission |= QBDI::PF_EXEC;
    ptr++;
    ptr++; // 跳过保护标志
 
    // 跳过空格
    while (isspace(*ptr))
      ptr++;
 
    // 跳过文件偏移量
    strtoul(ptr, &ptr, 16);
 
    // 跳过空格
    while (isspace(*ptr))
      ptr++;
 
    // 跳过设备 ID
    strtoul(ptr, &ptr, 16);
    ptr++; // 跳过 ':'
    strtoul(ptr, &ptr, 16);
 
    // 跳过空格
    while (isspace(*ptr))
      ptr++;
 
    // 跳过 inode
    strtoul(ptr, &ptr, 10);
 
    // 跳过空格
    while (isspace(*ptr))
      ptr++;
 
    // 获取文件名
    if (full_path) {
      m.name = ptr; // 如果需要完整路径, 则直接使用文件名
    } else {
      if ((ptr = strrchr(ptr, '/')) != nullptr) {
        m.name = ptr + 1; // 如果不需要完整路径, 则只保留文件名
      } else {
        m.name.clear(); // 如果没有路径分隔符, 则清空文件名
      }
    }
 
    // 打印调试信息
    QBDI_DEBUG("Read new map [0x{:x}, 0x{:x}] {} {}{}{}", m.range.start(),
               m.range.end(), m.name.c_str(),
               (m.permission & QBDI::PF_READ) ? "r" : "-",
               (m.permission & QBDI::PF_WRITE) ? "w" : "-",
               (m.permission & QBDI::PF_EXEC) ? "x" : "-");
    maps.push_back(m); // 将解析后的内存映射信息添加到列表中
  }
  fclose(mapfile); // 关闭文件
  return maps; // 返回内存映射列表
}
QBDI_EXPORT std::vector<MemoryMap>
getCurrentProcessMaps(bool full_path = false);
 
std::vector<MemoryMap> getCurrentProcessMaps(bool full_path) {
  return getRemoteProcessMaps(getpid(), full_path);
}
 
// 获取指定进程的内存映射
std::vector<MemoryMap> getRemoteProcessMaps(QBDI::rword pid, bool full_path) {
  static const int BUFFER_SIZE = 256; // 定义缓冲区大小
  char line[BUFFER_SIZE] = {0}; // 用于存储读取的每一行数据
  FILE *mapfile = nullptr; // 文件指针, 用于打开 /proc/[pid]/maps 文件
  std::vector<MemoryMap> maps; // 用于存储解析后的内存映射信息
 
  // 构造 /proc/[pid]/maps 文件的路径
  snprintf(line, BUFFER_SIZE, "/proc/%llu/maps", (unsigned long long)pid);
  // 打开文件
  mapfile = fopen(line, "r");
  // 打印调试信息
  QBDI_DEBUG("Querying memory maps from {}", line);
  // 确保文件打开成功
  QBDI_REQUIRE_ACTION(mapfile != nullptr, return maps);
 
  // 处理 /proc/[pid]/maps 文件中的每一行
  while (fgets(line, BUFFER_SIZE, mapfile) != nullptr) {
    char *ptr = nullptr; // 用于指向当前解析的位置
    MemoryMap m; // 创建一个 MemoryMap 对象, 用于存储当前行的解析结果
 
    // 去掉行尾的换行符
    if ((ptr = strchr(line, '\n')) != nullptr) {
      *ptr = '\0';
    }
    ptr = line; // 从行首开始解析
    QBDI_DEBUG("Parsing line: {}", line);
 
    // 解析内存范围
    m.range.setStart(strtoul(ptr, &ptr, 16)); // 解析起始地址
    ptr++; // 跳过 '-'
    m.range.setEnd(strtoul(ptr, &ptr, 16)); // 解析结束地址
 
    // 跳过空格
    while (isspace(*ptr))
      ptr++;
 
    // 解析权限标志
    m.permission = QBDI::PF_NONE; // 初始化权限标志
    if (*ptr == 'r') // 读权限
      m.permission |= QBDI::PF_READ;
    ptr++;
    if (*ptr == 'w') // 写权限
      m.permission |= QBDI::PF_WRITE;
    ptr++;
    if (*ptr == 'x') // 执行权限
      m.permission |= QBDI::PF_EXEC;
    ptr++;
    ptr++; // 跳过保护标志
 
    // 跳过空格
    while (isspace(*ptr))
      ptr++;
 
    // 跳过文件偏移量
    strtoul(ptr, &ptr, 16);
 
    // 跳过空格
    while (isspace(*ptr))
      ptr++;
 
    // 跳过设备 ID
    strtoul(ptr, &ptr, 16);
    ptr++; // 跳过 ':'
    strtoul(ptr, &ptr, 16);
 
    // 跳过空格
    while (isspace(*ptr))
      ptr++;
 
    // 跳过 inode
    strtoul(ptr, &ptr, 10);
 
    // 跳过空格
    while (isspace(*ptr))
      ptr++;
 
    // 获取文件名
    if (full_path) {
      m.name = ptr; // 如果需要完整路径, 则直接使用文件名
    } else {
      if ((ptr = strrchr(ptr, '/')) != nullptr) {
        m.name = ptr + 1; // 如果不需要完整路径, 则只保留文件名
      } else {
        m.name.clear(); // 如果没有路径分隔符, 则清空文件名
      }
    }
 
    // 打印调试信息
    QBDI_DEBUG("Read new map [0x{:x}, 0x{:x}] {} {}{}{}", m.range.start(),
               m.range.end(), m.name.c_str(),
               (m.permission & QBDI::PF_READ) ? "r" : "-",
               (m.permission & QBDI::PF_WRITE) ? "w" : "-",
               (m.permission & QBDI::PF_EXEC) ? "x" : "-");
    maps.push_back(m); // 将解析后的内存映射信息添加到列表中
  }
  fclose(mapfile); // 关闭文件
  return maps; // 返回内存映射列表
}
bool ExecBroker::addInstrumentedModule(const std::string &name) {
  bool instrumented = false;
  if (name.empty()) {
    return false;
  }
 
  for (const MemoryMap &m : getCurrentProcessMaps()) {
    if ((m.name == name) && (m.permission & QBDI::PF_EXEC)) {
      addInstrumentedRange(m.range);
      instrumented = true;
    }
  }
  return instrumented;
}
 
void ExecBroker::addInstrumentedRange(const Range<rword> &r) {
  QBDI_DEBUG("Adding instrumented range [0x{:x}, 0x{:x}]", r.start(), r.end());
  instrumented.add(r);
}
bool ExecBroker::addInstrumentedModule(const std::string &name) {
  bool instrumented = false;
  if (name.empty()) {
    return false;
  }
 
  for (const MemoryMap &m : getCurrentProcessMaps()) {
    if ((m.name == name) && (m.permission & QBDI::PF_EXEC)) {
      addInstrumentedRange(m.range);
      instrumented = true;
    }
  }
  return instrumented;
}
 
void ExecBroker::addInstrumentedRange(const Range<rword> &r) {
  QBDI_DEBUG("Adding instrumented range [0x{:x}, 0x{:x}]", r.start(), r.end());
  instrumented.add(r);
}
bool ExecBroker::addInstrumentedModuleFromAddr(rword addr) {
  for (const MemoryMap &m : getCurrentProcessMaps()) {
    if (m.range.contains(addr)) {
      if (not m.name.empty()) {
        return addInstrumentedModule(m.name);
      } else if (m.permission & QBDI::PF_EXEC) {
        addInstrumentedRange(m.range);
        return true;
      } else {
        return false;
      }
    }
  }
  return false;
}
bool ExecBroker::addInstrumentedModuleFromAddr(rword addr) {
  for (const MemoryMap &m : getCurrentProcessMaps()) {
    if (m.range.contains(addr)) {
      if (not m.name.empty()) {
        return addInstrumentedModule(m.name);
      } else if (m.permission & QBDI::PF_EXEC) {
        addInstrumentedRange(m.range);
        return true;
      } else {
        return false;
      }
    }
  }
  return false;
}
/*! 使用 DBI(及其当前状态)调用一个函数.
 *  当 VM 已经运行时, 不能调用此方法.
 *
 * @param[in] instance   VM 实例.
 * @param[in] [retval]   指向返回值的指针(可选).
 * @param[in] function   函数起始指令的地址.
 * @param[in] argNum     可变参数列表中的参数数量.
 * @param[in] ...        可变参数列表.
 *
 * @return  如果至少执行了一个块, 则返回 True.
 *
 * @details 示例:
 *
 *     // 使用 QBDI 执行类似于 (*funcPtr)(42) 的调用
 *     uint8_t *fakestack = NULL;
 *     VMInstanceRef vm;
 *     qbdi_initVM(&vm, NULL, NULL);
 *     GPRState* gprState = qbdi_getGPRState(vm);
 *     qbdi_allocateVirtualStack(gprState, 0x1000000, &fakestack);
 *     qbdi_addInstrumentedModuleFromAddr(vm, funcPtr);
 *     rword retVal;
 *     qbdi_call(vm, &retVal, funcPtr, 1, 42);
 *     qbdi_alignedFree(fakestack);
 *
 */
QBDI_EXPORT bool qbdi_call(VMInstanceRef instance, rword *retval,
                           rword function, uint32_t argNum, ...);
/*! 使用 DBI(及其当前状态)调用一个函数.
 *  当 VM 已经运行时, 不能调用此方法.
 *
 * @param[in] instance   VM 实例.
 * @param[in] [retval]   指向返回值的指针(可选).
 * @param[in] function   函数起始指令的地址.
 * @param[in] argNum     可变参数列表中的参数数量.
 * @param[in] ...        可变参数列表.
 *
 * @return  如果至少执行了一个块, 则返回 True.
 *
 * @details 示例:
 *
 *     // 使用 QBDI 执行类似于 (*funcPtr)(42) 的调用
 *     uint8_t *fakestack = NULL;
 *     VMInstanceRef vm;
 *     qbdi_initVM(&vm, NULL, NULL);
 *     GPRState* gprState = qbdi_getGPRState(vm);
 *     qbdi_allocateVirtualStack(gprState, 0x1000000, &fakestack);
 *     qbdi_addInstrumentedModuleFromAddr(vm, funcPtr);
 *     rword retVal;
 *     qbdi_call(vm, &retVal, funcPtr, 1, 42);
 *     qbdi_alignedFree(fakestack);
 *
 */
QBDI_EXPORT bool qbdi_call(VMInstanceRef instance, rword *retval,
                           rword function, uint32_t argNum, ...);
bool qbdi_call(VMInstanceRef instance, rword *retval, rword function,
               uint32_t argNum, ...) {
  QBDI_REQUIRE_ACTION(instance, return false);
  va_list ap;
  va_start(ap, argNum);
  bool res = static_cast<VM *>(instance)->callV(retval, function, argNum, ap);
  va_end(ap);
  return res;
}
bool qbdi_call(VMInstanceRef instance, rword *retval, rword function,
               uint32_t argNum, ...) {
  QBDI_REQUIRE_ACTION(instance, return false);
  va_list ap;
  va_start(ap, argNum);
  bool res = static_cast<VM *>(instance)->callV(retval, function, argNum, ap);
  va_end(ap);
  return res;
}
bool VM::callV(rword *retval, rword function, uint32_t argNum, va_list ap) {
  std::vector<rword> args(argNum);
  for (uint32_t i = 0; i < argNum; i++) {
    args[i] = va_arg(ap, rword);
  }
 
  bool res = this->callA(retval, function, argNum, args.data());
 
  return res;
}
bool VM::callV(rword *retval, rword function, uint32_t argNum, va_list ap) {
  std::vector<rword> args(argNum);
  for (uint32_t i = 0; i < argNum; i++) {
    args[i] = va_arg(ap, rword);
  }
 
  bool res = this->callA(retval, function, argNum, args.data());
 
  return res;
}
#define FAKE_RET_ADDR 42
 
bool VM::callA(rword *retval, rword function, uint32_t argNum,
               const rword *args) {
  GPRState *state = getGPRState();
  QBDI_REQUIRE_ABORT(state != nullptr, "Fail to get VM GPRState");
 
  // a stack pointer must be set in state
  if (QBDI_GPR_GET(state, REG_SP) == 0) {
    return false;
  }
  // push arguments in current context
  simulateCallA(state, FAKE_RET_ADDR, argNum, args);
  // call function
  bool res = run(function, FAKE_RET_ADDR);
  // get return value from current state
  if (retval != nullptr) {
    *retval = QBDI_GPR_GET(state, REG_RETURN);
  }
  return res;
}
#define FAKE_RET_ADDR 42
 
bool VM::callA(rword *retval, rword function, uint32_t argNum,
               const rword *args) {
  GPRState *state = getGPRState();
  QBDI_REQUIRE_ABORT(state != nullptr, "Fail to get VM GPRState");
 
  // a stack pointer must be set in state
  if (QBDI_GPR_GET(state, REG_SP) == 0) {
    return false;
  }
  // push arguments in current context
  simulateCallA(state, FAKE_RET_ADDR, argNum, args);
  // call function
  bool res = run(function, FAKE_RET_ADDR);
  // get return value from current state
  if (retval != nullptr) {
    *retval = QBDI_GPR_GET(state, REG_RETURN);
  }
  return res;
}
#define FRAME_LENGTH 16
 
void simulateCallA(GPRState *ctx, rword returnAddress, uint32_t argNum,
                   const rword *args) {
  uint32_t i = 0; // 用于遍历参数的索引
  uint32_t argsoff = 0; // 用于计算栈帧偏移
  uint32_t limit = FRAME_LENGTH; // 栈帧的最大长度
 
  // 为参数分配栈帧
  QBDI_GPR_SET(ctx, REG_SP,
               QBDI_GPR_GET(ctx, REG_SP) - FRAME_LENGTH * sizeof(rword));
 
  // 处理返回地址
#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)
  // 在 x86 和 x86_64 架构上, 返回地址存储在栈上
  QBDI_GPR_SET(ctx, REG_SP, QBDI_GPR_GET(ctx, REG_SP) - sizeof(rword));
  *(rword *)(QBDI_GPR_GET(ctx, REG_SP)) = returnAddress; // 将返回地址压入栈
  argsoff++;
#elif defined(QBDI_ARCH_ARM) || defined(QBDI_ARCH_AARCH64)
  // 在 ARM 和 AArch64 架构上, 返回地址存储在链接寄存器中
  QBDI_DEBUG("Set LR to: 0x{:x}", returnAddress);
  ctx->lr = returnAddress; // 设置链接寄存器
#endif
 
#define UNSTACK_ARG(REG)  \
  if (i < argNum) {       \
    ctx->REG = args[i++]; \
  }
  // 根据架构不同, 将参数加载到寄存器中
#if defined(QBDI_ARCH_X86_64)
#if defined(QBDI_PLATFORM_WINDOWS)
  // Windows 平台的 x86_64 架构有影子空间, 在Windows平台的x86_64架构中, 为了确保栈对齐和兼容性, 调用者需要在栈上预留一个固定大小的空间, 这个空间被称为影子空间
  argsoff += 4;
  // 寄存器参数
  UNSTACK_ARG(rcx);
  UNSTACK_ARG(rdx);
  UNSTACK_ARG(r8);
  UNSTACK_ARG(r9);
#else
  // 非 Windows 平台的 x86_64 架构
  // 寄存器参数
  UNSTACK_ARG(rdi);
  UNSTACK_ARG(rsi);
  UNSTACK_ARG(rdx);
  UNSTACK_ARG(rcx);
  UNSTACK_ARG(r8);
  UNSTACK_ARG(r9);
#endif // OS
#elif defined(QBDI_ARCH_X86)
  // x86 架构不使用寄存器传递参数
#elif defined(QBDI_ARCH_ARM)
  // ARM 架构的寄存器参数
  UNSTACK_ARG(r0);
  UNSTACK_ARG(r1);
  UNSTACK_ARG(r2);
  UNSTACK_ARG(r3);
#elif defined(QBDI_ARCH_AARCH64)
  // AArch64 架构的寄存器参数
  UNSTACK_ARG(x0);
  UNSTACK_ARG(x1);
  UNSTACK_ARG(x2);
  UNSTACK_ARG(x3);
  UNSTACK_ARG(x4);
  UNSTACK_ARG(x5);
  UNSTACK_ARG(x6);
  UNSTACK_ARG(x7);
#endif // ARCH
#undef UNSTACK_ARG
  limit -= argsoff;
 
  // 将剩余的参数压入栈
  rword *frame = (rword *)QBDI_GPR_GET(ctx, REG_SP);
  for (uint32_t j = 0; (i + j) < argNum && j < limit; j++) {
    frame[argsoff + j] = args[i + j];
  }
}
#define FRAME_LENGTH 16
 
void simulateCallA(GPRState *ctx, rword returnAddress, uint32_t argNum,
                   const rword *args) {
  uint32_t i = 0; // 用于遍历参数的索引
  uint32_t argsoff = 0; // 用于计算栈帧偏移
  uint32_t limit = FRAME_LENGTH; // 栈帧的最大长度
 
  // 为参数分配栈帧
  QBDI_GPR_SET(ctx, REG_SP,
               QBDI_GPR_GET(ctx, REG_SP) - FRAME_LENGTH * sizeof(rword));
 
  // 处理返回地址
#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)
  // 在 x86 和 x86_64 架构上, 返回地址存储在栈上
  QBDI_GPR_SET(ctx, REG_SP, QBDI_GPR_GET(ctx, REG_SP) - sizeof(rword));
  *(rword *)(QBDI_GPR_GET(ctx, REG_SP)) = returnAddress; // 将返回地址压入栈
  argsoff++;
#elif defined(QBDI_ARCH_ARM) || defined(QBDI_ARCH_AARCH64)
  // 在 ARM 和 AArch64 架构上, 返回地址存储在链接寄存器中
  QBDI_DEBUG("Set LR to: 0x{:x}", returnAddress);
  ctx->lr = returnAddress; // 设置链接寄存器
#endif
 
#define UNSTACK_ARG(REG)  \
  if (i < argNum) {       \
    ctx->REG = args[i++]; \
  }
  // 根据架构不同, 将参数加载到寄存器中
#if defined(QBDI_ARCH_X86_64)
#if defined(QBDI_PLATFORM_WINDOWS)
  // Windows 平台的 x86_64 架构有影子空间, 在Windows平台的x86_64架构中, 为了确保栈对齐和兼容性, 调用者需要在栈上预留一个固定大小的空间, 这个空间被称为影子空间
  argsoff += 4;
  // 寄存器参数
  UNSTACK_ARG(rcx);
  UNSTACK_ARG(rdx);
  UNSTACK_ARG(r8);
  UNSTACK_ARG(r9);
#else
  // 非 Windows 平台的 x86_64 架构
  // 寄存器参数
  UNSTACK_ARG(rdi);
  UNSTACK_ARG(rsi);
  UNSTACK_ARG(rdx);
  UNSTACK_ARG(rcx);
  UNSTACK_ARG(r8);
  UNSTACK_ARG(r9);
#endif // OS
#elif defined(QBDI_ARCH_X86)
  // x86 架构不使用寄存器传递参数
#elif defined(QBDI_ARCH_ARM)
  // ARM 架构的寄存器参数
  UNSTACK_ARG(r0);
  UNSTACK_ARG(r1);
  UNSTACK_ARG(r2);
  UNSTACK_ARG(r3);
#elif defined(QBDI_ARCH_AARCH64)
  // AArch64 架构的寄存器参数
  UNSTACK_ARG(x0);
  UNSTACK_ARG(x1);
  UNSTACK_ARG(x2);
  UNSTACK_ARG(x3);
  UNSTACK_ARG(x4);
  UNSTACK_ARG(x5);
  UNSTACK_ARG(x6);
  UNSTACK_ARG(x7);
#endif // ARCH
#undef UNSTACK_ARG
  limit -= argsoff;
 
  // 将剩余的参数压入栈
  rword *frame = (rword *)QBDI_GPR_GET(ctx, REG_SP);
  for (uint32_t j = 0; (i + j) < argNum && j < limit; j++) {
    frame[argsoff + j] = args[i + j];
  }
}
bool VM::run(rword start, rword stop) {
  uint32_t stopCB =
      addCodeAddrCB(stop, InstPosition::PREINST, stopCallback, nullptr);
  bool ret = engine->run(start, stop);
  deleteInstrumentation(stopCB);
  return ret;
}
bool VM::run(rword start, rword stop) {
  uint32_t stopCB =
      addCodeAddrCB(stop, InstPosition::PREINST, stopCallback, nullptr);
  bool ret = engine->run(start, stop);
  deleteInstrumentation(stopCB);
  return ret;
}
VMAction stopCallback(VMInstanceRef vm, GPRState *gprState, FPRState *fprState,
                      void *data) {
  return VMAction::STOP;
}
VMAction stopCallback(VMInstanceRef vm, GPRState *gprState, FPRState *fprState,
                      void *data) {
  return VMAction::STOP;
}
uint32_t VM::addCodeAddrCB(rword address, InstPosition pos, InstCallback cbk,
                           void *data, int priority) {
  QBDI_REQUIRE_ACTION(cbk != nullptr, return VMError::INVALID_EVENTID);
  return engine->addInstrRule(InstrRuleBasicCBK::unique(
      AddressIs::unique(address), cbk, data, pos, true, priority,
      (pos == PREINST) ? RelocTagPreInstStdCBK : RelocTagPostInstStdCBK));
}
uint32_t VM::addCodeAddrCB(rword address, InstPosition pos, InstCallback cbk,
                           void *data, int priority) {
  QBDI_REQUIRE_ACTION(cbk != nullptr, return VMError::INVALID_EVENTID);
  return engine->addInstrRule(InstrRuleBasicCBK::unique(
      AddressIs::unique(address), cbk, data, pos, true, priority,
      (pos == PREINST) ? RelocTagPreInstStdCBK : RelocTagPostInstStdCBK));
}
class AddressIs : public AutoClone<PatchCondition, AddressIs> {
  rword breakpoint;
 
public:
  /*! Return true if on specified address
   */
  AddressIs(rword breakpoint_) : breakpoint(breakpoint_) {
    // for ARM, remove the LSB
    if constexpr (is_arm) {
      breakpoint &= (~1);
    }
  }
 
  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override {
    return patch.metadata.address == breakpoint;
  }
 
  RangeSet<rword> affectedRange() const override {
    RangeSet<rword> r;
    r.add(Range<rword>(breakpoint, breakpoint + 1));
    return r;
  }
};
class AddressIs : public AutoClone<PatchCondition, AddressIs> {
  rword breakpoint;
 
public:
  /*! Return true if on specified address
   */
  AddressIs(rword breakpoint_) : breakpoint(breakpoint_) {
    // for ARM, remove the LSB
    if constexpr (is_arm) {
      breakpoint &= (~1);
    }
  }
 
  bool test(const Patch &patch, const LLVMCPU &llvmcpu) const override {
    return patch.metadata.address == breakpoint;
  }
 
  RangeSet<rword> affectedRange() const override {
    RangeSet<rword> r;
    r.add(Range<rword>(breakpoint, breakpoint + 1));
    return r;
  }
};
bool Engine::run(rword start, rword stop) {
  QBDI_REQUIRE_ABORT(not running, "Cannot run an already running Engine");
 
  rword currentPC = start;
  bool hasRan = false;
  curGPRState = gprState.get();
  curFPRState = fprState.get();
 
  rword basicBlockBeginAddr = 0;
  rword basicBlockEndAddr = 0;
 
  // Start address is out of range
  if (!execBroker->isInstrumented(start)) {
    return false;
  }
 
  running = true;
 
  // Execute basic block per basic block
  do {
    VMAction action = CONTINUE;
 
    // If this PC is not instrumented try to transfer execution
    if (execBroker->isInstrumented(currentPC) == false and
        execBroker->canTransferExecution(curGPRState)) {
 
#if defined(QBDI_ARCH_ARM)
      // Handle ARM mode
      // If we switch to the execbroker without an exchange, keep the current
      // mode
      bool changeCPUMode = curExecBlock == nullptr ||
                           curExecBlock->getContext()->hostState.exchange == 1;
      if (not changeCPUMode) {
        if (curCPUMode == CPUMode::Thumb) {
          currentPC = currentPC | 1;
        } else {
          currentPC = currentPC & (~1);
        }
      }
#endif
 
      curExecBlock = nullptr;
      basicBlockBeginAddr = 0;
      basicBlockEndAddr = 0;
 
      QBDI_DEBUG("Executing 0x{:x} through execBroker", currentPC);
      action = signalEvent(EXEC_TRANSFER_CALL, currentPC, nullptr, 0,
                           curGPRState, curFPRState);
      // transfer execution
      if (action == CONTINUE) {
        execBroker->transferExecution(currentPC, curGPRState, curFPRState);
        action = signalEvent(EXEC_TRANSFER_RETURN, currentPC, nullptr, 0,
                             curGPRState, curFPRState);
      }
    }
    // Else execute through DBI
    else {
      VMEvent event = VMEvent::SEQUENCE_ENTRY;
 
#if defined(QBDI_ARCH_ARM)
      // Handle ARM mode switching, only at the start of an execution or when
      // the guest signal an exchange.
      bool changeCPUMode = curExecBlock == nullptr ||
                           curExecBlock->getContext()->hostState.exchange == 1;
      if (changeCPUMode) {
        curCPUMode = currentPC & 1 ? CPUMode::Thumb : CPUMode::ARM;
        QBDI_DEBUG("CPUMode set to {}",
                   curCPUMode == CPUMode::ARM ? "ARM" : "Thumb");
      } else if (curCPUMode == CPUMode::ARM) {
        QBDI_REQUIRE_ABORT((currentPC & 1) == 0,
                           "Unexpected address in ARM mode");
      } else {
        QBDI_REQUIRE_ABORT((currentPC & 1) == 1,
                           "Unexpected address in Thumb mode");
      }
      currentPC = currentPC & (~1);
#endif
 
      QBDI_DEBUG("Executing 0x{:x} through DBI in mode {}", currentPC,
                 curCPUMode);
 
      // Is cache flush pending?
      if (blockManager->isFlushPending()) {
        // Backup fprState and gprState
        *gprState = *curGPRState;
        *fprState = *curFPRState;
        curGPRState = gprState.get();
        curFPRState = fprState.get();
        // Commit the flush
        blockManager->flushCommit();
      }
 
      // Test if we have it in cache
      SeqLoc currentSequence;
      curExecBlock = blockManager->getProgrammedExecBlock(currentPC, curCPUMode,
                                                          &currentSequence);
      if (curExecBlock == nullptr) {
        QBDI_DEBUG(
            "Cache miss for 0x{:x}, patching & instrumenting new basic block",
            currentPC);
        handleNewBasicBlock(currentPC);
        // Signal a new basic block
        event |= BASIC_BLOCK_NEW;
        // Set new basic block as current
        curExecBlock = blockManager->getProgrammedExecBlock(
            currentPC, curCPUMode, &currentSequence);
        QBDI_REQUIRE_ABORT(curExecBlock != nullptr,
                           "Fail to instrument the next basic block");
      }
 
      if (basicBlockEndAddr == 0) {
        event |= BASIC_BLOCK_ENTRY;
        basicBlockEndAddr = currentSequence.bbEnd;
        basicBlockBeginAddr = currentPC;
      }
 
      // Set context if necessary
      if (&(curExecBlock->getContext()->gprState) != curGPRState ||
          &(curExecBlock->getContext()->fprState) != curFPRState) {
        curExecBlock->getContext()->gprState = *curGPRState;
        curExecBlock->getContext()->fprState = *curFPRState;
      }
      curGPRState = &(curExecBlock->getContext()->gprState);
      curFPRState = &(curExecBlock->getContext()->fprState);
 
      action = signalEvent(event, currentPC, &currentSequence,
                           basicBlockBeginAddr, curGPRState, curFPRState);
 
      if (action == CONTINUE) {
        hasRan = true;
        action = curExecBlock->execute();
        // Signal events if normal exit
        if (action == CONTINUE) {
          if (basicBlockEndAddr == currentSequence.seqEnd) {
            action = signalEvent(SEQUENCE_EXIT | BASIC_BLOCK_EXIT, currentPC,
                                 &currentSequence, basicBlockBeginAddr,
                                 curGPRState, curFPRState);
            basicBlockBeginAddr = 0;
            basicBlockEndAddr = 0;
          } else {
            action = signalEvent(SEQUENCE_EXIT, currentPC, &currentSequence,
                                 basicBlockBeginAddr, curGPRState, curFPRState);
          }
        }
      }
    }
    if (action == STOP) {
      QBDI_DEBUG("Receive STOP Action");
      break;
    }
    if (action != CONTINUE) {
      basicBlockBeginAddr = 0;
      basicBlockEndAddr = 0;
      curExecBlock = nullptr;
    }
    // Get next block PC
    currentPC = QBDI_GPR_GET(curGPRState, REG_PC);
    QBDI_DEBUG("Next address to execute is 0x{:x}", currentPC);
  } while (currentPC != stop);
 
  // Copy final context
  *gprState = *curGPRState;
  *fprState = *curFPRState;
  curGPRState = gprState.get();
  curFPRState = fprState.get();
  curExecBlock = nullptr;
  running = false;
 
  if (blockManager->isFlushPending()) {
    blockManager->flushCommit();
  }
 
  return hasRan;
}
bool Engine::run(rword start, rword stop) {
  QBDI_REQUIRE_ABORT(not running, "Cannot run an already running Engine");
 
  rword currentPC = start;
  bool hasRan = false;
  curGPRState = gprState.get();
  curFPRState = fprState.get();
 
  rword basicBlockBeginAddr = 0;
  rword basicBlockEndAddr = 0;
 
  // Start address is out of range
  if (!execBroker->isInstrumented(start)) {
    return false;
  }
 
  running = true;
 
  // Execute basic block per basic block
  do {
    VMAction action = CONTINUE;
 
    // If this PC is not instrumented try to transfer execution
    if (execBroker->isInstrumented(currentPC) == false and
        execBroker->canTransferExecution(curGPRState)) {
 
#if defined(QBDI_ARCH_ARM)
      // Handle ARM mode
      // If we switch to the execbroker without an exchange, keep the current
      // mode
      bool changeCPUMode = curExecBlock == nullptr ||
                           curExecBlock->getContext()->hostState.exchange == 1;
      if (not changeCPUMode) {
        if (curCPUMode == CPUMode::Thumb) {
          currentPC = currentPC | 1;
        } else {
          currentPC = currentPC & (~1);
        }
      }
#endif
 
      curExecBlock = nullptr;
      basicBlockBeginAddr = 0;
      basicBlockEndAddr = 0;
 
      QBDI_DEBUG("Executing 0x{:x} through execBroker", currentPC);
      action = signalEvent(EXEC_TRANSFER_CALL, currentPC, nullptr, 0,
                           curGPRState, curFPRState);
      // transfer execution
      if (action == CONTINUE) {
        execBroker->transferExecution(currentPC, curGPRState, curFPRState);
        action = signalEvent(EXEC_TRANSFER_RETURN, currentPC, nullptr, 0,
                             curGPRState, curFPRState);
      }
    }
    // Else execute through DBI
    else {
      VMEvent event = VMEvent::SEQUENCE_ENTRY;
 
#if defined(QBDI_ARCH_ARM)
      // Handle ARM mode switching, only at the start of an execution or when
      // the guest signal an exchange.
      bool changeCPUMode = curExecBlock == nullptr ||
                           curExecBlock->getContext()->hostState.exchange == 1;
      if (changeCPUMode) {
        curCPUMode = currentPC & 1 ? CPUMode::Thumb : CPUMode::ARM;
        QBDI_DEBUG("CPUMode set to {}",
                   curCPUMode == CPUMode::ARM ? "ARM" : "Thumb");
      } else if (curCPUMode == CPUMode::ARM) {
        QBDI_REQUIRE_ABORT((currentPC & 1) == 0,
                           "Unexpected address in ARM mode");
      } else {
        QBDI_REQUIRE_ABORT((currentPC & 1) == 1,
                           "Unexpected address in Thumb mode");
      }
      currentPC = currentPC & (~1);
#endif
 
      QBDI_DEBUG("Executing 0x{:x} through DBI in mode {}", currentPC,
                 curCPUMode);
 
      // Is cache flush pending?
      if (blockManager->isFlushPending()) {
        // Backup fprState and gprState
        *gprState = *curGPRState;
        *fprState = *curFPRState;
        curGPRState = gprState.get();
        curFPRState = fprState.get();
        // Commit the flush
        blockManager->flushCommit();
      }
 
      // Test if we have it in cache
      SeqLoc currentSequence;
      curExecBlock = blockManager->getProgrammedExecBlock(currentPC, curCPUMode,
                                                          &currentSequence);
      if (curExecBlock == nullptr) {
        QBDI_DEBUG(
            "Cache miss for 0x{:x}, patching & instrumenting new basic block",
            currentPC);
        handleNewBasicBlock(currentPC);
        // Signal a new basic block
        event |= BASIC_BLOCK_NEW;
        // Set new basic block as current
        curExecBlock = blockManager->getProgrammedExecBlock(
            currentPC, curCPUMode, &currentSequence);
        QBDI_REQUIRE_ABORT(curExecBlock != nullptr,
                           "Fail to instrument the next basic block");
      }
 
      if (basicBlockEndAddr == 0) {
        event |= BASIC_BLOCK_ENTRY;
        basicBlockEndAddr = currentSequence.bbEnd;
        basicBlockBeginAddr = currentPC;
      }
 
      // Set context if necessary
      if (&(curExecBlock->getContext()->gprState) != curGPRState ||
          &(curExecBlock->getContext()->fprState) != curFPRState) {
        curExecBlock->getContext()->gprState = *curGPRState;
        curExecBlock->getContext()->fprState = *curFPRState;
      }
      curGPRState = &(curExecBlock->getContext()->gprState);
      curFPRState = &(curExecBlock->getContext()->fprState);
 
      action = signalEvent(event, currentPC, &currentSequence,
                           basicBlockBeginAddr, curGPRState, curFPRState);
 
      if (action == CONTINUE) {
        hasRan = true;
        action = curExecBlock->execute();
        // Signal events if normal exit
        if (action == CONTINUE) {
          if (basicBlockEndAddr == currentSequence.seqEnd) {
            action = signalEvent(SEQUENCE_EXIT | BASIC_BLOCK_EXIT, currentPC,
                                 &currentSequence, basicBlockBeginAddr,
                                 curGPRState, curFPRState);
            basicBlockBeginAddr = 0;
            basicBlockEndAddr = 0;
          } else {
            action = signalEvent(SEQUENCE_EXIT, currentPC, &currentSequence,
                                 basicBlockBeginAddr, curGPRState, curFPRState);
          }
        }
      }
    }
    if (action == STOP) {
      QBDI_DEBUG("Receive STOP Action");
      break;
    }
    if (action != CONTINUE) {
      basicBlockBeginAddr = 0;
      basicBlockEndAddr = 0;
      curExecBlock = nullptr;
    }
    // Get next block PC
    currentPC = QBDI_GPR_GET(curGPRState, REG_PC);
    QBDI_DEBUG("Next address to execute is 0x{:x}", currentPC);
  } while (currentPC != stop);
 
  // Copy final context
  *gprState = *curGPRState;
  *fprState = *curFPRState;
  curGPRState = gprState.get();
  curFPRState = fprState.get();
  curExecBlock = nullptr;
  running = false;
 
  if (blockManager->isFlushPending()) {
    blockManager->flushCommit();
  }
 
  return hasRan;
}
std::unique_ptr<GPRState> gprState;
std::unique_ptr<FPRState> fprState;
std::unique_ptr<GPRState> gprState;
std::unique_ptr<FPRState> fprState;
/*! X86_64 Floating Point Register context.
 */ // SPHINX_X86_64_FPRSTATE_BEGIN
typedef struct QBDI_ALIGNED(16) {
  union {
    FPControl fcw; /* x87 FPU control word */
    uint16_t rfcw;
  };
  union {
    FPStatus fsw; /* x87 FPU status word */
    uint16_t rfsw;
  };
  uint8_t ftw;        /* x87 FPU tag word */
  uint8_t rsrv1;      /* reserved */
  uint16_t fop;       /* x87 FPU Opcode */
  uint32_t ip;        /* x87 FPU Instruction Pointer offset */
  uint16_t cs;        /* x87 FPU Instruction Pointer Selector */
  uint16_t rsrv2;     /* reserved */
  uint32_t dp;        /* x87 FPU Instruction Operand(Data) Pointer offset */
  uint16_t ds;        /* x87 FPU Instruction Operand(Data) Pointer Selector */
  uint16_t rsrv3;     /* reserved */
  uint32_t mxcsr;     /* MXCSR Register state */
  uint32_t mxcsrmask; /* MXCSR mask */
  MMSTReg stmm0;      /* ST0/MM0   */
  MMSTReg stmm1;      /* ST1/MM1  */
  MMSTReg stmm2;      /* ST2/MM2  */
  MMSTReg stmm3;      /* ST3/MM3  */
  MMSTReg stmm4;      /* ST4/MM4  */
  MMSTReg stmm5;      /* ST5/MM5  */
  MMSTReg stmm6;      /* ST6/MM6  */
  MMSTReg stmm7;      /* ST7/MM7  */
  char xmm0[16];      /* XMM 0  */
  char xmm1[16];      /* XMM 1  */
  char xmm2[16];      /* XMM 2  */
  char xmm3[16];      /* XMM 3  */
  char xmm4[16];      /* XMM 4  */
  char xmm5[16];      /* XMM 5  */
  char xmm6[16];      /* XMM 6  */
  char xmm7[16];      /* XMM 7  */
  char xmm8[16];      /* XMM 8  */
  char xmm9[16];      /* XMM 9  */
  char xmm10[16];     /* XMM 10  */
  char xmm11[16];     /* XMM 11  */
  char xmm12[16];     /* XMM 12  */
  char xmm13[16];     /* XMM 13  */
  char xmm14[16];     /* XMM 14  */
  char xmm15[16];     /* XMM 15  */
  char reserved[6 * 16];
  char ymm0[16];  /* YMM0[255:128] */
  char ymm1[16];  /* YMM1[255:128] */
  char ymm2[16];  /* YMM2[255:128] */
  char ymm3[16];  /* YMM3[255:128] */
  char ymm4[16];  /* YMM4[255:128] */
  char ymm5[16];  /* YMM5[255:128] */
  char ymm6[16];  /* YMM6[255:128] */
  char ymm7[16];  /* YMM7[255:128] */
  char ymm8[16];  /* YMM8[255:128] */
  char ymm9[16];  /* YMM9[255:128] */
  char ymm10[16]; /* YMM10[255:128] */
  char ymm11[16]; /* YMM11[255:128] */
  char ymm12[16]; /* YMM12[255:128] */
  char ymm13[16]; /* YMM13[255:128] */
  char ymm14[16]; /* YMM14[255:128] */
  char ymm15[16]; /* YMM15[255:128] */
} FPRState;
/*! X86_64 Floating Point Register context.
 */ // SPHINX_X86_64_FPRSTATE_BEGIN
typedef struct QBDI_ALIGNED(16) {
  union {
    FPControl fcw; /* x87 FPU control word */
    uint16_t rfcw;
  };
  union {
    FPStatus fsw; /* x87 FPU status word */
    uint16_t rfsw;
  };
  uint8_t ftw;        /* x87 FPU tag word */
  uint8_t rsrv1;      /* reserved */
  uint16_t fop;       /* x87 FPU Opcode */
  uint32_t ip;        /* x87 FPU Instruction Pointer offset */
  uint16_t cs;        /* x87 FPU Instruction Pointer Selector */
  uint16_t rsrv2;     /* reserved */
  uint32_t dp;        /* x87 FPU Instruction Operand(Data) Pointer offset */
  uint16_t ds;        /* x87 FPU Instruction Operand(Data) Pointer Selector */
  uint16_t rsrv3;     /* reserved */
  uint32_t mxcsr;     /* MXCSR Register state */
  uint32_t mxcsrmask; /* MXCSR mask */
  MMSTReg stmm0;      /* ST0/MM0   */
  MMSTReg stmm1;      /* ST1/MM1  */
  MMSTReg stmm2;      /* ST2/MM2  */
  MMSTReg stmm3;      /* ST3/MM3  */
  MMSTReg stmm4;      /* ST4/MM4  */
  MMSTReg stmm5;      /* ST5/MM5  */
  MMSTReg stmm6;      /* ST6/MM6  */
  MMSTReg stmm7;      /* ST7/MM7  */
  char xmm0[16];      /* XMM 0  */
  char xmm1[16];      /* XMM 1  */
  char xmm2[16];      /* XMM 2  */
  char xmm3[16];      /* XMM 3  */
  char xmm4[16];      /* XMM 4  */
  char xmm5[16];      /* XMM 5  */
  char xmm6[16];      /* XMM 6  */
  char xmm7[16];      /* XMM 7  */
  char xmm8[16];      /* XMM 8  */
  char xmm9[16];      /* XMM 9  */
  char xmm10[16];     /* XMM 10  */
  char xmm11[16];     /* XMM 11  */
  char xmm12[16];     /* XMM 12  */
  char xmm13[16];     /* XMM 13  */
  char xmm14[16];     /* XMM 14  */
  char xmm15[16];     /* XMM 15  */
  char reserved[6 * 16];
  char ymm0[16];  /* YMM0[255:128] */
  char ymm1[16];  /* YMM1[255:128] */
  char ymm2[16];  /* YMM2[255:128] */
  char ymm3[16];  /* YMM3[255:128] */
  char ymm4[16];  /* YMM4[255:128] */
  char ymm5[16];  /* YMM5[255:128] */
  char ymm6[16];  /* YMM6[255:128] */
  char ymm7[16];  /* YMM7[255:128] */
  char ymm8[16];  /* YMM8[255:128] */
  char ymm9[16];  /* YMM9[255:128] */
  char ymm10[16]; /* YMM10[255:128] */
  char ymm11[16]; /* YMM11[255:128] */
  char ymm12[16]; /* YMM12[255:128] */
  char ymm13[16]; /* YMM13[255:128] */
  char ymm14[16]; /* YMM14[255:128] */
  char ymm15[16]; /* YMM15[255:128] */
} FPRState;
// Start address is out of range
if (!execBroker->isInstrumented(start)) {
    return false;
}
// Start address is out of range
if (!execBroker->isInstrumented(start)) {
    return false;
}
running = true;
 
do {
    // ...
} while (currentPC != stop);
running = true;
 
do {
    // ...
} while (currentPC != stop);
/*! 回调函数执行结果.  */
typedef enum {
  _QBDI_EI(CONTINUE) = 0,    /*!< 基本块继续执行.  */
  _QBDI_EI(SKIP_INST) = 1,   /*!< 仅适用于PREINST(指令执行前)回调.
                              *   跳过当前指令及后续的PREINST回调,
                              *   直接执行POSTINST(指令执行后)回调.
                              *
                              *   建议将此结果用于低优先级的PREINST回调,
                              *   以便在跳过指令时仍保留POSTINST回调功能.
                              */
  _QBDI_EI(SKIP_PATCH) = 2,  /*!< 仅适用于InstCallback(指令回调).
                              *   跳过当前指令及剩余的PRE/POST回调,
                              *   继续执行下一条指令.
                              *
                              *   注意: 修改指令指针的指令(跳转/调用/返回)
                              *   必须使用BREAK_TO_VM而非SKIP.
                              *
                              *   SKIP可能中断当前指令的内存访问记录.
                              */
  _QBDI_EI(BREAK_TO_VM) = 3, /*!< 中断执行并返回虚拟机(VM),
                              *   触发执行状态的完整重新评估.
                              *   需BREAK_TO_VM以确保程序计数器(PC)
                              *   或程序代码的修改生效.
                              */
  _QBDI_EI(STOP) = 4,        /*!< 终止程序执行,
                              *   导致run函数提前返回.
                              */
} VMAction;
/*! 回调函数执行结果.  */
typedef enum {
  _QBDI_EI(CONTINUE) = 0,    /*!< 基本块继续执行.  */
  _QBDI_EI(SKIP_INST) = 1,   /*!< 仅适用于PREINST(指令执行前)回调.
                              *   跳过当前指令及后续的PREINST回调,
                              *   直接执行POSTINST(指令执行后)回调.
                              *
                              *   建议将此结果用于低优先级的PREINST回调,
                              *   以便在跳过指令时仍保留POSTINST回调功能.
                              */
  _QBDI_EI(SKIP_PATCH) = 2,  /*!< 仅适用于InstCallback(指令回调).
                              *   跳过当前指令及剩余的PRE/POST回调,
                              *   继续执行下一条指令.
                              *
                              *   注意: 修改指令指针的指令(跳转/调用/返回)
                              *   必须使用BREAK_TO_VM而非SKIP.
                              *
                              *   SKIP可能中断当前指令的内存访问记录.
                              */
  _QBDI_EI(BREAK_TO_VM) = 3, /*!< 中断执行并返回虚拟机(VM),
                              *   触发执行状态的完整重新评估.
                              *   需BREAK_TO_VM以确保程序计数器(PC)
                              *   或程序代码的修改生效.
                              */
  _QBDI_EI(STOP) = 4,        /*!< 终止程序执行,
                              *   导致run函数提前返回.
                              */
} VMAction;
// If this PC is not instrumented try to transfer execution
if (execBroker->isInstrumented(currentPC) == false and
    execBroker->canTransferExecution(curGPRState)){
 
} // Else execute through DBI
else {
 
}
// If this PC is not instrumented try to transfer execution
if (execBroker->isInstrumented(currentPC) == false and
    execBroker->canTransferExecution(curGPRState)){
 
} // Else execute through DBI
else {
 
}
typedef enum {
  _QBDI_EI(NO_EVENT) = 0,                /*!< 无事件 */
  _QBDI_EI(SEQUENCE_ENTRY) = 1,          /*!< 当执行进入一个序列时触发 */
  _QBDI_EI(SEQUENCE_EXIT) = 1 << 1,      /*!< 当执行退出当前序列时触发 */
  _QBDI_EI(BASIC_BLOCK_ENTRY) = 1 << 2,  /*!< 当执行进入一个基本块时触发 */
  _QBDI_EI(BASIC_BLOCK_EXIT) = 1 << 3,   /*!< 当执行退出当前基本块时触发 */
  _QBDI_EI(BASIC_BLOCK_NEW) = 1 << 4,    /*!< 当执行进入新(未知)基本块时触发 */
  _QBDI_EI(EXEC_TRANSFER_CALL) = 1 << 5, /*!< 当ExecBroker执行控制权转移时触发 */
  _QBDI_EI(EXEC_TRANSFER_RETURN) = 1 << 6, /*!< 当ExecBroker从控制权转移返回时触发 */
  _QBDI_EI(SYSCALL_ENTRY) = 1 << 7,      /*!< 未实现 */
  _QBDI_EI(SYSCALL_EXIT) = 1 << 8,       /*!< 未实现 */
  _QBDI_EI(SIGNAL) = 1 << 9,             /*!< 未实现 */
} VMEvent;
typedef enum {
  _QBDI_EI(NO_EVENT) = 0,                /*!< 无事件 */
  _QBDI_EI(SEQUENCE_ENTRY) = 1,          /*!< 当执行进入一个序列时触发 */
  _QBDI_EI(SEQUENCE_EXIT) = 1 << 1,      /*!< 当执行退出当前序列时触发 */
  _QBDI_EI(BASIC_BLOCK_ENTRY) = 1 << 2,  /*!< 当执行进入一个基本块时触发 */
  _QBDI_EI(BASIC_BLOCK_EXIT) = 1 << 3,   /*!< 当执行退出当前基本块时触发 */
  _QBDI_EI(BASIC_BLOCK_NEW) = 1 << 4,    /*!< 当执行进入新(未知)基本块时触发 */
  _QBDI_EI(EXEC_TRANSFER_CALL) = 1 << 5, /*!< 当ExecBroker执行控制权转移时触发 */
  _QBDI_EI(EXEC_TRANSFER_RETURN) = 1 << 6, /*!< 当ExecBroker从控制权转移返回时触发 */
  _QBDI_EI(SYSCALL_ENTRY) = 1 << 7,      /*!< 未实现 */
  _QBDI_EI(SYSCALL_EXIT) = 1 << 8,       /*!< 未实现 */
  _QBDI_EI(SIGNAL) = 1 << 9,             /*!< 未实现 */
} VMEvent;
#if defined(QBDI_ARCH_ARM)
      // Handle ARM mode switching, only at the start of an execution or when
      // the guest signal an exchange.
      bool changeCPUMode = curExecBlock == nullptr ||
                           curExecBlock->getContext()->hostState.exchange == 1;
      if (changeCPUMode) {
        curCPUMode = currentPC & 1 ? CPUMode::Thumb : CPUMode::ARM;
        QBDI_DEBUG("CPUMode set to {}",
                   curCPUMode == CPUMode::ARM ? "ARM" : "Thumb");
      } else if (curCPUMode == CPUMode::ARM) {
        QBDI_REQUIRE_ABORT((currentPC & 1) == 0,
                           "Unexpected address in ARM mode");
      } else {
        QBDI_REQUIRE_ABORT((currentPC & 1) == 1,
                           "Unexpected address in Thumb mode");
      }
      currentPC = currentPC & (~1);
#endif
#if defined(QBDI_ARCH_ARM)
      // Handle ARM mode switching, only at the start of an execution or when
      // the guest signal an exchange.
      bool changeCPUMode = curExecBlock == nullptr ||
                           curExecBlock->getContext()->hostState.exchange == 1;
      if (changeCPUMode) {
        curCPUMode = currentPC & 1 ? CPUMode::Thumb : CPUMode::ARM;
        QBDI_DEBUG("CPUMode set to {}",
                   curCPUMode == CPUMode::ARM ? "ARM" : "Thumb");
      } else if (curCPUMode == CPUMode::ARM) {
        QBDI_REQUIRE_ABORT((currentPC & 1) == 0,
                           "Unexpected address in ARM mode");
      } else {
        QBDI_REQUIRE_ABORT((currentPC & 1) == 1,
                           "Unexpected address in Thumb mode");
      }
      currentPC = currentPC & (~1);
#endif
// Is cache flush pending?
if (blockManager->isFlushPending()) {
    // Backup fprState and gprState
    *gprState = *curGPRState;
    *fprState = *curFPRState;
    curGPRState = gprState.get();
    curFPRState = fprState.get();
    // Commit the flush
    blockManager->flushCommit();
}
// Is cache flush pending?
if (blockManager->isFlushPending()) {
    // Backup fprState and gprState
    *gprState = *curGPRState;
    *fprState = *curFPRState;
    curGPRState = gprState.get();
    curFPRState = fprState.get();
    // Commit the flush
    blockManager->flushCommit();
}
bool isFlushPending() { return needFlush; }
bool isFlushPending() { return needFlush; }
void ExecBlockManager::flushCommit() {
  // 如果有需要刷新的区域
  if (needFlush) {
    QBDI_DEBUG("Flushing analysis caches"); // 打印调试信息
    // 定义一个删除函数, 用于检查区域是否需要刷新
    auto delFunc = [&](const ExecRegion &r) -> bool {
      // 如果区域需要刷新
      if (r.toFlush) {
        // 打印调试信息, 显示正在移除的区域
        QBDI_DEBUG("Erasing region [0x{:x}, 0x{:x}]", r.covered.start(),
                   r.covered.end());
        // 遍历区域中的所有代码块, 并从代码块映射中移除
        for (const auto &block : r.blocks) {
          codeBlockMap.erase(block->getBaseCodeBlock());
        }
      }
      // 返回是否需要移除该区域
      return r.toFlush;
    };
 
    // 使用 std::remove_if 移除所有需要刷新的区域
    regions.erase(std::remove_if(regions.begin(), regions.end(), delFunc),
                  regions.end());
    // 设置刷新标志为 false, 表示已经完成刷新
    needFlush = false;
  }
}
void ExecBlockManager::flushCommit() {
  // 如果有需要刷新的区域
  if (needFlush) {
    QBDI_DEBUG("Flushing analysis caches"); // 打印调试信息
    // 定义一个删除函数, 用于检查区域是否需要刷新
    auto delFunc = [&](const ExecRegion &r) -> bool {
      // 如果区域需要刷新
      if (r.toFlush) {
        // 打印调试信息, 显示正在移除的区域
        QBDI_DEBUG("Erasing region [0x{:x}, 0x{:x}]", r.covered.start(),
                   r.covered.end());
        // 遍历区域中的所有代码块, 并从代码块映射中移除
        for (const auto &block : r.blocks) {
          codeBlockMap.erase(block->getBaseCodeBlock());
        }
      }
      // 返回是否需要移除该区域
      return r.toFlush;
    };
 
    // 使用 std::remove_if 移除所有需要刷新的区域
    regions.erase(std::remove_if(regions.begin(), regions.end(), delFunc),
                  regions.end());
    // 设置刷新标志为 false, 表示已经完成刷新
    needFlush = false;
  }
}
class ExecRegion : public MovableDoubleLinkedListElement<ExecRegion> {
public:
  Range<rword> covered; // 覆盖的地址范围
  unsigned translated = 0; // 已翻译的指令数量
  unsigned available = 0; // 可用的指令数量
  std::vector<std::unique_ptr<ExecBlock>> blocks; // 存储执行块的数组
  // 用于序列缓存和指令缓存的映射
  // 键必须通过 getExecRegionKey 生成
  std::map<rword, SeqLoc> sequenceCache; // 序列缓存
  std::map<rword, InstLoc> instCache; // 指令缓存
  bool toFlush = false; // 是否需要刷新
 
  // 用于用户回调的 lambda 指针, 通过 addInstrRule 设置
  // 这些指针应该在区域被移除时同时移除
  std::vector<std::unique_ptr<InstCbLambda>> userInstCB;
 
  // 构造函数
  ExecRegion(const Range<rword> &c)
      : MovableDoubleLinkedListElement<ExecRegion>(), covered(c) {}
};
class ExecRegion : public MovableDoubleLinkedListElement<ExecRegion> {
public:
  Range<rword> covered; // 覆盖的地址范围
  unsigned translated = 0; // 已翻译的指令数量
  unsigned available = 0; // 可用的指令数量
  std::vector<std::unique_ptr<ExecBlock>> blocks; // 存储执行块的数组
  // 用于序列缓存和指令缓存的映射
  // 键必须通过 getExecRegionKey 生成
  std::map<rword, SeqLoc> sequenceCache; // 序列缓存
  std::map<rword, InstLoc> instCache; // 指令缓存
  bool toFlush = false; // 是否需要刷新
 
  // 用于用户回调的 lambda 指针, 通过 addInstrRule 设置
  // 这些指针应该在区域被移除时同时移除
  std::vector<std::unique_ptr<InstCbLambda>> userInstCB;
 
  // 构造函数
  ExecRegion(const Range<rword> &c)
      : MovableDoubleLinkedListElement<ExecRegion>(), covered(c) {}
};
class ExecBlock {
private:
  using PF = llvm::sys::Memory::ProtectionFlags;
 
  enum PageState { RX, RW }; // 页面状态: 只读可执行(RX)或读写(RW)
 
  VMInstanceRef vminstance; // 指向虚拟机实例的引用
  llvm::sys::MemoryBlock codeBlock; // 代码块
  llvm::sys::MemoryBlock dataBlock; // 数据块
  unsigned codeBlockPosition; // 当前代码块的位置
  unsigned codeBlockMaxSize; // 代码块的最大大小
  const LLVMCPUs &llvmCPUs; // 用于在执行块中组装指令的 LLVMCPU
  Context *context;
  rword *shadows;
  std::vector<ShadowInfo> shadowRegistry;
  std::vector<TagInfo> tagRegistry; // 标签信息注册表
  uint16_t shadowIdx;
  std::vector<InstMetadata> instMetadata; // 指令元数据
  std::vector<InstInfo> instRegistry; // 指令信息注册表
  std::vector<SeqInfo> seqRegistry; // 序列信息注册表
  PageState pageState; // 当前页面状态
  uint16_t currentSeq;
  uint16_t currentInst;
  uint32_t epilogueSize;
  bool isFull; // 执行块是否已满
  ScratchRegisterInfo srInfo; // 暂存寄存器信息
 
  // 检查代码块是否处于只读可执行模式
  inline bool isRX() const { return pageState == RX; }
 
  // 检查代码块是否处于读写模式
  inline bool isRW() const { return pageState == RW; }
 
  // 将代码块权限更改为只读可执行
  void makeRX();
 
  // 将代码块权限更改为读写
  void makeRW();
 
  // 为补丁初始化暂存寄存器
  void initScratchRegisterForPatch(std::vector<Patch>::const_iterator seqStart,
                                   std::vector<Patch>::const_iterator seqEnd);
 
  // 写入补丁
  bool writePatch(std::vector<Patch>::const_iterator seqCurrent,
                  std::vector<Patch>::const_iterator seqEnd,
                  const LLVMCPU &llvmcpu);
 
  // 写入代码字节
  bool writeCodeByte(const llvm::ArrayRef<char> &);
 
  // .......
}
class ExecBlock {
private:
  using PF = llvm::sys::Memory::ProtectionFlags;
 
  enum PageState { RX, RW }; // 页面状态: 只读可执行(RX)或读写(RW)
 
  VMInstanceRef vminstance; // 指向虚拟机实例的引用
  llvm::sys::MemoryBlock codeBlock; // 代码块
  llvm::sys::MemoryBlock dataBlock; // 数据块
  unsigned codeBlockPosition; // 当前代码块的位置
  unsigned codeBlockMaxSize; // 代码块的最大大小
  const LLVMCPUs &llvmCPUs; // 用于在执行块中组装指令的 LLVMCPU
  Context *context;
  rword *shadows;
  std::vector<ShadowInfo> shadowRegistry;
  std::vector<TagInfo> tagRegistry; // 标签信息注册表
  uint16_t shadowIdx;
  std::vector<InstMetadata> instMetadata; // 指令元数据
  std::vector<InstInfo> instRegistry; // 指令信息注册表
  std::vector<SeqInfo> seqRegistry; // 序列信息注册表
  PageState pageState; // 当前页面状态
  uint16_t currentSeq;
  uint16_t currentInst;
  uint32_t epilogueSize;
  bool isFull; // 执行块是否已满
  ScratchRegisterInfo srInfo; // 暂存寄存器信息
 
  // 检查代码块是否处于只读可执行模式
  inline bool isRX() const { return pageState == RX; }
 
  // 检查代码块是否处于读写模式
  inline bool isRW() const { return pageState == RW; }
 
  // 将代码块权限更改为只读可执行
  void makeRX();
 
  // 将代码块权限更改为读写
  void makeRW();
 
  // 为补丁初始化暂存寄存器
  void initScratchRegisterForPatch(std::vector<Patch>::const_iterator seqStart,
                                   std::vector<Patch>::const_iterator seqEnd);
 
  // 写入补丁
  bool writePatch(std::vector<Patch>::const_iterator seqCurrent,
                  std::vector<Patch>::const_iterator seqEnd,
                  const LLVMCPU &llvmcpu);
 
  // 写入代码字节
  bool writeCodeByte(const llvm::ArrayRef<char> &);
 
  // .......
}
ExecBlockManager::ExecBlockManager(const LLVMCPUs &llvmCPUs,
                                   VMInstanceRef vminstance)
    : total_translated_size(1), total_translation_size(1),
      vminstance(vminstance), llvmCPUs(llvmCPUs),
      execBlockPrologue(
          getExecBlockPrologue(llvmCPUs.getCPU(CPUMode::DEFAULT))),
      execBlockEpilogue(
          getExecBlockEpilogue(llvmCPUs.getCPU(CPUMode::DEFAULT))) {
 
  auto execBrokerBlock = std::make_unique<ExecBlock>(
      llvmCPUs, vminstance, &execBlockPrologue, &execBlockEpilogue, 0);
  epilogueSize = execBrokerBlock->getEpilogueSize();
  execBroker = std::make_unique<ExecBroker>(std::move(execBrokerBlock),
                                            llvmCPUs, vminstance);
}
ExecBlockManager::ExecBlockManager(const LLVMCPUs &llvmCPUs,
                                   VMInstanceRef vminstance)
    : total_translated_size(1), total_translation_size(1),
      vminstance(vminstance), llvmCPUs(llvmCPUs),
      execBlockPrologue(
          getExecBlockPrologue(llvmCPUs.getCPU(CPUMode::DEFAULT))),
      execBlockEpilogue(
          getExecBlockEpilogue(llvmCPUs.getCPU(CPUMode::DEFAULT))) {
 
  auto execBrokerBlock = std::make_unique<ExecBlock>(
      llvmCPUs, vminstance, &execBlockPrologue, &execBlockEpilogue, 0);
  epilogueSize = execBrokerBlock->getEpilogueSize();
  execBroker = std::make_unique<ExecBroker>(std::move(execBrokerBlock),
                                            llvmCPUs, vminstance);
}
// Test if we have it in cache
SeqLoc currentSequence;
curExecBlock = blockManager->getProgrammedExecBlock(currentPC, curCPUMode,
                                                    &currentSequence);
// Test if we have it in cache
SeqLoc currentSequence;
curExecBlock = blockManager->getProgrammedExecBlock(currentPC, curCPUMode,
                                                    &currentSequence);
/*! X86_64 CPU modes.
 */
typedef enum { X86_64 = 0, DEFAULT = 0, COUNT } CPUMode;
/*! X86_64 CPU modes.
 */
typedef enum { X86_64 = 0, DEFAULT = 0, COUNT } CPUMode;
struct SeqLoc {
  uint16_t blockIdx;
  uint16_t seqID;
  rword bbEnd;
  rword seqStart;
  rword seqEnd;
};
struct SeqLoc {
  uint16_t blockIdx;
  uint16_t seqID;
  rword bbEnd;
  rword seqStart;
  rword seqEnd;
};
ExecBlock *ExecBlockManager::getProgrammedExecBlock(rword address,
                                                    CPUMode cpumode,
                                                    SeqLoc *programmedSeqLock) {
  QBDI_DEBUG("Looking up sequence at address {:x} mode {}", address, cpumode);
 
  size_t r = searchRegion(address);
 
  if (r < regions.size() && regions[r].covered.contains(address)) {
    ExecRegion &region = regions[r];
    const auto target = getExecRegionKey(address, cpumode);
 
    // Attempting sequenceCache resolution
    const auto seqLoc = region.sequenceCache.find(target);
    if (seqLoc != region.sequenceCache.end()) {
      QBDI_DEBUG("Found sequence 0x{:x} ({}) in ExecBlock 0x{:x} as seqID {:x}",
                 address, cpumode,
                 reinterpret_cast<uintptr_t>(
                     region.blocks[seqLoc->second.blockIdx].get()),
                 seqLoc->second.seqID);
      // copy current sequence info
      if (programmedSeqLock != nullptr) {
        *programmedSeqLock = seqLoc->second;
      }
      // Select sequence and return execBlock
      region.blocks[seqLoc->second.blockIdx]->selectSeq(seqLoc->second.seqID);
      return region.blocks[seqLoc->second.blockIdx].get();
    }
 
    // Attempting instCache resolution
    const auto instLoc = region.instCache.find(target);
    if (instLoc != region.instCache.end()) {
      // Retrieving corresponding block and seqLoc
      ExecBlock *block = region.blocks[instLoc->second.blockIdx].get();
      uint16_t existingSeqId = block->getSeqID(instLoc->second.instID);
      const SeqLoc &existingSeqLoc = region.sequenceCache[getExecRegionKey(
          block->getInstMetadata(block->getSeqStart(existingSeqId)).address,
          cpumode)];
      // Creating a new sequence at that instruction and
      // saving it in the sequenceCache
      uint16_t newSeqID = block->splitSequence(instLoc->second.instID);
      region.sequenceCache[target] = SeqLoc{
          instLoc->second.blockIdx, newSeqID, existingSeqLoc.bbEnd, address,
          existingSeqLoc.seqEnd,
      };
      QBDI_DEBUG(
          "Splitted seqID {:x} at instID {:x} in ExecBlock 0x{:x} as new "
          "sequence with seqID {:x}",
          existingSeqId, instLoc->second.instID,
          reinterpret_cast<uintptr_t>(block), newSeqID);
      // copy current sequence info
      if (programmedSeqLock != nullptr) {
        *programmedSeqLock = regions[r].sequenceCache[target];
      }
      block->selectSeq(newSeqID);
      return block;
    }
  }
  QBDI_DEBUG("Cache miss for sequence 0x{:x} ({})", address, cpumode);
  return nullptr;
}
ExecBlock *ExecBlockManager::getProgrammedExecBlock(rword address,
                                                    CPUMode cpumode,
                                                    SeqLoc *programmedSeqLock) {
  QBDI_DEBUG("Looking up sequence at address {:x} mode {}", address, cpumode);
 
  size_t r = searchRegion(address);
 
  if (r < regions.size() && regions[r].covered.contains(address)) {
    ExecRegion &region = regions[r];
    const auto target = getExecRegionKey(address, cpumode);
 
    // Attempting sequenceCache resolution
    const auto seqLoc = region.sequenceCache.find(target);
    if (seqLoc != region.sequenceCache.end()) {
      QBDI_DEBUG("Found sequence 0x{:x} ({}) in ExecBlock 0x{:x} as seqID {:x}",
                 address, cpumode,
                 reinterpret_cast<uintptr_t>(
                     region.blocks[seqLoc->second.blockIdx].get()),
                 seqLoc->second.seqID);
      // copy current sequence info
      if (programmedSeqLock != nullptr) {
        *programmedSeqLock = seqLoc->second;
      }
      // Select sequence and return execBlock
      region.blocks[seqLoc->second.blockIdx]->selectSeq(seqLoc->second.seqID);
      return region.blocks[seqLoc->second.blockIdx].get();
    }
 
    // Attempting instCache resolution
    const auto instLoc = region.instCache.find(target);
    if (instLoc != region.instCache.end()) {
      // Retrieving corresponding block and seqLoc
      ExecBlock *block = region.blocks[instLoc->second.blockIdx].get();
      uint16_t existingSeqId = block->getSeqID(instLoc->second.instID);
      const SeqLoc &existingSeqLoc = region.sequenceCache[getExecRegionKey(
          block->getInstMetadata(block->getSeqStart(existingSeqId)).address,
          cpumode)];
      // Creating a new sequence at that instruction and
      // saving it in the sequenceCache
      uint16_t newSeqID = block->splitSequence(instLoc->second.instID);
      region.sequenceCache[target] = SeqLoc{
          instLoc->second.blockIdx, newSeqID, existingSeqLoc.bbEnd, address,
          existingSeqLoc.seqEnd,
      };
      QBDI_DEBUG(
          "Splitted seqID {:x} at instID {:x} in ExecBlock 0x{:x} as new "
          "sequence with seqID {:x}",
          existingSeqId, instLoc->second.instID,
          reinterpret_cast<uintptr_t>(block), newSeqID);
      // copy current sequence info
      if (programmedSeqLock != nullptr) {
        *programmedSeqLock = regions[r].sequenceCache[target];
      }
      block->selectSeq(newSeqID);
      return block;
    }
  }
  QBDI_DEBUG("Cache miss for sequence 0x{:x} ({})", address, cpumode);
  return nullptr;
}
size_t ExecBlockManager::searchRegion(rword address) const {
  size_t low = 0;
  size_t high = regions.size();
  if (regions.size() == 0) {
    return 0;
  }
  QBDI_DEBUG("Searching for address 0x{:x}", address);
  // Binary search of the first region to look at
  while (low + 1 != high) {
    size_t idx = (low + high) / 2;
    if (regions[idx].covered.start() > address) {
      high = idx;
    } else if (regions[idx].covered.end() <= address) {
      low = idx;
    } else {
      QBDI_DEBUG("Exact match for region {} [0x{:x}, 0x{:x}]", idx,
                 regions[idx].covered.start(), regions[idx].covered.end());
      return idx;
    }
  }
  QBDI_DEBUG("Low match for region {} [0x{:x}, 0x{:x}]", low,
             regions[low].covered.start(), regions[low].covered.end());
  return low;
}
size_t ExecBlockManager::searchRegion(rword address) const {
  size_t low = 0;
  size_t high = regions.size();
  if (regions.size() == 0) {
    return 0;
  }
  QBDI_DEBUG("Searching for address 0x{:x}", address);
  // Binary search of the first region to look at
  while (low + 1 != high) {
    size_t idx = (low + high) / 2;
    if (regions[idx].covered.start() > address) {
      high = idx;
    } else if (regions[idx].covered.end() <= address) {
      low = idx;
    } else {
      QBDI_DEBUG("Exact match for region {} [0x{:x}, 0x{:x}]", idx,
                 regions[idx].covered.start(), regions[idx].covered.end());
      return idx;
    }
  }
  QBDI_DEBUG("Low match for region {} [0x{:x}, 0x{:x}]", low,
             regions[low].covered.start(), regions[low].covered.end());
  return low;
}
if (r < regions.size() && regions[r].covered.contains(address)) {
  ExecRegion &region = regions[r]; // 获取对应的区域
  const auto target = getExecRegionKey(address, cpumode);
 
  // 尝试从 sequenceCache 中查找目标地址所在序列
  const auto seqLoc = region.sequenceCache.find(target);
  if (seqLoc != region.sequenceCache.end()) {
    // 如果找到了对应的序列
    QBDI_DEBUG("Found sequence 0x{:x} ({}) in ExecBlock 0x{:x} as seqID {:x}",
                address, cpumode,
                reinterpret_cast<uintptr_t>(region.blocks[seqLoc->second.blockIdx].get()),
                seqLoc->second.seqID);
    // 如果提供了 programmedSeqLock 参数, 则将当前序列信息复制到其中
    if (programmedSeqLock != nullptr) {
      *programmedSeqLock = seqLoc->second;
    }
    // 选择对应的序列并返回对应的 ExecBlock
    region.blocks[seqLoc->second.blockIdx]->selectSeq(seqLoc->second.seqID);
    return region.blocks[seqLoc->second.blockIdx].get();
  }
 
  // 如果 sequenceCache 中没有找到, 尝试从 instCache 中查找目标地址所在指令
  const auto instLoc = region.instCache.find(target);
  if (instLoc != region.instCache.end()) {
    // 获取对应的块和序列位置
    ExecBlock *block = region.blocks[instLoc->second.blockIdx].get();
    uint16_t existingSeqId = block->getSeqID(instLoc->second.instID);
    const SeqLoc &existingSeqLoc = region.sequenceCache[getExecRegionKey(
        block->getInstMetadata(block->getSeqStart(existingSeqId)).address,
        cpumode)];
    // 在该指令处创建一个新的序列, 并将其保存到 sequenceCache 中
    uint16_t newSeqID = block->splitSequence(instLoc->second.instID);
    region.sequenceCache[target] = SeqLoc{
        instLoc->second.blockIdx, newSeqID, existingSeqLoc.bbEnd, address,
        existingSeqLoc.seqEnd,
    };
    QBDI_DEBUG(
        "Splitted seqID {:x} at instID {:x} in ExecBlock 0x{:x} as new "
        "sequence with seqID {:x}",
        existingSeqId, instLoc->second.instID,
        reinterpret_cast<uintptr_t>(block), newSeqID);
    // 如果提供了 programmedSeqLock 参数, 则将当前序列信息复制到其中
    if (programmedSeqLock != nullptr) {
      *programmedSeqLock = regions[r].sequenceCache[target];
    }
    block->selectSeq(newSeqID);
    return block;
  }
}
if (r < regions.size() && regions[r].covered.contains(address)) {
  ExecRegion &region = regions[r]; // 获取对应的区域
  const auto target = getExecRegionKey(address, cpumode);
 
  // 尝试从 sequenceCache 中查找目标地址所在序列
  const auto seqLoc = region.sequenceCache.find(target);
  if (seqLoc != region.sequenceCache.end()) {
    // 如果找到了对应的序列
    QBDI_DEBUG("Found sequence 0x{:x} ({}) in ExecBlock 0x{:x} as seqID {:x}",
                address, cpumode,
                reinterpret_cast<uintptr_t>(region.blocks[seqLoc->second.blockIdx].get()),
                seqLoc->second.seqID);
    // 如果提供了 programmedSeqLock 参数, 则将当前序列信息复制到其中
    if (programmedSeqLock != nullptr) {
      *programmedSeqLock = seqLoc->second;
    }
    // 选择对应的序列并返回对应的 ExecBlock
    region.blocks[seqLoc->second.blockIdx]->selectSeq(seqLoc->second.seqID);
    return region.blocks[seqLoc->second.blockIdx].get();
  }
 
  // 如果 sequenceCache 中没有找到, 尝试从 instCache 中查找目标地址所在指令
  const auto instLoc = region.instCache.find(target);
  if (instLoc != region.instCache.end()) {
    // 获取对应的块和序列位置
    ExecBlock *block = region.blocks[instLoc->second.blockIdx].get();
    uint16_t existingSeqId = block->getSeqID(instLoc->second.instID);
    const SeqLoc &existingSeqLoc = region.sequenceCache[getExecRegionKey(
        block->getInstMetadata(block->getSeqStart(existingSeqId)).address,
        cpumode)];
    // 在该指令处创建一个新的序列, 并将其保存到 sequenceCache 中
    uint16_t newSeqID = block->splitSequence(instLoc->second.instID);
    region.sequenceCache[target] = SeqLoc{
        instLoc->second.blockIdx, newSeqID, existingSeqLoc.bbEnd, address,
        existingSeqLoc.seqEnd,
    };
    QBDI_DEBUG(
        "Splitted seqID {:x} at instID {:x} in ExecBlock 0x{:x} as new "
        "sequence with seqID {:x}",
        existingSeqId, instLoc->second.instID,
        reinterpret_cast<uintptr_t>(block), newSeqID);
    // 如果提供了 programmedSeqLock 参数, 则将当前序列信息复制到其中
    if (programmedSeqLock != nullptr) {
      *programmedSeqLock = regions[r].sequenceCache[target];
    }
    block->selectSeq(newSeqID);
    return block;
  }
}
inline rword getExecRegionKey(rword address, CPUMode cpumode) {
  if constexpr (is_arm) {
    if (cpumode != CPUMode::DEFAULT) {
      address |= 1;
    } else {
      address &= (~1);
    }
  }
  return address;
}
inline rword getExecRegionKey(rword address, CPUMode cpumode) {
  if constexpr (is_arm) {
    if (cpumode != CPUMode::DEFAULT) {
      address |= 1;
    } else {
      address &= (~1);
    }
  }
  return address;
}
struct SeqLoc {
  uint16_t blockIdx;
  uint16_t seqID;
  rword bbEnd;
  rword seqStart;
  rword seqEnd;
};
struct SeqLoc {
  uint16_t blockIdx;
  uint16_t seqID;
  rword bbEnd;
  rword seqStart;
  rword seqEnd;
};
void ExecBlock::selectSeq(uint16_t seqID) {
  QBDI_REQUIRE(seqID < seqRegistry.size());
  currentSeq = seqID;
  currentInst = seqRegistry[currentSeq].startInstID;
  context->hostState.selector =
      reinterpret_cast<rword>(codeBlock.base()) +
      static_cast<rword>(instRegistry[currentInst].offset);
  context->hostState.executeFlags = seqRegistry[currentSeq].executeFlags;
}
void ExecBlock::selectSeq(uint16_t seqID) {
  QBDI_REQUIRE(seqID < seqRegistry.size());
  currentSeq = seqID;
  currentInst = seqRegistry[currentSeq].startInstID;
  context->hostState.selector =
      reinterpret_cast<rword>(codeBlock.base()) +
      static_cast<rword>(instRegistry[currentInst].offset);
  context->hostState.executeFlags = seqRegistry[currentSeq].executeFlags;
}
struct SeqInfo {
  uint16_t startInstID;
  uint16_t endInstID;
  uint8_t executeFlags;
  CPUMode cpuMode;
  ScratchRegisterSeqInfo sr;
};
struct SeqInfo {
  uint16_t startInstID;
  uint16_t endInstID;
  uint8_t executeFlags;
  CPUMode cpuMode;
  ScratchRegisterSeqInfo sr;
};
struct InstInfo {
  uint16_t seqID;
  uint16_t offset;
  uint16_t offsetSkip;
  uint16_t shadowOffset;
  uint16_t shadowSize;
  uint16_t tagOffset;
  uint16_t tagSize;
  ScratchRegisterSeqInfo sr;
};
struct InstInfo {
  uint16_t seqID;
  uint16_t offset;
  uint16_t offsetSkip;
  uint16_t shadowOffset;
  uint16_t shadowSize;
  uint16_t tagOffset;
  uint16_t tagSize;
  ScratchRegisterSeqInfo sr;
};
struct InstLoc {
  uint16_t blockIdx;
  uint16_t instID;
};
struct InstLoc {
  uint16_t blockIdx;
  uint16_t instID;
};
uint16_t ExecBlock::getSeqID(uint16_t instID) const {
  QBDI_REQUIRE(instID < instRegistry.size());
  return instRegistry[instID].seqID;
}
uint16_t ExecBlock::getSeqID(uint16_t instID) const {
  QBDI_REQUIRE(instID < instRegistry.size());
  return instRegistry[instID].seqID;
}
uint16_t ExecBlock::getSeqStart(uint16_t seqID) const {
  QBDI_REQUIRE(seqID < seqRegistry.size());
  return seqRegistry[seqID].startInstID;
}
uint16_t ExecBlock::getSeqStart(uint16_t seqID) const {
  QBDI_REQUIRE(seqID < seqRegistry.size());
  return seqRegistry[seqID].startInstID;
}
const InstMetadata &ExecBlock::getInstMetadata(uint16_t instID) const {
  QBDI_REQUIRE(instID < instMetadata.size());
  return instMetadata[instID];
}
const InstMetadata &ExecBlock::getInstMetadata(uint16_t instID) const {
  QBDI_REQUIRE(instID < instMetadata.size());
  return instMetadata[instID];
}
class InstMetadata {
public:
  llvm::MCInst inst;                 // LLVM 的机器指令对象
  rword address;                     // 指令的地址
  uint32_t instSize;                 // 指令的大小(字节)
  uint32_t patchSize;                // 指令的补丁大小(字节)
  CPUMode cpuMode;                   // 指令对应的 CPU 模式
  bool modifyPC;                     // 指令是否修改程序计数器(PC)
  uint8_t execblockFlags;            // 执行块的标志
  mutable InstAnalysisPtr analysis;  // 指令的分析结果指针
  InstMetadataArch archMetadata;     // 架构相关的元数据
 
#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)
  // 对于 X86_64 指令, 存储前缀(如 ``lock``)
  std::vector<llvm::MCInst> prefix;
#endif
 
  // 构造函数
  InstMetadata(const llvm::MCInst &inst, rword address, uint32_t instSize,
               uint32_t patchSize, CPUMode cpuMode, bool modifyPC,
               uint8_t execblockFlags, InstAnalysisPtr analysis)
      : inst(inst), address(address), instSize(instSize), patchSize(patchSize),
        cpuMode(cpuMode), modifyPC(modifyPC), execblockFlags(execblockFlags),
        analysis(std::move(analysis)) {}
 
  // 简化版构造函数
  InstMetadata(const llvm::MCInst &inst, rword address, uint32_t instSize,
               CPUMode cpuMode, uint8_t execblockFlags)
      : inst(inst), address(address), instSize(instSize), patchSize(0),
        cpuMode(cpuMode), modifyPC(false), execblockFlags(execblockFlags),
        analysis(nullptr) {}
 
  // 计算指令的结束地址
  inline rword endAddress() const { return address + instSize; }
 
  // 创建一个副本
  inline InstMetadata lightCopy() const {
    InstMetadata cpy{inst,    address,  instSize,       patchSize,
                     cpuMode, modifyPC, execblockFlags, nullptr};
#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)
    cpy.prefix = prefix; // 如果是 X86 架构, 复制前缀
#endif
    cpy.archMetadata = archMetadata; // 复制架构相关的元数据
    return cpy;
  }
};
class InstMetadata {
public:
  llvm::MCInst inst;                 // LLVM 的机器指令对象
  rword address;                     // 指令的地址
  uint32_t instSize;                 // 指令的大小(字节)
  uint32_t patchSize;                // 指令的补丁大小(字节)
  CPUMode cpuMode;                   // 指令对应的 CPU 模式
  bool modifyPC;                     // 指令是否修改程序计数器(PC)
  uint8_t execblockFlags;            // 执行块的标志
  mutable InstAnalysisPtr analysis;  // 指令的分析结果指针
  InstMetadataArch archMetadata;     // 架构相关的元数据
 
#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)
  // 对于 X86_64 指令, 存储前缀(如 ``lock``)
  std::vector<llvm::MCInst> prefix;
#endif
 
  // 构造函数
  InstMetadata(const llvm::MCInst &inst, rword address, uint32_t instSize,
               uint32_t patchSize, CPUMode cpuMode, bool modifyPC,
               uint8_t execblockFlags, InstAnalysisPtr analysis)
      : inst(inst), address(address), instSize(instSize), patchSize(patchSize),
        cpuMode(cpuMode), modifyPC(modifyPC), execblockFlags(execblockFlags),
        analysis(std::move(analysis)) {}
 
  // 简化版构造函数
  InstMetadata(const llvm::MCInst &inst, rword address, uint32_t instSize,
               CPUMode cpuMode, uint8_t execblockFlags)
      : inst(inst), address(address), instSize(instSize), patchSize(0),
        cpuMode(cpuMode), modifyPC(false), execblockFlags(execblockFlags),
        analysis(nullptr) {}
 
  // 计算指令的结束地址
  inline rword endAddress() const { return address + instSize; }
 
  // 创建一个副本
  inline InstMetadata lightCopy() const {
    InstMetadata cpy{inst,    address,  instSize,       patchSize,
                     cpuMode, modifyPC, execblockFlags, nullptr};
#if defined(QBDI_ARCH_X86_64) || defined(QBDI_ARCH_X86)
    cpy.prefix = prefix; // 如果是 X86 架构, 复制前缀
#endif
    cpy.archMetadata = archMetadata; // 复制架构相关的元数据
    return cpy;
  }
};
uint16_t ExecBlock::splitSequence(uint16_t instID) {
  QBDI_REQUIRE(instID < instRegistry.size());
  uint16_t seqID = instRegistry[instID].seqID;
  seqRegistry.push_back(SeqInfo{
      instID, seqRegistry[seqID].endInstID, seqRegistry[seqID].executeFlags,
      seqRegistry[seqID].cpuMode, instRegistry[instID].sr});
  return getNextSeqID() - 1;
}
uint16_t ExecBlock::splitSequence(uint16_t instID) {
  QBDI_REQUIRE(instID < instRegistry.size());
  uint16_t seqID = instRegistry[instID].seqID;
  seqRegistry.push_back(SeqInfo{
      instID, seqRegistry[seqID].endInstID, seqRegistry[seqID].executeFlags,
      seqRegistry[seqID].cpuMode, instRegistry[instID].sr});
  return getNextSeqID() - 1;
}
if (curExecBlock == nullptr) {
  QBDI_DEBUG(
      "Cache miss for 0x{:x}, patching & instrumenting new basic block",
      currentPC);
  handleNewBasicBlock(currentPC);
  // Signal a new basic block
  event |= BASIC_BLOCK_NEW;
  // Set new basic block as current
  curExecBlock = blockManager->getProgrammedExecBlock(
      currentPC, curCPUMode, &currentSequence);
  QBDI_REQUIRE_ABORT(curExecBlock != nullptr,
                      "Fail to instrument the next basic block");
}
if (curExecBlock == nullptr) {
  QBDI_DEBUG(
      "Cache miss for 0x{:x}, patching & instrumenting new basic block",
      currentPC);
  handleNewBasicBlock(currentPC);
  // Signal a new basic block
  event |= BASIC_BLOCK_NEW;
  // Set new basic block as current
  curExecBlock = blockManager->getProgrammedExecBlock(
      currentPC, curCPUMode, &currentSequence);
  QBDI_REQUIRE_ABORT(curExecBlock != nullptr,
                      "Fail to instrument the next basic block");
}
void Engine::handleNewBasicBlock(rword pc) {
  // disassemble and patch new basic block
  Patch::Vec basicBlock = patch(pc);
  // Reserve cache and get uncached instruction
  size_t patchEnd = blockManager->preWriteBasicBlock(basicBlock);
  // instrument uncached instruction
  instrument(basicBlock, patchEnd);
  // Write in the cache
  blockManager->writeBasicBlock(std::move(basicBlock), patchEnd);
}
void Engine::handleNewBasicBlock(rword pc) {
  // disassemble and patch new basic block
  Patch::Vec basicBlock = patch(pc);
  // Reserve cache and get uncached instruction
  size_t patchEnd = blockManager->preWriteBasicBlock(basicBlock);
  // instrument uncached instruction
  instrument(basicBlock, patchEnd);
  // Write in the cache
  blockManager->writeBasicBlock(std::move(basicBlock), patchEnd);
}
std::vector<Patch> Engine::patch(rword start) {
  std::vector<Patch> basicBlock; // 用于存储生成的补丁
  const LLVMCPU &llvmcpu = llvmCPUs->getCPU(curCPUMode);
 
  // 如果起始地址在执行范围内, 则确定基本块的最大大小
  size_t sizeCode = (size_t)-1; // 初始化为最大值
  const Range<rword> *curRange =
      execBroker->getInstrumentedRange().getElementRange(start);
  if (curRange != nullptr) {
    sizeCode = curRange->end() - start; // 计算范围内的代码大小
  }
 
  // 创建一个代码片段, 从起始地址开始, 大小为 sizeCode
  const llvm::ArrayRef<uint8_t> code((uint8_t *)start, sizeCode);
  rword address = start;
  QBDI_DEBUG("Patching basic block at address 0x{:x}", start);
 
  bool endLoop = false;
  do {
    llvm::MCInst inst; // llvm 中中一个低级的、与目标架构无关的指令表示.
    uint64_t instSize; // 指令的大小
    // 反汇编指令
    bool dstatus = llvmcpu.getInstruction(inst, instSize,
                                          code.slice(address - start), address);
 
    // 处理反汇编错误
    if (not dstatus) {
      QBDI_DEBUG("Bump into invalid instruction at address {:x}", address);
 
      // 当前指令无效, 立即停止基本块的生成
      bool rollbackOK = patchRuleAssembly->earlyEnd(llvmcpu, basicBlock);
 
      // 如果回滚失败或未生成任何补丁, 则失败
      if ((not rollbackOK) or (basicBlock.size() == 0)) {
        size_t sizeDump = start + sizeCode - address;
        if (sizeDump > 16) {
          sizeDump = 16;
        }
        QBDI_ABORT(
            "Disassembly error : fail to parse address 0x{:x} (CPUMode {}) "
            "({:n})",
            address, curCPUMode,
            spdlog::to_hex(reinterpret_cast<uint8_t *>(address),
                           reinterpret_cast<uint8_t *>(address + sizeDump)));
      } else {
        endLoop = true;
        break;
      }
    }
    QBDI_DEBUG("Disassembly address 0x{:x} ({:n})", address,
               spdlog::to_hex(reinterpret_cast<uint8_t *>(address),
                              reinterpret_cast<uint8_t *>(address + instSize)));
 
    // 为当前指令生成补丁
    QBDI_REQUIRE_ABORT(dstatus, "Unexpected disassembly status");
    QBDI_DEBUG_BLOCK({
      std::string disass = llvmcpu.showInst(inst, address);
      QBDI_DEBUG("Patching 0x{:x} {}", address, disass.c_str());
    });
    endLoop = not patchRuleAssembly->generate(inst, address, instSize, llvmcpu,
                                              basicBlock);
    address += instSize; // 更新当前地址
  } while (endLoop);
 
  QBDI_REQUIRE_ABORT(basicBlock.size() > 0,
                     "No instruction to disassemble found");
 
  QBDI_DEBUG("Basic block starting at address 0x{:x} ended at address 0x{:x}",
             start, basicBlock.back().metadata.endAddress());
 
  return basicBlock; // 返回生成的补丁
}
std::vector<Patch> Engine::patch(rword start) {
  std::vector<Patch> basicBlock; // 用于存储生成的补丁
  const LLVMCPU &llvmcpu = llvmCPUs->getCPU(curCPUMode);
 
  // 如果起始地址在执行范围内, 则确定基本块的最大大小
  size_t sizeCode = (size_t)-1; // 初始化为最大值
  const Range<rword> *curRange =
      execBroker->getInstrumentedRange().getElementRange(start);
  if (curRange != nullptr) {
    sizeCode = curRange->end() - start; // 计算范围内的代码大小
  }
 
  // 创建一个代码片段, 从起始地址开始, 大小为 sizeCode
  const llvm::ArrayRef<uint8_t> code((uint8_t *)start, sizeCode);
  rword address = start;
  QBDI_DEBUG("Patching basic block at address 0x{:x}", start);
 
  bool endLoop = false;
  do {
    llvm::MCInst inst; // llvm 中中一个低级的、与目标架构无关的指令表示.
    uint64_t instSize; // 指令的大小
    // 反汇编指令
    bool dstatus = llvmcpu.getInstruction(inst, instSize,
                                          code.slice(address - start), address);
 
    // 处理反汇编错误
    if (not dstatus) {
      QBDI_DEBUG("Bump into invalid instruction at address {:x}", address);
 
      // 当前指令无效, 立即停止基本块的生成
      bool rollbackOK = patchRuleAssembly->earlyEnd(llvmcpu, basicBlock);
 
      // 如果回滚失败或未生成任何补丁, 则失败
      if ((not rollbackOK) or (basicBlock.size() == 0)) {
        size_t sizeDump = start + sizeCode - address;
        if (sizeDump > 16) {
          sizeDump = 16;
        }
        QBDI_ABORT(
            "Disassembly error : fail to parse address 0x{:x} (CPUMode {}) "
            "({:n})",
            address, curCPUMode,
            spdlog::to_hex(reinterpret_cast<uint8_t *>(address),
                           reinterpret_cast<uint8_t *>(address + sizeDump)));
      } else {
        endLoop = true;
        break;
      }
    }
    QBDI_DEBUG("Disassembly address 0x{:x} ({:n})", address,
               spdlog::to_hex(reinterpret_cast<uint8_t *>(address),
                              reinterpret_cast<uint8_t *>(address + instSize)));
 
    // 为当前指令生成补丁
    QBDI_REQUIRE_ABORT(dstatus, "Unexpected disassembly status");
    QBDI_DEBUG_BLOCK({
      std::string disass = llvmcpu.showInst(inst, address);
      QBDI_DEBUG("Patching 0x{:x} {}", address, disass.c_str());
    });
    endLoop = not patchRuleAssembly->generate(inst, address, instSize, llvmcpu,
                                              basicBlock);
    address += instSize; // 更新当前地址
  } while (endLoop);
 
  QBDI_REQUIRE_ABORT(basicBlock.size() > 0,
                     "No instruction to disassemble found");
 
  QBDI_DEBUG("Basic block starting at address 0x{:x} ended at address 0x{:x}",
             start, basicBlock.back().metadata.endAddress());
 
  return basicBlock; // 返回生成的补丁
}
const RangeSet<rword> &getInstrumentedRange() const { return instrumented; }
const RangeSet<rword> &getInstrumentedRange() const { return instrumented; }
const Range<T> *getElementRange(const T &t) const {
  // 使用 std::lower_bound 查找第一个满足条件的范围
  // 条件是范围的结束地址大于或等于 t
  auto lower = std::lower_bound(
      ranges.cbegin(), ranges.cend(), t,
      [](const Range<T> &r, const T &value) { return r.end() <= value; });
 
  // 如果找到的范围是最后一个范围(即超出范围)或者该范围不包含 t
  if (lower == ranges.cend() || (!lower->contains(t))) {
    return nullptr; // 返回 nullptr, 表示没有找到包含 t 的范围
  } else {
    return &*lower; // 返回找到的范围的指针
  }
}
const Range<T> *getElementRange(const T &t) const {
  // 使用 std::lower_bound 查找第一个满足条件的范围
  // 条件是范围的结束地址大于或等于 t
  auto lower = std::lower_bound(
      ranges.cbegin(), ranges.cend(), t,
      [](const Range<T> &r, const T &value) { return r.end() <= value; });
 
  // 如果找到的范围是最后一个范围(即超出范围)或者该范围不包含 t
  if (lower == ranges.cend() || (!lower->contains(t))) {
    return nullptr; // 返回 nullptr, 表示没有找到包含 t 的范围
  } else {
    return &*lower; // 返回找到的范围的指针
  }
}
bool LLVMCPU::getInstruction(llvm::MCInst &instr, uint64_t &size,
                             llvm::ArrayRef<uint8_t> bytes,
                             uint64_t address) const {
  // 调用 LLVM 的反汇编器, 尝试反汇编指令
  auto status =
      disassembler->getInstruction(instr, size, bytes, address, llvm::nulls());
 
  // 如果反汇编状态为 SoftFail(软失败), 记录警告信息
  if (status == llvm::MCDisassembler::SoftFail) {
    // 生成指令的汇编代码字符串
    std::string dissas = showInst(instr, address);
    QBDI_WARN("Disassembly softfail on 0x{:x} : {} (CPUMode {}) ({:n})",
              address, dissas, getCPUMode(),
              spdlog::to_hex(bytes.data(), bytes.data() + size));
  }
  // 返回是否成功反汇编, 如果状态不是 Fail(失败), 则返回 true
  return status != llvm::MCDisassembler::Fail;
}
bool LLVMCPU::getInstruction(llvm::MCInst &instr, uint64_t &size,
                             llvm::ArrayRef<uint8_t> bytes,
                             uint64_t address) const {
  // 调用 LLVM 的反汇编器, 尝试反汇编指令
  auto status =
      disassembler->getInstruction(instr, size, bytes, address, llvm::nulls());
 
  // 如果反汇编状态为 SoftFail(软失败), 记录警告信息
  if (status == llvm::MCDisassembler::SoftFail) {
    // 生成指令的汇编代码字符串
    std::string dissas = showInst(instr, address);
    QBDI_WARN("Disassembly softfail on 0x{:x} : {} (CPUMode {}) ({:n})",
              address, dissas, getCPUMode(),
              spdlog::to_hex(bytes.data(), bytes.data() + size));
  }
  // 返回是否成功反汇编, 如果状态不是 Fail(失败), 则返回 true
  return status != llvm::MCDisassembler::Fail;
}
class LLVMCPU {
 
private:
  std::string tripleName;
  std::string cpu;
  std::string arch;
  std::vector<std::string> mattrs;
  const llvm::Target *target;
  Options options;
  CPUMode cpumode;
 
  std::unique_ptr<llvm::MCAsmInfo> MAI;
  std::unique_ptr<llvm::MCContext> MCTX;
  std::unique_ptr<llvm::MCInstrInfo> MCII;
  std::unique_ptr<llvm::MCObjectFileInfo> MOFI;
  std::unique_ptr<llvm::MCRegisterInfo> MRI;
  std::unique_ptr<llvm::MCSubtargetInfo> MSTI;
 
  std::unique_ptr<llvm::MCAssembler> assembler;
  std::unique_ptr<llvm::MCDisassembler> disassembler;
  std::unique_ptr<llvm::MCInstPrinter> asmPrinter;
  std::unique_ptr<llvm::raw_pwrite_stream> null_ostream;
 
  // ......
}
class LLVMCPU {
 
private:
  std::string tripleName;
  std::string cpu;
  std::string arch;
  std::vector<std::string> mattrs;
  const llvm::Target *target;
  Options options;
  CPUMode cpumode;
 
  std::unique_ptr<llvm::MCAsmInfo> MAI;
  std::unique_ptr<llvm::MCContext> MCTX;
  std::unique_ptr<llvm::MCInstrInfo> MCII;
  std::unique_ptr<llvm::MCObjectFileInfo> MOFI;
  std::unique_ptr<llvm::MCRegisterInfo> MRI;
  std::unique_ptr<llvm::MCSubtargetInfo> MSTI;
 
  std::unique_ptr<llvm::MCAssembler> assembler;
  std::unique_ptr<llvm::MCDisassembler> disassembler;
  std::unique_ptr<llvm::MCInstPrinter> asmPrinter;
  std::unique_ptr<llvm::raw_pwrite_stream> null_ostream;
 
  // ......
}
/// 返回单条指令的反汇编结果.
///
/// \param Instr    - 将被填入指令内容的 MCInst 对象.
/// \param Size     - 将被填入指令大小的值;若试图解码无效指令, 则为实际消耗的字节数.
/// \param Address  - 指令第一个字节在其所属内存区域中的地址.
/// \param Bytes    - 指令实际字节数据的引用.
/// \param CStream  - 用于输出反汇编注释和标注信息的流.
/// \return         - 返回解码状态:
///                   MCDisassembler::Success  -> 指令有效
///                   MCDisassembler::SoftFail -> 指令可被反汇编但无效
///                   MCDisassembler::Fail     -> 指令无效
virtual DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size,
                                    ArrayRef<uint8_t> Bytes, uint64_t Address,
                                    raw_ostream &CStream) const = 0;
/// 返回单条指令的反汇编结果.
///
/// \param Instr    - 将被填入指令内容的 MCInst 对象.
/// \param Size     - 将被填入指令大小的值;若试图解码无效指令, 则为实际消耗的字节数.
/// \param Address  - 指令第一个字节在其所属内存区域中的地址.
/// \param Bytes    - 指令实际字节数据的引用.
/// \param CStream  - 用于输出反汇编注释和标注信息的流.
/// \return         - 返回解码状态:
///                   MCDisassembler::Success  -> 指令有效
///                   MCDisassembler::SoftFail -> 指令可被反汇编但无效
///                   MCDisassembler::Fail     -> 指令无效
virtual DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size,
                                    ArrayRef<uint8_t> Bytes, uint64_t Address,
                                    raw_ostream &CStream) const = 0;
std::string LLVMCPU::showInst(const llvm::MCInst &inst, rword address) const {
  std::string out;
  llvm::raw_string_ostream rso(out);
 
  llvm::StringRef unusedAnnotations;
  asmPrinter->printInst(&inst, address, unusedAnnotations, *MSTI, rso);
 
  rso.flush();
  return out;
}
std::string LLVMCPU::showInst(const llvm::MCInst &inst, rword address) const {
  std::string out;
  llvm::raw_string_ostream rso(out);
 
  llvm::StringRef unusedAnnotations;
  asmPrinter->printInst(&inst, address, unusedAnnotations, *MSTI, rso);
 
  rso.flush();
  return out;
}
/// 将指定的 MCInst 对象打印到指定的原始输出流(raw_ostream).
///
/// \p Address 在多数目标平台上表示当前指令的地址, 用于将 PC 相对立即数打印为目标地址.
/// 在 PC 相对立即数基于下一条指令地址的目标平台(如 x86)或 MCInst 长度难以测量的情况下,
/// 此参数需传入下一条指令的地址. 若 Address 为 0, 则直接打印立即数值.
virtual void printInst(const MCInst *MI, uint64_t Address, StringRef Annot,
                       const MCSubtargetInfo &STI, raw_ostream &OS) = 0;
/// 将指定的 MCInst 对象打印到指定的原始输出流(raw_ostream).
///
/// \p Address 在多数目标平台上表示当前指令的地址, 用于将 PC 相对立即数打印为目标地址.
/// 在 PC 相对立即数基于下一条指令地址的目标平台(如 x86)或 MCInst 长度难以测量的情况下,
/// 此参数需传入下一条指令的地址. 若 Address 为 0, 则直接打印立即数值.
virtual void printInst(const MCInst *MI, uint64_t Address, StringRef Annot,
                       const MCSubtargetInfo &STI, raw_ostream &OS) = 0;
bool PatchRuleAssembly::generate(const llvm::MCInst &inst, rword address,
                                 uint32_t instSize, const LLVMCPU &llvmcpu,
                                 std::vector<Patch> &patchList) {
    // 创建一个 Patch 对象, 用于存储当前指令的补丁信息
    Patch instPatch{inst, address, instSize, llvmcpu};
    // 设置当前指令的寄存器保存信息
    setRegisterSaved(instPatch);
 
    // 遍历所有补丁规则
    for (uint32_t j = 0; j < patchRules.size(); j++) {
        // 检查当前补丁规则是否适用于当前指令
        if (patchRules[j].canBeApplied(instPatch, llvmcpu)) {
            QBDI_DEBUG("Patch rule {} applied", j);
            // 如果存在待合并的补丁
            if (mergePending) {
                QBDI_REQUIRE_ABORT(patchList.size() > 0, "No previous patch to merge");
                QBDI_DEBUG("Previous instruction merged");
 
                // 1. 为当前指令生成补丁
                patchRules[j].apply(instPatch, llvmcpu);
 
                // 2. 获取插入位置
                int position = -1;
                for (auto &p : instPatch.patchGenFlags) {
                    if (p.second == PatchGeneratorFlags::ModifyInstructionBeginFlags) {
                        position = p.first;
                        break;
                    }
                }
                QBDI_REQUIRE_ABORT(
                    position != -1,
                    "Fail to get the position to insert the new patch {}", instPatch);
 
                // 3. 在指定位置插入指令以合并
                Patch &mergePatch = patchList.back();
                instPatch.insertAt(position, std::move(mergePatch.insts));
 
                // 4. 保留一些元数据
                instPatch.metadata.address = mergePatch.metadata.address;
                instPatch.metadata.instSize += mergePatch.metadata.instSize;
                instPatch.metadata.execblockFlags |= mergePatch.metadata.execblockFlags;
                instPatch.metadata.prefix = mergePatch.metadata.prefix;
                instPatch.metadata.prefix.push_back(mergePatch.metadata.inst);
 
                // 5. 替换补丁
                mergePatch = std::move(instPatch);
 
            } else {
                // 如果没有待合并的补丁, 直接应用补丁规则并添加到补丁列表中
                patchRules[j].apply(instPatch, llvmcpu);
                patchList.push_back(std::move(instPatch));
            }
            // 获取最后一个补丁的引用
            Patch &patch = patchList.back();
            // 重置合并标志
            mergePending = false;
            // 检查是否有待合并的补丁
            for (auto &p : patch.patchGenFlags) {
                mergePending |= (p.second == PatchGeneratorFlagsX86_64::MergeFlag);
            }
 
            // 如果有待合并的补丁, 返回 false 表示继续处理
            if (mergePending) {
                return false;
            } else if (patch.metadata.modifyPC) {
                // 如果补丁修改了程序计数器(PC), 重置状态并返回 true
                reset();
                return true;
            } else {
                // 如果补丁没有修改 PC, 返回 false 表示继续处理
                return false;
            }
        }
    }
    // 如果没有找到适用的补丁规则, 终止程序并报错
    QBDI_ABORT("Not PatchRule found for: {}", instPatch);
}
 
static void setRegisterSaved(Patch &patch) {
 
  if constexpr (is_x86) {
    // 根据指令的操作码设置寄存器的保存状态
    switch (patch.metadata.inst.getOpcode()) {
      // 对于 x86 架构中的 PUSHA 和 POPA 指令
      case llvm::X86::PUSHA16: // 16 位保存所有通用寄存器
      case llvm::X86::PUSHA32: // 32 位保存所有通用寄存器
      case llvm::X86::POPA16:  // 16 位恢复所有通用寄存器
      case llvm::X86::POPA32:  // 32 位恢复所有通用寄存器
        // 允许 TmpManager 重用这些寄存器
        for (unsigned i = 0; i < AVAILABLE_GPR; i++) {
          patch.regUsage[i] |= RegisterUsage::RegisterSaved;
        }
        break;
      default:
        break;
    }
  }
 
  return;
}
bool PatchRuleAssembly::generate(const llvm::MCInst &inst, rword address,
                                 uint32_t instSize, const LLVMCPU &llvmcpu,
                                 std::vector<Patch> &patchList) {
    // 创建一个 Patch 对象, 用于存储当前指令的补丁信息
    Patch instPatch{inst, address, instSize, llvmcpu};
    // 设置当前指令的寄存器保存信息
    setRegisterSaved(instPatch);
 
    // 遍历所有补丁规则
    for (uint32_t j = 0; j < patchRules.size(); j++) {
        // 检查当前补丁规则是否适用于当前指令
        if (patchRules[j].canBeApplied(instPatch, llvmcpu)) {
            QBDI_DEBUG("Patch rule {} applied", j);
            // 如果存在待合并的补丁
            if (mergePending) {
                QBDI_REQUIRE_ABORT(patchList.size() > 0, "No previous patch to merge");
                QBDI_DEBUG("Previous instruction merged");
 
                // 1. 为当前指令生成补丁
                patchRules[j].apply(instPatch, llvmcpu);
 
                // 2. 获取插入位置
                int position = -1;
                for (auto &p : instPatch.patchGenFlags) {
                    if (p.second == PatchGeneratorFlags::ModifyInstructionBeginFlags) {
                        position = p.first;
                        break;
                    }
                }
                QBDI_REQUIRE_ABORT(
                    position != -1,
                    "Fail to get the position to insert the new patch {}", instPatch);
 
                // 3. 在指定位置插入指令以合并
                Patch &mergePatch = patchList.back();
                instPatch.insertAt(position, std::move(mergePatch.insts));
 
                // 4. 保留一些元数据
                instPatch.metadata.address = mergePatch.metadata.address;
                instPatch.metadata.instSize += mergePatch.metadata.instSize;
                instPatch.metadata.execblockFlags |= mergePatch.metadata.execblockFlags;
                instPatch.metadata.prefix = mergePatch.metadata.prefix;
                instPatch.metadata.prefix.push_back(mergePatch.metadata.inst);
 
                // 5. 替换补丁
                mergePatch = std::move(instPatch);
 
            } else {
                // 如果没有待合并的补丁, 直接应用补丁规则并添加到补丁列表中
                patchRules[j].apply(instPatch, llvmcpu);
                patchList.push_back(std::move(instPatch));
            }
            // 获取最后一个补丁的引用
            Patch &patch = patchList.back();
            // 重置合并标志
            mergePending = false;
            // 检查是否有待合并的补丁
            for (auto &p : patch.patchGenFlags) {
                mergePending |= (p.second == PatchGeneratorFlagsX86_64::MergeFlag);
            }
 
            // 如果有待合并的补丁, 返回 false 表示继续处理
            if (mergePending) {
                return false;
            } else if (patch.metadata.modifyPC) {
                // 如果补丁修改了程序计数器(PC), 重置状态并返回 true
                reset();
                return true;
            } else {
                // 如果补丁没有修改 PC, 返回 false 表示继续处理
                return false;
            }
        }
    }
    // 如果没有找到适用的补丁规则, 终止程序并报错
    QBDI_ABORT("Not PatchRule found for: {}", instPatch);
}
 
static void setRegisterSaved(Patch &patch) {
 
  if constexpr (is_x86) {
    // 根据指令的操作码设置寄存器的保存状态
    switch (patch.metadata.inst.getOpcode()) {
      // 对于 x86 架构中的 PUSHA 和 POPA 指令
      case llvm::X86::PUSHA16: // 16 位保存所有通用寄存器
      case llvm::X86::PUSHA32: // 32 位保存所有通用寄存器
      case llvm::X86::POPA16:  // 16 位恢复所有通用寄存器
      case llvm::X86::POPA32:  // 32 位恢复所有通用寄存器
        // 允许 TmpManager 重用这些寄存器
        for (unsigned i = 0; i < AVAILABLE_GPR; i++) {
          patch.regUsage[i] |= RegisterUsage::RegisterSaved;
        }
        break;
      default:
        break;
    }
  }
 
  return;
}
PatchRuleAssembly::PatchRuleAssembly(Options opts)
    : patchRules(getDefaultPatchRules(opts)), options(opts),
      mergePending(false) {}
PatchRuleAssembly::PatchRuleAssembly(Options opts)
    : patchRules(getDefaultPatchRules(opts)), options(opts),
      mergePending(false) {}
std::vector<PatchRule> getDefaultPatchRules(Options opts) {
  std::vector<PatchRule> rules;
 
  /* Rule #0: Avoid instrumenting instruction prefixes.
   * Target:  X86 prefixes (LOCK, REP and other REX prefixes).
   * Patch:   Output the unmodified MCInst but flag the patch as "do not
   * instrument".
   */
  rules.emplace_back(
      Or::unique(conv_unique<PatchCondition>(
          OpIs::unique(llvm::X86::LOCK_PREFIX),
          OpIs::unique(llvm::X86::REX64_PREFIX),
          OpIs::unique(llvm::X86::REP_PREFIX),
          OpIs::unique(llvm::X86::REPNE_PREFIX),
          OpIs::unique(llvm::X86::DATA16_PREFIX),
          OpIs::unique(llvm::X86::CS_PREFIX),
          OpIs::unique(llvm::X86::SS_PREFIX),
          OpIs::unique(llvm::X86::DS_PREFIX),
          OpIs::unique(llvm::X86::ES_PREFIX),
          OpIs::unique(llvm::X86::FS_PREFIX),
          OpIs::unique(llvm::X86::GS_PREFIX),
          OpIs::unique(llvm::X86::XACQUIRE_PREFIX),
          OpIs::unique(llvm::X86::XRELEASE_PREFIX))),
      conv_unique<PatchGenerator>(
          PatchGenFlags::unique(PatchGeneratorFlagsX86_64::MergeFlag),
          ModifyInstruction::unique(InstTransform::UniquePtrVec())));
 
  /* Rule #1: Simulate jmp to memory value using RIP addressing.
   * Target:  JMP *[RIP + IMM]
   * Patch:   Temp(0) := RIP + Constant(0)
   *          JMP *[RIP + IMM] --> MOV Temp(1), [Temp(0) + IMM]
   *          DataBlock[Offset(RIP)] := Temp(1)
   */
  rules.emplace_back(
      And::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::JMP64m),
                                              UseReg::unique(Reg(REG_PC)))),
      conv_unique<PatchGenerator>(
          GetPCOffset::unique(Temp(0), Constant(0)),
          ModifyInstruction::unique(conv_unique<InstTransform>(
              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0)),
              SetOpcode::unique(llvm::X86::MOV64rm),
              AddOperand::unique(Operand(0), Temp(1)))),
          WriteTemp::unique(Temp(1), Offset(Reg(REG_PC)))));
 
  /* Rule #2: Simulate call to memory value using RIP addressing.
   * Target:  CALL *[RIP + IMM]
   * Patch:   Temp(0) := RIP + Constant(0)
   *          CALL *[RIP + IMM] --> MOV Temp(1), [Temp(0) + IMM]
   *          SimulateCall(Temp(1))
   */
  rules.emplace_back(
      And::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::CALL64m),
                                              UseReg::unique(Reg(REG_PC)))),
      conv_unique<PatchGenerator>(
          GetPCOffset::unique(Temp(0), Constant(0)),
          ModifyInstruction::unique(conv_unique<InstTransform>(
              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0)),
              SetOpcode::unique(llvm::X86::MOV64rm),
              AddOperand::unique(Operand(0), Temp(1)))),
          SimulateCall::unique(Temp(1))));
 
  /* Rule #3: Generic RIP patching.
   * Target:  Any instruction with RIP as operand, e.g. LEA RAX, [RIP + 1]
   * Patch:   Temp(0) := rip
   *          LEA RAX, [RIP + IMM] --> LEA RAX, [Temp(0) + IMM]
   */
  rules.emplace_back(
      UseReg::unique(Reg(REG_PC)),
      conv_unique<PatchGenerator>(
          GetPCOffset::unique(Temp(0), Constant(0)),
          ModifyInstruction::unique(conv_unique<InstTransform>(
              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0))))));
 
  /* Rule #4: Simulate JMP to memory value.
   * Target:  JMP *MEM
   * Patch:   JMP *MEM --> MOV Temp(0), MEM
   *          DataBlock[Offset(RIP)] := Temp(0)
   */
  rules.emplace_back(
      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::JMP32m),
                                             OpIs::unique(llvm::X86::JMP64m))),
      conv_unique<PatchGenerator>(
          ModifyInstruction::unique(conv_unique<InstTransform>(
              SetOpcode::unique(is_x86 ? llvm::X86::MOV32rm
                                       : llvm::X86::MOV64rm),
              AddOperand::unique(Operand(0), Temp(0)))),
          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));
 
  /* Rule #5: Simulate CALL to memory value.
   * Target:  CALL MEM
   * Patch:   CALL MEM --> MOV Temp(0), MEM
   *          SimulateCall(Temp(1))
   */
  rules.emplace_back(
      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::CALL32m),
                                             OpIs::unique(llvm::X86::CALL64m))),
      conv_unique<PatchGenerator>(
          ModifyInstruction::unique(conv_unique<InstTransform>(
              SetOpcode::unique(is_x86 ? llvm::X86::MOV32rm
                                       : llvm::X86::MOV64rm),
              AddOperand::unique(Operand(0), Temp(0)))),
          SimulateCall::unique(Temp(0))));
 
  /* Rule #6: Simulate JMP to constant value.
   * Target:  JMP IMM
   * Patch:   Temp(0) := RIP + Operand(0)
   *          DataBlock[Offset(RIP)] := Temp(0)
   */
  rules.emplace_back(
      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::JMP_1),
                                             OpIs::unique(llvm::X86::JMP_2),
                                             OpIs::unique(llvm::X86::JMP_4))),
      conv_unique<PatchGenerator>(
          GetPCOffset::unique(Temp(0), Operand(0)),
          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));
 
  /* Rule #7: Simulate JMP to register value.
   * Target:  JMP REG
   * Patch:   Temp(0) := Operand(0)
   *          DataBlock[Offset(RIP)] := Temp(0)
   */
  rules.emplace_back(
      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::JMP32r),
                                             OpIs::unique(llvm::X86::JMP64r))),
      conv_unique<PatchGenerator>(
          GetOperand::unique(Temp(0), Operand(0)),
          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));
 
  /* Rule #8: Simulate CALL to register value.
   * Target:  CALL REG
   * Patch:   Temp(0) := Operand(0)
   *          SimulateCall(Temp(0))
   */
  rules.emplace_back(
      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::CALL32r),
                                             OpIs::unique(llvm::X86::CALL64r))),
      conv_unique<PatchGenerator>(GetOperand::unique(Temp(0), Operand(0)),
                                  SimulateCall::unique(Temp(0))));
 
  /* Rule #9: Simulate Jcc IMM8.
   * Target:  Jcc IMM8
   * Patch:     Temp(0) := RIP + Operand(0)
   *         ---Jcc IMM8 --> Jcc END
   *         |  Temp(0) := RIP + Constant(0)
   *         -->END: DataBlock[Offset(RIP)] := Temp(0)
   */
  rules.emplace_back(
      Or::unique(conv_unique<PatchCondition>(
          OpIs::unique(llvm::X86::JCC_1), OpIs::unique(llvm::X86::LOOP),
          OpIs::unique(llvm::X86::LOOPE), OpIs::unique(llvm::X86::LOOPNE),
          OpIs::unique(llvm::X86::JRCXZ), OpIs::unique(llvm::X86::JECXZ),
          OpIs::unique(llvm::X86::JCXZ))),
      conv_unique<PatchGenerator>(
          GetPCOffset::unique(Temp(0), Operand(0)),
          ModifyInstruction::unique(
              conv_unique<InstTransform>(SetOperand::unique(
                  Operand(0),
                  Constant(is_x86 ? 6 : 11)) // Offset to jump the next load.
                                         )),
          GetPCOffset::unique(Temp(0), Constant(0)),
          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));
 
  /* Rule #10: Simulate Jcc IMM16.
   * Target:  Jcc IMM16
   * Patch:     Temp(0) := RIP + Operand(0)
   *         ---Jcc IMM16 --> Jcc END
   *         |  Temp(0) := RIP + Constant(0)
   *         -->END: DataBlock[Offset(RIP)] := Temp(0)
   */
  rules.emplace_back(
      OpIs::unique(llvm::X86::JCC_2),
      conv_unique<PatchGenerator>(
          GetPCOffset::unique(Temp(0), Operand(0)),
          ModifyInstruction::unique(conv_unique<InstTransform>(
              SetOperand::unique(Operand(0), Constant(is_x86 ? 7 : 12)))),
          GetPCOffset::unique(Temp(0), Constant(0)),
          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));
 
  /* Rule #11: Simulate Jcc IMM32.
   * Target:  Jcc IMM32
   * Patch:     Temp(0) := RIP + Operand(0)
   *         ---Jcc IMM32 --> Jcc END
   *         |  Temp(0) := RIP + Constant(0)
   *         -->END: DataBlock[Offset(RIP)] := Temp(0)
   */
  rules.emplace_back(
      OpIs::unique(llvm::X86::JCC_4),
      conv_unique<PatchGenerator>(
          GetPCOffset::unique(Temp(0), Operand(0)),
          ModifyInstruction::unique(conv_unique<InstTransform>(
              SetOperand::unique(Operand(0), Constant(is_x86 ? 9 : 14)))),
          GetPCOffset::unique(Temp(0), Constant(0)),
          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));
 
  /* Rule #12: Simulate CALL to constant offset.
   * Target:   CALL IMM
   * Patch:    Temp(0) := RIP + Operand(0)
   *           SimulateCall(Temp(0))
   */
  rules.emplace_back(
      Or::unique(
          conv_unique<PatchCondition>(OpIs::unique(llvm::X86::CALL64pcrel32),
                                      OpIs::unique(llvm::X86::CALLpcrel16),
                                      OpIs::unique(llvm::X86::CALLpcrel32))),
      conv_unique<PatchGenerator>(GetPCOffset::unique(Temp(0), Operand(0)),
                                  SimulateCall::unique(Temp(0))));
 
  /* Rule #13: Simulate return.
   * Target:   RET
   * Patch:    SimulateRet(Temp(0))
   */
  rules.emplace_back(
      Or::unique(conv_unique<PatchCondition>(
          OpIs::unique(llvm::X86::RET32), OpIs::unique(llvm::X86::RET64),
          OpIs::unique(llvm::X86::RET16), OpIs::unique(llvm::X86::RETI32),
          OpIs::unique(llvm::X86::RETI64), OpIs::unique(llvm::X86::RETI16))),
      conv_unique<PatchGenerator>(SimulateRet::unique(Temp(0))));
 
  /* Rule #14: Default rule for every other instructions.
   * Target:   *
   * Patch:    Output original unmodified instructions.
   */
  rules.emplace_back(True::unique(),
                     conv_unique<PatchGenerator>(ModifyInstruction::unique(
                         InstTransform::UniquePtrVec())));
 
  return rules;
}
std::vector<PatchRule> getDefaultPatchRules(Options opts) {
  std::vector<PatchRule> rules;
 
  /* Rule #0: Avoid instrumenting instruction prefixes.
   * Target:  X86 prefixes (LOCK, REP and other REX prefixes).
   * Patch:   Output the unmodified MCInst but flag the patch as "do not
   * instrument".
   */
  rules.emplace_back(
      Or::unique(conv_unique<PatchCondition>(
          OpIs::unique(llvm::X86::LOCK_PREFIX),
          OpIs::unique(llvm::X86::REX64_PREFIX),
          OpIs::unique(llvm::X86::REP_PREFIX),
          OpIs::unique(llvm::X86::REPNE_PREFIX),
          OpIs::unique(llvm::X86::DATA16_PREFIX),
          OpIs::unique(llvm::X86::CS_PREFIX),
          OpIs::unique(llvm::X86::SS_PREFIX),
          OpIs::unique(llvm::X86::DS_PREFIX),
          OpIs::unique(llvm::X86::ES_PREFIX),
          OpIs::unique(llvm::X86::FS_PREFIX),
          OpIs::unique(llvm::X86::GS_PREFIX),
          OpIs::unique(llvm::X86::XACQUIRE_PREFIX),
          OpIs::unique(llvm::X86::XRELEASE_PREFIX))),
      conv_unique<PatchGenerator>(
          PatchGenFlags::unique(PatchGeneratorFlagsX86_64::MergeFlag),
          ModifyInstruction::unique(InstTransform::UniquePtrVec())));
 
  /* Rule #1: Simulate jmp to memory value using RIP addressing.
   * Target:  JMP *[RIP + IMM]
   * Patch:   Temp(0) := RIP + Constant(0)
   *          JMP *[RIP + IMM] --> MOV Temp(1), [Temp(0) + IMM]
   *          DataBlock[Offset(RIP)] := Temp(1)
   */
  rules.emplace_back(
      And::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::JMP64m),
                                              UseReg::unique(Reg(REG_PC)))),
      conv_unique<PatchGenerator>(
          GetPCOffset::unique(Temp(0), Constant(0)),
          ModifyInstruction::unique(conv_unique<InstTransform>(
              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0)),
              SetOpcode::unique(llvm::X86::MOV64rm),
              AddOperand::unique(Operand(0), Temp(1)))),
          WriteTemp::unique(Temp(1), Offset(Reg(REG_PC)))));
 
  /* Rule #2: Simulate call to memory value using RIP addressing.
   * Target:  CALL *[RIP + IMM]
   * Patch:   Temp(0) := RIP + Constant(0)
   *          CALL *[RIP + IMM] --> MOV Temp(1), [Temp(0) + IMM]
   *          SimulateCall(Temp(1))
   */
  rules.emplace_back(
      And::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::CALL64m),
                                              UseReg::unique(Reg(REG_PC)))),
      conv_unique<PatchGenerator>(
          GetPCOffset::unique(Temp(0), Constant(0)),
          ModifyInstruction::unique(conv_unique<InstTransform>(
              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0)),
              SetOpcode::unique(llvm::X86::MOV64rm),
              AddOperand::unique(Operand(0), Temp(1)))),
          SimulateCall::unique(Temp(1))));
 
  /* Rule #3: Generic RIP patching.
   * Target:  Any instruction with RIP as operand, e.g. LEA RAX, [RIP + 1]
   * Patch:   Temp(0) := rip
   *          LEA RAX, [RIP + IMM] --> LEA RAX, [Temp(0) + IMM]
   */
  rules.emplace_back(
      UseReg::unique(Reg(REG_PC)),
      conv_unique<PatchGenerator>(
          GetPCOffset::unique(Temp(0), Constant(0)),
          ModifyInstruction::unique(conv_unique<InstTransform>(
              SubstituteWithTemp::unique(Reg(REG_PC), Temp(0))))));
 
  /* Rule #4: Simulate JMP to memory value.
   * Target:  JMP *MEM
   * Patch:   JMP *MEM --> MOV Temp(0), MEM
   *          DataBlock[Offset(RIP)] := Temp(0)
   */
  rules.emplace_back(
      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::JMP32m),
                                             OpIs::unique(llvm::X86::JMP64m))),
      conv_unique<PatchGenerator>(
          ModifyInstruction::unique(conv_unique<InstTransform>(
              SetOpcode::unique(is_x86 ? llvm::X86::MOV32rm
                                       : llvm::X86::MOV64rm),
              AddOperand::unique(Operand(0), Temp(0)))),
          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));
 
  /* Rule #5: Simulate CALL to memory value.
   * Target:  CALL MEM
   * Patch:   CALL MEM --> MOV Temp(0), MEM
   *          SimulateCall(Temp(1))
   */
  rules.emplace_back(
      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::CALL32m),
                                             OpIs::unique(llvm::X86::CALL64m))),
      conv_unique<PatchGenerator>(
          ModifyInstruction::unique(conv_unique<InstTransform>(
              SetOpcode::unique(is_x86 ? llvm::X86::MOV32rm
                                       : llvm::X86::MOV64rm),
              AddOperand::unique(Operand(0), Temp(0)))),
          SimulateCall::unique(Temp(0))));
 
  /* Rule #6: Simulate JMP to constant value.
   * Target:  JMP IMM
   * Patch:   Temp(0) := RIP + Operand(0)
   *          DataBlock[Offset(RIP)] := Temp(0)
   */
  rules.emplace_back(
      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::JMP_1),
                                             OpIs::unique(llvm::X86::JMP_2),
                                             OpIs::unique(llvm::X86::JMP_4))),
      conv_unique<PatchGenerator>(
          GetPCOffset::unique(Temp(0), Operand(0)),
          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));
 
  /* Rule #7: Simulate JMP to register value.
   * Target:  JMP REG
   * Patch:   Temp(0) := Operand(0)
   *          DataBlock[Offset(RIP)] := Temp(0)
   */
  rules.emplace_back(
      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::JMP32r),
                                             OpIs::unique(llvm::X86::JMP64r))),
      conv_unique<PatchGenerator>(
          GetOperand::unique(Temp(0), Operand(0)),
          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));
 
  /* Rule #8: Simulate CALL to register value.
   * Target:  CALL REG
   * Patch:   Temp(0) := Operand(0)
   *          SimulateCall(Temp(0))
   */
  rules.emplace_back(
      Or::unique(conv_unique<PatchCondition>(OpIs::unique(llvm::X86::CALL32r),
                                             OpIs::unique(llvm::X86::CALL64r))),
      conv_unique<PatchGenerator>(GetOperand::unique(Temp(0), Operand(0)),
                                  SimulateCall::unique(Temp(0))));
 
  /* Rule #9: Simulate Jcc IMM8.
   * Target:  Jcc IMM8
   * Patch:     Temp(0) := RIP + Operand(0)
   *         ---Jcc IMM8 --> Jcc END
   *         |  Temp(0) := RIP + Constant(0)
   *         -->END: DataBlock[Offset(RIP)] := Temp(0)
   */
  rules.emplace_back(
      Or::unique(conv_unique<PatchCondition>(
          OpIs::unique(llvm::X86::JCC_1), OpIs::unique(llvm::X86::LOOP),
          OpIs::unique(llvm::X86::LOOPE), OpIs::unique(llvm::X86::LOOPNE),
          OpIs::unique(llvm::X86::JRCXZ), OpIs::unique(llvm::X86::JECXZ),
          OpIs::unique(llvm::X86::JCXZ))),
      conv_unique<PatchGenerator>(
          GetPCOffset::unique(Temp(0), Operand(0)),
          ModifyInstruction::unique(
              conv_unique<InstTransform>(SetOperand::unique(
                  Operand(0),
                  Constant(is_x86 ? 6 : 11)) // Offset to jump the next load.
                                         )),
          GetPCOffset::unique(Temp(0), Constant(0)),
          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));
 
  /* Rule #10: Simulate Jcc IMM16.
   * Target:  Jcc IMM16
   * Patch:     Temp(0) := RIP + Operand(0)
   *         ---Jcc IMM16 --> Jcc END
   *         |  Temp(0) := RIP + Constant(0)
   *         -->END: DataBlock[Offset(RIP)] := Temp(0)
   */
  rules.emplace_back(
      OpIs::unique(llvm::X86::JCC_2),
      conv_unique<PatchGenerator>(
          GetPCOffset::unique(Temp(0), Operand(0)),
          ModifyInstruction::unique(conv_unique<InstTransform>(
              SetOperand::unique(Operand(0), Constant(is_x86 ? 7 : 12)))),
          GetPCOffset::unique(Temp(0), Constant(0)),
          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));
 
  /* Rule #11: Simulate Jcc IMM32.
   * Target:  Jcc IMM32
   * Patch:     Temp(0) := RIP + Operand(0)
   *         ---Jcc IMM32 --> Jcc END
   *         |  Temp(0) := RIP + Constant(0)
   *         -->END: DataBlock[Offset(RIP)] := Temp(0)
   */
  rules.emplace_back(
      OpIs::unique(llvm::X86::JCC_4),
      conv_unique<PatchGenerator>(
          GetPCOffset::unique(Temp(0), Operand(0)),
          ModifyInstruction::unique(conv_unique<InstTransform>(
              SetOperand::unique(Operand(0), Constant(is_x86 ? 9 : 14)))),
          GetPCOffset::unique(Temp(0), Constant(0)),
          WriteTemp::unique(Temp(0), Offset(Reg(REG_PC)))));
 
  /* Rule #12: Simulate CALL to constant offset.
   * Target:   CALL IMM
   * Patch:    Temp(0) := RIP + Operand(0)
   *           SimulateCall(Temp(0))
   */
  rules.emplace_back(
      Or::unique(
          conv_unique<PatchCondition>(OpIs::unique(llvm::X86::CALL64pcrel32),
                                      OpIs::unique(llvm::X86::CALLpcrel16),
                                      OpIs::unique(llvm::X86::CALLpcrel32))),
      conv_unique<PatchGenerator>(GetPCOffset::unique(Temp(0), Operand(0)),
                                  SimulateCall::unique(Temp(0))));
 
  /* Rule #13: Simulate return.
   * Target:   RET
   * Patch:    SimulateRet(Temp(0))
   */
  rules.emplace_back(
      Or::unique(conv_unique<PatchCondition>(
          OpIs::unique(llvm::X86::RET32), OpIs::unique(llvm::X86::RET64),
          OpIs::unique(llvm::X86::RET16), OpIs::unique(llvm::X86::RETI32),
          OpIs::unique(llvm::X86::RETI64), OpIs::unique(llvm::X86::RETI16))),
      conv_unique<PatchGenerator>(SimulateRet::unique(Temp(0))));
 
  /* Rule #14: Default rule for every other instructions.
   * Target:   *
   * Patch:    Output original unmodified instructions.
   */
  rules.emplace_back(True::unique(),
                     conv_unique<PatchGenerator>(ModifyInstruction::unique(
                         InstTransform::UniquePtrVec())));
 
  return rules;
}
/*! 一个使用 PatchDSL 编写的补丁规则.
 */
class PatchRule {
  std::unique_ptr<PatchCondition> condition;
  std::vector<std::unique_ptr<PatchGenerator>> generators;
 
public:
  /*! 分配新补丁规则对象(含条件与生成器集合)
   *
   * @param[in] condition   用于判定当前规则是否适用的 PatchCondition 条件对象
   * @param[in] generators  生成补丁指令的 PatchGenerator 向量
   */
  PatchRule(std::unique_ptr<PatchCondition> &&condition,
            std::vector<std::unique_ptr<PatchGenerator>> &&generators);
 
  PatchRule(PatchRule &&);
  ~PatchRule();       
 
  /*! 通过评估当前上下文的条件, 判定规则是否适用
   *
   * @param[in] patch    待检查的 Patch 对象
   * @param[in] llvmcpu  LLVMCPU 上下文对象
   *
   * @return 若当前上下文满足条件则返回 true
   */
  bool canBeApplied(const Patch &patch, const LLVMCPU &llvmcpu) const;
 
  /*! 通过在当前上下文执行生成器来生成补丁输出
   *   同时管理补丁的临时寄存器分配
   *
   * @param[in] patch    待应用规则的 Patch 对象
   * @param[in] llvmcpu  LLVMCPU 上下文对象
   */
  void apply(Patch &patch, const LLVMCPU &llvmcpu) const;
};
/*! 一个使用 PatchDSL 编写的补丁规则.
 */
class PatchRule {
  std::unique_ptr<PatchCondition> condition;
  std::vector<std::unique_ptr<PatchGenerator>> generators;
 
public:
  /*! 分配新补丁规则对象(含条件与生成器集合)
   *
   * @param[in] condition   用于判定当前规则是否适用的 PatchCondition 条件对象
   * @param[in] generators  生成补丁指令的 PatchGenerator 向量
   */
  PatchRule(std::unique_ptr<PatchCondition> &&condition,
            std::vector<std::unique_ptr<PatchGenerator>> &&generators);
 
  PatchRule(PatchRule &&);
  ~PatchRule();       
 
  /*! 通过评估当前上下文的条件, 判定规则是否适用
   *
   * @param[in] patch    待检查的 Patch 对象
   * @param[in] llvmcpu  LLVMCPU 上下文对象
   *
   * @return 若当前上下文满足条件则返回 true
   */
  bool canBeApplied(const Patch &patch, const LLVMCPU &llvmcpu) const;
 
  /*! 通过在当前上下文执行生成器来生成补丁输出
   *   同时管理补丁的临时寄存器分配
   *
   * @param[in] patch    待应用规则的 Patch 对象
   * @param[in] llvmcpu  LLVMCPU 上下文对象
   */
  void apply(Patch &patch, const LLVMCPU &llvmcpu) const;
};
bool PatchRule::canBeApplied(const Patch &patch, const LLVMCPU &llvmcpu) const {
  return condition->test(patch, llvmcpu);
}
bool PatchRule::canBeApplied(const Patch &patch, const LLVMCPU &llvmcpu) const {
  return condition->test(patch, llvmcpu);
}
template <class T, class... Args>
inline std::vector<std::unique_ptr<T>> conv_unique(Args... args) {
  std::vector<std::unique_ptr<T>> vec;
  _conv_unique<T>(vec, std::forward<Args>(args)...);
  return vec;
}
 
template <class T>
inline void _conv_unique(std::vector<std::unique_ptr<T>> &vec,
                         std::unique_ptr<T> &&u) {
  vec.push_back(std::forward<std::unique_ptr<T>>(u));
}
 
template <class T, class... Args>
inline void _conv_unique(std::vector<std::unique_ptr<T>> &vec,
                         std::unique_ptr<T> &&u, Args... args) {
  vec.push_back(std::forward<std::unique_ptr<T>>(u));
  _conv_unique<T>(vec, std::forward<Args>(args)...);
}
template <class T, class... Args>
inline std::vector<std::unique_ptr<T>> conv_unique(Args... args) {
  std::vector<std::unique_ptr<T>> vec;
  _conv_unique<T>(vec, std::forward<Args>(args)...);
  return vec;
}
 
template <class T>
inline void _conv_unique(std::vector<std::unique_ptr<T>> &vec,
                         std::unique_ptr<T> &&u) {
  vec.push_back(std::forward<std::unique_ptr<T>>(u));
}
 
template <class T, class... Args>
inline void _conv_unique(std::vector<std::unique_ptr<T>> &vec,
                         std::unique_ptr<T> &&u, Args... args) {
  vec.push_back(std::forward<std::unique_ptr<T>>(u));

[招生]科锐逆向工程师培训(2026年11月13日实地,远程教学同时开班, 第57期)

最后于 2025-7-22 09:48 被nothing233编辑 ,原因:
收藏
点赞 ・142
打赏
分享
最新回复 (59)
雪    币: 2719
活跃值: (5916)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2 楼
你是真的卷~
2025-7-21 01:10
0
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3 楼
感谢分享
2025-7-21 06:12
0
雪    币: 2314
活跃值: (1966)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
4 楼
hacker521 你是真的卷~
呃, 感觉探索一些未知的领域, 实现一些有创意的想法比较有意思, 做出来也很有成就感, 不是在卷, hh
2025-7-21 08:55
0
雪    币: 2057
活跃值: (8293)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
5 楼
1
2025-7-21 09:18
0
雪    币: 11
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6 楼
666
2025-7-21 09:26
0
雪    币: 969
活跃值: (5488)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7 楼
tql
2025-7-21 09:35
0
雪    币: 4824
活跃值: (8332)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
8 楼
感谢分享
2025-7-21 09:41
0
雪    币: 0
活跃值: (2555)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9 楼
6666
2025-7-21 10:38
0
雪    币: 343
活跃值: (1915)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10 楼
77777
2025-7-21 10:54
0
雪    币: 342
活跃值: (405)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11 楼
66
2025-7-21 11:20
0
雪    币: 319
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12 楼
能不能教一下手把手写trace
2025-7-21 13:39
0
雪    币: 2314
活跃值: (1966)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
13 楼
mb_cfjwplfo 能不能教一下手把手写trace[em_006]
等后面放假会实战写一写
2025-7-21 13:54
0
雪    币: 110
活跃值: (9656)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14 楼
牛逼
2025-7-21 14:28
0
雪    币: 190
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15 楼
2025-7-21 17:32
0
雪    币: 107
活跃值: (1138)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16 楼
666
2025-7-22 17:51
0
雪    币: 213
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
17 楼
666666666
2025-7-22 22:01
0
雪    币: 8410
活跃值: (13534)
能力值: (RANK:385 )
在线值:
发帖
回帖
粉丝
18 楼
牛 学到了.
2025-7-23 09:51
0
雪    币: 292
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
19 楼
66666
2025-7-24 16:26
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
20 楼
感谢分享
2025-7-25 03:52
0
雪    币: 117
活跃值: (2396)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21 楼
666
2025-7-25 09:12
0
雪    币: 1122
活跃值: (2226)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
22 楼
666学习一下
2025-7-25 09:13
0
雪    币: 1901
活跃值: (6063)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
23 楼
牛逼
2025-7-27 22:55
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
24 楼
1
2025-7-27 23:20
0
雪    币: 7
活跃值: (1989)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25 楼
321321213
2025-8-5 00:28
0
游客
登录 | 注册 方可回帖
返回