swift指针

1、指针的类型

1.1、分类

swift中指针分为两类,typed pointer指定数据类型的指针,raw pointer未指定类型的指针(原生指针)。
Swift 中的指针和 OC 中指针的对应关系:

Swift Object-C 说明
UnsafePointer <T> const T* 指针及所指向的内容都不可变
UnsafeMutablePointer <T> T* 指针及所指向的内容均可变
UnsafeRawPointer const void* 指针指向的内存区,类型未定
UnsafeMutablePointer void* 指针指向的内存区,类型未定
UnsafeBufferPointer/UnsafeMutableBufferPointer Int a [ ] 一种数组指针

1.2、typed pointer(类型指针)

1.2.1、UnsafePointer

UnsafePointer 是不可变的,C 中 const 修饰的指针对应 UnsafePointer (最常见的是 C 字符串的 const char * )。

  • UnsafePointer中的pointee属性只能get不能set。
  • UnsafePointer中没有allocate方法。

初始化
通过已有变量获取

UnsafePointer初始化1.png

直接分配内存
UnsafePointer初始化2.png

1.2.2、UnsafeMutablePointer

初始化
UnsafeMutablePointer是可变的,有getter和setter
通过已有变量获取

UnsafeMutablePointer初始化1.png

直接分配内存


UnsafeMutablePointer初始化2.png

被UnsafeMutablePointe引用的内存有三种状态:

  • Not Allocated:内存没有被分配,这意味着这是一个 null 指针,或者是之前已经释放过
  • Allocated but not initialized:内存进行了分配,但是值还没有被初始化
  • Allocated and initialized:内存进行了分配,并且值已经被初始化

可以通过下图直观表示


UnsafeMutablePointer状态.png

1.2.3、UnsafeBufferPointer

UnsafeBufferPointer表示一组连续数据指针。BufferPointer实现了Collection,因此可以直接使用Collection中的各种方法来遍历操作数据,filter,map...,Buffer可以实现对一块连续存在空间进行操作,类似C中的数组的指针。 但是同样的,这个UnsafeBufferPointer是常量,它只能获取到数据,不能通过这个指针去修改数据。与之对应的是UnsafeMutableBufferPointer指针。

UnsafeBufferPointer.png

1.2.4、UnsafeMutableBufferPointer

可变的序列指针,UnsafeMutableBufferPointer拥有对指向序列修改的能力:

UnsafeMutableBufferPointer.png

注意:如果一个序列被初始化之后,没有给每一个元素赋值的话,这些元素的值都将出现随机值。

1.3、raw pointer(原生指针)

1.3.1、UnsafeMutableRawPointer

用于访问和操作非类型化数据的原始指针。


UnsafeMutableRawPointer存取值操作.png

注意:在存值的时候,需要移动内存地址,不然就会覆盖掉当前内存上之前存储的值

1.3.2、UnsafeRawPointer

用于访问非类型化数据的原始指针。UnsafeRawPointer只能由其他指针用init方法得到,与UnsafePointer类似,没有allocate静态方法。

UnsafeRawPointer存取值操作.png

2、指针的使用

2.1 获取strcut实例对象的指针

struct YYPeople {
    var age :Int
    var name :String
    init(name: String,age: Int){
        self.name = name
        self.age = age
    }
}
var p = YYPeople(name: "lisi", age: 18)
print(p)//YYPeople(age: 18, name: "lisi")
var ptr = withUnsafeMutablePointer(to: &p ,{$0})
ptr.pointee = YYPeople(name: "hahah", age: 20)
print(p)//YYPeople(age: 20, name: "hahah")

2.2 获取class实例对象的指针

获取 class 类型实例的指针和上面不同,不是使用withUnsafePointerwithUnsafeMutablePointer,而是使用下面讲到的Unmanaged

class YYPeople {
    var age :Int
    var name :String
    init(name: String,age: Int){
        self.name = name
        self.age = age
    }
}
var p = YYPeople(name: "lisi", age: 18)
// 获取指针
let ptr = UnsafeMutableRawPointer(Unmanaged<YYPeople>.passUnretained(p).toOpaque())
//获取对象
let obj = Unmanaged<YYPeople>.fromOpaque(ptr).takeUnretainedValue()
print(obj.name)//"lisi"

