此功能使用SCAS和STOS的组合来完成其工作。第一, 解释第1行和第8行中[EBP + 8]和[EBP + C]的类型, 分别。接下来,解释这个代码片段的作用:
01: 8B 7D 08 mov edi, [ebp+8]
02: 8B D7 mov edx, edi
03: 33 C0 xor eax, eax
04: 83 C9 FF or ecx, 0FFFFFFFFh
05: F2 AE repne scasb
06: 83 C1 02 add ecx, 2
07: F7 D9 neg ecx
08: 8A 45 0C mov al, [ebp+0Ch]
09: 8B AA mov edi, edx
10: F3 AA rep stosb
11: 8B C2 mov eax, edx
在使用在线解决方案(https://johannesbader.ch/2014/05/practical-reverse-engineering-exercises-page-11/)检查后,我几乎找不到所有内容,但是,此代码段中的一步仍然没有意义。
根据在线解决方案,当我们在第4行运行命令or ecx, 0FFFFFFFFh
时,它说
我们[现在]将ECX解释为有符号整数-1
为了知道or
命令的结果,我们之前不需要知道ECX
的值是多少?为什么值为-1?
由于
答案 0 :(得分:4)
-1
的32位two's complement representation为0xFFFFFFFF
(全1)。 1 OR x
始终为1
,因此无条件地将ecx
设置为-1。此技巧仅适用于-1,因为OR只能设置位,而不能将它们清零。
您引用的解决方案的一部分,关于将#34; ecx
解释为有符号整数-1",只有在后面的gdb命令的上下文中才是合理的:{{1} } - > (gdb) p/d $ecx
。
$7 = -1
前缀将ecx视为未签名的计数器。将ecx设置为-1 / UINT_MAX意味着repne scasb
只会在内存中找到零时停止,而不是因为rep
一直倒计时。 (理论上,如果没有零,那么它会倒计时并以此方式结束,但在实践中它会首先发生段错误。ecx
不是-1
的特殊情况。
rep
:代码大小"正常"将寄存器设置为anything other than zero的方法是使用5字节mov r32, imm32
insn,例如or
。
如果你更关心代码大小而不是速度,或者你知道B9 FF FF FF FF mov ecx,-1
对ecx
的错误依赖不是问题,你可以通过使用符号扩展的8位立即节省两个字节:or r/m32, imm8
。
83 C9 FF or ecx, 0FFFFFFFFh
结果中的所有位实际上都不依赖于ecx的旧值,因为。但是,真正的CPU并不是特殊情况,因此在ecx
准备就绪之前无法执行无序执行。这是对ecx 的旧值的错误依赖。 mov
打破了对先前值的依赖。 (有关详情,请参阅x86代码wiki,尤其是Agner Fog's guides)。
or ecx, imm8
需要一个ModRM字节来将目标编码为ecx,这与mov
的形式不同,其中每个目标寄存器都有一个单独的操作码。遗憾的是,mov r/m32, imm8
没有操作码,可以在许多指令中保存2个字节的代码。
如果英特尔愿意放弃backwards compatibility with undocumented instructions,他们可以添加它。 (8086没有它,因为它只会帮助16位代码将立即移动到内存。他们已经将8个操作码专用于mov r16, imm16
,这是16位模式下的3个字节,它不会#39; t需要一个操作数大小的前缀,就像不存在的mov r/m16, imm8
一样。)
所以在优化代码大小时,这是一个有用的习惯用法,例如用于引导加载程序,或https://codegolf.stackexchange.com/上的机器代码答案。 (是的,这是一件事。)
另一个相关技巧是使用3字节lea
创建一个常量,如果你已经在另一个寄存器中有另一个常量。,例如对于x86-64 Adler32, I needed two zeroed registers and a 1
,所以我使用了
401120: 31 c0 xor eax,eax
401122: 99 cdq # zero rdx by sign-extending eax (0) into edx
401123: 8d 7a 01 lea edi,[rdx+0x1] # edi=0+1, using a reg + disp8 addressing mode
答案 1 :(得分:2)
F
是二进制的1111
,因此0ffffffffh
是一个DWORD,所有位都设置为1
。
根据{{1}}的真值表,如果您OR
OR
的任何内容,您仍会得到1
。
因此,无论先前持有的1
是什么,在此特定ECX
操作之后,其所有位都将设置为OR
,即1
。这一点在Jose Manuel Abarca Rodriguez在评论中提供的第一个链接中进行了解释。
英特尔架构(以及大多数硬件架构)使用所谓的“二进制补码表示”来建立寄存器或内存中数字和位模式之间的映射。在二进制补码表示中,0ffffffffh
表示为-1
。这一点在Jose Manuel Abarca Rodriguez在评论中提供的第二个链接中进行了解释。