《coredump问题原理探究》Linux x86版4.4节函数的逆向之循环结构

在x86里,循环指令有:

LOOP:ECX不为零时循环
LOOPE/LOOPZ:ECX不为零且标志Z=1时循环
LOOPNE/LOOPNZ:ECX不为零且标志Z=0时循环

但实质上,由于上面指令适用范围太窄,只能用于循环体非常简单的。所以往往是用条件跳转指令来实际循环。用例子来体现一下:

 #include <stdio.h>

 int loop_for( int n )
 {
     int sum = 0;

     for ( int i = 0; i < n; i++ )
     {
         sum += i;
     }
 
     return sum;
 }
 
 int loop_while( int n )
 {
     int sum = 0;
     int i = 0;
 
     while ( i < n )
     {
         sum += i;
         i++;
     }
 
     return sum;
 }
 
 int loop_do( int n )
 {
     int sum = 0;
     int i = 0;
	 
     do
     {
         sum += i;
         i++;
     } while ( i < n );
 
     return sum;
 }
 
 int main()
 {
     int n = 0;

     scanf( "%d", &n );

     return loop_for( n ) + loop_while( n )
         + loop_do( n );
 }

看一下loop_do,loop_for, loop_while的汇编:

(gdb) disassemble loop_do
Dump of assembler code for function _Z7loop_doi:
   0x080485d4 <+0>:     push   %ebp
   0x080485d5 <+1>:     mov    %esp,%ebp
   0x080485d7 <+3>:     sub    $0x10,%esp
   0x080485da <+6>:     movl   $0x0,-0x4(%ebp)
   0x080485e1 <+13>:    movl   $0x0,-0x8(%ebp)
   0x080485e8 <+20>:    mov    -0x8(%ebp),%eax
   0x080485eb <+23>:    add    %eax,-0x4(%ebp)
   0x080485ee <+26>:    addl   $0x1,-0x8(%ebp)
   0x080485f2 <+30>:    mov    -0x8(%ebp),%eax
   0x080485f5 <+33>:    cmp    0x8(%ebp),%eax
   0x080485f8 <+36>:    setl   %al
   0x080485fb <+39>:    test   %al,%al

   0x080485fd <+41>:    jne    0x80485e8 <_Z7loop_doi+20>

   0x080485ff <+43>:    mov    -0x4(%ebp),%eax
   0x08048602 <+46>:    leave  
   0x08048603 <+47>:    ret    
End of assembler dump.

(gdb) disassemble loop_for
Dump of assembler code for function _Z8loop_fori:
   0x08048570 <+0>:     push   %ebp
   0x08048571 <+1>:     mov    %esp,%ebp
   0x08048573 <+3>:     sub    $0x10,%esp
   0x08048576 <+6>:     movl   $0x0,-0x4(%ebp)
   0x0804857d <+13>:    movl   $0x0,-0x8(%ebp)

   0x08048584 <+20>:    jmp    0x8048590 <_Z8loop_fori+32>

   0x08048586 <+22>:    mov    -0x8(%ebp),%eax
   0x08048589 <+25>:    add    %eax,-0x4(%ebp)
   0x0804858c <+28>:    addl   $0x1,-0x8(%ebp)
   0x08048590 <+32>:    mov    -0x8(%ebp),%eax
   0x08048593 <+35>:    cmp    0x8(%ebp),%eax
   0x08048596 <+38>:    setl   %al
   0x08048599 <+41>:    test   %al,%al

   0x0804859b <+43>:    jne    0x8048586 <_Z8loop_fori+22>

   0x0804859d <+45>:    mov    -0x4(%ebp),%eax
   0x080485a0 <+48>:    leave  
   0x080485a1 <+49>:    ret    
End of assembler dump.

(gdb) disassemble loop_while
Dump of assembler code for function _Z10loop_whilei:
   0x080485a2 <+0>:     push   %ebp
   0x080485a3 <+1>:     mov    %esp,%ebp
   0x080485a5 <+3>:     sub    $0x10,%esp
   0x080485a8 <+6>:     movl   $0x0,-0x4(%ebp)
   0x080485af <+13>:    movl   $0x0,-0x8(%ebp)

   0x080485b6 <+20>:    jmp    0x80485c2 <_Z10loop_whilei+32>

   0x080485b8 <+22>:    mov    -0x8(%ebp),%eax
   0x080485bb <+25>:    add    %eax,-0x4(%ebp)
   0x080485be <+28>:    addl   $0x1,-0x8(%ebp)
   0x080485c2 <+32>:    mov    -0x8(%ebp),%eax
   0x080485c5 <+35>:    cmp    0x8(%ebp),%eax
   0x080485c8 <+38>:    setl   %al
   0x080485cb <+41>:    test   %al,%al

   0x080485cd <+43>:    jne    0x80485b8 <_Z10loop_whilei+22>

   0x080485cf <+45>:    mov    -0x4(%ebp),%eax
   0x080485d2 <+48>:    leave  
   0x080485d3 <+49>:    ret    
End of assembler dump.

由于loop_for,loop_while的代码逻辑一样,连生成的汇编都是一样。loop_do和两者不大一样,所以,汇编不一样。现在只以loo_do进行分析:

(gdb) disassemble loop_do
Dump of assembler code for function _Z7loop_doi:
   0x080485d4 <+0>:     push   %ebp
   0x080485d5 <+1>:     mov    %esp,%ebp
   0x080485d7 <+3>:     sub    $0x10,%esp
   0x080485da <+6>:     movl   $0x0,-0x4(%ebp)
   0x080485e1 <+13>:    movl   $0x0,-0x8(%ebp)
   0x080485e8 <+20>:    mov    -0x8(%ebp),%eax
   0x080485eb <+23>:    add    %eax,-0x4(%ebp)
   0x080485ee <+26>:    addl   $0x1,-0x8(%ebp)
   0x080485f2 <+30>:    mov    -0x8(%ebp),%eax
   0x080485f5 <+33>:    cmp    0x8(%ebp),%eax
   0x080485f8 <+36>:    setl   %al
   0x080485fb <+39>:    test   %al,%al

   0x080485fd <+41>:    jne    0x80485e8 <_Z7loop_doi+20>

   0x080485ff <+43>:    mov    -0x4(%ebp),%eax
   0x08048602 <+46>:    leave  
   0x08048603 <+47>:    ret    
End of assembler dump.

由指令地址0x080485fd这一条指令可知,执行流程会回到0x080485e8,这个地址是比跳转指令地址更小,直到满足eax的值和ebp+8的内容相等才会停止。也就是说,从0x080485e8到0x080485fd就构成了一个循环。由于

   0x080485f5 <+33>:    cmp    0x8(%ebp),%eax

是用来设置标志位的,所以应该是对应于i < n子句

也就是说,

   0x080485e8 <+20>:    mov    -0x8(%ebp),%eax
   0x080485eb <+23>:    add    %eax,-0x4(%ebp)
   0x080485ee <+26>:    addl   $0x1,-0x8(%ebp)

对应于

36	         sum += i;
37	         i++;

如果同样分析loop_for,loop_while也会发现,跳转指令是跳到比当前指令地址更小的地址来执行。

 

从这里可以看出,如果在分析函数的汇编时,遇到跳转指令,如果它并不是跳到比当前指令地址更大的地址执行,那么它有可能是一个循环,否则就是一个普通的条件跳转结构






你可能感兴趣的:(C++,C++,linux,linux,linux,X86,X86,调试技术,程序崩溃)