2.3、还原类实例对象的数据结构

struct HeapObject {
    var kind: UnsafeRawPointer
    var strongRef: UInt32
    var unownedRef: UInt32
}
class YYPeople{
    var age = 18
    var name = "lisi"
}
var p = YYPeople()
//将p绑定到结构体内存中
//1、获取实例变量的内存地址,声明成了非托管对象
/*
 通过Unmanaged指定内存管理,类似于OC与CF的交互方式(所有权的转换 __bridge)
 - passUnretained 不增加引用计数,即不需要获取所有权
 - passRetained 增加引用计数,即需要获取所有权
 - toOpaque 不透明的指针
 */
let ptr = Unmanaged.passUnretained(p as AnyObject).toOpaque()
//2、绑定到结构体内存,返回值是UnsafeMutablePointer<T>
/*
 - bindMemory 更改当前 UnsafeMutableRawPointer 的指针类型,绑定到具体的类型值
    - 如果没有绑定,则绑定
    - 如果已经绑定,则重定向到 HeapObject类型上
 */
let heapObject = ptr.bindMemory(to: HeapObject.self, capacity: 1)
//3、访问成员变量
print(heapObject.pointee.kind)//0x0000000100008198
print(heapObject.pointee.strongRef)//3
print(heapObject.pointee.unownedRef)//0
//4、将HeapObject的kind 绑定成MetaData(类的结构体)
struct MetaData {
    var kind: UnsafeRawPointer
    var superClass: UnsafeRawPointer
    var cachedata1: UnsafeRawPointer
    var cachedata2: UnsafeRawPointer
    var data: UnsafeRawPointer
    var flags: UInt32
    var instanceAddressOffset: UInt32
    var instanceSize: UInt32
    var flinstanceAlignMask: UInt16
    var reserved: UInt16
    var classSize: UInt32
    var classAddressOffset: UInt32
    var typeDescriptor: UnsafeMutableRawPointer
    var iVarDestroyer: UnsafeRawPointer
}
let metaDataPtr = heapObject.pointee.kind.bindMemory(to: MetaData.self, capacity: 1)
print(metaClassPtr.pointee)
//输出结果
//MetaData(kind: 0x0000000100008170, superClass: 0x00007fff80732978, cachedata1: 0x00007fff2031aaa0, cachedata2: 0x0000803000000000, data: 0x00000001007080e2, flags: 2, instanceAddressOffset: 0, instanceSize: 40, flinstanceAlignMask: 7, reserved: 0, classSize: 168, classAddressOffset: 16, typeDescriptor: 0x0000000100003c5c, iVarDestroyer: 0x0000000000000000)

2.4、指针读取MachO文件中的属性名称

需要对MachO的基础有一定的了解
通过MachOView查看MachO文件,计算对比验证信息

创建类

class YYPeople{
    
    var age :Int = 18
    var name :String = "lisi"
    var height :Double = 175.0
    
    func testFunc() {
        print("testFunc")
    }
    func test() {
        print("test")
    }
}

获取app运行的基地址

var headerPtr = _dyld_get_image_header(0)
print("headerPtr :\(String(describing: headerPtr))")
//控制台输出headerPtr :Optional(0x0000000100000000)
//将程序运行的基地址转成UInt64类型
let headerPtr_IntRepresentation = UInt64(bitPattern:
Int64(Int(bitPattern: headerPtr)))
print("headerPtr_IntRepresentation:\(headerPtr_IntRepresentation)")
//控制台输出headerPtr_IntRepresentation:4294967296

app运行的基地址可以通过image list获取

app运行的基地址.png

获取链接的基地址

//第一种方法
var sect64LinkeditPtr = getsegbyname("__LINKEDIT")
var linkBaseAddress: UInt64 = 0
if let vmaddr = sect64LinkeditPtr?.pointee.vmaddr, let fileOff = sect64LinkeditPtr?.pointee.fileoff{
    linkBaseAddress = vmaddr - fileOff
    print("vmaddr:\(vmaddr),fileOff:\(fileOff)")
    //控制台输出vmaddr:4295049216,fileOff:81920
}
print("linkBaseAddress :\(linkBaseAddress)")
//控制台输出linkBaseAddress :4294967296

