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

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

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

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变化了,

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

时间: 2024-11-09 04:48:51

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

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

一丶do While在汇编中的表现形式 1.1高级代码: #include "stdafx.h" int main(int argc, char* argv[]) { int nSum = 0; int i = 0; do { nSum = nSum + i; } while (i <=100); return 0; } 高级代码很简单,只是一个简单的求1~100的累加 1.2 Debug版本下的汇编表现形式 代码定式很简单 ADDR .....do While逻辑代码块 xxx

逆向知识第九讲,switch case语句在汇编中表达的方式

一丶Switch Case语句在汇编中的第一种表达方式 (引导性跳转表) 第一种表达方式生成条件: case 个数偏少,那么汇编中将会生成引导性的跳转表,会做出 if else的情况(类似,但还是能分辨出来的) 1.高级代码: #include "stdafx.h" int main(int argc, char* argv[]) { switch(argc) { case 0: printf("case 0\n"); break; case 1: printf(&

逆向知识第八讲,if语句在汇编中表达的方式

一丶if else的最简单情况还原(无分支情况) 高级代码: #include "stdafx.h" int main(int argc, char* argv[]) { unsigned int nNumber = 0; scanf("%ud",&nNumber); if(argc == 0) { nNumber = 0; //第一种情况下无分支 } else { nNumber = -1; } return nNumber; } 总共两种情况,我们看下R

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

逆向课程第三讲逆向中的优化方式,以及加减乘 一丶为什么要熟悉优化方式 熟悉优化方式,可以在看高级代码的时候浮现出汇编代码,以及做逆向对抗的时候,了解汇编代码混淆 优化和混淆是相反的 优化: 指的是汇编代码越少越好,让程序更快的执行 混淆: 一条汇编代码变为多条汇编代码,影响逆向人员的破解能力,但是软件的效率大大降低 二丶加减乘的常见的几种优化方式 优化方式分为: 1.常量折叠 2.常量传播 3.变量去除 这些优化方式成为窥孔优化 (有10几种后面会一一到来) 首先了解什么是常量折叠,常量传播,然

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

分析红色警戒金钱基址.以及确定基址小技巧. 一丶基址简介 通过第二讲.我们寻找植物大战僵尸无限阳光.学习到了相关的逆向知识.以及认识了基址.动态地址. 静态地址的区别.现在我们拿红色警戒这款单击游戏进行练手. PS: 主要目的是分析数据.学习逆向知识.并为之所用.并不是教大家如何制作外挂. 1.怎么确定我们寻找到的是基址? 有三种方法. CE中绿色表示基址,黑色表示内存地址.一般绿色的很有可能是基址(当然不一定是.不过大部分是) 退出游戏重新进入游戏.地址不会改变.其值的内容也不会改变. 搜索指

【汇编杂项】关于_高级语言中 数组越界与汇编中 栈溢出的_联系的思考

数组越界 数组越界,是刚开始学习编程时,就不断被别人提醒的一个点,“相当可怕”.获取不合理数值,造成程序异常or操作计算机重要内存,造成威胁...原因是什么呢?数组在汇编中以栈机制实现,其中内存分配的机制与数组越界的风险有很大关系.今天做个小实验,来简单探讨下这个. 代码 先展示问题代码 1 #include<stdio.h> 2 int main(){ 3 int a[3]={0,1,2}; 4 for(int i=0;i<=3;i++){ 5 a[i]=0; 6 printf(&qu

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

目录 一丶简介 二丶代码还原讲解 1.被除数无符号 除数非2的幂 2.被除数无符号 除数为特例7 三丶代码还原总结 一丶简介 上一篇博客说的除2的幂. 如果被除数是有符号的,那么会进行调整,并使用位操作进行优化 本片博客专门讲解除数不是2的幂 二丶代码还原讲解 1.被除数无符号 除数非2的幂 高级代码: Release汇编 .text:0040101A mov eax, 38E38E39h .text:00401023 mul [esp+10h+var_8] .text:00401027 shr

七十、shell中的函数、shell中的数组、告警系统需求分析

一.shell中的函数 函数就是把一段代码整理到了一个小单元中,并给这个小单元起一个名字,当用到这段代码时直接调用这个小单元的名字即可.函数就是一个子shell,一个代码段. shell中的函数必须要定义在脚本的最上面. 格式: function f_name() { command }函数必须要放在最前面 f_name:函数名,最好不要和shell中的一些关键词冲突,比如for. command:具体命令 function这个单词可以省略掉的. 示例1 #!//bin/bash functio

逆向知识第六讲,取摸优化的几种方式

逆向知识第六讲,取摸优化的几种方式 除法讲完之后,直接开始讲 % 运算符在汇编中表现形式 首先C的高级代码贴上来. 高级代码: // Tedy.cpp : Defines the entry point for the console application. // #include "stdafx.h" int main(int argc, char* argv[]) { unsigned Number; scanf("%d",&Number); //防止