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
第⼆种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                                          |
这⾥我写了⼀段双循环代码,当程序运⾏后外部每循环⼀次内层则循环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
定义并使⽤⼆维的数组: ⼆维数组是⼀维数组的⾼阶抽象,其在内存中的排列也是线性存储的,只是在寻址⽅式上有所区别⽽已. #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;
}
⼀张图理解寻址过程。
⾸先来研究⼀下第⼀个元素数组元素的寻址,也就是寻到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                                |
sizeof 指针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]            | 取出内存地址中的值

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。