Aspects swift 源代码分析

steipete/Aspects

的规划,挺不可捉摸的

  • 一般咱们 hook, hook 一个类的全部实例的办法

这儿 hook 的粒度,到了政策等级

  • 缓存且不需求appreciate树立,许多的 I缓存视频MP

利用到了固有的办法转发的流程

本文首要参阅 woshiccm/Aspect

分析下 Aspects 的 swift 源代码结束

Aspect数据结构难学吗s 的大约规划

1, 办法换一个机会实施

1.1, 办法转发的流程

办法实施的时分,将 hook 的 selector 的结束 IMP 替换为 _objc_msgForward 或许_objc_msgForward_stret

github中文官网网页刻调用 se数据结构题库及答案lector, 就会进行消息转发

1.2,实践做事情的函数

forwardInvocation 的结束, 替换为自定义的函数

__ASPECTS_ARE_BEING_CALLED__

并增加 __aspects_forwardInvocation 的结束为 forwardInvocation 本来的结束。

咱们 hook 的办法 selector ,需求进行消APP息转发

咱们 hoAPPok 的办法 selector, 都会实施自定义的函数

__AS数据结构知识点总结PECTS_ARE_BEING_CALLED__

1.3, 自定义的函giti轮胎数中

实施了本来的办法,和 hook 的 block

这样,本来的办法,仍缓存视频在手机哪里找是跑了,但是在一个不同的机会

便利咱们处理

2, 需求实施的 hook 办法

得到了缓存的视频怎样保存到本地有利的机会

把 hook 在前面,在后面,用来替换的办法,给实施了

2.1, 实施对应的 block

Aspects 中,把 hook 的 block 的描述信息GitHub中,捞出 NSMethodSignature

运用 NgitlabSMethodS缓存视频兼并ignature,实施 hook 的 block

2.2,实施本来的办法

NSInvocation 的实例, invokeappstore

Swift Asgithub下载pect 中的详细结束

3.1 调用

有这么个实例办法

    @objc dynamic func test(id缓存视频怎样转入本地视频: Int, name: String) {
print("come on")
}

hook 上面那个办法

        _ = try? hook(selector: #selector(ViewController.test(id:namegithub是干什么的:)), strategy: .before) { (_, idgitlab: Int, name: String) in
print("done")
}

初步 hook


@discagithub中文官网网页rdableResult
func hook<Arg1, Arg2>(
selector: Selector,
strategy: AspectStapproverategy = .before,
block: @escaping(AspegitlabctInfo, Agithub永久回家地址rg1, Arg2) -> Voi数据结构与算法d) throwsgithub中文社区 -> AspectToken
{
// 拿到需求刺进实施的 block
let w数据结构与算法rappedBlock: @appearco缓存的视频怎样保存到本地nvention(block) (A数据结构严蔚敏spectInfoappearance) -缓存视频变成本地视频&g缓存视频兼并t; Void = { aspectInfo in
guard aspectInfo.arguments.count == 2,
let agithub怎样下载文件rg1 = aspectInfo.arguments[0] as? Arg1,
leGitt arg2 = aspectInfo.arguments[1] as? Arg2 else { return }
block(aspectInfo, arappleg1, arg2)
}
let wrappedObject: AnyObapproachject = unsafeBitCast(wrappedBlock, to: AnyObject.self)
// 合作appstore hook 住的办法,去 hook
returngit教程 try hook(selector: selector, strAPPategy: strategy, block: wraappreciateppedObject)
}

增加分类,

语法糖,便利办法

extension NSObappearanceject {
pu缓存视频兼并app下载blic func hook(缓存的视频怎样保存到本地selector: Selector, strategy: AspectStrategy = .befgithub是干什么的ore, bappstorelock: AnyObj数据结构ect) throws -> AspectToken {
r缓存文件在哪里eturn try ahook(object: self, selector: selector, strateggiti轮胎y: strategy, blockgithub敞开私库: block)
}
}

3.2 hookGit 首要流程

真实做事情的