//第二种方法
var pageZeroPtr = getsegbyname("__PAGEZERO")
var linkBaseAddress1: UInt64 = 0
linkBaseAddress1 = pageZeroPtr?.pointee.vmsize ?? 0
print("linkBaseAddress1 :\(linkBaseAddress1)")
//控制台输出linkBaseAddress1 :4294967296
__LINKEDIT.png

__PAGEZERO.png

获取__swift5_types的偏移
__swift5_types中存储的就是swift的classstrcutenum

//获取__swift5_types的指针
var size :UInt = 0
var sectPtr = getsectdata("__TEXT", "__swift5_types", &size)
print("sectPtr :\(String(describing: sectPtr))")
//获取__swift5_types的偏移offset
var offset: UInt64 = 0
if let unwrappedPtr = sectPtr {
    let intRepresentation = UInt64(bitPattern: Int64(Int(bitPattern:unwrappedPtr)))
    offset = intRepresentation + UInt64(i * 4) - linkBaseAddress
    print("offset :\(offset)")
//控制台输出offset :48548
}

将48548转换成10进制得到0xBDA4,在macho文件定位


__swift5_types偏移量.png

获取Data LO信息

//Data LO实际运行首地址(app运行基地址+偏移量)
var dataLoAddress = headerPtr_IntRepresentation + offset
print("dataLoAddress:\(dataLoAddress)")
//Data LO指针
let dataLoPtr = withUnsafePointer(to: &dataLoAddress){$0}
print("dataLoPtr:\(String(describing: dataLoPtr))")
//获取指针指向第一个4字节的内存地址
//Int(exactly:dataLoAddress) ?? 0) 将dataLoAddress转换成Int类型
let dataLoContent = UnsafePointer<UInt32>.init(bitPattern: Int(exactly:
dataLoAddress) ?? 0)?.pointee
print("dataLoContent:\(String(describing: dataLoContent))")
//控制台输出
//dataLoAddress:4295015844 dataLoPtr:0x00007ffeefbff058 dataLoContent:Optional(4294964260)
dataLoContent.png

获取TargetClassDescriptor(描述文件首地址)

//获取typeDesc的偏移量
    let typeDescOffset = UInt64(dataLoContent!) + offset - linkBaseAddress
    print("typeDescOffset :\(typeDescOffset)")
    //获取typeDesc在运行内存中的地址
    let typeDescAddress = typeDescOffset + headerPtr_IntRepresentation
    print("typeDescAddress :\(typeDescAddress)")
//控制台输出
//typeDescOffset :45512
//typeDescAddress :4295012808
TargetClassDescriptor地址.png

将描述文件地址绑定到TargetClassDescriptor结构体

 //TargetClassDescriptor结构体
    struct TargetClassDescriptor{
        var flags: UInt32
        var parent: UInt32
        var name: Int32 //类名
        var accessFunctionPointer: Int32
        var fieldDescriptor: Int32 //描述信息
        var superClassType: Int32
        var metadataNegativeSizeInWords: UInt32
        var metadataPositiveSizeInWords: UInt32
        var numImmediateMembers: UInt32
        var numFields: UInt32
        var fieldOffsetVectorOffset: UInt32
        var Offset: UInt32
        var size: UInt32 //方法个数
        //var vTable: UInt32 //方法列表(占位用,这个地址就是vtable,方便后面通过偏移得到vtable首地址)
    }
    //将typeDescAddress转换成TargetClassDescriptor
    let classDescPtr = UnsafePointer<TargetClassDescriptor>.init(bitPattern: Int(exactly:typeDescAddress) ?? 0)?.pointee
    print("classDescPtr:\(String(describing: classDescPtr))")
//控制台输出
//classDescPtr:Optional(StructAndClass.(unknown context at $10000b314).(unknown context at $10000b34c).TargetClassDescriptor(flags: 2147483728, parent: 4294967268, name: -20, accessFunctionPointer: -6500, fieldDescriptor: 2456, superClassType: 0, metadataNegativeSizeInWords: 2, metadataPositiveSizeInWords: 25, numImmediateMembers: 15, numFields: 3, fieldOffsetVectorOffset: 10, Offset: 13, size: 12))

