逆向知识十三讲,汇编中数组的表现形式,以及还原数组

Posted iBinary

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了逆向知识十三讲,汇编中数组的表现形式,以及还原数组相关的知识,希望对你有一定的参考价值。

            逆向知识十三讲,汇编中数组的表现形式,以及还原数组

讲解数组之前,要了解数组的特性

 

1.数据具有连续性

2.数据类型相同

比如:

  int Ary[3] = {0,1,2};

我们可以看出,上面定义的数组,数据是连续的,其中每个数据类型大小都是int类型(类型也是一样的)

汇编中识别数组:

    1.地址连续

    2.带有比例因子寻址   (lea  reg32,[xxx  + 4 *xxxx])

一丶一维数组在汇编中的表现形式

首先说下数组寻址公式,便于下面讲解

公式: 数组首地址 + sizeof(type) * n

伪代码:

  int Ary[3] = {1,2,3};

   Ary[N] = 1;

sizeof(type) : 这个是求数组元素的类型的,比如上面是int类型数组,我们求数组元素的类型  sizeof(Ary[0]);

n:      n的取值是下标运算,比如我们要求第数组中的第一项,(元素为2,从零开始),

代入公式:   Ary + sizeof(Ary[0]) * 1

        =  Ary +4 * 1

         = Ary + 4  取内容则是元素2了.

看例子:

  高级代码:

  

int main(int argc, char* argv[])
{
    int Ary[3] = {0,1,2};
    int i = 0;

    scanf("%d",&i);

    Ary[i] = 3;  //这句话会产生数组寻址公式
    return Ary[i];
}

Debug下的汇编代码:

之截图重要代码

红色区域还有下面的add esp,8属于scanf上面的代码,给数组初始化等等,重要代码属于粉红框内的

1.  局部变量赋值给ecx

2.[ebp + ecx * 4 + var_c],写入了3,其中 ebp + var_c 是数组首地址, 4是sizeof(type), ecx则是n值.

由此代入我们的数组寻址公式  

Ary + sizeof(type) * n 

=  [ebp + Var_c + 4 * ecx]

只不过比例因子寻址会变化,转为公式是一样的,其中sizeof()求出的值变为了常量.

如果喜欢汇编的这种表达形式,可以把数组公式变换一下,

变为:

Ary + (n * sizeof(type))汇编是这种的,其实是一样的.

 

Release下的汇编

Release下也是一样的,可能和Debug汇编不一样,但是其本质也就是数组寻址公式一样的.

Ary + sizeof(type) * n

Ary+ (n*sizeof(type))

在这里可能大家会有疑问,为什么esp + var_c是数组首地址,而不用+18h?

因为在vc6.0下,是esp寻址,而这个18h只是做调整,IDA中显示成这样是想告诉我们,我要用到Var_C,但是因为我是esp寻址,所以我要调整一下才能找到var_c

而在高版本下,则会直接ebp寻址.不重要,知道就好.

 

二丶二维数组在汇编下的表现形式

数组寻址公式是一样的,但不同,

1.sizeof(type)变了. type的取值变为的自己的低维

2.不光求高维,低维也要求

现在的数组寻址公式变为了:

int ary[M][C];

  数组首地址 +sizeof(type[C]) * i + sizeof(type) * j;  i和j是下标运算的值,  比如 ary[3][4] = 1, 3是i,j是4,  不要和MC搞混,MC是数组定义的时候的值.

其中的sizeof(type[C])变为了二维数组的低维了.

如有一个数组为:

  int Ary[2][3] = {{1,2,3},{4,5,6}};  

我要求4所在的位置,

  我们打印的时候要输入 ary[1][0] 可以打印出4

那么我们可以通过手来计算出其位置

得出:

  Ary + sizeof(type[C]) * i + sizeof(type) * j

简化公式:

  Ary + C * sizeof(Type)*i + sizeof(type) * j     在Debug下会到这一步

简化公式:

  ary + sizeof(type) * (i * C + j);      在Release下会优化为这一步,因为发现了公因子 sizeof(type)了,可以提出来

代入公式得到:

  ary + 4 * 1 * 3 + 0

=  ary + 12

也就是说数组首地址 + 12 就得出4所在的地址位置.

+12在高级语言中,因为要%4对齐,所以我们要/4

所以得出 12 / 4 = 3,那么如果是指针指向数组的首地址,那么只需要+3即可取得数组的元素4,这也是一维数组访问二维数组元素的公式.

代码:

  

总结一下:

  首先要知道数组的寻址公式,因为维数组多了一维,所以要求出高维还要求出低纬度.而其中的type取值是取自己的低维

   公式;  数组首地址 + sizeof(type[C]) * i + sizeof(type)*j  重要,必须了解

举例子了解Debug下的汇编和Release下的数组寻址公式的区别

高级代码:

  

