lyl610abc 发表于 2021-3-10 18:57

逆向基础笔记二十二 汇编 指针(三)

本帖最后由 lyl610abc 于 2021-3-12 16:28 编辑

继续更新个人的学习笔记,
其它笔记传送门
逆向基础笔记一 进制篇
逆向基础笔记二 数据宽度和逻辑运算
逆向基础笔记三 通用寄存器和内存读写
逆向基础笔记四 堆栈篇
逆向基础笔记五 标志寄存器
逆向基础笔记六 汇编跳转和比较指令
逆向基础笔记七 堆栈图(重点)
逆向基础笔记八 反汇编分析C语言
逆向基础笔记九 C语言内联汇编和调用协定
逆向基础笔记十 汇编寻找C程序入口
逆向基础笔记十一 汇编C语言基本类型
逆向基础笔记十二 汇编 全局和局部 变量
逆向基础笔记十三 汇编C语言类型转换
逆向基础笔记十四 汇编嵌套if else
逆向基础笔记十五 汇编比较三种循环
逆向基础笔记十六 汇编一维数组
逆向基础笔记十七 汇编二维数组 位移 乘法
逆向基础笔记十八 汇编 结构体和内存对齐
逆向基础笔记十九 汇编switch比较if else
逆向基础笔记二十 汇编 指针(一)
逆向基础笔记二十一 汇编 指针(二)
逆向基础笔记二十三 汇编 指针(四)
逆向基础笔记二十四 汇编 指针(五) 系列完结

# 指针三

通过先前指针的学习,了解了指针和地址以及数据的关系,现在结合先前的知识继续学习巩固

## 指针遍历数组

有了先前的基础,再来看看如何用指针遍历数组

### 代码

```c
#include "stdafx.h"
void function(){
      short arr={1,2,3,4,5};

      short* p=&arr;
      short* p2=arr;

      if(p==p2){
                printf("equal\n");
      }else{
                printf("not equal\n");
      }

      int i;
      for(i=0;i<5;i++){
                int j=*(p+i);
                printf("addr:%x value:%d\n",p+i,j);
      }      
}
int main(int argc, char* argv[])
{
      function();
      return 0;
}
```

------

### 代码说明

稍微说明的一下代码部分

主要是声明了一个数组,然后用两种方法取得数组的首地址,一种是&arr,另一种则直接是arr

后面则是通过循环配合指针遍历数组成员并输出

按顺序依次涉及先前指针笔记的**知识点**:

1. 取变量地址
2. 指针赋值
3. 指针之间的比较
4. 取地址中存储数据
5. 指针的加减

------

### 运行结果

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210310153516983.png)

------

可以看到通过&arr和arr取数组首地址得到的结果是**一致**的

并且能够通过指针来输出数组成员的**地址**和对应的**数据**

这里还会观察到数组里的每个地址都相差2(short类型的数据宽度),和先前数组的学习又匹配上了

------

### 反汇编代码

```assembly
11:       short arr={1,2,3,4,5};
0040D7A8   mov         word ptr ,offset function+1Ch (0040d7ac)
0040D7AE   mov         word ptr ,offset function+22h (0040d7b2)
0040D7B4   mov         word ptr ,offset function+28h (0040d7b8)
0040D7BA   mov         word ptr ,offset function+2Eh (0040d7be)
0040D7C0   mov         word ptr ,offset function+34h (0040d7c4)
12:
13:       short* p=&arr;
0040D7C6   lea         eax,
0040D7C9   mov         dword ptr ,eax
14:       short* p2=arr;
0040D7CC   lea         ecx,
0040D7CF   mov         dword ptr ,ecx
15:
16:       if(p==p2){
0040D7D2   mov         edx,dword ptr
0040D7D5   cmp         edx,dword ptr
0040D7D8   jne         function+59h (0040d7e9)
17:         printf("equal\n");
0040D7DA   push      offset string "equal\n" (00422fbc)
0040D7DF   call      printf (0040d710)
0040D7E4   add         esp,4
18:       }else{
0040D7E7   jmp         function+66h (0040d7f6)
19:         printf("not equal\n");
0040D7E9   push      offset string "not equal\n" (00422fb0)
0040D7EE   call      printf (0040d710)
0040D7F3   add         esp,4
20:       }
21:
22:       int i;
23:
24:       for(i=0;i<5;i++){
0040D7F6   mov         dword ptr ,0
0040D7FD   jmp         function+78h (0040d808)
0040D7FF   mov         eax,dword ptr
0040D802   add         eax,1
0040D805   mov         dword ptr ,eax
0040D808   cmp         dword ptr ,5
0040D80C   jge         function+0A8h (0040d838)
25:         int j=*(p+i);
0040D80E   mov         ecx,dword ptr
0040D811   mov         edx,dword ptr
0040D814   movsx       eax,word ptr
0040D818   mov         dword ptr ,eax
26:         printf("addr:%x value:%d\n",p+i,j);
0040D81B   mov         ecx,dword ptr
0040D81E   push      ecx
0040D81F   mov         edx,dword ptr
0040D822   mov         eax,dword ptr
0040D825   lea         ecx,
0040D828   push      ecx
0040D829   push      offset string "addr:%x value%d:\n" (00422f9c)
0040D82E   call      printf (0040d710)
0040D833   add         esp,0Ch
27:       }
0040D836   jmp         function+6Fh (0040d7ff)
28:
29:   }
```

