[英]Can the LEA instruction mimic every MOV instruction?
最近遇到了M/o/Vfuscator
一个完整的单指令 C 编译器,它将程序编译成“mov”指令,并且只编译成“mov”指令。 算术、比较、跳转、function 调用,以及程序需要的所有其他内容,都通过 mov 操作执行; 没有自修改代码,没有传输触发的计算,也没有其他形式的非移动作弊。
问题
我很好奇是否每个 MOV 指令都可以用等效的 LEA 替换?
例如
mov rax, 10
mov rbx, rax
...
可以替换为
lea rax, [10]
lea rbx, [rax]
...
更新
糟糕,完全忘记了 LEA 不能进行指针取消引用。 例如
.data
ten dq 10
.code
main proc
mov rax, offset ten
mov rbx, [rax] ; <--- dereference, move 10 into rbx
lea rax, [ten]
; <--- no dereference equivalent using lea
ret
main endp
end
不,LEA 不能从 memory 加载或存储,不像mov eax, [rdi]
或mov [rdi+rcx*8], rax
。
movfuscator依赖于使用mov
进行查表来做逻辑,所以这是一个很大的问题。 lea
只能做加法(寄存器和有符号常量)和左移,所以它不那么强大。 以及不允许访问除寄存器以外的任何内容。
LEA 无法模仿每个立即源mov
,因为mov r64, imm64
是唯一可以使用 64 位立即数的 x86-64 指令(例如mov rcx, 0xdeadbeefdeadbeef
)。 lea r64, [disp32]
只能使用符号扩展的 32 位绝对位移。 (相反,相对于 RIP 的 LEA 可以做 MOV 做不到的事情,比call $+5
/ pop rdi
/ add rdi, imm32
更有效)
LEA 无法读取或写入 8 位寄存器,包括无法模拟mov cl, ah
或类似内容。
LEA 可以将寄存器复制到 16、32 和 64 位寄存器的寄存器,但效率较低( 寄存器重命名时的 mov-elimination特定于mov
)。 一些指令还需要更长的机器代码,例如lea eax, [rbp]
或lea rax, [rsp]
由于寻址模式特殊情况需要额外的机器代码字节,而mov eax, ebp
和mov rax, rsp
是 2 和 3字节,分别。 看到rbp 不允许作为 SIB 基础? . 这也会影响[r12]
和[r13]
,它们使用相同的 ModRM 值但在 REX 中有一点区别。
401000: 8d 03 lea eax,[rbx] # normal length, no REX
401002: 89 d8 mov eax,ebx # equivalent MOV
401004: 41 8d 03 lea eax,[r11] # normal length, with a REX
401007: 44 89 d8 mov eax,r11d
40100a: 8d 04 24 lea eax,[rsp] # [rsp] can only be encoded with a SIB byte
40100d: 89 e0 mov eax,esp # but mov reg,reg uses register-direct
40100f: 41 8d 04 24 lea eax,[r12]
401013: 44 89 e0 mov eax,r12d
401016: 8d 45 00 lea eax,[rbp+0x0] # [rbp] can only be encoded with disp8=0 (or disp32 but that's longer)
401019: 89 e8 mov eax,ebp
40101b: 41 8d 45 00 lea eax,[r13+0x0]
40101f: 44 89 e8 mov eax,r13d
401022: 88 e1 mov cl,ah # LEA can't do this at all.
当然,您使用 LEA 的原因是在寻址模式下进行数学运算,而不是复制寄存器或将其设置为立即数。 例如复制和增加像mov
+ add
和/或移位和添加寄存器。 在不是地址/指针的值上使用 LEA?
你刚刚写的是相同的,但lea
最常用于在方括号[]
内进行一些地址计算,它可以将 2 个数字相乘,将第三个数字加到这些数字等。在某些地方,我遇到了lea
指令做一些数学(这是为了那个)。 例如,当你有struct
地址并且想要检索它的成员时,比如offset + 8
bytes away from offset 你做lea rax, [rbx+8]
, rbx
是结构的地址。 mov
可用于加载地址(就像lea
一样),但也可以用于加载值。 mov rax, [rbx]
等同于取消引用rbx
中指向的指针。 通常你会看到mov rax, [rbx+8*4]
这就是你如何加载int
数组的值(假设它需要 4 个字节来存储你的系统上的 int),相当于 C 中的array[8]
。 因此,要结束lea
和mov
可以在寄存器中加载地址时互换使用,但lea
不能执行指针取消引用,你需要mov
为此,或者当在 memory 地址存储值时lea
无济于事,你需要mov
那些操作也。 请注意在lea
的方括号[]
中进行数学计算,问题是使用lea
在数学方面具有更大的灵活性。 以下是在访问 memory 时如何使用mov
进行数学运算以不违反规则。 链接。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.