int main(int argc, char* argv[])
{
    int Ary[2][3] = {{1,2,3},{4,5,6}};
    int i = 0;
    int j = 0;
    scanf("%d%d",&i,&j);

    Ary[i][j] = 9;  //会产生数组寻址公式

    return 0;
}

Debug下的汇编

通过我们的数组寻址公式得出

  1.edx 是获取i的值

  2. edx * C  相当于我们的数组中的寻址公式 sizeof(type[C]) *i 的值.

  3.lea的时候 求出数组首地址 + sizeof(type[c])的值.

  4.ecx得出j的值

  5.eax + 4 * ecx  相当于数组首地址 + sizeof(type)*j

致此熟悉数组寻址公式看汇编代码很简单了.

所以Debug下的数组公式会变成

  数组首地址 + sizeof(type[C]) * i + sizeof(type] * j

Release下的汇编

上面说过,在Release下会优化我们的原始的公式为

数组首地址 + sizeof(type) * (C * i + j)的形式

我们代入到汇编中查看.

1.eax 得出i的值

2.edx得出数组首地址的值

3.ecx的出   数组首地址 + i * 2 的值

4.add eax,ecx 重新写会eax,eax = 数组首地址 + i * 2 + i  那么可以简化为数组首地址 + i * 3即可.

5.运用数组寻址公式 [esp + var18 + 4 * eax]  esp + var18得出数组首地址  + i *3 + 4 

因为我们的j的取值是0,所以在Release下不是我们想象的 数组首地址 + i * 3 *4 + 0,+0优化掉了.

 

三丶三维数组在汇编中的表现形式

其实二维数组就介绍了高维数组怎么求了,以不变应万变.

有一个三维数组

  int Ary[M][C][H]

下标操作:

  ary[i][j][k] = 1;

数组寻址公式为:

  Ary + sizeof(type[C][H]) * i + sizeof(type[H])*j + sizeof(type)*k  在Debug下原模原样

在Release下会优化公式为:

  Ary + sizeof(type)*c*h*i + sizeof(type)*h*j + sizeof(type)*k

发现公因数继续优化

  Ary + sizeof(type) * (c*h*i + h*j + k)

发现了两个h

继续简化

  Ary + sizeof(type) * (h*(c*i + j) + k);

所以上面就是最终公式

高级代码:

  

int main(int argc, char* argv[])
{
    int Ary[2][3][4] = {NULL};
    int i = 0;
    int j = 0;
    int k = 0;
    scanf("%d%d%d",&i,&j,&k);

    Ary[i][j][k] = 9;  //会产生数组寻址公式

    return 0;
}

Debug下的反汇编代码:

  

公式先贴出来:

  Ary + sizeof(type[C][H]) * i + sizeof(type[H])*j + sizeof(type)*k

 代入公式看汇编

  1.eax = i的值

  2. eax * 30 ,相当于求 sizeof(type[C][H]) * i

  3.求出数组首地址+eax,也就是求出了 Ary[M]的位置,给Ecx赋值

  4.求出j的值

  5.左移4位,相当于2^4次方也就是16 这一步相当于求 sizeof(type[H])的值

  6.ary[M] + sizeof(type[H])的值得出了 ary[M][C]的值

  7.求出k的值

  8.数组寻址公式 ary[M][C]的值 + 4 * k的值.

在Debug下代入公式即可.

 

Release下的汇编

上面说了,Release下汇编会优化,也就是我们的公式会优化.

优化为:

  Ary + sizeof(type) * (h*(c*i + j) + k);

根据公式代入即可.

 

四维数组,高维数组,数组公式同上,只不过注意两点

1.sizeof(type) type类型比如是自己的低维

2.要加一条新的公式

比如

  int ary[A][B][C][D];

下标分别为

  i j k l

数组公式为:

  Ary + sizeof(type[B][C][D]) * i + sizeof(type[C][D]) * j + sizeof(tyep[D]) * j + sizeof(type)*k

自己优化一下即可

总结:

  数组寻址公式要熟悉最简单的数组寻址公式,因为更高纬度也是从上面公式,只不过type变化了,

会了数组寻址公式,可以说你用指针指向任何一个高维数组的值,取值使用即可.因为在高维在内存中也是线性存储,也就是一维数组的表现形式.

以上是关于逆向知识十三讲,汇编中数组的表现形式,以及还原数组的主要内容,如果未能解决你的问题,请参考以下文章

逆向知识第十讲,循环在汇编中的表现形式,以及代码还原

逆向课程第三讲逆向中的优化方式,以及加减乘

汇编代码还原第一讲,基本类型以及浮点编码.

C++反汇编第三讲,反汇编中识别继承关系,父类,子类,成员对象

学习逆向知识之用于游戏外挂的实现.第三讲,通过游戏外挂.分析红色警戒金钱基址.以及确定基址小技巧.

PC逆向之代码还原技术,第六讲汇编中除法代码还原以及原理第二讲,被除数是正数 除数非2的幂