------

### 反汇编分析

由于循环和数组等相关的知识在先前的笔记已经详细学习过了,这里就直接看指针相关的代码

```assembly
25:         int j=*(p+i);
0040D80E   mov         ecx,dword ptr
0040D811   mov         edx,dword ptr
0040D814   movsx       eax,word ptr
0040D818   mov         dword ptr ,eax
```

------

1.将ebp-18h里的值赋值给ecx,这里的其实对应的就是 i

```assembly
0040D80E   mov         ecx,dword ptr (i)
```

------

2.将ebp-10h里的值赋值给edx,这里的其实对应的是p,即数组首地址

```assembly
0040D811   mov         edx,dword ptr (p)
```

------

3.movsx是带符号扩展赋值,将edx+ecx*2,也就是p+i\*数据宽度地址里存储的值赋给eax

```assembly
0040D814   movsx       eax,word ptr
```

------

为什么要使用movsx指令?

内存对齐的结果,先前的笔记就提到过:char short 在计算时都会转变为dword宽度来进行计算      

相关笔记可以参考:

[逆向基础笔记十三 汇编C语言类型转换](https://www.52pojie.cn/thread-1382219-1-1.html)

[逆向基础笔记十八 汇编 结构体和内存对齐](https://www.52pojie.cn/thread-1385641-1-1.html#37246226_%E5%86%85%E5%AD%98%E5%AF%B9%E9%BD%90)

------

4.将前面暂存在寄存器中的值赋给变量j

```assembly
0040D818   mov         dword ptr ,eax
```

------

## 指针翻转数组

### 翻转数组思想

翻转数组的思想就是从数组两端(数组首部和数组尾部)开始然后逐渐向**中间**靠拢,相互交换数组中的内容

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210310163600879.png)

------

### 代码

```c
#include "stdafx.h"
void function(){
      int arr={1,2,3,4,5};

      int* begin=&arr;
      int* end=begin+4;
      
      while(begin<end){
                int tmp=*begin;
                *begin=*end;
                *end=tmp;
                begin++;
                end--;
      }

      int i;
      for(i=0;i<5;i++){
                printf("%d\n",arr);
      }      
      
}
int main(int argc, char* argv[])
{
      function();
      return 0;
}
```

------

### 代码分析

数组翻转的关键代码是:

```c
int* begin=&arr;
int* end=begin+4;
      
while(begin<end){
      int tmp=*begin;
      *begin=*end;
      *end=tmp;
      begin++;
      end--;
}
```

------

1.获取数组首地址和尾地址

```c
int* begin=&arr;
int* end=begin+4;
```

------

2.循环直到所有数组成员交换结束

```c
while(begin<end){
}
```

------

3.取出begin中的数据放在临时变量中

```c
int tmp=*begin;
```

------

4.用end里存储的值覆盖begin

```c
*begin=*end;
```

------

5.将原本备份的begin的变量tmp赋值给end,此时已经完成了交换

```c
*end=tmp;
```

6.继续交换,让指针向数组中间靠拢

```c
begin++;
end--;
```

------

### 运行结果

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210310165950306.png)

可以看到,数组成功翻转了

------

## 反汇编实现翻转数组

前面使用指针实现了数组的翻转,为进一步了解其本质,自己手写汇编代码实现翻转数组

下面的汇编代码中省略了 dword ptr ds:[],默认就是取dword

### 代码

