C++反汇编学习笔记7——数组和指针以及他们的寻址

本文介绍了C++中数组和指针的使用及寻址方式,包括函数内数组的初始化、数组作为参数、局部数组作为返回值的细节。在函数调用时,数组的首地址会被传递,而不是整个数组。通过反汇编分析,揭示了数组在内存中的存储方式以及数组越界的风险。同时,文中探讨了多维数组和指针数组的差异,以及如何通过指针调用函数。
摘要由CSDN通过智能技术生成

两年前写的,欢迎大家吐槽! 

转载请注明出处。

1.      数组在函数内

先通过一个简单的例子来看一下数组和普通变量在初始化时的不同之处:

这是数组初始化:

    42:   int nArry[5] = {1, 2, 3, 4, 5};

0042B758  mov        dword ptr [ebp-1Ch],1 

0042B75F mov         dword ptr[ebp-18h],2 

0042B766  mov        dword ptr [ebp-14h],3 

0042B76D  mov         dword ptr [ebp-10h],4 

0042B774  mov        dword ptr [ebp-0Ch],5

下面是变量的初始化:

    43:  charcChar = 'A';

0042B77B  mov        byte ptr [ebp-25h],41h 

    44:  floatfFloat = 1.0f;

0042B77F fld1 

0042B781  fstp       dword ptr [ebp-34h] 

    45:  short sShort = 1;

0042B784  mov        eax,1 

0042B789  mov        word ptr [ebp-40h],ax 

    46:  intnInt = 2;

0042B78D  mov        dword ptr [ebp-4Ch],2 

    47:   double dDouble = 2.0f;

0042B794  fld        qword ptr [__real@4000000000000000 (47DC90h)] 

0042B79A fstp        qword ptr[ebp-5Ch] 

数组中每个元素的数据类型是相同的,并且地址是连续的,通过这一点可以很容易的区别出在函数中的数组,而全局数组则在后面会讲到。Release版本的没有多大变化,和局部变量一样优化,但是数组不会因为被赋值常量而进行常量传递,代码如下:

.text:0042B758                 mov     [ebp+nArray], 1

.text:0042B75F                mov     [ebp+nArray+4], 2

.text:0042B766                 mov    [ebp+nArray+8], 3

.text:0042B76D                 mov     [ebp+nArray+0Ch], 4

.text:0042B774                 mov     [ebp+nArray+10h], 5

现在再来看一下字符串,其实字符串就是字符数组,只是最后一个元素为‘\0’作为字符串结束标志而已。而字符串的初始化就是复制字节的过程,书上说VC++6.0编译器是通过寄存器来赋值的,因为每个寄存器拥有4字节,所以每次最多可以复制4字节的内容。但是我电脑上只有VS2010,它的编译器选择了直接将字符串常量写进文件然后将指针直接指向字符串常量装载入内存的地址。下面看这个例子使理解更加深刻一些:

    55:   char *szHello = "Hello world";

0042B74E  mov        dword ptr [szHello],offset string "Hello world" (47DD10h) 

通过监视可以看到变量所在地址:

szHello的地址中存储的就是"Hello world"所在文件中的地址,再根据这个地址可以找到字符串:

2.      数组作为参数

首先来看个例子(Debug版本):

这是main函数里面数组定义、初始化以及作为参数调用函数的代码

    57:  charszHello[20] = {0};

0042B758  mov        byte ptr [ebp-1Ch],0  ;这个字节是数组的首地址

0042B75C xor         eax,eax 

;下面则是数组的剩余19个元素的初始化,利用eax寄存器进行赋值

0042B75E  mov         dword ptr [ebp-1Bh],eax 

0042B761  mov        dword ptr [ebp-17h],eax 

0042B764  mov        dword ptr [ebp-13h],eax 

0042B767  mov        dword ptr [ebp-0Fh],eax 

;当不足4字节时编译器会做相应处理,这里的3字节被拆成2字节和1字节

0042B76A mov         word ptr [ebp-0Bh],ax  

0042B76E  mov        byte ptr [ebp-9],al 

    58:   Show(szHello);

0042B771  lea        eax,[ebp-1Ch] 

0042B774  push       eax  ;取出数组首地址并入栈作为参数传到函数里面

0042B775  call       Show (429FA5h) 

0042B77A add         esp,4 

下面是show函数的定义:

     9: void Show(char szBuff[])

    10: { ;先前代码略

    11:   strcpy(szBuff, "Hello World");

0042B66E  push       offset string "Hello World" (47DC6Ch)  ;获取字符串常量的首地址

0042B673  mov        eax,dword ptr [esp+8]  ;获取参数

0042B676  push       eax  ;将其作为字符串复制函数的参数入栈

0042B677  call       @ILT+1770(_strcpy) (4296EFh) 

0042B67C add         esp,8 

    12:   printf(szBuff);

;printf函数代码略

    13: };后续代码略

从以上的例子可以很清楚的看到,数组作为函数的参数时是将数组的首地址入栈作为参数,然后根据首地址找到数组的每一个元素。

字符串处理函数在Debug版本下非常容易识别,但是在Release版本中字符串处理函数内联到程序中,没有call指令对函数进行调用,因此识别这些函数有一定的困难,但是可以根据反汇编的代码确定其内联代码的功能,并非必须还原出原函数。下面就用一个例子来具体说明:

先是C源代码:

main()

{

    charszHello[20] = {0};

    Show(szHello);

}

void Show(charszBuff[])

{

    strcpy(szBuff, "HelloWorld");

    printf(szBuff);

}

首先对main函数中的代码用IDA P ro进行反汇编得到如下代码:

.text:00401030 aHello          = byte ptr -18h ;这是一个数组,可以看到大小为0x14

.text:00401030 var_4           = dword ptr -4

.text:00401030

.text:00401030                 push    ebp

.text:00401031                 mov     ebp, esp

.text:00401033                 sub     esp, 18h

.text:00401036                 mov     eax, dword_40B014

.text:0040103B                 xor     eax, ebp

.text:0040103D                 mov     [ebp+var_4], eax

.text:00401040                 xor     eax, eax

;和Debug版本一样,下面是对数组的初始化

.text:00401042                 mov     [ebp+aHello], al

.text:00401045                 mov     dword ptr [ebp+aHello+1], eax

.text:00401048                 mov     dword ptr [ebp+aHello+5], eax

.text:0040104B                 mov     dword ptr [ebp+aHello+9], eax

.text:0040104E                 mov    dword ptr [ebp+aHello+0Dh], eax

.text:00401051                 mov     word ptr [ebp+aHello+11h], ax

.text:00401055                 mov     [ebp+aHello+13h], al

;取出数组首地址作为函数参数入栈

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值