美文网首页
iOS逆向实战--007:指针

iOS逆向实战--007:指针

作者: 帅驼驼 | 来源:发表于2021-04-11 08:48 被阅读0次

在计算机科学中,指针(Pointer),是编程语言中的一类数据类型及其对象或变量,用来表示或存储一个存储器地址,这个地址的值直接指向存在该地址的对象的值。

指针参考了存储器中一个地址。通过被称为指针反参考(Dereferencing)的动作,可以取出在那个地址中存储的值。保存在指针指向的地址中的值,可能代表另一个变量、结构、对象或函数。但是从指针值是无法得知它所引用的存储器中存储了什么资料类型的信息。

基础常识

一个变量的地址称为该变量的指针,它类似于公民的身份证号码。如果把人比作对象,那么身份证号码就是这个人的地址

指针也是一个对象,只是负责的工作特殊。它类似于明星的经纪人,你想找到明星,必须由经纪人传达,找到经纪人相当于找到明星本人

每个对象都会占用内存空间,每个对象都会存在一个对应的内存地址。因此,同样身为对象的指针,也有它自己的内存地址

基本定义

  • 指针:一个变量的地址称为该变量的指针
  • 指针类型:例如,指向整形数据的指针类型为int *
  • 指针变量:存放另外一个变量地址的变量,指针变量的值是地址
  • 指针变量中只能存放地址,不能将一个整数赋值给指针变量
  • 指针变量的定义: 类型名 * 指针变量名
  • 类型可以强制转换,但结构体和基本类型不能强制转换
  • 任何变量都可以使用取地址运算符进行内存寻址

运算符

  • &:取地址运算符。例如:&a代表a的地址
  • *:指针运算符,又称间接访问运算符。例如:p代表指针,p指向的对象a = xxx

案例1:

指针类型的数据宽度

打开ViewController.m文件,写入以下代码:

#import "ViewController.h"

void func(){
   int *a;
   printf("%lu",sizeof(a));
}

@implementation ViewController

- (void)viewDidLoad {
//    [super viewDidLoad];
   func();
}

@end

真机运行项目,来到func方法

  • sizeof不是函数,而是一个操作符。编译后直接显示数字
  • 指针类型的数据宽度8字节

案例2:

指针的自增、自减

打开ViewController.m文件,写入以下代码:

void func(){
   int *a;
   a = (int *)100;
   a++;
   printf("%d", a);
}
  • 指针的自增、自减运算,和指向的数据类型的宽度有关
  • int类型占4字节a++相当于+4
  • 案例打印结果:104

如果a指向char类型

void func(){
   char *a;
   a = (char *)100;
   a++;
   printf("%d", a);
}
  • char类型占1字节a++相当于+1
  • 案例打印结果:101

如果a为二级指针

void func(){
   int **a;
   a = (int **)100;
   a = a + 1;
   printf("%d", a);
}
  • 指针的加减,无论何种写法,都跟指向的数据宽度有关
  • a为二级指针,指向的还是指针类型,占8字节a = a + 1相当于+8
  • 案例打印结果:108

自增、自减和编译器有关,在a++a--的时候,部分编译器不遵守规则,得到的结果会出现问题,所以尽量使用a = a + 1的方式代替

案例3:

指针的运算单位

打开ViewController.m文件,写入以下代码:

void func(){
   int *a;
   a = (int *)100;

   int *b;
   b = (int *)200;
   
   int x = a - b;
   printf("%d", x);
}
  • 指针的运算单位是指向的数据类型的宽度
  • 案例中a - b等于-100,由于指向的int类型,运算结果还要除以4
  • 案例打印结果:-25

指针之间比较大小

void func(){
   int *a;
   a = (int *)100;
   
   int *b;
   b = (int *)200;
   
   if(a > b){
       printf("a > b");
   }
   else{
       printf("a <= b");
   }
}

案例打印结果:a <= b

案例4:

指针的反汇编形式

#import "ViewController.h"

void func(){
   int *a;
   int b = 100;
   a = &b;
}

@implementation ViewController

- (void)viewDidLoad {
//    [super viewDidLoad];
   func();
}

@end

