我将尝试通过将代码反转回 C 来解释它。
英特尔的指令集参考(Software Developer's Manual 第 2 卷)对于这种逆向工程来说非常宝贵。
REPNE SCASB
REPNE 和 SCASB 结合的逻辑:
while (ecx != 0) {
temp = al - *(BYTE *)edi;
SetStatusFlags(temp);
if (DF == 0) // DF = Direction Flag
edi = edi + 1;
else
edi = edi - 1;
ecx = ecx - 1;
if (ZF == 1) break;
}
或者更简单地说:
while (ecx != 0) {
ZF = (al == *(BYTE *)edi);
if (DF == 0)
edi++;
else
edi--;
ecx--;
if (ZF) break;
}
字符串长度
但是,以上内容不足以解释它如何计算字符串的长度。根据您的问题中 not ecx 的存在,我假设 sn-p 属于此成语(或类似),用于使用 REPNE SCASB 计算字符串长度:
sub ecx, ecx
sub al, al
not ecx
cld
repne scasb
not ecx
dec ecx
翻译成 C 语言并使用上一节中的逻辑,我们得到:
ecx = (unsigned)-1;
al = 0;
DF = 0;
while (ecx != 0) {
ZF = (al == *(BYTE *)edi);
if (DF == 0)
edi++;
else
edi--;
ecx--;
if (ZF) break;
}
ecx = ~ecx;
ecx--;
使用al = 0 和DF = 0 进行简化:
ecx = (unsigned)-1;
while (ecx != 0) {
ZF = (0 == *(BYTE *)edi);
edi++;
ecx--;
if (ZF) break;
}
ecx = ~ecx;
ecx--;
注意事项:
- 在二进制补码表示法中,翻转
ecx 的位相当于-1 - ecx。
- 在循环中,
ecx 在循环中断前递减,因此总共递减length(edi) + 1。
-
ecx 在循环中永远不能为零,因为字符串必须占据整个地址空间。
所以在上面的循环之后,ecx 包含-1 - (length(edi) + 1),它与-(length(edi) + 2) 相同,我们将其翻转为length(edi) + 1,最后递减为length(edi)。
或者重新排列循环并简化:
const char *s = edi;
size_t c = (size_t)-1; // c == -1
while (*s++ != '\0') c--; // c == -1 - length(s)
c = ~c; // c == length(s)
并反转计数:
size_t c = 0;
while (*s++ != '\0') c++;
这是 C 中的 strlen 函数:
size_t strlen(const char *s) {
size_t c = 0;
while (*s++ != '\0') c++;
return c;
}