Akkuman 的博客

习技术,不积跬步无以至千里,不积小流无以成江海

逆向学习笔记(1)- 为什么代码不停地循环运行

对于下面这段 c 语言代码会一直不停地循环,为什么呢?

#include<stdio.h>

void HelloWorld()
{
    int i = 0;
    int a[] = {1,2,3,4,5,6,7,8,9,10};
    for(i=0; i<=10; i++)
    {
        a[i] = 0;
        printf("Hello World!\n");
    }
}

int main(int argc, char* argv[])
{
    HelloWorld();
    getchar();
    return 0;
}

问题

当你运行上面这串代码的时候,因为 c 语言并不会对数组越界进行检查,所以是不会报错可以直接运行的,那么是什么原因导致了下面这张图的结果呢?

GIF.gif

分析

我们可以调试跟进看看,在 HelloWorld 函数上加一个断点跟进去看看

snipaste_20170309_165830.png

这个函数主要的汇编代码如下

8:        int i = 0;
00401038   mov         dword ptr [ebp-4],0
9:        int a[] = {1,2,3,4,5,6,7,8,9,10};
0040103F   mov         dword ptr [ebp-2Ch],1
00401046   mov         dword ptr [ebp-28h],2
0040104D   mov         dword ptr [ebp-24h],3
00401054   mov         dword ptr [ebp-20h],4
0040105B   mov         dword ptr [ebp-1Ch],5
00401062   mov         dword ptr [ebp-18h],6
00401069   mov         dword ptr [ebp-14h],7
00401070   mov         dword ptr [ebp-10h],8
00401077   mov         dword ptr [ebp-0Ch],9
0040107E   mov         dword ptr [ebp-8],0Ah
10:       for(i=0; i<=10; i++)
00401085   mov         dword ptr [ebp-4],0
0040108C   jmp         HelloWorld+77h (00401097)
0040108E   mov         eax,dword ptr [ebp-4]
00401091   add         eax,1
00401094   mov         dword ptr [ebp-4],eax
00401097   cmp         dword ptr [ebp-4],0Ah
0040109B   jg          HelloWorld+97h (004010b7)
11:       {
12:           a[i] = 0;
0040109D   mov         ecx,dword ptr [ebp-4]
004010A0   mov         dword ptr [ebp+ecx*4-2Ch],0
13:           printf("Hello World!\n");
004010A8   push        offset string "Hello World!\n" (0042301c)
004010AD   call        printf (004011a0)
004010B2   add         esp,4
14:       }
004010B5   jmp         HelloWorld+6Eh (0040108e)
15:   }

int i = 0;开始看直到for(i=0; i<=10; i++)的堆栈图是

snipaste_20170309_170508.png

第一次进入循环开始先把 0 放到了 [ebp-4],然后跳到了00401097 cmp dword ptr [ebp-4],0Ah以及下面的 jg,这里的意思是如果 ebp-4 中存放的值比 0A 大那么就执行jg HelloWorld+97h (004010b7)跳到 004010b7 函数结束
第一次进入循环时,cmp 之后(ebp-4 中存放的值比 0A 小)执行0040109D处的语句,此时ECX中的值变成了 [ebp-4] 中的值也就是 0,然后mov dword ptr [ebp+ecx*4-2Ch],0将 0 放到ebp+ecx*4-2Ch处也就是EBP-2C处,下面的两条语句不用管是执行输出的,然后到了add esp,4将栈顶的值加 4,这里我们无需关注栈顶,然后jmp HelloWorld+6Eh (0040108e)跳回到0040108e继续执行

snipaste_20170309_170508.png

跳到0040108E mov eax,dword ptr [ebp-4]开始执行,紧接着这三条语句的作用是把EBP-4中的值加了 1,也就是EBP-4中的值现在为 1

mov eax,dword ptr [ebp-4]
add eax,1
mov dword ptr [ebp-4],eax

cmp 比较之后再次执行循环体,循环体完成后再次跳到0040108e,此时EBP-28的值变为了 0,栈顶 esp 再次增加了 4(这个例子中栈顶是不用关注的)

snipaste_20170309_172648.png

紧接着下次执行后

snipaste_20170309_172805.png

直到这个数组长度为 10 的数组执行到第十次

snipaste_20170309_173122.png

此时再次跳转到0040108e,然后EBP-4中的值再次增加了 1,现在也就是EBP-4中的值变为了 0A,cmp 比较之后EBP-4中的值依旧不比 0A 大,接着���行mov ecx,dword ptr [ebp-4],此时 ECX 的值变成了 0A,接着执行mov dword ptr [ebp+ecx*4-2Ch],0也就是mov dword ptr [ebp-4],0

然后呢,你发现了什么???就是他喵的EBP-4中的值变成了 0

snipaste_20170309_173729.png

变成 0 代表着什么???EBP-4中的值是我们拿来干嘛的?是用来和 0A 进行 cmp 然后决定是否结束函数的,可是我们辛辛苦苦循环了 10 次,第 11 次全泡汤了,唯一的变化就是数组都成了 0,栈顶的值变化了不少,然后再次 cmp 的时候,0 和 0A 比,决定了你还是要循环,不管多少次,最后都会把你用来计数的地址EBP-4中的值清零

这也就是为什么上面这段 c 语言代码会一直不停地循环的原因

转载请注明出处

留下你的脚步