func ahook(object: AnyObject, selector: Selector, strategy: Aspect数据结构教程第5版李春葆答案Strategy, block: AnyObject) throws -> AspectTgitlaboken {
// 加锁保护
return try l缓存文件在哪里ock.performLock数据结构与算法ed {
// 记录全部的信息
let identifier = tr数据结构c语言版第二版课后答案y Aspecgit教程tIdentif缓存的视频怎样保存到本地ier.identifier(with: selector, object: object, strategy: strategy, block: block)
// 存起来
let cache = getAspectCache(for: object, selector: selector)
cache.add(identifier, option: strategy)
// 为了 hook
// 初步预备
// 创建待 hook 的子类
let subclass: AnyClass = try hookClass(object: object, selector: selector)
let method = class_gegit教程tInstanceMethod(subclass, selegithubctor) ?? class_getClassMethod(subclass, selector)
guard let impl = method.map(method_getImplementation), let typeEncoding = method.flatMap(method_getTypeEncoding) else {
throw AspectError.unrecognizedSelector
}
assert(checkTypeEncoding(typeEncoding))
let aliasSelector = selector.alias
if !subclass.instancesRespond(to: aliasSelector) {
// 这儿是,把办法本来的结束github中文社区,增加到另一个 selector
// 意图是,调用被 hook 的办法
let succeeds = class_aappearanceddMethod(subclass, aliasSelectappointmentor,apple impl, typeEncoding)
precondition(succeeds, "not OK")
}
/appear/ 将被 hook数据结构题库及答案 办法的结束改为
// forwardInvocation ( 消息转发 )
// 与上面的 1.缓存文件在哪里1, 一起
class_replaceMethod(github直播渠道永久回家subclass, selector, _aspect_objc_msgForward, typeappearEncoding)
return identifier
}
}

3.3, 动态创建子类 ( 有点 KVO 那味 )


private func hookClass(object: AnyO数据结构题库及答案bject, selector: Selector) throws -> AnyClass {
let perceivedClass: AnyClass = object.objcCla数据结构ss
//  获取 object 的 iapplicationsa 指针
let realClass: AapproachnyClass = object_getClass(object)!
let className = String(cString: clagithub直播渠道永久回家ss数据结构知识点总结_getName(realClass))
if classNgiteeame.hasPrefix(Constants.subclassPrefix) {
// 之前 hook 过数据结构教程第5版李春葆答案
// 当 hook 一个政策的 selector 时会生成一个子类,
// 子类给了一个前缀
// 如 object 对应的类便是生成的子类,直接回来
return realClass
} else if class_isMetaClass(realClass) {
// 判别是否为类政策,假设是,则直接在其时类中进行 swizzle
if class_getInstanceMethod(perceivedClass, selector) == nil {
// 判别是否为 KVO 过的政策,
// 由于 KVO 的政策 ISA 指针指向一个中心类,
// 则giti直接在这个直接类,进行 swizzle
// 中心类便是,咱们动态创建的子类
// hook 了,持续 hook
swizzleForwardInvgithub永久回家地址ocation(realClass)
r缓存文件在哪里eturn realClass
} else {
swappearanceizzlgiteeeForwardInvocatioappearancen(perceivedClass)
return perceivedClass
}
}
let subclassName = Constants.subclassPrefix+className
let subclass: AnyClass? = subclagithub永久回家地址ssapproveName.缓存文件在哪里withCString { cString in
if let existingClass = objc_getClass(cString) as! AnyClass? {
return existingClas缓存视频兼并app下载s
} else {
// 动态创建子类
if let subclass: AnyClass = objc_allocateClassPair(perceivedClass, cString, 0) {
// 替换其时类的 forwardInvocati数据结构严蔚敏on 的办法结束为
// __ASPECTS_ARE_BEING_CALLED__
// 这儿的 aspectForwardInvocation
swizzleForwardInvocation(subclaAPPss)
// 把生成子类的 isa 指针, 指向本来的类
// 并且,把生成子类的元类的 isa 指针,指数据结构题库及答案向本来的类
replaceGetClass(in: subclass, decoy: per数据结构严蔚敏ceivedClass)
// 注册其时生成的子类
objc_registerClappreciateassPair(subclass)
return subclass
} else {
return nil
}
}
}
guard let nonnullSubclass = subclass else {
throw AspectError.failedToAllocategithub是干什么的ClassPair
}
// 将其时政策的 isa 指针
// 指向刚生成的子类
// 即 KVO 的中心类
objgithubect_setClass(object, nonnullSubclass)
return nonnullSubclass
}

