swift指针

作者: 余晖依旧耀眼 | 来源:发表于2022-01-14 19:55 被阅读0次

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();
}

相关文章

  • Swift指针|内存管理

    一、Swift指针 1.Swift指针简介 swift中的指针分为两类 typed pointer 指定数据类型指...

  • swift pointer

    swift 指针 swift中的指针分为两类 typed pointer 指定数据类型指针,即 UnsafePoi...

  • 在Swift中使用C语言的指针

    在Swift中使用C语言的指针 在Swift中使用C语言的指针

  • swift基础_指针

    一.swift指针类型 swift认为指针是不安全的,所有指针的类的前缀为unsafe UnsafePointer...

  • swift 指针学习

    学习资料 Swift 指针UnsafePointerUnsafe Swift: Using Pointers an...

  • Swift-进阶 04:指针

    本文主要介绍swift中的指针 swift中的指针分为两类 typed pointer 指定数据类型指针,即 Un...

  • Swift进阶 04:指针

    本文主要介绍Swift中的指针 Swift中的指针主要分为两类 typed pointer 指定数据类型的指针,即...

  • siwft指针

    swift 指针 UnsafePointer,UnsafeMutablePointer ,可变和不可变指针可以相互...

  • Swift5.1 - 指针Pointer

    指针分类 使用swift提供指针类型: UnsafePointer UnsafeMutablePointer Un...

  • swift中的指针

    Swift 中的指针使用 本文转载自OneV's Den的blogApple 期望在 Swift 中指针能够尽量减...

网友评论

    本文标题:swift指针

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