```assembly
#include "stdafx.h"
void function(){
      int arr={1,2,3,4,5};      
      int len=sizeof(arr)/sizeof(int)-1;      
      __asm{
                xor ecx,ecx
_begin:
                mov eax,len
                sub eax,ecx

                lea edx,      
                push edx
                mov edx,
                lea ebx,
                push ebx
                mov ebx,
                xchg ,ebx
                pop ebx
      
                mov ,edx
                pop edx
                inc ecx
      
                cmp edx,ebx
                jb _begin

      }
      int i;
      for(i=0;i<5;i++){
                printf("%d\n",arr);
      }      
      
}
int main(int argc, char* argv[])
{
      function();
      return 0;
}
```

------

### 运行结果

!(https://610-pic-bed.oss-cn-shenzhen.aliyuncs.com/image-20210310184633838.png)

能够正确地实现相同的功能O(∩_∩)O

------

### 反汇编代码分析

```assembly
__asm{
                xor ecx,ecx
_begin:
                mov eax,len
                sub eax,ecx

                lea edx,      
                push edx
                mov edx,
                lea ebx,
                push ebx
                mov ebx,
                xchg ,ebx
                pop ebx
      
                mov ,edx
                pop edx
                inc ecx
      
                cmp edx,ebx
                jb _begin

      }
```

------

1.将ecx清零,初始化ecx,ecx在这里是作为偏移量来使用的(刚开始为首地址的偏移,后来慢慢往中间靠拢)

```assembly
xor ecx,ecx
```

------

2.声明一个程序段,后续跳转会用到

```assembly
_begin:
```

------

3.将数组的长度减1的值赋给eax,因为数组从0开始,所以要减1

```assembly
mov eax,len
```

------

4.用先前的eax减去ecx获得偏移(刚开始为尾地址的偏移,后来慢慢往中间靠拢)

```assembly
sub eax,ecx
```

------

5.通过数组首地址加上ecx偏移取得地址,刚开始取得的为首地址,相当于edx=begin

```assembly
lea edx,      
```

------

6.将前面获得的地址edx放入堆栈中

```assembly
push edx
```

7.取出edx中的值,这里相当于edx=\*begin=tmp

```assembly
mov edx,
```

------

8.通过数组首地址加上偏移eax取得地址,刚开始取得的为尾地址,相当于ebx=end

```assembly
lea ebx,
```

------

9.将前面获得的地址ebx放入堆栈中

```assembly
push ebx
```

------

10.取出ebx中的值,这里对相当于ebx=\*end

```assembly
mov ebx,
```

------

11.交换arr+ecx\*4(\*begin)和ebx(\*end)里存储的值,这里相当于\*begin=\*end

```assembly
xchg ,ebx
```

------

12.将先前的push的end的地址恢复到ebx,使得ebx=end

```assembly
pop ebx
```

------

13.这里相当于*end=tmp,此时数组中的两个成员就已经交换完毕了

```assembly
mov ,edx
```

------

14.将先前push的begin的地址恢复到edx,使得edx=begin

```assembly
pop edx
```

------

15.让ecx自增一,这里相当于begin++;end--; 因为这里end的偏移是通过len-begin的偏移得到的

```assembly
inc ecx
```

------

16.比较edx和ebx

jb:jump below,小于则跳转(无符号),这里相当于while(begin<end)中的比较

如果begin<end则继续跳回去执行

```assembly
cmp edx,ebx
jb _begin
```

------

# 总结

可以通过指针来存储数组的各成员地址,然后用指针来遍历数组,翻转数组

lyl610abc 发表于 2021-3-22 20:48

aswcy815174418 发表于 2021-3-22 20:30
,话说pe多久更新,汇编第一个系列马上看完了

{:301_1010:}PE系列已经更新了两篇,不过最近在持续摸鱼{:301_998:}

aswcy815174418 发表于 2021-3-22 20:30

lyl610abc 发表于 2021-3-22 13:24
重在学习汇编指令
搞点花里胡哨的

:eee,话说pe多久更新,汇编第一个系列马上看完了

Psyber 发表于 2021-3-11 01:39

好东西,收藏了

grkgood 发表于 2021-3-11 04:13

硬是没看懂~~~

love514415 发表于 2021-3-11 08:57

这是一天一更的节奏?

我不是24K 发表于 2021-3-11 09:57

正好来复习看看

yyyt110 发表于 2021-3-11 11:01

感觉好难

mika_2003 发表于 2021-3-11 11:01

好东西,收藏了

在线小学生 发表于 2021-3-11 14:38

打卡,大佬加油!

金龙影子 发表于 2021-3-12 08:24

这个很强大

despy 发表于 2021-3-12 09:47

金龙影子 发表于 2021-3-12 08:24
这个很强大

如果合在一起,做成PDF或WORD就更好了
页: [1] 2
查看完整版本: 逆向基础笔记二十二 汇编 指针(三)