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方法。
初始化
通过已有变量获取
直接分配内存
1.2.2、UnsafeMutablePointer
初始化
UnsafeMutablePointer
是可变的,有getter和setter
通过已有变量获取
直接分配内存
被UnsafeMutablePointe引用的内存有三种状态:
- Not Allocated:内存没有被分配,这意味着这是一个 null 指针,或者是之前已经释放过
- Allocated but not initialized:内存进行了分配,但是值还没有被初始化
- Allocated and initialized:内存进行了分配,并且值已经被初始化
可以通过下图直观表示
1.2.3、UnsafeBufferPointer
UnsafeBufferPointer
表示一组连续数据指针。BufferPointer实现了Collection,因此可以直接使用Collection中的各种方法来遍历操作数据,filter,map...,Buffer可以实现对一块连续存在空间进行操作,类似C中的数组的指针。 但是同样的,这个UnsafeBufferPointer是常量,它只能获取到数据,不能通过这个指针去修改数据。与之对应的是UnsafeMutableBufferPointer
指针。
1.2.4、UnsafeMutableBufferPointer
可变的序列指针,UnsafeMutableBufferPointer
拥有对指向序列修改的能力:
注意:如果一个序列被初始化之后,没有给每一个元素赋值的话,这些元素的值都将出现随机值。
1.3、raw pointer(原生指针)
1.3.1、UnsafeMutableRawPointer
用于访问和操作非类型化数据的原始指针。
注意:在存值的时候,需要移动内存地址,不然就会覆盖掉当前内存上之前存储的值
1.3.2、UnsafeRawPointer
用于访问非类型化数据的原始指针。UnsafeRawPointer
只能由其他指针用init方法得到,与UnsafePointer
类似,没有allocate
静态方法。
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 类型实例的指针和上面不同,不是使用withUnsafePointer
或 withUnsafeMutablePointer
,而是使用下面讲到的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
获取
获取链接的基地址
//第一种方法
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
获取__swift5_types的偏移
__swift5_types
中存储的就是swift的class
,strcut
和enum
//获取__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文件定位
获取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)
获取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结构体
//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
macho计算name的地址信息
0xB1D0 + 0xFFFFFFEC = 0x10000B1BC
转换成10进制 4295012796 跟计算出来的地址一致
name的偏移量
0x10000B1BC - 0x0x100000000 = 0xB1BC
转换成10进制45500跟计算出来的一致
通过B1BC在macho中找到name存储的信息YYPeople
获取属性名称和类型
//获取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
获取方法列表
//方法结构
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();
}