吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 8305|回复: 44
收起左侧

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

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

继续更新个人的学习笔记,
其它笔记传送门
逆向基础笔记一 进制篇
逆向基础笔记二 数据宽度和逻辑运算
逆向基础笔记三 通用寄存器和内存读写
逆向基础笔记四 堆栈篇
逆向基础笔记五 标志寄存器
逆向基础笔记六 汇编跳转和比较指令
逆向基础笔记七 堆栈图(重点)
逆向基础笔记八 反汇编分析C语言
逆向基础笔记九 C语言内联汇编和调用协定
逆向基础笔记十 汇编寻找C程序入口
逆向基础笔记十一 汇编C语言基本类型
逆向基础笔记十二 汇编 全局和局部 变量
逆向基础笔记十三 汇编C语言类型转换
逆向基础笔记十四 汇编嵌套if else
逆向基础笔记十五 汇编比较三种循环
逆向基础笔记十六 汇编一维数组
逆向基础笔记十七 汇编二维数组 位移 乘法
逆向基础笔记十八 汇编 结构体和内存对齐
逆向基础笔记十九 汇编switch比较if else
逆向基础笔记二十一 汇编 指针(二)
逆向基础笔记二十二 汇编 指针(三)
逆向基础笔记二十三 汇编 指针(四)
逆向基础笔记二十四 汇编 指针(五) 系列完结
终于来到C语言中极其重要的部分——指针,由于指针的内容相对较多,于是将其拆分为好几个笔记,并且在指针结束之后该系列也差不多要完结了,下一系列暂定为PE的学习

指针

什么是指针

一般关于指针的解释都离不开地址。这里先暂且忘记这个概念

指针其实也是一种数据类型,和先前学习的int float等数据类型没有实质上的区别,只不过这个数据类型是在先前学习的所有数据类型后面加上若干个*号,如char *,int *等等,这种数据类型被称为指针


  • 任意类型后面都可以加上*号,使其成为新的指针数据类型
  • *可以是任意多个

指针的声明

指针的声明其实在前面介绍什么是指针的时候就已经讲到了,例子如下:

struct S1{
        int a;
};

void function(){                
        char* a;
        short** b;
        int*** c;
        long**** d;
        _int64***** e;
        float****** f;
        double******* g;
        S1******** s1;
}

可以看到所有的其它数据类型(包括结构体)后面加上若干个*后就是所谓的指针了

推荐的声明方式如上

但也可以这样将*放在变量前面,但不推荐,因为这样相当于将这个数据类型拆开了,不利于理解

struct S1{
        int a;
};

void function(){                
        char *a;
        short **b;
        int ***c;
        long ****d;
        _int64 *****e;
        float ******f;
        double *******g;
        S1 ********s1;
}

指针的赋值

在说指针的赋值之前先看看先前普通变量的赋值

普通变量的赋值貌似是直接使用 变量=值即可,但其实是编译器简化了赋值的步骤,实际上在赋值前本应该加上要赋值类型

例子如下:

void function(){                
        int a;
    a=610;
    a=(int)610;
}

现在再来看指针的赋值

void function(){                
        char* a;
        a=(char*) 610;
    int** b;
    b=(int**) 610;
}

在要赋的值前面加上指针的类型即可,貌似和普通变量的赋值并无太大差别,此时也注意到这里的指针也和地址没有什么关联

指针的的类型转换这里暂且不提


指针的数据宽度

研究数据宽度方法

先前研究过其它基本变量的数据宽度,会发现char、short、int都是按照4字节来分配的(内存对齐),但实际使用的情况下则是按照其原本类型的数据宽度来赋值或进行其它操作的

如:

void function(){                
        char a;
        short b;
        int c;
        a=1;
        b=2;
        c=3;
}

其对应的反汇编代码为:

