strcmp 的编译器优化我不明白,针对常量字符串

Compiler optimization of strcmp I don't understand, against a constant string

为了提高我的二进制开发技能,加深我在低级环境中的理解,我尝试解决 pwnable.kr 中的挑战,第一个挑战 - 称为 fd 具有以下 C 代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char buf[32];
int main(int argc, char* argv[], char* envp[]){
        if(argc<2){
                printf("pass argv[1] a number\n");
                return 0;
        }
        int fd = atoi( argv[1] ) - 0x1234;
        int len = 0;
        len = read(fd, buf, 32);
        if(!strcmp("LETMEWIN\n", buf)){
                printf("good job :)\n");
                system("/bin/cat flag");
                exit(0);
        }
        printf("learn about Linux file IO\n");
        return 0;

}

我使用 objdump -S -g ./fd 来反汇编它,但我很困惑,因为我没有调用 strcmp 函数。它只是比较字符串而不调用它。 这是我所说的汇编代码:

 80484c6:   e8 05 ff ff ff          call   80483d0 <atoi@plt> 
 80484cb:   2d 34 12 00 00          sub    eax,0x1234 
 ; eax = atoi( argv[1] ) - 0x1234;
 ; initialize fd=eax
 80484d0:   89 44 24 18             mov    DWORD PTR [esp+0x18],eax
 ; initialize len
 80484d4:   c7 44 24 1c 00 00 00    mov    DWORD PTR [esp+0x1c],0x0

 ; Set up read variables
 80484db:   00 
 80484dc:   c7 44 24 08 20 00 00    mov    DWORD PTR [esp+0x8],0x20 ; read 32 bytes
 80484e3:   00 
 80484e4:   c7 44 24 04 60 a0 04    mov    DWORD PTR [esp+0x4],0x804a060 ; buf variable address
 80484eb:   08 
 80484ec:   8b 44 24 18             mov    eax,DWORD PTR [esp+0x18]
 80484f0:   89 04 24                mov    DWORD PTR [esp],eax ; fd variable
 80484f3:   e8 78 fe ff ff          call   8048370 <read@plt> 

 80484f8:   89 44 24 1c             mov    DWORD PTR [esp+0x1c],eax
 80484fc:   ba 46 86 04 08          mov    edx,0x8048646 ; "LETMEWIN\n" address
 8048501:   b8 60 a0 04 08          mov    eax,0x804a060 ; buf address
 8048506:   b9 0a 00 00 00          mov    ecx,0xa ; what is this?
 ; strcmp starts here?
 804850b:   89 d6                   mov    esi,edx
 804850d:   89 c7                   mov    edi,eax
 804850f:   f3 a6                   repz cmps BYTE PTR ds:[esi],BYTE PTR es:[edi] ; <------- ?STRCMP?

我不明白的是:

  1. strcmp 调用在哪里?为什么会这样?
  2. 这个8048506: b9 0a 00 00 00 mov ecx,0xa有什么作用?

编译器使用实现 memcmp 的 repe cmpsb 针对已知长度的字符串内联 strcmp

它将常量文字字符串"LETMEWIN\n"的地址加载到寄存器esi。请注意,此字符串的长度为 10(末尾为“\0”)。 然后它将buf的地址加载到edi寄存器中,然后调用x86指令:

repz cmps BYTE PTR ds:[esi],BYTE PTR es:[edi]

repz 重复以下指令,只要设置了零标志并达到存储在 ecx 中的次数(这向您解释了 mov ecx,0xa ; what is this?)。

重复指令是cmps,它比较字符串(逐字节)并在每次迭代时自动将指针加1。 当比较的字节相等时,它设置零标志。

所以根据你的问题:

Where is the strcmp call? And why is it like that?

没有显式调用 strcmp,它被优化并替换为内联代码:

 80484fc:   ba 46 86 04 08          mov    edx,0x8048646 ; "LETMEWIN\n" address
 8048501:   b8 60 a0 04 08          mov    eax,0x804a060 ; buf address
 8048506:   b9 0a 00 00 00          mov    ecx,0xa ; number of bytes to compare
 804850b:   89 d6                   mov    esi,edx
 804850d:   89 c7                   mov    edi,eax
 804850f:   f3 a6                   repz cmps BYTE PTR ds:[esi],BYTE PTR es:[edi] ;

实际上它错过了应该检查 strcmp 的返回值是否为零的部分。我想你只是没有在这里复制它。在 repz ... 行之后可能应该有类似 je ... / jz ... / jne ... / jnz ... 的内容。

What does this 8048506: b9 0a 00 00 00 mov ecx,0xa do?

它设置要比较的最大字节数。