Swift源码还原StructMetadata

329 阅读3分钟

我们上篇讲到了还原 MacO 文件信息,那么我们这篇来看下如何参考 Swift 源码来还原 StructMetadata 信息,达到打印出属性名称、类型、值的作用

StructMetadata定义

TagerStructMetadata

我们通过Swift 源码及Swift类和结构体(二) 的了解,来还原 StructMetadata 信息。

    1. 在源码中直接搜StructMetadata 可以发现是 TargetStructMetadata 别名。

image.png

  • 2. 在 TargetStructMetadata 中看到是 继承自 TargetValueMetadata , 并有一个 Description 属性。

image.png

image.png

    1. TargetValueMetadata 中看到是 继承自 TargetMetadata , 并有一个 Kind 属性

image.png

通过Kind属性的查看,得知其是一个 UInt64(64位机器,32位机器则为UInt32) 指针.

固初步能得到 TagerStructMetadata的结构为

struct TagerStructMetadata {
    var kind:UnsafePointer<UInt64>
    var typeDescription: UnsafeMutablePointer
}

TargetValueTypeDescriptor

    1. 我们在 TagerStructMetadata 中看到 Description 属性是通过 getDescription 来获取的,并其 泛型类型是 TargetStructDescriptor 通过对类 TargetStructDescriptor 解读,发现两个属性,并其继承自 TargetValueTypeDescriptor
uint32_t NumFields;
uint32_t FieldOffsetVectorOffset;

image.png

image.png

    1. 在 类 TargetValueTypeDescriptor 中又发现 三个属性
TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false> Name;
TargetRelativeDirectPointer<Runtime, MetadataResponse(...), /*Nullable*/ true> AccessFunctionPtr;

TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor, /*nullable*/ true> Fields;

image.png

    1. 而类 TargetValueTypeDescriptor 又继承自 TargetContextDescriptor ,在 TargetContextDescriptor 中 也有两个属性
ContextDescriptorFlags Flags;
TargetRelativeContextPointer<Runtime> Parent;

image.png

固整体 我们得知

struct TargetValueTypeDescriptor {
    var flags: UInt32
    var parent: UInt32
    var name: TargetRelativeDirectPointer<CChar>
    var accessFunctionPtr: TargetRelativeDirectPointer<UnsafeRawPointer>
    var fieldDescriptor: TargetRelativeDirectPointer<FieldDescriptor>
    var numFields: UInt32
    var fieldOffsetVectorOffset: Int32
}

FieldDescriptor

在这个结构体里 FieldDescriptor 通过以上的方法步骤可以得到

struct FieldDescriptor {
    var mangledTypeName: TargetRelativeDirectPointer<CChar>
    var superclass: TargetRelativeDirectPointer<CChar>
    var kind: UInt16
    var fieldRecordSize: UInt16
    var numFields: UInt32
    var fields: FiledRecordBuffer<FieldRecord>
}

image.png

image.png

FieldRecord

在这个结构体里 FieldDescriptor 里 用到了 FieldRecord,同样可以得到

struct FieldRecord {
    var flags: UInt32
    var mangledTypeName: TargetRelativeDirectPointer<CChar>
    var fieldName: TargetRelativeDirectPointer<CChar>
}

image.png

StructMetadata完整实现

综合整理后得到如下代码


protocol BrigeProtocol {}

extension BrigeProtocol {
    static func get(from pointer: UnsafeRawPointer) -> Any {
        pointer.assumingMemoryBound(to: Self.self).pointee
    }
}

//协议的Metadata:
//协议见证表
struct BrigeProtocolMetadata {
    let type: Any.Type
    let witness: Int
}

func customCast(type: Any.Type) -> BrigeProtocol.Type {
    let container = BrigeProtocolMetadata(type: type, witness: 0)
    let protocolType = BrigeProtocol.Type.self
    let cast = unsafeBitCast(container, to: protocolType)
    return cast
}





struct TagerStructMetadata {
    var kind:UnsafePointer<UInt64>
    var typeDescription: UnsafeMutablePointer<TargetValueTypeDescriptor>
}

struct TargetValueTypeDescriptor {
    var flags: UInt32
    var parent: UInt32
    var name: TargetRelativeDirectPointer<CChar>
    var accessFunctionPtr: TargetRelativeDirectPointer<UnsafeRawPointer>
    var fieldDescriptor: TargetRelativeDirectPointer<FieldDescriptor>
    var numFields: UInt32
    var fieldOffsetVectorOffset: Int32
    
    func getFieldOffsets(_ metadata: UnsafeRawPointer) -> UnsafePointer<Int32>{
        
        print("self.fieldOffsetVectorOffset \(self.fieldOffsetVectorOffset)" )
        return metadata.assumingMemoryBound(to: Int32.self).advanced(by: numericCast(self.fieldOffsetVectorOffset)*2)
    }
    
    var genericArgumentOffset: Int {
        return 2
    }
}

struct TargetRelativeDirectPointer<Pointee>{
    
    var offset: Int32
    
