吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4814|回复: 19
收起左侧

[调试逆向] 逆向基础笔记二十二 汇编 指针(三)

  [复制链接]
lyl610abc 发表于 2021-3-10 18:57
本帖最后由 lyl610abc 于 2021-3-12 16:28 编辑

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

指针三

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

指针遍历数组

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

代码

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

        short* p=&arr[0];
        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[0],另一种则直接是arr

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

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

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

运行结果

image-20210310153516983


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

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

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


反汇编代码

11:       short arr[5]={1,2,3,4,5};
0040D7A8   mov         word ptr [ebp-0Ch],offset function+1Ch (0040d7ac)
0040D7AE   mov         word ptr [ebp-0Ah],offset function+22h (0040d7b2)
0040D7B4   mov         word ptr [ebp-8],offset function+28h (0040d7b8)
0040D7BA   mov         word ptr [ebp-6],offset function+2Eh (0040d7be)
0040D7C0   mov         word ptr [ebp-4],offset function+34h (0040d7c4)
12:
13:       short* p=&arr[0];
0040D7C6   lea         eax,[ebp-0Ch]
0040D7C9   mov         dword ptr [ebp-10h],eax
14:       short* p2=arr;
0040D7CC   lea         ecx,[ebp-0Ch]
0040D7CF   mov         dword ptr [ebp-14h],ecx
15:
16:       if(p==p2){
0040D7D2   mov         edx,dword ptr [ebp-10h]
0040D7D5   cmp         edx,dword ptr [ebp-14h]
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 [ebp-18h],0
0040D7FD   jmp         function+78h (0040d808)
0040D7FF   mov         eax,dword ptr [ebp-18h]
0040D802   add         eax,1
0040D805   mov         dword ptr [ebp-18h],eax
0040D808   cmp         dword ptr [ebp-18h],5
0040D80C   jge         function+0A8h (0040d838)
25:           int j=*(p+i);
0040D80E   mov         ecx,dword ptr [ebp-18h]
0040D811   mov         edx,dword ptr [ebp-10h]
0040D814   movsx       eax,word ptr [edx+ecx*2]
0040D818   mov         dword ptr [j],eax
26:           printf("addr:%x value:%d\n",p+i,j);
0040D81B   mov         ecx,dword ptr [j]
0040D81E   push        ecx
0040D81F   mov         edx,dword ptr [ebp-18h]
0040D822   mov         eax,dword ptr [ebp-10h]
0040D825   lea         ecx,[eax+edx*2]
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:   }

反汇编分析

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

25:           int j=*(p+i);
0040D80E   mov         ecx,dword ptr [ebp-18h]
0040D811   mov         edx,dword ptr [ebp-10h]
0040D814   movsx       eax,word ptr [edx+ecx*2]
0040D818   mov         dword ptr [j],eax

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

0040D80E   mov         ecx,dword ptr [ebp-18h] (i)

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

0040D811   mov         edx,dword ptr [ebp-10h] (p)

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

0040D814   movsx       eax,word ptr [edx+ecx*2]

为什么要使用movsx指令?

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

相关笔记可以参考:

逆向基础笔记十三 汇编C语言类型转换

逆向基础笔记十八 汇编 结构体和内存对齐


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

0040D818   mov         dword ptr [j],eax

指针翻转数组

翻转数组思想

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

image-20210310163600879


代码

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

        int* begin=&arr[0];
        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[i]);
        }        

}
int main(int argc, char* argv[])
{
        function();
        return 0;
}

代码分析

数组翻转的关键代码是:

int* begin=&arr[0];
int* end=begin+4;

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

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

int* begin=&arr[0];
int* end=begin+4;

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

while(begin<end){
}

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

int tmp=*begin;

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

*begin=*end;

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

*end=tmp;

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

begin++;
end--;

运行结果

image-20210310165950306

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


反汇编实现翻转数组

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

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

代码

#include "stdafx.h"
void function(){
        int arr[5]={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,[arr+ecx*4]        
                push edx
                mov edx,[edx]
                lea ebx,[arr+4*eax]
                push ebx
                mov ebx,[ebx]
                xchg [arr+ecx*4],ebx
                pop ebx

                mov [ebx],edx
                pop edx
                inc ecx

                cmp edx,ebx
                jb _begin

        }
        int i;
        for(i=0;i<5;i++){
                printf("%d\n",arr[i]);
        }        

}
int main(int argc, char* argv[])
{
        function();
        return 0;
}

运行结果

image-20210310184633838

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


反汇编代码分析

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

                lea edx,[arr+ecx*4]        
                push edx
                mov edx,[edx]
                lea ebx,[arr+4*eax]
                push ebx
                mov ebx,[ebx]
                xchg [arr+ecx*4],ebx
                pop ebx

                mov [ebx],edx
                pop edx
                inc ecx

                cmp edx,ebx
                jb _begin

        }

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

xor ecx,ecx

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

_begin:

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

mov eax,len

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

sub eax,ecx

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

lea edx,[arr+ecx*4]        

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

push edx

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

mov edx,[edx]

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

lea ebx,[arr+4*eax]

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

push ebx

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

mov ebx,[ebx]

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

xchg [arr+ecx*4],ebx

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

pop ebx

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

mov [ebx],edx

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

pop edx

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

inc ecx

16.比较edx和ebx

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

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

cmp edx,ebx
jb _begin

总结

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

免费评分

参与人数 21吾爱币 +18 热心值 +21 收起 理由
sorina + 1 + 1 谢谢@Thanks!
Alutemurat + 1 + 1 谢谢大佬
zhh4827 + 1 + 1 谢谢@Thanks!
还嫩有谁 + 1 + 1 我很赞同!
谁卑微了承诺 + 1 谢谢@Thanks!
幼幼小白兔 + 1 热心回复!
雪莱鸟 + 2 + 1 谢谢@Thanks!
yan182 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
国际豆哥 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
在线小学生 + 1 + 1 谢谢@Thanks!
debug_cat + 1 + 1 大佬~~~~~~~牛
bailemenmlbj + 1 + 1 谢谢@Thanks!
忆魂丶天雷 + 1 + 1 大佬~嘤嘤嘤
sam喵喵 + 1 + 1 谢谢@Thanks!
安德鲁儿 + 1 + 1 我很赞同!
like852 + 1 + 1 我很赞同!
hxw0204 + 1 + 1 热心回复!
khadwf + 1 + 1 谢谢@Thanks!
Psyber + 1 谢谢@Thanks!
朱朱你堕落了 + 1 大佬是真的牛B!
glengan + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

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

PE系列已经更新了两篇,不过最近在持续摸鱼
aswcy815174418 发表于 2021-3-22 20:30
lyl610abc 发表于 2021-3-22 13:24
重在学习汇编指令
搞点花里胡哨的

,话说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

如果合在一起,做成PDF或WORD就更好了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-23 15:55

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表