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

use*_*356 1 c x86 assembly gcc strcmp

为了提高我的二进制开发技能,并加深我在低级环境中的理解,我尝试解决 中的挑战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;

}
Run Code Online (Sandbox Code Playgroud)

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?
Run Code Online (Sandbox Code Playgroud)

我不明白的事情是:

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

Ale*_*op. 5

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

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

repz cmps BYTE PTR ds:[esi],BYTE PTR es:[edi]
Run Code Online (Sandbox Code Playgroud)

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

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

所以根据你的问题:

strcmp 调用在哪里?为什么会这样?

没有显式调用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] ;
Run Code Online (Sandbox Code Playgroud)

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

这个 8048506: b9 0a 00 00 00 mov ecx,0xa 有什么作用?

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