11:   void function(){
00401010   push        ebp
00401011   mov         ebp,esp
00401013   sub         esp,4Ch
00401016   push        ebx
00401017   push        esi
00401018   push        edi
00401019   lea         edi,[ebp-4Ch]
0040101C   mov         ecx,13h
00401021   mov         eax,0CCCCCCCCh
00401026   rep stos    dword ptr [edi]
12:       char a;
13:       short b;
14:       int c;
15:       a=1;
00401028   mov         byte ptr [ebp-4],1
16:       b=2;
0040102C   mov         word ptr [ebp-8],offset function+20h (00401030)
17:       c=3;
00401032   mov         dword ptr [ebp-0Ch],3
18:   }
00401039   pop         edi
0040103A   pop         esi
0040103B   pop         ebx
0040103C   mov         esp,ebp
0040103E   pop         ebp
0040103F   ret

可以注意到此时提升的堆栈为4Ch,而默认(空函数时)提升的堆栈为40h

00401013   sub         esp,4Ch

于是此时为三个变量分配的空间是:4Ch-40h=0xC=12=3×4,即为char short int都分配了4个字节


但在这三个变量赋值的时候展现出来的就是其原本的数据类型宽度了

15:       a=1;
00401028   mov         byte ptr [ebp-4],1

在char类型的a中的赋值宽度是 byte,1字节


16:       b=2;
0040102C   mov         word ptr [ebp-8],offset function+20h (00401030)

在short类型的b中的赋值宽度是word,2字节


17:       c=3;
00401032   mov         dword ptr [ebp-0Ch],3

在int类型的c中的赋值宽度是dword,4字节


研究指针数据宽度

于是如法炮制,按照前面的方法来研究指针的数据宽度

在前面的数据类型后添加*,使其成为指针类型

void function(){                
        char* a;
        short* b;
        int* c;
        a=(char*)  1;
        b= (short*) 2;
        c=(int*)  3;
}

其对应的反汇编代码为

11:   void function(){
00401010   push        ebp
00401011   mov         ebp,esp
00401013   sub         esp,4Ch
00401016   push        ebx
00401017   push        esi
00401018   push        edi
00401019   lea         edi,[ebp-4Ch]
0040101C   mov         ecx,13h
00401021   mov         eax,0CCCCCCCCh
00401026   rep stos    dword ptr [edi]
12:       char* a;
13:       short* b;
14:       int* c;
15:       a=(char*)  1;
00401028   mov         dword ptr [ebp-4],1
16:       b= (short*) 2;
0040102F   mov         dword ptr [ebp-8],2
17:       c=(int*)  3;
00401036   mov         dword ptr [ebp-0Ch],3
18:   }
0040103D   pop         edi
0040103E   pop         esi
0040103F   pop         ebx
00401040   mov         esp,ebp
00401042   pop         ebp
00401043   ret

直接观察对应的赋值语句:

15:       a=(char*)  1;
00401028   mov         dword ptr [ebp-4],1
16:       b= (short*) 2;
0040102F   mov         dword ptr [ebp-8],2
17:       c=(int*)  3;
00401036   mov         dword ptr [ebp-0Ch],3

可以看到,所有赋值的宽度都为dowrd,说明无论是char*、short、int\其数据宽度都为4字节

可以使用同样的方法研究float、double、struct等其它数据类型

并且在这里会注意到,指针类型的赋值和非指针类型的赋值在反汇编中并没有什么区别

总结

无论是什么类型,在其后面加上(无论加几个\都一样)后其数据宽度都变为4字节

指针的加减

例子

指针类型也支持加减的操作,但不支持乘和除编译器决定的),来看例子:

#include "stdafx.h"

void function(){                
        char* a;
        short* b;
        int* c;
        a=(char*)  1;
        b= (short*) 2;
        c=(int*)  3;

        a++;
        b++;
        c++;

        printf("a:%d\t b:%d\tc:%d\n",a,b,c);

}

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

运行结果

image-20210309164329745


分析

这里会观察到结果并不是想象中的2,3,4;而是2,4,7

细心的小伙伴肯定发现了:

  • 2 = 1 + 1  (char数据宽度为1字节)
  • 4 = 2 + 2  (short数据宽度为2字节)
  • 7 = 3 + 4  (int数据宽度为4字节)