3.4 办法交流

交流结束,把办法转发的结束,运用自定义的匿名函数 closure, aspectForwardInvocation

aspectForwardInvocation, 相当于 OC 版其他

__ASPEC缓存清理TS_ARE_BEING数据结构严蔚敏_CALLED__giti


private func swizzleForwardIgiti轮胎nvocation(_ realClass数据结构题库及答案: AnapplicationyClasGitHubs) {
guard let originalImplementation = class_replaceMethod(realClass,
ObjCSel数据结构c语言版第二版课后答案ector.forwardInvocation,
imp_implementationWithBlock(aspectForwardInvocation as Any),
ObjCMethgiti轮胎odEnc数据结构难学吗oding.forwardInvocatappearanceion) else {
return
}
}

把原appreciate本的办法,和刺进的 block 都给实施了


private let aspectForwardInvocatAPPion: @convention(block) (Unmanaged<NSObject>, AnyObject) -> Void = { objectRef, invocation in
// 预备实施的信息
let objegithub永久回家地址ct = obj数据结构c语言版ectRef.takeUgithub中文社区nretainedValue() as AnyObject
let selector数据结构题库及答案 = invocation.sel数据结构c语言版ector!
var aliasS缓存文件在哪里elector = selector.alias
var aliasSegithub中文社区lectorKey = AssociationKey<AspectsCache?>(aliasSelector)
let selectorKey = AssociationKey<AspectsCache?>(selector)
let assocappointmentiationsgithub永久回家地址 = Associations(object.objcClass as Agit命令nyObject)
let aspectCache: AspectsCache
// 掏出缓存
if let cache = associations.value(forKey: selectorKey) {
aspectCache = cache
} else if let ca数据结构与算法che = (objectRef.takeUnretainedValue() as数据结构与算法 NSObject).associations.val数据结构题库及答案ue(forKey: aliasSelectorKey) {
aspectCache = cache
} else {
return
}
var info = AspectInfo(instance: obje数据结构ct, invocation: ingithub打不开voc缓存视频变成本地视频ation)
// Before hooks.
// 刺进前
aspectCache.beforeAspects.invoke(with: &info)
if aspect缓存视频在手机哪里找Cache.insteadAspects.isEmpty {
// 实施本来的
invocation.setSelgithub中文官网网页ector(aliasSelector)
invocation.invoke()
}缓存视频变成本地视频 e缓存视频变成本地视频lse {
// Instead hooks
// 实施,刺进替换
aspectCache.insteadAspects.invoke(with: &info)
}
// 刺进后
// After hooks.
aspectCache.afterAspects.invoke(with: &i数据结构c语言版nfo)
}

3.5 之前的预备,信息搜集

把 hook 住的 selector 的信息,和刺进的github永久回家地址实施 block , 都git教程记录下来


static func identifier(with selector: S缓存的视频怎样保存到本地elector, object:github中文官网网页 AnyObject, strategy: AspectStrategyappreciate, block: AnyObject) throws -> Aspapp安装下载ectIdentifier {
guard let blockSignature = AspectBlock(blgitiock).blockSignature else {
thrgithub直播渠道永久回家ow AspectE数据结构与算法rr数据结构课程规划or.missingBlockSignature
}
do {
try isCompatibleBlockSignature(blockSignature: blockSignature, object: object, selector: selector)
var aspgithub永久回家地址ectIdentifgiti轮胎ier = AspectId数据结构与算法entifier(selector: selector, object: object, strategy: strategy, block: block)
asp缓存的视频怎样保存到本地ectIdentifier.blockSignature = blockSignature
return aspectIdentifier
} catch {
throw error
}
}

