C语言反汇编-多维数组与指针

反汇编(Disassembly) 即把目标二进制机器码转为汇编代码的过程,该技术常用于软件破解、外挂技术、病毒分析、逆向工程、软件汉化等领域,学习和理解反汇编对软件调试、系统漏洞挖掘、内核原理及理解高级语言代码都有相当大的帮助,软件一切神秘的运行机制全在反汇编代码里面。

数组和指针都是针对地址操作,但它们有许多不同之处,数组是相同数据类型的集合,以线性方式连续存储在内存中,而指针只是一个保存地址值的4字节变量。

在使用中,数组名是一个地址常量值,保存数组首元素地址不可修改,只能以此为基地址访问内存数据;而指针却是一个变量,只要修改指针中所保存的地址数据,就可以随意访问,不受约束.本章将深入介绍数组的构成以及两种寻址方式。

定义单循环一维的数组: 数组默认是使用局部变量存储的,拥有局部变量的所有特性,且数组中的数据在内存中是线性存储的,其数据由低到高在内存中的堆栈中存储,如下是一个简单的数组定义:

#include <stdio.h>

int main(int argc, char *argv[])
{
	int array[5] = { 1, 2, 3, 4, 5 };
	int x;
	for (x = 0; x < 5; x++){
		printf("打印数组元素: %d \n", array[x]);
	}
	return 0;
}

第一种Debug版反汇编代码如下,可以看到重点的部分是mov ecx,dword ptr ss:[ebp+eax*4-0x18]其中eax寄存器存储的就是数组下标,而乘以4是因为整数占4字节的存储空间所以要乘以4,最后的减0x18则是将堆栈指向第一个数组元素.

004113DE  | C745 E8 01000000         | mov dword ptr ss:[ebp-0x18],0x1           | 数组第1个元素
004113E5  | C745 EC 02000000         | mov dword ptr ss:[ebp-0x14],0x2           | 数组第2个元素
004113EC  | C745 F0 03000000         | mov dword ptr ss:[ebp-0x10],0x3           | 数组第3个元素
004113F3  | C745 F4 04000000         | mov dword ptr ss:[ebp-0xC],0x4            | 数组第4个元素
004113FA  | C745 F8 05000000         | mov dword ptr ss:[ebp-0x8],0x5            | 数组第5个元素
00411401  | C745 DC 00000000         | mov dword ptr ss:[ebp-0x24],0x0           | for循环初始化条件
00411408  | EB 09                    | jmp 0x411413                              |
0041140A  | 8B45 DC                  | mov eax,dword ptr ss:[ebp-0x24]           | x++
0041140D  | 83C0 01                  | add eax,0x1                               | for循环每次加1
00411410  | 8945 DC                  | mov dword ptr ss:[ebp-0x24],eax           |
00411413  | 837D DC 05               | cmp dword ptr ss:[ebp-0x24],0x5           |
00411417  | 7D 21                    | jge 0x41143A                              | 判断x是否大于等于5
00411419  | 8BF4                     | mov esi,esp                               | main.c:9
0041141B  | 8B45 DC                  | mov eax,dword ptr ss:[ebp-0x24]           | 取出第一个数组元素的基址
0041141E  | 8B4C85 E8                | mov ecx,dword ptr ss:[ebp+eax*4-0x18]     | 一维数组寻址公式
00411422  | 51                       | push ecx                                  |
00411423  | 68 58584100              | push consoleapplication1.415858           | 415858:"打印数组元素: %d \n"
00411428  | FF15 14914100            | call dword ptr ds:[<&printf>]             | 打印出来
0041142E  | 83C4 08                  | add esp,0x8                               |
00411431  | 3BF4                     | cmp esi,esp                               |
00411433  | E8 FEFCFFFF              | call 0x411136                             |
00411438  | EB D0                    | jmp 0x41140A                              | main.c:10
0041143A  | 33C0                     | xor eax,eax                               | main.c:11

C语言反汇编-多维数组与指针

第二种Release版反汇编代码如下,相比于Debug版本的代码,编译器对代码进行了一定程度的优化,g观察反汇编代码可以看出数组元素1-4是直接通过mxx0寄存器直接存储的,也就是编译器将其写死在了代码里,其他地方变化不大,需要注意在寻址过程中,数组不同于局部变量,不会被赋予常量值而使用常量传播.

