《学习笔记》指针有啥难?
2020-01-17 11:37:31 Author: bbs.pediy.com(查看原文) 阅读量:340 收藏

今天下雪了,所以我来看雪!!!

最近两个月我木有发帖,学校里的事比较多哈。。。第14届安徽省大学生职业规划大赛(金奖)主题就是软件逆向工程师(评委:这是啥???)。然后就是国家奖学金等。不亏,给

钱让我上大学。。。

好的,装0xD完了。进入正题。

首先我要解释一个梗:指针就是地址??? 不完全是! 

指针难么?

首先我们来看指针的宽度是啥。

char a=1;

char* b=(char*)1; //下面解释        

char** c=(char**)1;

a的宽度是1个字节

b的宽度是4个字节(32位)

二级指针 c也是4???

啥是二级指针 ?指向一级指针的东西???弄弄弄! 不一定是

继续,

short y;//宽度2字节

int z;//宽度4字节(32位机)

float f;//宽度4(32位机)

double d;//宽度8

加个*会怎样???

char* x;   

short* y; 

int* z;      

float* f;   

double* d;       

2020年我国全面进入小康社会,所有屌丝都有女朋友!!!人人平等!

咳咳,跑题了。。。它们的宽度都是4个字节,不信?你看!

14:   char* x=(char*)10;

00401048 C7 45 FC 0A 00 00 00 mov         dword ptr [ebp-4],0Ah

15:

16:   short* y=(short*)10;

0040104F C7 45 F8 0A 00 00 00 mov         dword ptr [ebp-8],0Ah

17:

18:   int* z=(int*)10;

00401056 C7 45 F4 0A 00 00 00 mov         dword ptr [ebp-0Ch],0Ah

19:

20:   float* f=(float*)10;

0040105D C7 45 F0 0A 00 00 00 mov         dword ptr [ebp-10h],0Ah

21:

22:   double* d=(double*)10;

00401064 C7 45 EC 0A 00 00 00 mov         dword ptr [ebp-14h],0Ah

:

那么,二级指针是啥?恩。。。

14:   char** x=(char**)10;

00401048 C7 45 FC 0A 00 00 00 mov         dword ptr [ebp-4],0Ah

15:

16:   short** y=(short**)10;

0040104F C7 45 F8 0A 00 00 00 mov         dword ptr [ebp-8],0Ah

17:

18:   int** z=(int**)10;

00401056 C7 45 F4 0A 00 00 00 mov         dword ptr [ebp-0Ch],0Ah

19:

20:   float** f=(float**)10;

0040105D C7 45 F0 0A 00 00 00 mov         dword ptr [ebp-10h],0Ah

21:

22:   double** d=(double**)10;

00401064 C7 45 EC 0A 00 00 00 mov         dword ptr [ebp-14h],0Ah

恩,也是4个字节!  你爹有5个亿,你有女票。我爹是画家,我也有女票!

总结:普通类型的指针宽度都是4个字节。多级指针也是4个字节。

下面我们看看一些奇葩的指针。

1. 指针数组(是指针的数组,是数组!)

为啥?  char[10]的宽度是10个字节,因为char宽度*10,那么char* [10]的宽度就是10个char*的宽度。也就是40。

人人平等,人人都有女票!

也是40个字节

那么 两个星呢?

恩,也是40个字节。

2 结构体指针

也是4个字节。 为啥? 因为这个指针通常指向了结构体变量的首地址(这个不能乱用)

3 函数指针

16:   int (*pf)(int)=f;

00401088 C7 45 FC 0F 10 40 00 mov         dword ptr [ebp-4],offset @ILT+10(f) (0040100f)

0040108F 8B 45 FC             mov         eax,dword ptr [ebp-4]

00401092 89 45 FC             mov         dword ptr [ebp-4],eax

宽度也是4,因为它现在指向了函数的首地址(其实是跳转表)。

指针运算

指针在做运算时,要拿砍掉一个星后的宽度然后在算。这样的运算规则便于开发寻址。

a的类型char* 宽度 砍掉一个*,变成了char。然后char的宽度是1。100+1*5=105.