    mutating func getmeasureRelativeOffset() -> UnsafeMutablePointer<Pointee>{
        let offset = self.offset
        
        return withUnsafePointer(to: &self) { p in
            return UnsafeMutablePointer(mutating: UnsafeRawPointer(p).advanced(by: numericCast(offset)).assumingMemoryBound(to: Pointee.self))
        }
    }
}

struct FieldDescriptor {
    var mangledTypeName: TargetRelativeDirectPointer<CChar>
    var superclass: TargetRelativeDirectPointer<CChar>
    var kind: UInt16
    var fieldRecordSize: UInt16
    var numFields: UInt32
    var fields: FiledRecordBuffer<FieldRecord>
}

struct FieldRecord {
    var flags: UInt32
    var mangledTypeName: TargetRelativeDirectPointer<CChar>
    var fieldName: TargetRelativeDirectPointer<CChar>
}

struct FiledRecordBuffer<Element>{
    var element: Element
    
    mutating func buffer(n: Int) -> UnsafeBufferPointer<Element> {
        return withUnsafePointer(to: &self) {
            let ptr = $0.withMemoryRebound(to: Element.self, capacity: 1) { start in
                return start
            }
            return UnsafeBufferPointer(start: ptr, count: n)
        }
    }
    
    mutating func index(of i: Int) -> UnsafeMutablePointer<Element> {
        return withUnsafePointer(to: &self) {
            return UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self).advanced(by: i))
        }
    }
}

struct Teacher {
    let age: Int = 18
    let name: String = "32"
    let name2: String = "33"
    let sex: Bool = true
}

var t = Teacher()


//结构体放入 TagerStructMetadata 这个自定义的metadata
let ptr = unsafeBitCast(Teacher.self as Any.Type, to: UnsafeMutablePointer<TagerStructMetadata>.self)

//结构体名称
let namePtr = ptr.pointee.typeDescription.pointee.name.getmeasureRelativeOffset()
print("当前结构体名称: \(String(cString: namePtr))")

//结构体的属性数量
let numFileds = ptr.pointee.typeDescription.pointee.numFields
print("当前结构体属性的个数: \(numFileds)")

//属性值的偏移量metadata.
let offsets = ptr.pointee.typeDescription.pointee.getFieldOffsets(UnsafeRawPointer(ptr))//不用多次绑定 .assumingMemoryBound(to: UInt32.self) 

//桥接 系统中 C++ 的方法,得到真实的Type类型
@_silgen_name("swift_getTypeByMangledNameInContext")
func swift_LI_getTypeByMangledNameInContext(typeName: UnsafeRawPointer, len: Int, context: UnsafeRawPointer, generic: UnsafeRawPointer) -> UnsafeRawPointer


print("======= start fetch filed ======")

for i in 0..<numFileds{

    let fieldDespritor = ptr.pointee.typeDescription.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)).pointee.fieldName.getmeasureRelativeOffset()
    print("--- filed: \(String(cString: fieldDespritor)) info begin ----")

    let fieldOffset = offsets[Int(i)]
    print("属性偏移地址: \(fieldOffset)")
    //Int ,String
    let typeMangleName = ptr.pointee.typeDescription.pointee.fieldDescriptor.getmeasureRelativeOffset().pointee.fields.index(of: Int(i)).pointee.mangledTypeName.getmeasureRelativeOffset()
    print("混编的类型名称:\(String(cString: typeMangleName))")


    let genericVector = UnsafeRawPointer(ptr).advanced(by: ptr.pointee.typeDescription.pointee.genericArgumentOffset * MemoryLayout<UnsafeRawPointer>.size).assumingMemoryBound(to: Any.Type.self)

    //HandJSON
    let fieldType = swift_LI_getTypeByMangledNameInContext(
        typeName: typeMangleName,
        len: 256,
        context: UnsafeRawPointer(ptr.pointee.typeDescription),
        generic: UnsafeRawPointer(genericVector)!.assumingMemoryBound(to: Optional<UnsafeRawPointer>.self))

    //比较难理解,参考 HandJSON
    let type = unsafeBitCast(fieldType, to: Any.Type.self)

    let value = customCast(type: type)
    
    let instanceAddress = withUnsafePointer(to: &t) {
        return UnsafeRawPointer($0).assumingMemoryBound(to: Int8.self)
    }
    
    print("fieldType:\(type) \nfieldValue: \(value.get(from: UnsafeRawPointer(UnsafeRawPointer(instanceAddress).advanced(by: numericCast(fieldOffset))))) ")
    
    print("--- filed: \(String(cString: fieldDespritor)) info end ---- \n")
}

在这代码中 genericArgumentOffset 函数返回了2 , 这个我们通过源码可以找到这个描述, 在64位机器下经过测量

let uLong = MemoryLayout<TargetStructMetadata>.size
let uInt = MemoryLayout<UInt64>.size
print("long \(uLong)  uInt \(uInt)")

打印结果
long 16  uInt 8
Program ended with exit code: 0

image.png

至于 getFieldOffsets 函数 中 fieldOffsetVectorOffset 函数为啥要 *2 ,我们在 HandJSON 中 找到答案,也是因为64位机器还是32位机器的原因

image.png

StructMetadata实现结果

运行上面的完整实例后,得到如下信息

image.png 至此我们已经完成了对Swift源码的 StructMetadata 的还原及验证