真机运行项目,来到func方法

  • add x8, sp, #0x4:将sp + #0x4地址,写入x8寄存器
  • mov w9, #0x64:将#0x64,即10进制100写入w9寄存器,相当于b = 100
  • str w9, [sp, #0x4]:将w9的值,写入sp + #0x4地址。写入w9值的地址和x8寄存器指向的是相同地址,相当于a = &b
  • str x8, [sp, #0x8]:将x8的值,写入sp + #0x8地址。这里sp偏移8字节写入的就是指针,存储指向w9值的所在地址

案例5:

指针的基本用法

打开ViewController.m文件,写入以下代码:

void func(){
   int arr[5] = {1, 2, 3, 4, 5};

   for (int i = 0; i < 5; i++) {
       printf("%d", arr[i]);
   }
}
  • 常规方式,循环遍历arr数组的元素
  • 案例打印结果:12345

使用指针运算的方式,循环遍历arr数组的元素

void func(){

   int arr[5] = {1, 2, 3, 4, 5};

   for (int i = 0; i < 5; i++) {
       printf("%d", *(arr + i));
   }
}
  • arr也是一个指针
  • int *a == arr == &arr[0],它们之间是对等关系
  • 指针指向int类型,所以步长为4字节
  • 每次arr + i相当于指针移动了i * 4字节
  • 案例打印结果:12345

案例6:

EXC_BAD_ACCESS的反汇编形式

打开ViewController.m文件,写入以下代码:

#import "ViewController.h"

void func(){
   int *a;
   int b = *a;
}

@implementation ViewController

- (void)viewDidLoad {
//    [super viewDidLoad];
   func();
}

@end

真机运行项目,来到func方法

  • ldr x8, [sp, #0x8]:读取sp + #0x8地址,写入x8寄存器
  • 由于*a未初始化,从栈中读取的值为0
  • ldr w9, [x8]:将x8的值0,寻址写入x9。此时:EXC_BAD_ACCESS

案例7:

指针移动步长的反汇编形式

打开ViewController.m文件,写入以下代码:

void func(){
   int *a;
   int b = *a;
   int c = (a + 0);
}

真机运行项目,来到func方法

  • 两种方式的反汇编代码完全相同
  • 步长+0,所在地址直接取值

修改代码,查看步长+1的反汇编形式

void func(){
   int *a;
   int b = *a;
   int c = *(a + 0);
   int d = *(a + 1);
}

真机运行项目,来到func方法

  • 步长+1,相当于从地址 + #0x4位置取值
  • #0x4:指针指向的数据类型int4字节
  • 代码中bcd三个指针变量,共计24字节。对栈空间的操作为16字节对齐,所以需要拉伸栈空间32字节,即:#0x20

案例8:

二级指针寻址的反汇编形式

打开ViewController.m文件,写入以下代码:

void func(){
   int **a;
   int b = **a;
   int c = **(a + 0);
   int d = **(a + 1);
}

真机运行项目,来到func方法

  • 步长+1,相当于从地址 + #0x8获取值
  • #0x8:二级指针指向的数据类型是指针,占8字节
  • x8从栈中取值后,先将x8的值作为地址,取值写入x8。再将x8的值作为地址,取值写入w9
  • 遇到ldr寻址两次,说明此处是二级指针寻址

案例9:

二级指针移动步长的反汇编形式

打开ViewController.m文件,写入以下代码:

void func(){
   char **a;
   char b = *(*(a + 2) + 2);
}

真机运行项目,来到func方法

  • 第一次运算为二级指针char **,指向的数据类型是char *,占8字节。步长+216字节,即:#0x10
  • 第二次运算为指针char *,指向的数据类型是char,占1字节。步长+22字节,即:#0x2

另一种移动步长的写法:

void func(){
   char **a;
   char b = a[1][2];
}

真机运行项目,来到func方法

  • 第一次运算#0x8
  • 第二次运算#0x2
总结

指针

  • 指针的宽度是指它所指向的数据类型的宽度,即:步长
  • 指针的自增、自减,按照指针的步长来计算的

相关文章

  • iOS逆向实战--007:指针

    在计算机科学中,指针(Pointer),是编程语言中的一类数据类型及其对象或变量,用来表示或存储一个存储器地址,这...

  • 关于逆向的一些资料

    转自《ios应用逆向工程 分析与实战》 -------------------------------------...

  • 收集的编程书籍

    IOS应有逆向工程:分析与实战[Wrox] Professional iOS ProgrammingiOS开发进阶...

  • 逆向系列序章

    在连续阅读了两本IOS逆向有关的两本书籍《ios应用安全攻防实战》、《iOS逆向工程》后,了解iOS越狱相关知识的...

  • iOS微信逆向实战--自动抢红包、修改步数、防止消息撤回

    iOS微信逆向实战--自动抢红包、修改步数、防止消息撤回

  • iOS逆向开发:密码学 HASH

    前言 最近看了一篇关于逆向实战的文章:来谈谈iOS逆向工程之App脱壳[https://juejin.cn/pos...

  • iOS逆向-05: 指针

    在oc中对象其实是结构体指针,当然在oc中都叫对象,为了研究对象,这里简单补充一下指针下面代码输出什么? 14? ...

  • 一 iOS 逆向工程概述

    1 什么是iOS逆向工程 2 iOS逆向的目的 3 iOS逆向过程以及方法 一 什么是iOS逆向工程 iOS逆向...

  • iOS逆向学习

    参考文章:iOS逆向开发记录:iOS逆向之手机越狱iOS逆向之介绍iOS逆向之文件系统结构iOS逆向之文件权限及类...

  • iOS逆向之反HOOK的基本防护

    iOS逆向之Method Swizzle iOS逆向之fishHook原理探究 iOS逆向之fishHook怎么通...

网友评论

      本文标题:iOS逆向实战--007:指针

      本文链接:https://www.haomeiwen.com/subject/qmlhkltx.html