以此类推。

自加

减法同理

指针与指针的运算

指针主要是用于寻址的。所以,我们假设a指向了一个变量,b也指向了一个变量,所以相减就是他们的地址差。指针与指针的运算应该先减,然后除以砍掉一个*的宽度。

同理,a-b=100,100/int的宽度4字节,最后得25。

多级指针与多级指针的运算

因为普通类型的指针宽度都是4个字节(32位机),所以我就举着一个例子吧。。

引用&的用法

引用的本质就是取地址,比如:

10:   char a = 10;

00401048 C6 45 FC 0A          mov         byte ptr [ebp-4],0Ah

11:   short b = 20;

0040104C 66 C7 45 F8 14 00    mov         word ptr [ebp-8],offset main+20h (00401050)

12:   int c = 30;

00401052 C7 45 F4 1E 00 00 00 mov         dword ptr [ebp-0Ch],1Eh

13:

14:   char* pa = (char*)&a;

00401059 8D 45 FC             lea         eax,[ebp-4]

0040105C 89 45 F0             mov         dword ptr [ebp-10h],eax

15:   short* pb = (short*)&b;

0040105F 8D 4D F8             lea         ecx,[ebp-8]

00401062 89 4D EC             mov         dword ptr [ebp-14h],ecx

16:   int* pc = (int*)&c;

00401065 8D 55 F4             lea         edx,[ebp-0Ch]

00401068 89 55 E8             mov         dword ptr [ebp-18h],edx

指针与引用正常情况下可以看成是一对,也就是间接寻址的引用。

比如说

10:   char a = 10;

00401048   mov         byte ptr [ebp-4],0Ah

11:

12:

13:   char* pa = (char*)&a;//取地址

0040104C   lea         eax,[ebp-4]

0040104F   mov         dword ptr [ebp-8],eax

14:

15:

16:   printf("%d ",*pa);

00401052   mov         ecx,dword ptr [ebp-8]//取ebp-8的数据 然后把它当成地址来取里面的值。

00401055   movsx       edx,byte ptr [ecx]

00401058   push        edx

00401059   push        offset string "%d " (0042e01c)

0040105E   call        printf (00408160)

00401063   add         esp,8

17:

说的简单粗暴点就是:变量前面加一个*就是把这个变量里面的值当成地址,然后取这个地址里面的值(相当于砍掉一个*)。 变量前面加一个&就是取这个变量的地址(相当于加一个星)。

例题:

打印数组

char arr[10];   

char* p = &arr[0]; //取数组第一个元素的地址   

for(int k=0;k<10;k++)                         

{                          

         printf("%d\n",*(p+k));      //取地址里的值     

}       

对应反汇编

12:   char* p = &arr[0]; //取数组第一个元素的地址

00401048   lea         eax,[ebp-0Ch]

0040104B   mov         dword ptr [ebp-10h],eax

13:   for(int k=0;k<10;k++)

0040104E   mov         dword ptr [ebp-14h],0

00401055   jmp         main+30h (00401060)

00401057   mov         ecx,dword ptr [ebp-14h]

0040105A   add         ecx,1

0040105D   mov         dword ptr [ebp-14h],ecx

00401060   cmp         dword ptr [ebp-14h],0Ah

00401064   jge         main+4Fh (0040107f)

14:   {

15:       printf("%d\n",*(p+k));  //取地址里的值

00401066   mov         edx,dword ptr [ebp-10h]//数组首地址

00401069   add         edx,dword ptr [ebp-14h]//k变量

0040106C   movsx       eax,byte ptr [edx]//寻址

0040106F   push        eax

00401070   push        offset string "%d\n" (0042e01c)

00401075   call        printf (00408180)

0040107A   add         esp,8

16:   }

0040107D   jmp         main+27h (00401057)

17:            

指针类型

虽然指针的宽度都是4字节,但是用的时候还是与区别的.

char类型的指针一次访问内存一个字节。

Short 类型的指针一次访问内存两个字节

Int 类型的指针一次访问内存4个字节

比如:

