我想对一个二进制进行逆向工程,下面的指令让我很困惑,谁能说明一下这到底是干什么的?
=>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",我不太清楚。
我试着把代码反转回C语言来解释一下。
英特尔的指令集参考(第2卷) 软件开发人员手册)对于这种逆向工程是非常宝贵的。
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
在你的问题中,我假设这个片段属于这个成语(或类似的),用于使用 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++;
这就是... strlen
涉及到C的函数。
size_t strlen(const char *s) {
size_t c = 0;
while (*s++ != '\0') c++;
return c;
}
AL
涉及,因为 scas
扫描内存中的 AL
. AL
已被清零,因此该指令在字符串的末尾找到终止的零。scas
本身会递增(或递减,取决于方向标志)。EDI
自动。的 REPNZ
前缀(这在 REPNE
形式)重复了 scas
只要比较是假的 (REP趁 N鮸 Equal)和 ECX > 0
. 它还减少了 ECX
在每一次迭代中都会自动执行。ECX
已被初始化为尽可能长的字符串,这样它就不会提前结束循环。
由于 ECX
倒数 0xffffffff
(也就是-1),所得长度为 -1-ECX
由于2补码算术的特殊性,它可以用一个 NOT
指令。
它比较了字节在 es:[edi]
以至于 al
是,并重复此步骤,直到任何一个 ecx
变成零或在 es:[edi]
中的数值相匹配。al
. 在每一步之后。edi
是递增的,所以它指向内存中的下一个字节。not
到之后的计数器(ecx),基于以下指令。
repnz
意思是 "重复执行,直到不设置零标志 和 cx不为零"。每一次迭代都会递减 ecx
.scas
或者更准确地说 scasb
中的值进行比较。al
到内存操作数(总是 es:[edi]
或 es:[di]
根据地址大小),然后相应地设置标志(如果两个值相等,则设置零标志),并递增(或递减,根据方向标志)。edi
.