结果是加上了原本各自的数据类型的宽度


拓展例子

前面只是都是一级指针,现在将指针换为二级指针:

void function(){                
        char** a;
        short** b;
        int** c;
        a=(char**)  1;
        b= (short**) 2;
        c=(int**)  3;

        a++;
        b++;
        c++;

        printf("a:%d\t b:%d\tc:%d\n",a,b,c);

}

运行结果

image-20210309165051328


分析

此时的结果为:

  • 5= 1 + 4  (char* 数据宽度为4字节)
  • 6= 2 + 4  (short* 数据宽度为4字节)
  • 7 = 3 + 4  (int* 数据宽度为4字节)

结果为加上 去掉一个*后的数据宽度


拓展例子二

前面的加法操作都只增加了1,现在再来查看增加大于1时的情况

void function(){                
        char* a;
        short* b;
        int* c;
        a=(char*)  1;
        b= (short*) 2;
        c=(int*)  3;

        a=a+5;
        b=b+5;
        c=c+5;

        printf("a:%d\t b:%d\tc:%d\n",a,b,c);

}

运行结果

image-20210309170142595


分析

此时的结果为:

  • 6= 1 + 5*1 (char 数据宽度为1字节)
  • 12= 2 + 5*2  (short 数据宽度为2字节)
  • 23 = 3 + 5*4  (int 数据宽度为4字节)

结果为加上 去掉一个*后的数据宽度 × 增加的数值

总结

无论是指针的加亦或是减(这里只演示了加法,但减法同理),其加或减的单位为去掉一个*后的数据宽度

也就是实际增减的数值=去掉一个*后的数据宽度 × 增减的数值

指针类型相减