反汇编代码:

11:   int i=300;

00401048   mov         dword ptr [ebp-4],12Ch

12:   char* b=(char*)&i;

0040104F   lea         eax,[ebp-4]

00401052   mov         dword ptr [ebp-8],eax

13:

14:   printf("%d",*b);

00401055   mov         ecx,dword ptr [ebp-8]

00401058   movsx       edx,byte ptr [ecx] //这里 ,只取了1个byte

300的二进制是100101100   而char*只能取1个字节的数据,所以取低8位,也就是101100

=42。

例题

模拟实现CE的数据搜索功能:                                                                   

         这一堆数据中存储了角色的血值信息,假设血值的类型为int类型,值为100(10进制)                                                            

         请列出所有可能的值以及该值对应的地址.                                                               

         0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x07,0x09,                                                                

         0x00,0x20,0x10,0x03,0x03,0x0C,0x00,0x00,0x44,0x00,                                                               

         0x00,0x33,0x00,0x47,0x0C,0x0E,0x00,0x0D,0x00,0x11,                                                               

         0x00,0x00,0x00,0x02,0x64,0x00,0x00,0x00,0xAA,0x00,                                                               

         0x00,0x00,0x64,0x10,0x00,0x00,0x00,0x00,0x00,0x00,                                                                

         0x00,0x00,0x02,0x00,0x74,0x0F,0x41,0x00,0x00,0x00,                                                                

         0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x0A,0x00,                                                               

         0x00,0x02,0x74,0x0F,0x41,0x00,0x06,0x08,0x00,0x00,                                                                

         0x00,0x00,0x00,0x64,0x00,0x0F,0x00,0x00,0x0D,0x00,                                                                

         0x00,0x00,0x23,0x00,0x00,0x64,0x00,0x00,0x64,0x00                                                        

答案

                  void f(char a[],int l)                    

         {                          

for(int i=0;i<l;i++)                                         

{                                            

         if(*(int*)(a+i)==0x64)          //十进制100

         printf("%0x\n",&a[i]);                                 

}                                            

}                                            

int main()                                     

{                                            

char a[]=                                      

{                                            

0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x07,0x09,                                             

0x00,0x20,0x10,0x03,0x03,0x0C,0x00,0x00,0x44,0x00,                                             

0x00,0x33,0x00,0x47,0x0C,0x0E,0x00,0x0D,0x00,0x11,                                            

0x00,0x00,0x00,0x02,0x64,0x00,0x00,0x00,0xAA,0x00,                                            

0x00,0x00,0x64,0x10,0x00,0x00,0x00,0x00,0x00,0x00,                                             

0x00,0x00,0x02,0x00,0x74,0x0F,0x41,0x00,0x00,0x00,                                              

0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x0A,0x00,                                             

0x00,0x02,0x74,0x0F,0x41,0x00,0x06,0x08,0x00,0x00,                                              

0x00,0x00,0x00,0x64,0x00,0x0F,0x00,0x00,0x0D,0x00,                                             

0x00,0x00,0x23,0x00,0x00,0x64,0x00,0x00,0x64,0x00                                     

};                                           

int l=sizeof(a)/sizeof(a[0]);                //数组长度                        

f((char*)a,l);                                         

    return 0;                                      

}                

核心算法反编译代码:

11:   for(int i=0;i<l;i++)

00401048   mov         dword ptr [ebp-4],0

0040104F   jmp         f+2Ah (0040105a)

00401051   mov         eax,dword ptr [ebp-4]

00401054   add         eax,1

00401057   mov         dword ptr [ebp-4],eax

0040105A   mov         ecx,dword ptr [ebp-4]

0040105D   cmp         ecx,dword ptr [ebp+0Ch]

00401060   jge         f+53h (00401083)

