逆向_10变量循环数组结构体逆向分析

  1. 变量的声明
  2. 类型转换
  3. for循环
  4. while循环
  5. 返回值如何返回
  6. 参数传递的本质
  7. 局部变量的内存分配
  8. 赋值的本质
  9. 数组的本质
  10. 多维数组
  11. 结构体
  12. switch语句

变量的声明

  • 本质就是声明我要用一块内存
  • 全局变量
    • 程序执行的时候内存空间已经分配好了,直到程序运行结束
  • 局部变量
    • 只有程序执行到相应的位置才会给它分配内存空间

类型转换

  • MOVSX 先符号拓展,再传送

    MOV AL,0FF
    MOVSX CX,AL
    MOV AL,80
    MOVSX CX,AL
  • MOVZX 先零拓展,再传送

    MOV AL,0FF
    MOVZX CX,AL
    MOV AL,80
    MOVZX CX,AL
  • 无符号数和有符号数相加,转为无符号数

for循环

for(表达式1;表达式2;表达式3){
    //代码执行4
}
  • 以上for循环的执行流程为:

    第一次:1,2,4,3
    第二次:2,4,3
    第三次:2,4,3
    第N次:2,结束for循环
  • C代码

    for(int i = 0;i<=10;i++){
        printf("%d",i);
    }
  • 汇编代码

    for(int i = 0;i<=10;i++){
    00D11A4E  mov         dword ptr [ebp-8],0  
    00D11A55  jmp         00D11A60  
    00D11A57  mov         eax,dword ptr [ebp-8]  
    00D11A5A  add         eax,1  
    00D11A5D  mov         dword ptr [ebp-8],eax  
    00D11A60  cmp         dword ptr [ebp-8],0Ah  
    00D11A64  jg          00D11A83  
            printf("%d",i);
    00D11A66  mov         esi,esp  
    00D11A68  mov         eax,dword ptr [ebp-8]  
    00D11A6B  push        eax  
    00D11A6C  push        0D1573Ch  
    00D11A71  call        dword ptr ds:[00D182B4h]  
    00D11A77  add         esp,8  
    00D11A7A  cmp         esi,esp  
    00D11A7C  call        00D11131  
        }
    00D11A81  jmp         00D11A57  

while循环

  • while循环的效率比for要高

  • do-while循环

    • C代码

      int i = 0;
      do 
      {
          printf("%d",i);
          i++;
      } while (i<=10);
    • 汇编代码

          int i = 0;
      004C1A4E  mov         dword ptr [ebp-8],0
          do 
          {
              printf("%d",i);
      004C1A55  mov         esi,esp  
      004C1A57  mov         eax,dword ptr [ebp-8]  
      004C1A5A  push        eax  
      004C1A5B  push        4C573Ch  
      004C1A60  call        dword ptr ds:[004C82B4h]  
      004C1A66  add         esp,8  
      004C1A69  cmp         esi,esp  
      004C1A6B  call        004C1131  
              i++;
      004C1A70  mov         eax,dword ptr [ebp-8]  
      004C1A73  add         eax,1  
      004C1A76  mov         dword ptr [ebp-8],eax  
          } while (i<=10);
      004C1A79  cmp         dword ptr [ebp-8],0Ah  
      004C1A7D  jle         004C1A55 
  • while-do循环

    • C代码

      int i = 0;
      while (i<=10)
      {
          printf("%d",i);
          i++;
      }
    • 汇编代码

          int i = 0;
      00D71A4E  mov         dword ptr [ebp-8],0  
          while (i<=10)
      00D71A55  cmp         dword ptr [ebp-8],0Ah  
      00D71A59  jg          00D71A81  
          {
              printf("%d",i);
      00D71A5B  mov         esi,esp  
      00D71A5D  mov         eax,dword ptr [ebp-8]  
      00D71A60  push        eax  
      00D71A61  push        0D7573Ch  
      00D71A66  call        dword ptr ds:[00D782B4h]  
      00D71A6C  add         esp,8  
      00D71A6F  cmp         esi,esp  
      00D71A71  call        00D71131  
              i++;
      00D71A76  mov         eax,dword ptr [ebp-8]  
      00D71A79  add         eax,1  
      00D71A7C  mov         dword ptr [ebp-8],eax  
          }
      00D71A7F  jmp         00D71A55

返回值如何返回

  • 8位(char类型):存到寄存器al中
  • 16位(short类型): 存到寄存器ax中
  • 32位(int类型): 存到寄存器eax中
  • 64位(long long类型):64位的话存到EAX,EDX寄存器中,EAX存低位,EDX存高位

参数传递的本质

  • 寄存器可以传值,堆栈也可传值
  • 无论参数宽度是多少,在堆栈中所占宽度都是4个字节
  • 宽度再次说明
    1. 本机尺寸:如果本机是32位的,那么对32位的数据支持最好,如果是64位的,那么对64位的支持
    2. 编译器遵守了这个规则:
      • char类型或者short类型的参数不但没有节省空间,反而浪费了多余的操作
    3. 结论:整数类型的参数,一律使用int类型
  • 参数传递的本质:将上层函数的变量,或者表达式的值“复制一份”,传递给下层函数

局部变量的内存分配

  • 小于32位的局部变量,空间在分配时,按32位分配.
  • 使用时按实际的宽度使用.
  • 不要定义char/short类型的局部变量.
  • 参数与局部变量没有本质区别,都是局部变量,都在栈中分配.一个在函数调用前分配空间,一个是在函数调用时分配内存
  • 完全可以把参数当初局部变量使用