获取类的名称

    if let name = classDescPtr?.name{
        //类名偏移量 name的偏移 + 描述文件的偏移 + 4(flags)+ 4 (parent)
        let nameOffset = Int64(name) + Int64(typeDescOffset) + 8
        print(nameOffset)
        //类名地址
        let nameAddress = nameOffset + Int64(headerPtr_IntRepresentation)
        print(nameAddress)
        //获取地址里面的内容
        if let cChar = UnsafePointer<CChar>.init(bitPattern: Int(nameAddress)){
            let className = (String(cString: cChar))
            print("类的名称:\(className)")
        }
    }
//控制台输出45500 4295012796 类的名称:YYPeople
nameAddress.png

macho计算name的地址信息
0xB1D0 + 0xFFFFFFEC = 0x10000B1BC
转换成10进制 4295012796 跟计算出来的地址一致
name的偏移量
0x10000B1BC - 0x0x100000000 = 0xB1BC
转换成10进制45500跟计算出来的一致
通过B1BC在macho中找到name存储的信息YYPeople


macho中name.png

获取属性名称和类型

 //获取FieldDescriptor相对地址: 描述文件的偏移 + 4(flags)+ 4 (parent)+4(name)+ 4(accessFunctionPointer)
    let fieldDescriptorRelaticveAddress = typeDescOffset + 16 + headerPtr_IntRepresentation
    print("fieldDescriptorRelaticveAddress:\(fieldDescriptorRelaticveAddress)")

    //计算FieldDescriptor偏移量
    let fieldDescriptorOffset = UnsafePointer<UInt32>.init(bitPattern: (Int(exactly: fieldDescriptorRelaticveAddress) ?? 0))?.pointee
    print("fieldDescriptorOffset:\(fieldDescriptorOffset!)")

    //计算FieldDescriptor的绝对地址
    let fieldDescriptorAddress = fieldDescriptorRelaticveAddress + UInt64(fieldDescriptorOffset!)
    print("fieldDescriptorAddress:\(fieldDescriptorAddress)")

    struct FieldDescriptor  {
        var mangledTypeName: Int32
        var superclass: Int32
        var Kind: UInt16
        var fieldRecordSize: UInt16
        var numFields: UInt32
       // var fieldRecords: [FieldRecord]//属性列表(占位用,方便后面通过偏移得到首地址)
    }

    struct FieldRecord{
        var Flags: UInt32
        var mangledTypeName: Int32//属性类型
        var fieldName: UInt32//属性名称
    }

    //将fieldDescriptorAddress转换成FieldDescriptor
    let fieldDescPtr = UnsafePointer<FieldDescriptor>.init(bitPattern: Int(exactly:fieldDescriptorAddress) ?? 0)?.pointee

    for i in 0..<fieldDescPtr!.numFields{
        //计算FieldRecord的步长(4+4+4 = 12)
        let stride: UInt64 = UInt64(i * 12)
        //计算FieldRecord的地址:fieldDescriptorAddress地址 偏移16字节(4+4+2+2+4)找到FieldRecord首地址,然后偏移对应的步长得到fieldRecords中所有属性FieldRecord
        let fieldRecordAddress = fieldDescriptorAddress + stride + 16
        //找到fieldName相对地址
        let fieldNameRelactiveAddress = UInt64(2 * 4) + fieldRecordAddress - linkBaseAddress + headerPtr_IntRepresentation
        //计算fieldName的偏移量
        let fieldNameOffset = UnsafePointer<UInt32>.init(bitPattern: Int(exactly: fieldNameRelactiveAddress) ?? 0)?.pointee
    //    print("fieldNameOffset:\(String(describing: fieldNameOffset))")
        //找到fieldName实际地址
        let fieldNameAddress = fieldNameRelactiveAddress + UInt64(fieldNameOffset!) - linkBaseAddress
        if let cChar = UnsafePointer<CChar>.init(bitPattern: Int(fieldNameAddress)){
            print("属性名称:\(String(cString: cChar))")
        }

        //找到mangledTypeName相对地址
        let mangledTypeNameRelactiveAddress = UInt64(4) + fieldRecordAddress - linkBaseAddress + headerPtr_IntRepresentation
        //计算fieldName的偏移量
        let mangledTypeNameOffset = UnsafePointer<UInt32>.init(bitPattern: Int(exactly: mangledTypeNameRelactiveAddress) ?? 0)?.pointee
       // print("mangledTypeNameOffset:\(String(describing: mangledTypeNameOffset))")
        //找到mangledTypeName实际地址
        let mangledTypeNameAddress = mangledTypeNameRelactiveAddress + UInt64(mangledTypeNameOffset!) - linkBaseAddress
        if let cChar = UnsafePointer<CChar>.init(bitPattern: Int(mangledTypeNameAddress)){
            //Si : 就是Int,SS : 就是String,Sd : 就是Double
            var typeStr :String
            switch (String(cString: cChar)){
            case "Si":
                 typeStr = "Int"
            case "SS":
                 typeStr = "String"
            case "Sd":
                 typeStr = "Double"
            default:
                 typeStr = "unknow"
            }
            print("属性类型:\(typeStr)")
        }
    }