00401006 | 66:0F6F05 10214000       | movdqa xmm0,xmmword ptr ds:[<__xmm@000000040000000300 | 将1-4元素压入xmm0寄存器
0040100E | 56                       | push esi                                              |
0040100F | 57                       | push edi                                              |
00401010 | 8B3D 90204000            | mov edi,dword ptr ds:[<&printf>]                      | edi 存储printf地址
00401016 | 33F6                     | xor esi,esi                                           | 清除esi做循环条件
00401018 | F3:0F7F45 EC             | movdqu xmmword ptr ss:[ebp-0x14],xmm0                 | 直接将1-4写入内存
0040101D | C745 FC 05000000         | mov dword ptr ss:[ebp-0x4],0x5                        | 最后写一个5
00401024 | FF74B5 EC                | push dword ptr ss:[ebp+esi*4-0x14]                    | 寻址方式未变动
00401028 | 68 00214000              | push disable.402100                                   | 402100:"打印数组元素: %d \n"
0040102D | FFD7                     | call edi                                              | 调用Printf
0040102F | 46                       | inc esi                                               | 每次递增
00401030 | 83C4 08                  | add esp,0x8                                           |
00401033 | 83FE 05                  | cmp esi,0x5                                           | 判断是否小于5
00401036 | 7C EC                    | jl 0x401024                                           |

C语言反汇编-多维数组与指针

这里我写了一段双循环代码,当程序运行后外部每循环一次内层则循环3次,你可以尝试逆向它并总结经验.

#include <stdio.h>

int main(int argc, char *argv[])
{
	int array1[5] = { 1, 2, 3, 4, 5 };
	int array2[3] = { 99,88,77 };
	int x,y;

	int external_len = sizeof(array1) / sizeof(array1[0]);
	for (x = 0; x < external_len; x++)
	{
		printf("外层循环计数: %d \n", array1[x]);
		int inside_len = sizeof(array2) / sizeof(array2[0]);
		for (y = 0; y < inside_len; y++)
		{
			printf("内层循环计数: %d \n", array2[y]);
		}
		printf("\n");
	}
	getchar();
	return 0;
}

汇编代码

004113CC | 8DBD E0FEFFFF            | lea edi,dword ptr ss:[ebp-0x120]            |
004113D2 | B9 48000000              | mov ecx,0x48                                | 48:'H'
004113D7 | B8 CCCCCCCC              | mov eax,0xCCCCCCCC                          |
004113DC | F3:AB                    | rep stosd                                   |
004113DE | C745 E8 01000000         | mov dword ptr ss:[ebp-0x18],0x1             | 数组第1个元素
004113E5 | C745 EC 02000000         | mov dword ptr ss:[ebp-0x14],0x2             | 数组第2个元素
004113EC | C745 F0 03000000         | mov dword ptr ss:[ebp-0x10],0x3             | 数组第3个元素
004113F3 | C745 F4 04000000         | mov dword ptr ss:[ebp-0xC],0x4              | 数组第4个元素
004113FA | C745 F8 05000000         | mov dword ptr ss:[ebp-0x8],0x5              | 数组第5个元素
00411401 | C745 D4 63000000         | mov dword ptr ss:[ebp-0x2C],0x63            | main.c:6, 63:'c'
00411408 | C745 D8 58000000         | mov dword ptr ss:[ebp-0x28],0x58            | 58:'X'
0041140F | C745 DC 4D000000         | mov dword ptr ss:[ebp-0x24],0x4D            | 4D:'M'
00411416 | C745 B0 05000000         | mov dword ptr ss:[ebp-0x50],0x5             | main.c:9
0041141D | C745 C8 00000000         | mov dword ptr ss:[ebp-0x38],0x0             | main.c:10
00411424 | EB 09                    | jmp 0x41142F                                |
00411426 | 8B45 C8                  | mov eax,dword ptr ss:[ebp-0x38]             |
00411429 | 83C0 01                  | add eax,0x1                                 | 外层循环递增条件
0041142C | 8945 C8                  | mov dword ptr ss:[ebp-0x38],eax             |
0041142F | 8B45 C8                  | mov eax,dword ptr ss:[ebp-0x38]             | 外层循环变量
00411432 | 3B45 B0                  | cmp eax,dword ptr ss:[ebp-0x50]             | 比较外层循环
00411435 | 7D 7D                    | jge 0x4114B4                                |
00411437 | 8BF4                     | mov esi,esp                                 | main.c:12
00411439 | 8B45 C8                  | mov eax,dword ptr ss:[ebp-0x38]             | 外层循环,循环次数 0,1,2,3,4
0041143C | 8B4C85 E8                | mov ecx,dword ptr ss:[ebp+eax*4-0x18]       | 通过公式定位到元素
00411440 | 51                       | push ecx                                    |
00411441 | 68 58584100              | push consoleapplication1.415858             | 415858:"外层循环计数: %d \n"
00411446 | FF15 10914100            | call dword ptr ds:[<&printf>]               | 打印外层循环计数
0041144C | 83C4 08                  | add esp,0x8                                 |
0041144F | 3BF4                     | cmp esi,esp                                 |
00411451 | E8 E0FCFFFF              | call 0x411136                               |
00411456 | C745 A4 03000000         | mov dword ptr ss:[ebp-0x5C],0x3             | 指定内层循环计数
0041145D | C745 BC 00000000         | mov dword ptr ss:[ebp-0x44],0x0             | y=0
00411464 | EB 09                    | jmp 0x41146F                                |
00411466 | 8B45 BC                  | mov eax,dword ptr ss:[ebp-0x44]             |
00411469 | 83C0 01                  | add eax,0x1                                 | 内层循环y每次递增
0041146C | 8945 BC                  | mov dword ptr ss:[ebp-0x44],eax             |
0041146F | 8B45 BC                  | mov eax,dword ptr ss:[ebp-0x44]             |
00411472 | 3B45 A4                  | cmp eax,dword ptr ss:[ebp-0x5C]             | 比较内层循环是否大于3
00411475 | 7D 21                    | jge 0x411498                                |
00411477 | 8BF4                     | mov esi,esp                                 | main.c:16
00411479 | 8B45 BC                  | mov eax,dword ptr ss:[ebp-0x44]             | 取出y的值
0041147C | 8B4C85 D4                | mov ecx,dword ptr ss:[ebp+eax*4-0x2C]       | 通过公式定位到元素位置
00411480 | 51                       | push ecx                                    |
00411481 | 68 70584100              | push consoleapplication1.415870             | 415870:"内层循环计数: %d \n"
00411486 | FF15 10914100            | call dword ptr ds:[<&printf>]               | 打印
0041148C | 83C4 08                  | add esp,0x8                                 |
0041148F | 3BF4                     | cmp esi,esp                                 |
00411491 | E8 A0FCFFFF              | call 0x411136                               |
00411496 | EB CE                    | jmp 0x411466                                | main.c:17
00411498 | 8BF4                     | mov esi,esp                                 | main.c:18
0041149A | 68 88584100              | push consoleapplication1.415888             | 415888:L"\n"
0041149F | FF15 10914100            | call dword ptr ds:[<&printf>]               |
004114A5 | 83C4 04                  | add esp,0x4                                 |
004114A8 | 3BF4                     | cmp esi,esp                                 |
004114AA | E8 87FCFFFF              | call 0x411136                               |
004114AF | E9 72FFFFFF              | jmp 0x411426                                | main.c:19

C语言反汇编-多维数组与指针

定义并使用二维的数组: 二维数组是一维数组的高阶抽象,其在内存中的排列也是线性存储的,只是在寻址方式上有所区别而已.

#include <stdio.h>

int main(int argc, char *argv[])
{
	int array[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
	int *Pointer1 = &array[0][0];

	printf("array[0][0]基址: %x \n", Pointer1);
	printf("array[0][0]数据:%d\n", *(Pointer1));
	printf("arrya[0][1]数据:%d\n", *(Pointer1 + 1));

	int *Pointer2 = &array[1][2];
	printf("array[1][2]基址: %x \n", Pointer2);

	printf("数组元素个数:%d\n", sizeof(array) / sizeof(int));
	return 0;
}

一张图理解寻址过程。

C语言反汇编-多维数组与指针

首先来研究一下第一个元素数组元素的寻址,也就是寻找到Pointer1 = > array[0][0]这个内存的空间,此处省略不必要的数据节约空间.

004113DE | C745 E4 01000000         | mov dword ptr ss:[ebp-0x1C],0x1             | 数组第1个元素
004113E5 | C745 E8 02000000         | mov dword ptr ss:[ebp-0x18],0x2             | 数组第2个元素
004113EC | C745 EC 03000000         | mov dword ptr ss:[ebp-0x14],0x3             | 数组第3个元素
004113F3 | C745 F0 04000000         | mov dword ptr ss:[ebp-0x10],0x4             | 数组第4个元素
004113FA | C745 F4 05000000         | mov dword ptr ss:[ebp-0xC],0x5              | 数组第5个元素
00411401 | C745 F8 06000000         | mov dword ptr ss:[ebp-0x8],0x6              | 数组第6个元素
00411408 | B8 0C000000              | mov eax,0xC                                 | 每一个一维数组的大小3*4=0C
0041140D | 6BC8 00                  | imul ecx,eax,0x0                            | 相乘ecx作为数组维度寻址
00411410 | 8D540D E4                | lea edx,dword ptr ss:[ebp+ecx-0x1C]         | 取第一个数组元素首地址(基地址)
00411414 | B8 04000000              | mov eax,0x4                                 | 每个元素占用4字节空间
00411419 | 6BC8 00                  | imul ecx,eax,0x0                            | 计算第一个数组元素偏移
0041141C | 03D1                     | add edx,ecx                                 | 得出array[0][0]的地址
0041141E | 8955 D8                  | mov dword ptr ss:[ebp-0x28],edx             | edx存储的就是 array[0][0] 的地址
00411421 | 8BF4                     | mov esi,esp                                 | main.c:8
00411423 | 8B45 D8                  | mov eax,dword ptr ss:[ebp-0x28]             |
00411426 | 50                       | push eax                                    | 压入堆栈,打印出来
00411427 | 68 58584100              | push consoleapplication1.415858             | 415858:"array[0][0]基址: %x \n"
0041142C | FF15 14914100            | call dword ptr ds:[<&printf>]               |
00411432 | 83C4 08                  | add esp,0x8                                 |
00411435 | 3BF4                     | cmp esi,esp                                 |
00411437 | E8 FAFCFFFF              | call 0x411136                               |
0041143C | 8BF4                     | mov esi,esp                                 | main.c:9
0041143E | 8B45 D8                  | mov eax,dword ptr ss:[ebp-0x28]             | 取出数组基地址
00411441 | 8B08                     | mov ecx,dword ptr ds:[eax]                  | 打印出 array[0][0]
00411443 | 51                       | push ecx                                    |
00411444 | 68 74584100              | push consoleapplication1.415874             | 415874:"array[0][0]数据:%d\n"
00411449 | FF15 14914100            | call dword ptr ds:[<&printf>]               |
0041144F | 83C4 08                  | add esp,0x8                                 |
00411452 | 3BF4                     | cmp esi,esp                                 |
00411454 | E8 DDFCFFFF              | call 0x411136                               |
00411459 | 8BF4                     | mov esi,esp                                 | main.c:10
0041145B | 8B45 D8                  | mov eax,dword ptr ss:[ebp-0x28]             |
0041145E | 8B48 04                  | mov ecx,dword ptr ds:[eax+0x4]              | 打印出 array[0][1]
00411461 | 51                       | push ecx                                    |
00411462 | 68 90584100              | push consoleapplication1.415890             | 415890:"arrya[0][1]数据:%d\n"
00411467 | FF15 14914100            | call dword ptr ds:[<&printf>]               |
0041146D | 83C4 08                  | add esp,0x8                                 |
00411470 | 3BF4                     | cmp esi,esp                                 |
00411472 | E8 BFFCFFFF              | call 0x411136                               |
00411477 | B8 0C000000              | mov eax,0xC                                 | 每一个一维数组的大小3*4=0C
0041147C | C1E0 00                  | shl eax,0x0                                 |
0041147F | 8D4C05 E4                | lea ecx,dword ptr ss:[ebp+eax-0x1C]         | 取第一个数组元素首地址(基地址)
00411483 | BA 04000000              | mov edx,0x4                                 | 每个元素占用4字节空间
00411488 | D1E2                     | shl edx,0x1                                 | 移位前 edx=4 移位后 edx=8
0041148A | 03CA                     | add ecx,edx                                 | 得出array[1][2]的地址
0041148C | 894D CC                  | mov dword ptr ss:[ebp-0x34],ecx             | 保存这个内存地址
0041148F | 8BF4                     | mov esi,esp                                 | main.c:13
00411491 | 8B45 CC                  | mov eax,dword ptr ss:[ebp-0x34]             | 取出内存地址中的值
00411494 | 50                       | push eax                                    | 压入堆栈,准备输出
00411495 | 68 AC584100              | push consoleapplication1.4158AC             | 4158AC:"array[1][2]基址: %x \n"
0041149A | FF15 14914100            | call dword ptr ds:[<&printf>]               |
004114A0 | 83C4 08                  | add esp,0x8                                 |
上一篇:整数溢出问题的坑,你真弄明白了吗?


下一篇:逆向入门分析实战(二)