赋值的本质

  • 将某个值存储到某个变量中就是赋值

数组的本质

  1. 一组相同类型的变量,为了方便读写,采用另外一种表示形式.

  2. 数组在声明的时候,必须用常量来指明长度,不能使用变量.

  3. 数组在使用时,可以通过变量来定位数据.

  4. 数组定位时,可以超过数组的长度,编译不会有错,但读取的数据是错的.

  5. 数组内存空间的使用

    • 根据所定义的类型进行空间分配
    • 如果最后一个没有占满4个字节,则按4个字节对齐
  6. 数组寻址

    int a[10] = {0,1,2,3,4,5,6,7,8,9}
    a = [ebp-30h]
    a[3] = [ebp+3*4-30h]
    a[5] = [ebp+5*4-30h]
    以此类推
    
    short a[10] = {0,1,2,3,4,5,6,7,8,9}
    a = [ebp-30h]
    a[3] = [ebp+3*2-30h]
    a[5] = [ebp+5*2-30h]
    以此类推

多维数组

  • 以二维数组进行演示

    int arr[3][4]{
        {1,2,3,4},
        {4,6,7,8},
        {9,1,2,3}
    };
  • 分配的空间实质上就是和一维数组一样的

  • 使用多维数组的原因:使数据更加直观,底层和一维数组一样的

  • 如何对数组进行寻址

    int arr[3][4]{
        {1,2,3,4},
        {4,6,7,8},
        {9,1,2,3}
    };
    arr = [ebp-30h]
    arr[0][3] = arr[0*4+3]
    arr[0][3] = [ebp+(0*4+3)*4-30h]
    
    arr[2][2] = arr[2*4+2]
    arr[2][2] = [ebp+(2*4+2)*4-30h]
    以此类推
  • 三维数组也是这个样子

    int arr[3][4][5]{
        {{1,2,3,4,5},{1,2,3,4,5},{1,2,3,4,5},{1,2,3,4,5}},
        {{1,2,3,4,5},{1,2,3,4,5},{1,2,3,4,5},{1,2,3,4,5}},
        {{1,2,3,4,5},{1,2,3,4,5},{1,2,3,4,5},{1,2,3,4,5}},
    };
    arr = [ebp-30h]
    arr[0][3][1] = arr[0*4*5+3*5+1]
    arr[0][3][1] = [ebp+(0*4*5+3*5+1)*4-30h]
    
    arr[2][2][3] = arr[2*4*5+2*5+3]
    arr[2][2][3] = [ebp+(2*4*5+2*5+3)*4-30h]
    以此类推
  • 归纳总结寻址方式

    int arr[n][m][k][l][p]
    arr[2][4][5][6][9] = arr[2*m*k*l*p + 4*k*l*p + 5*l*p + 6*p + 9]

结构体

  • 定义结构体

    typedef struct st{
        char name[10];
        int age;
        int number;
    }Student;
  • 使用时的反汇编

        Student a;
        strcpy(a.name,"廉毅");
    00C913C8  push        offset string "\xc1\xae\xd2\xe3" (0C95750h)  
    00C913CD  lea         eax,[ebp-1Ch]  
    00C913D0  push        eax  
    00C913D1  call        @ILT+155(_strcpy) (0C910A0h)  
    00C913D6  add         esp,8  
        a.age = 100;
    00C913D9  mov         dword ptr [ebp-10h],64h  
        a.number = 001;
    00C913E0  mov         dword ptr [ebp-0Ch],1  
  • 结构体的偏移和数组差不多,形式的展示和数组差不多

  • 结构体存在字节对齐

  • 示例

    typedef struct st{
        char name[10];
        int age;
        int number;
    }Student;
    大小为:20字节
    
    typedef struct st1{
        int age;
        char a1;
        char a2;
    }Student1;
    大小为:8字节
    
    typedef struct st2{
        char a1;
        int age;
        char a2;
    }Student2;
    大小为:12字节
  • 原因:在效率和空间的选择上,编译器考虑了效率,所以会占用更多地空间

  • 可以使用对齐参数进行对齐

    #pragma pack(1)
    ******
    #pragma pack()
  • 代码示例

    #pragma pack(1)
    typedef struct st{
        char name[10];
        int age;
        int number;
    }Student;
    大小为:18字节
    
    typedef struct st1{
        int age;
        char a1;
        char a2;
    }Student1;
    大小为:6字节
    
    typedef struct st2{
        char a1;
        int age;
        char a2;
    }Student2;
    大小为:6字节
    
    #pragma pack()

switch语句

  • 分支少于4的时候,用switch没有意义,因为编译器会生成类似if…else之类的反汇编
  • case后面的常量可以是无序的,并不影响大表的生成

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 787772394@qq.com

文章标题:逆向_10变量循环数组结构体逆向分析

本文作者:二豆子·pwnd0u

发布时间:2020-10-25, 14:44:18

最后更新:2023-05-18, 09:49:00

原始链接:http://blog.codefat.cn/2020/10/25/%E9%80%86%E5%90%91-10%E5%8F%98%E9%87%8F%E5%BE%AA%E7%8E%AF%E6%95%B0%E7%BB%84%E7%BB%93%E6%9E%84%E4%BD%93%E9%80%86%E5%90%91%E5%88%86%E6%9E%90/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏

/*爱心代码*/ /*雪花效果*/ /*百度代码自动提交*/