//控制台输出fieldDescriptorRelaticveAddress:4295012824
//fieldDescriptorOffset:2456
//fieldDescriptorAddress:4295015280
//属性名称:age
//属性类型:Int
//属性名称:name
//属性类型:String
//属性名称:height
//属性类型:Double
FieldDescriptor首地址计算.png
fieldName首地址计算.png

属性名称.png

获取方法列表

  //方法结构
  struct TargetMethodDescriptor {
  var kind: UInt32
   var impl: UInt32
   }
   //classDescriptor!.size存储着方法的个数
    for i in 0..<classDescPtr!.size{
        //偏移量 = 描述文件偏移量 + 描述文件结构尺寸 + i * 方法size
        let targetMethodOffset = Int(typeDescOffset) + MemoryLayout<TargetClassDescriptor>.size + Int(i) * MemoryLayout<TargetMethodDescriptor>.size
        //运行起始地址 + 偏移量 = 方法地址
        let targetMethodAddress = headerPtr_IntRepresentation + UInt64(targetMethodOffset)
        // 格式化数据结构
        let targetMethod = UnsafePointer<TargetMethodDescriptor>.init(bitPattern: Int(exactly: targetMethodAddress) ?? 0)?.pointee
        // 方法地址 = 地址 + Flags(4字节) + impl偏移量 - 基地址
        let impAddress = targetMethodAddress + 4 + UInt64(targetMethod!.impl) - linkBaseAddress
        //判断是自己自定义的方法,直接调用(过滤系统方法)
        if let kind = targetMethod?.kind  , (kind & 15) == 0{
            let imp = IMP(bitPattern: UInt(impAddress))
            //通过oc来调用,因为还原符号表麻烦(其实是不会😄),所有直接通过oc的imp调用
            YYTest.callImp(imp!)
            //控制台输出
            //testFunc 
            //test
        }
    }

YYTest代码

@interface YYTest : NSObject

+ (void)callImp:(IMP)imp;

@end

@implementation YYTest

+ (void)callImp:(IMP)imp
{
    imp();
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • 参考文献 Swift结构体指针操作官方文档Swift 和 C 不得不说的故事Swift指针和托管,你看我就够了 W...
    沉静BBQ阅读 8,001评论 1 25
  • 为了避免疏漏, 我从官方文档作了截图, 苹果官网文档1 , 文档2 本文概要 按照官方文档, 介绍Swift中的指...
    Lin__Chuan阅读 2,650评论 12 9
  • Swift 指针 前言 指针,作为编程中最重要的概念,一直存在于各大语言中,下面我们就来探索一下Swift中的指针...
    just东东阅读 681评论 0 3
  • 指针不是安全的 1.野指针。指针指向对象释放,指针变为野指针 2.指针超出内存空间边界访问。如数组越界 3.原⽣指...
    张天宇_bba7阅读 551评论 0 1
  • 指针分类: raw pointer:未指定数据类型的指针(原生指针) typed pointer:指定数据类型的指...
    BBLv阅读 555评论 0 1