逆向_10变量循环数组结构体逆向分析
变量的声明
- 本质就是声明我要用一块内存
- 全局变量
- 程序执行的时候内存空间已经分配好了,直到程序运行结束
- 局部变量
- 只有程序执行到相应的位置才会给它分配内存空间
类型转换
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个字节
- 宽度再次说明
- 本机尺寸:如果本机是32位的,那么对32位的数据支持最好,如果是64位的,那么对64位的支持
- 编译器遵守了这个规则:
- char类型或者short类型的参数不但没有节省空间,反而浪费了多余的操作
- 结论:整数类型的参数,一律使用int类型
- 参数传递的本质:将上层函数的变量,或者表达式的值“复制一份”,传递给下层函数
局部变量的内存分配
- 小于32位的局部变量,空间在分配时,按32位分配.
- 使用时按实际的宽度使用.
- 不要定义char/short类型的局部变量.
- 参数与局部变量没有本质区别,都是局部变量,都在栈中分配.一个在函数调用前分配空间,一个是在函数调用时分配内存
- 完全可以把参数当初局部变量使用
赋值的本质
- 将某个值存储到某个变量中就是赋值
数组的本质
一组相同类型的变量,为了方便读写,采用另外一种表示形式.
数组在声明的时候,必须用常量来指明长度,不能使用变量.
数组在使用时,可以通过变量来定位数据.
数组定位时,可以超过数组的长度,编译不会有错,但读取的数据是错的.
数组内存空间的使用
- 根据所定义的类型进行空间分配
- 如果最后一个没有占满4个字节,则按4个字节对齐
数组寻址
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" 转载请保留原文链接及作者。