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

时间:2019-12-18 11:50:27

标签: c gcc assembly x86 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;

}

我使用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的作用是什么?

1 个答案:

答案 0 :(得分:5)

编译器使用实现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。 当比较的字节相等时,它将设置零标志。

因此,根据您的问题:

  

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] ;

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

  

这8048506:b9 0a 00 00 00 mov ecx,0xa是做什么的?

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