12:   {

13:

14:   if(*(int*)(a+i)==0x64)//

00401062   mov         edx,dword ptr [ebp+8]//取数组的地址(参数1)

00401065   add         edx,dword ptr [ebp-4]//+i

00401068   cmp         dword ptr [edx],64h

0040106B   jne         f+51h (00401081)

15:       printf("%0x\n",&a[i]);

0040106D   mov         eax,dword ptr [ebp+8]

00401070   add         eax,dword ptr [ebp-4]

00401073   push        eax

00401074   push        offset string "%0x\n" (0042f01c)

00401079   call        printf (004083e0)

0040107E   add         esp,8

16:

17:   }

00401081   jmp         f+21h (00401051)

18:

字符串与字符数组

字符串其实是一个指针,指向常量区(不可写数据段)的对应字符。字符数组就是把常量区的对应字符拷贝过来(类似于内存拷贝)。

比如:

char arr[6] = {'A','B','C','D','E','\0'};                           

char names[] = "ABCDE";                                   

printf("%s\n",arr);                              

printf("%s\n",names);             

反汇编代码:

00401048   mov         byte ptr [ebp-8],41h                                     

0040104C   mov         byte ptr [ebp-7],42h                                     

00401050   mov         byte ptr [ebp-6],43h                                     

00401054   mov         byte ptr [ebp-5],44h                                     

00401058   mov         byte ptr [ebp-4],45h                                     

0040105C   mov         byte ptr [ebp-3],0    

上面是 char arr[6]                            

00401060   mov         eax,[string "ABCDE" (0042f020)]                                          

00401065   mov         dword ptr [ebp-10h],eax                                       

00401068   mov         cx,word ptr [string "ABCDE"+4 (0042f024)]                                         

0040106F   mov         word ptr [ebp-0Ch],cx      

上面是 char names[]                                 

00401073   lea         edx,[ebp-8]                                     

00401076   push        edx                                          

00401077   push        offset string "%s\n" (0042f01c)                                              

0040107C   call        printf (004081a0)                                              

00401081   add         esp,8                                      

00401084   lea         eax,[ebp-10h]                                          

00401087   push        eax                                          

00401088   push        offset string "%s\n" (0042f01c)                                              

0040108D   call        printf (004081a0)                                              

00401092   add         esp,8                                      

00401095   xor         eax,eax                                            

00401097   pop         edi                                          

00401098   pop         esi                                           

00401099   pop         ebx                                         

0040109A   add         esp,50h                                          

0040109D   cmp         ebp,esp                                         

0040109F   call        __chkesp (00408220)                                       

004010A4   mov         esp,ebp                                         

004010A6   pop         ebp                                         

004010A7   ret                                                             

指针数组

指针数组就是指针的数组,通常用来存放指针

比如:

int a=10;

int b=20;

int c=30;

int d=40;

int e=50;

int* arr[5]={&a,&b,&c,&d,&e};

for(int i=0;i<5;i++)

{

printf("%d\n",*(arr[i]));

}

反汇编代码

12:   int a=10;

00401048   mov         dword ptr [a],0Ah

13:   int b=20;

0040104F   mov         dword ptr [b],14h

14:   int c=30;

00401056   mov         dword ptr [c],1Eh

15:   int d=40;

0040105D   mov         dword ptr [d],28h

16:   int e=50;

00401064   mov         dword ptr [e],32h

17:

18:   int* arr[5]={&a,&b,&c,&d,&e};

0040106B   lea         eax,[a]

0040106E   mov         dword ptr [arr],eax

00401071   lea         ecx,[b]

00401074   mov         dword ptr [ebp-24h],ecx

00401077   lea         edx,[c]

0040107A   mov         dword ptr [ebp-20h],edx

0040107D   lea         eax,[d]

00401080   mov         dword ptr [ebp-1Ch],eax

00401083   lea         ecx,[e]

00401086   mov         dword ptr [ebp-18h],ecx

19:

20:   for(int i=0;i<5;i++)

00401089   mov         dword ptr [i],0

00401090   jmp         main+6Bh (0040109b)

00401092   mov         edx,dword ptr [i]

00401095   add         edx,1

00401098   mov         dword ptr [i],edx

0040109B   cmp         dword ptr [i],5

0040109F   jge         main+8Ah (004010ba)

