Rizin项目中的ESIL中间语言详解

Rizin项目中的ESIL中间语言详解

rizin UNIX-like reverse engineering framework and command-line toolset. rizin 项目地址: https://gitcode.com/gh_mirrors/ri/rizin

什么是ESIL?

ESIL(Evaluable Strings Intermediate Language)是Rizin逆向工程框架中采用的一种可评估字符串中间语言。它采用类似Forth语言的表示方法来描述每个操作码的行为,这种表示方法可以被评估和执行,从而实现代码模拟的功能。

ESIL虚拟机工作原理

ESIL虚拟机的工作流程可以简化为以下伪代码:

while ((word=haveCommand())) {
  if (word.isKeyword()) {
    esilCommands[word](esil);
  } else {
    esil.push(evaluateToNumber(word));
  }
  nextCommand();
}

ESIL命令是从栈中弹出值、执行计算操作并将结果(如果有)压回栈的操作。这些命令旨在覆盖CPU执行的所有常见操作,包括二进制运算、内存访问、系统调用等。

基本语法和使用

启用ESIL显示

要在反汇编时显示ESIL表达式,可以使用以下命令:

e asm.esil = true

表达式语法

操作码被翻译成逗号分隔的ESIL表达式列表:

  • 寄存器操作示例:

    xor eax, eax → 0,eax,=,1,zf,=
    
  • 内存访问使用方括号表示:

    mov eax, [0x80480] → 0x80480,[],eax,=
    
  • 指定操作大小(默认为目标操作数大小):

    movb $0, 0x80480 → 0,0x80480,=[1]
    

条件表达式

条件表达式以'?'字符开头,检查表达式结果是否为0,并根据结果决定是否跳过下一个表达式:

cmp eax, 123 → 123,eax,==,$z,zf,=
jz eax → zf,?{,eax,eip,=,}

对于多表达式条件,需要这样写:

zf,?{,eip,esp,=[],eax,eip,=,$r,esp,-=,}

特殊指令处理

系统调用

系统调用以'$'开头,后面可以跟指定系统调用号的数值:

0x80,$

陷阱指令

陷阱指令用于抛出异常(如无效指令、除零错误、内存读取错误等):

<trap>,<code>,$$

快速分析技巧

通过检查ESIL字符串可以快速获取信息:

  • indexOf('['):包含内存引用
  • indexOf("=["):内存写入操作
  • indexOf("pc,="):修改程序计数器(分支、跳转、调用)
  • indexOf("sp,="):修改栈指针
  • indexOf("?{"):条件指令
  • equalsTo(""):空字符串表示NOP指令

CPU标志和ESIL标志

CPU标志

CPU标志通常定义为RReg配置文件中的1位寄存器,有时归类为'flag'寄存器类型。

ESIL内部标志

ESIL虚拟机有内部状态标志(只读),可用于将值导出到底层CPU标志:

  • $z:零标志(结果为0时设置)
  • $b:借位标志(需指定位数,如$b4)
  • $c:进位标志(需指定位数,如$c7)
  • $p:奇偶标志
  • $r:寄存器大小(asm.bits/8)

变量和运算

变量特性

  1. 没有预定义的位宽(可轻松扩展至128、256和512位)
  2. 数量无限(兼容SSA形式)
  3. 寄存器名称没有特定语法
  4. 数字可以使用RzNum支持的任何进制表示
  5. 每个ESIL后端应有相关的RReg配置文件

算术运算

  1. 加法:"+"
  2. 乘法:"*"
  3. 减法:"-"
  4. 除法:"/"
  5. 取模:"%"
  6. 幂运算:"**"

位运算

  1. 与运算:"&"
  2. 或运算:"|"
  3. 异或:"^"
  4. 左移:"<<"
  5. 右移:">>"
  6. 循环左移:"<<<"
  7. 循环右移:">>>"
  8. 取反:"!"

控制流指令

ESIL规定解析控制流命令使用大写字母:

  • 3,SKIP:跳过N条指令(用于相对向前跳转)
  • 3,GOTO:跳转到指令3
  • LOOP:0,GOTO的别名
  • BREAK:停止评估表达式
  • STACK:将栈内容输出到屏幕
  • CLEAR:清空栈

x86 REP前缀示例

rep cmpsb → cx,!,?{,BREAK,},esi,[1],edi,[1],^,!,?{,BREAK,},esi,++,edi,++,cx,--,LOOP

未实现指令处理

未实现/未处理的指令用'TODO'命令表示,它会像'BREAK'一样停止执行,但会显示警告信息:

fmulp ST(1), ST(0) → TODO,fmulp ST(1),ST(0)

实际应用示例

AVR架构分析示例

AVR微控制器的ESIL分析实现示例展示了如何将指令表示为ESIL:

static int avr_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *buf, int len) {
  short ofst;
    int d, r, k;
    (...)

变量d、r和k分别表示"目标"、"寄存器"和"常数"。例如LDI(立即数加载)指令:

r_strbuf_setf(&op->esil, "0x%x,r%d,=", k, d);

反汇编显示:

0x00000080      30e0           0x0,r19,=                   ; LDI Rd,K. load immediate

内省和API钩子

内省表达式

为了简化ESIL解析,我们需要一种表达内省表达式的方法来提取所需数据。例如获取jmp指令的目标地址:

ao~esil,opcode
opcode: jmp 0x10000465a
esil: 0x10000465a,rip,=

我们需要能够检索'rip'的数值。更复杂的情况包括:

  • 操作码类型
  • 跳转目标
  • 条件依赖
  • 所有修改的寄存器(写)
  • 所有访问的寄存器(读)

API钩子

在解析器中设置钩子对于仿真非常重要,这样我们就可以扩展解析器来实现分析功能,而无需重复编写解析器。例如:

esil.on('regset', function(){..
esil.on('syscall', function(){esil.regset('rip'

现有钩子包括:

  • hook_flag_read()
  • hook_execute()
  • hook_mem_read()

返回true可以覆盖回调的默认操作(如使内存区域只读),返回false或0可以跟踪ESIL表达式解析(仿真)。

总结

ESIL作为Rizin中的中间语言,为代码分析和仿真提供了强大而灵活的基础。通过理解ESIL的工作原理和语法,逆向工程师可以更深入地分析二进制代码的行为,并构建自定义的分析工具。从简单的算术运算到复杂的控制流和系统调用,ESIL能够精确描述处理器指令的语义,为二进制分析开辟了新的可能性。

rizin UNIX-like reverse engineering framework and command-line toolset. rizin 项目地址: https://gitcode.com/gh_mirrors/ri/rizin

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叶彩曼Darcy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值