前面提到的指针的加减都是同一个指针里的加减,但指针之间其实也支持相减操作(不支持相加

但指针之间的加减要求指针的类型必须一致,即char*类型只能和char*类型相加减,不能和char**或其它类型相加减

例子

void function(){                
        char* a;
        char* b;
        short* c;
        short* d;
        int* e;
        int* f;

        a=(char*) 200;
        b=(char*) 100;

        c=(short*) 200;
        d=(short*) 100;

        e=(int*) 200;
        f=(int*) 100;

        printf("%d\n",a-b);
        printf("%d\n",c-d);
        printf("%d\n",e-f);

}

运行结果

image-20210309171355894


分析

此时的结果为:

  • 100 =  (200 - 100)/1(char 数据宽度为1字节)
  • 50 =  (200 - 100)/2  (short 数据宽度为2字节)
  • 25 =   (200 - 100)/4 (int 数据宽度为4字节)

结果为相减完后再除以  原本各自的数据宽度


扩展例子

前面只是都是一级指针,现在将指针换为四级指针:

void function(){                
        char**** a;
        char**** b;
        short**** c;
        short**** d;
        int**** e;
        int**** f;

        a=(char****) 200;
        b=(char****) 100;

        c=(short****) 200;
        d=(short****) 100;

        e=(int****) 200;
        f=(int****) 100;

        printf("%d\n",a-b);
        printf("%d\n",c-d);
        printf("%d\n",e-f);

}

运行结果

image-20210309171954363


分析

此时的结果为:

  • 25 =  (200 - 100)/4(char*** 数据宽度为4字节)
  • 25 =  (200 - 100)/4  (short*** 数据宽度为4字节)
  • 25 =   (200 - 100)/4 (int*** 数据宽度为4字节)

结果为相减后再除以  去掉一个*后的数据宽度

总结

指针之间的减法,其结果为相减后再除以去掉一个*后的数据宽度

指针之间的比较

指针之间也支持相互比较,但也和上面指针类型相减一样,要求指针类型一致

例子

void function(){                
        char**** a;
        char**** b;

        a=(char****) 200;
        b=(char****) 100;

        if (a>b)
        {
                printf("a>b\n");
        }else{
                printf("a<=b\n");
        }

}

运行结果

image-20210309172856531


结论

相同类型的指针之间是支持大小比较

总结

  • 指针的数据宽度为4字节,或者说:无论是什么类型,在其后面加上(无论加几个\都一样)后其数据宽度都变为4字节
  • 指针数值支持进行加减,加减的结果=去掉一个*后的数据宽度 × 增减的数值
  • 指针之间支持减法但不支持加法,其结果为相减后再除以去掉一个*后的数据宽度
  • 指针之间支持比较大小,但要求进行比较的两个指针为相同类型

免费评分

参与人数 19吾爱币 +23 热心值 +19 收起 理由
JJ啊 + 1 + 1 用心讨论,共获提升!
Chenda1 + 1 + 1 我很赞同!
DancingLight + 1 + 1 热心回复!
国际豆哥 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
重置001 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
yu6319119 + 1 + 1 谢谢@Thanks!
Cailgun + 1 + 1 我很赞同!
Va1 + 1 + 1 我很赞同!
蔡小飞 + 1 + 1 谢谢@Thanks!
ZZJxh + 1 + 1 谢谢@Thanks!
杨辣子 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
墨石不菲 + 1 + 1 谢谢@Thanks!
newsun + 1 + 1 用心讨论,共获提升!
debug_cat + 1 + 1 我来了,支持大佬更新
zj_tj + 1 + 1 我很赞同!
渣渣奶 + 2 + 1 反手给你一个赞
fengbolee + 2 + 1 用心讨论,共获提升!
朱朱你堕落了 + 3 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
sam喵喵 + 1 + 1 谢谢@Thanks!

查看全部评分

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

 楼主| lyl610abc 发表于 2021-3-9 19:24
本帖最后由 lyl610abc 于 2021-3-9 19:39 编辑
bester 发表于 2021-3-9 18:58
我有一个疑问,今天在用IsWow64Process()函数的时候,第二个参数是__out PBOOL Wow64Process   但是我直接 ...

这个是WIN32的内容吧,是不是问错地方了
不过还是解答一下:
函数原型:
[C] 纯文本查看 复制代码
BOOL WINAPI IsWow64Process(
  __in   HANDLE hProcess,
  __out  PBOOL Wow64Process
);


使用如下:
[C] 纯文本查看 复制代码
BOOL bIsWow64 = FALSE;
IsWow64Process(GetCurrentProcess(), &bIsWow64);

或者:
[C] 纯文本查看 复制代码
BOOL* bIsWow64 = (BOOL*) malloc(sizeof(BOOL*));
*bIsWow64 = FALSE;
IsWow64Process(GetCurrentProcess(), bIsWow64);

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
bester + 1 + 1 我很赞同!

查看全部评分

renshengchujian 发表于 2021-3-9 18:49
bester 发表于 2021-3-9 18:58
我有一个疑问,今天在用IsWow64Process()函数的时候,第二个参数是__out PBOOL Wow64Process   但是我直接定义bool Wow64的时候,我IsWow64Process(hprocess,&Wow64),为什么提示我实参和形参不符合呢?定义bool *Wow64 也不行,我就这点理解不了
SKgarlic 发表于 2021-3-9 19:37
谢谢分享
bester 发表于 2021-3-9 19:45
lyl610abc 发表于 2021-3-9 19:24
这个是WIN32的内容吧,是不是问错地方了
不过还是解答一下:
函数原型:

是我不明白指针和这个参数的关系,正好你讲到了指针,但是我用&符号确实提示我实参和形参不一致,可能是我没有初始化吧
python小甲鱼 发表于 2021-3-9 19:53
太棒了&#128536;
python小甲鱼 发表于 2021-3-9 20:14
希望大佬把pe解析的代码讲解一下,把这个坑填上。
xiazaidian1 发表于 2021-3-9 21:20
学习了支持大神
xlm0820 发表于 2021-3-9 22:46
66666666
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

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

GMT+8, 2024-4-24 13:00

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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