hook 前的判别

假设 hook 的函数签名appointment,与刺进的 bl缓存视频怎样转入本地视频ock 的函数签名,对不上,

就搞不下去了

签名信息参数前两位是默认的,

Argument 0 是 self/block

argument 1 是 SEL or id<AspectInfo>

所以从 index = 2 初步校验, 也便是第 3 位


static func i缓存视频变成本地视频sCompatibleBlockSignature(blockSignature: AnyObject, object: AnyObject, selector: Selector) throws {
let perceiveappointmentdClass: AnyClass = object.objcClass
let realClass: AnyClass = object_getClass(object)!
let method = class_getInstanceMethod(perceivedClass, selectorappear) ?? class_getClassMethod(realClass, selector)
guard let nonnullMethod = method, let t缓存文件在哪里ypeEncoding = method_getTypeEncoding(nonnulappearancelMethod)appreciate else {
object.doesNotRecognizeSelector?(selector)
throw AspectError.unrecognizedSelector
}
let signature = NSMethodSignature.signature(objCTypes: typeEncoding)
var signaturesMatch = true
if blockSign数据结构c语言版ature.objcNumb数据结构知识点总结erOfArguments >appear signature.objcNumberOfArgumengithub下载ts {
// block 签名参数一定是,不大于办法签名参数
// 检查签名的长度
signaturesMatch =github中文官网网页 fappearalse
} else {
if bloAPPckSignature.objcNumberOfArguments > 1 {
/缓存视频兼并/ 判别数据结构与算法第 2 位
let rawEncoding = blockSiggithubnature.getArgumentType(aapprovet: UInt(1))
let encoding = ObjCTypeEncoding(rawValue: rawEncoding.pointee) ?? .undef数据结构严蔚敏第二版课后答案ined
/缓存/ // 遵照 AspectInfo 协议政策
if enc数据结构c语言版oding != .object {
signaturesMatch = false
}
}
if signaturesMatch {
// 从第 3 位,初步判别
for index in 2 ..<github永久回家地址 blockSignature.objcNumberOfArguments {
let methodRawEncoding = sig数据结构教程第5版李春葆答案nature.getArgumentType(at: index)
let blockRawEncoding = blockSignatu缓存视频兼并re.getArgumentTypgithub打不开e(at: index)
let methodappearanceEncoding = ObjCTypeEncoding(rawValue: methodRawEncoding.Gitpointee) ?? .undefined
let blockEncoding = ObjCT数据结构与算法ypeEncoding(rawValue: blockR数据结构严蔚敏第二版课后答案awEncoding.pointeegit教程) ?? .undefined
if methodEncoding != blockEncoding缓存视频兼并app下载 {
signaturesMatch = false
breagithub是干什么的k
}
}
}
}
if !sigGitHubnatur缓存视频变成本地视频esMatch {
throw Aspectapp安装下载Error.blockSignatureNotMatch
}
}

特征:

Aspects 的源代码,有许gitlab多强转的当地

由于 runtime 的好多数据结构approach,不对数据结构题库及答案外揭穿

所以需求自个,定义一些类似的结构

编译时期,runtime 内部的内存结构

与咱们定数据结构c语言版第二版课后答案义的内存结构,

是一一对应得上的,那就能够强转

比照

Swift 版其他逻辑,底子转化自github敞开私库 OC 版其他

  • __aspect_forwardInvocation:, 没用的办法,相同没用

  • alia数据结构课程规划s 办法,本来的办法名加前缀

就像交流变量相同, 把 a 的值,换为 b 的值

原办法的 IMP, 变办法转数据结构c语言版第二版课后答案

增加 alias 办法,跑原办法的 IMP

Swift 版其他,够用,存在缺少

缺陷:

Swift 版其他,比起 OC 版其他,

功用不全 ( 一般用不到的 )

现存的功用,也没 OC 版其他健壮

OC 版其他 hook 的 block, 参数处理更典雅

github repo