我正试图反向工程一个二进制和以下指令是困惑我,谁能澄清这到底是做什么?
=>0x804854e: repnz scas al,BYTE PTR es:[edi]
0x8048550: not ecx
其中:
EAX: 0x0
ECX: 0xffffffff
EDI: 0xbffff3dc ("aaaaaa\n")
ZF: 1
我看到它在每次迭代时以某种方式将ECX减1,而EDI则沿着字符串的长度递增。我知道它计算字符串的长度,但至于它是如何发生的,以及为什么会涉及到“al”,我不太清楚。
3条答案
按热度按时间fjaof16o1#
我将试着通过将代码反转回C来解释它。
英特尔的指令集参考(Software Developer's Manual的第2卷)对于此类逆向工程非常有用。
重复扫描
REPNE和SCASB组合的逻辑:
或者更简单地说:
字符串长度
但是,上面的内容还不足以解释它是如何计算字符串长度的。基于您的问题中出现的
not ecx
,我假设该代码片段属于使用REPNE SCASB
计算字符串长度的习惯用法(或类似用法):翻译成C语言并使用上一节中的逻辑,我们得到:
使用
al = 0
和DF = 0
进行简化:注意事项:
ecx
的位等效于-1 - ecx
。ecx
在循环中永远不能为零,因为字符串必须占用整个地址空间。因此,在上面的循环之后,
ecx
包含-1 - (length(edi) + 1)
,它与-(length(edi) + 2)
相同,我们将其翻转位以给予length(edi) + 1
,最后递减以给出length(edi)
。或者重新排列循环并简化:
以及反转计数:
也就是C:中的
strlen
函数tjvv9vkg2#
AL
,因为scas
会扫描内存以寻找AL
的值。AL
已被置零,因此指令会在字符串末尾找到终止零。scas
本身会递增(或减量,取决于方向标志)EDI
。REPNZ
前缀(在REPNE
形式中更容易阅读)只要比较结果为false,就重复scas
。(REPeat whileNotEqual)和ECX > 0
。它还在每次迭代中自动递减ECX
。ECX
已被初始化为最长的字符串,因此它不会提前终止循环。由于
ECX
从0xffffffff
(也称为-1)开始递减计数,因此得到的长度将是-1-ECX
,由于2的补码运算的特殊性,可以使用NOT
指令来计算该长度。1cosmwyk3#
它将
es:[edi]
处的字节与al
中的字节进行比较,并重复此步骤,直到ecx
变为零或es:[edi]
处的值与al
中的值匹配。每执行一步后,edi
递增,以便指向内存中的下一个字节。程序将not
应用于计数器(ecx)之后,根据以下指令。repnz
表示“重复直到未设置零标志 * 且 * cx不为零”。每次迭代都会递减ecx
。scas
或更准确地说,scasb
将al
中的值与内存操作数进行比较(根据地址大小,始终为es:[edi]
或es:[di]
),然后相应地设置标志(如果两个值相等,则将设置零标志),并基于方向标志递增(或递减)edi
。