21:   {

22:   printf("%d\n",*(arr[i]));

004010A1   mov         eax,dword ptr [i]

004010A4   mov         ecx,dword ptr arr[eax*4]//int是4个字节

004010A8   mov         edx,dword ptr [ecx]

004010AA   push        edx

004010AB   push        offset string "%d\n" (0042f01c)

004010B0   call        printf (004081d0)

004010B5   add         esp,8

23:   }

004010B8   jmp         main+62h (00401092)

结构体指针

这种指针通常情况下是指向结构体变量的首地址,用的时候小心点。这个容易发生缓冲区溢出错误。

例题

         查找这些数据中,有几个id=1 level=8的结构体信息。                                        

         0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x07,0x09,                                             

         0x00,0x20,0x10,0x03,0x03,0x0C,0x00,0x00,0x44,0x00,                                             

         0x00,0x33,0x01,0x00,0x00,0x08,0x00,0x00,0x00,0x00,                                             

         0x00,0x00,0x00,0x02,0x64,0x00,0x00,0x00,0xAA,0x00,                                            

         0x00,0x00,0x64,0x01,0x00,0x00,0x00,0x08,0x00,0x00,                                             

         0x00,0x00,0x02,0x00,0x74,0x0F,0x41,0x00,0x00,0x00,                                              

         0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x0A,0x00,                                             

         0x00,0x02,0x57,0x4F,0x57,0x00,0x06,0x08,0x00,0x00,                                              

         0x00,0x00,0x00,0x64,0x00,0x0F,0x00,0x00,0x0D,0x00,                                             

         0x00,0x00,0x23,0x00,0x00,0x64,0x00,0x00,0x64,0x00                                     

         结构体定义如下:                                              

         typedef struct TagPlayer                                     

         {                                            

                   int id;                                   

                   int level;                              

         }Player;   

答案

                            typedef struct TagPlayer                                     

         {                                            

                   int id;                                   

                   int level;                              

         }Player;                                         

         Player* f(char* p,int a,int b,int size)                                            

         {                                            

                   if(size==0)//遍历完所有的数据                                

                            return 0;                    

         if(*(int*)p==1 && *(int*)(p+4)==8) //p是char 所以先+4个字节再用int*读数据                                

         {                                            

         printf("%0x\n",p);                                        

         f(++p,a,b,size--);                                           

         }                                            

         //printf("%0x %0x\n",*(int*)p,*(int*)(p+4));                                       

         f(++p,a,b,size--);                                           

         }                                            

         int main()                                     

         {                                            

                   char a[]={                            

         0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x07,0x09,                                             

         0x00,0x20,0x10,0x03,0x03,0x0C,0x00,0x00,0x44,0x00,                                             

         0x00,0x33,0x01,0x00,0x00,0x08,0x00,0x00,0x00,0x00,                                             

         0x00,0x00,0x00,0x02,0x64,0x00,0x00,0x00,0xAA,0x00,                                            

         0x00,0x00,0x64,0x01,0x00,0x00,0x00,0x08,0x00,0x00,                                             

         0x00,0x00,0x02,0x00,0x74,0x0F,0x41,0x00,0x00,0x00,                                              

         0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x0A,0x00,                                             

         0x00,0x02,0x57,0x4F,0x57,0x00,0x06,0x08,0x00,0x00,                                              

         0x00,0x00,0x00,0x64,0x00,0x0F,0x00,0x00,0x0D,0x00,                                             

         0x00,0x00,0x23,0x00,0x00,0x64,0x00,0x00,0x64,0x00                                     

                   };                                  

         f(a,1,8,sizeof(a)/sizeof(a[0]));                                     

          return 0;                                             

         }                                   

查找函数的核心算法的反编译代码:


数组指针

         。。。我还是发文件上来吧,感冒了,头痛。 指针资料.rar里还有我写的很多笔记,实验等

[2020元旦礼物]《看雪论坛精华17》发布!(补齐之前所有遗漏版本)!

最后于 10小时前 被AMask编辑 ,原因:


文章来源: https://bbs.pediy.com/thread-257273.htm
如有侵权请联系:admin#unsafe.sh