diff --git a/Sources/WeChatTweak/Command.swift b/Sources/WeChatTweak/Command.swift index f0642b3..5366db9 100644 --- a/Sources/WeChatTweak/Command.swift +++ b/Sources/WeChatTweak/Command.swift @@ -23,8 +23,8 @@ struct Command { try await Command.execute(command: "defaults read \(app.appendingPathComponent("Contents/Info.plist").path) CFBundleVersion") } - static func patch(app: URL, config: Config) async throws { - try Patcher.patch(binary: app.appendingPathComponent("Contents/MacOS/WeChat"), config: config) + static func patch(app: URL, targets: [Target]) async throws { + try Patcher.patch(binary: app.appendingPathComponent("Contents/MacOS/WeChat"), entries: targets.flatMap { $0.entries }) } static func resign(app: URL) async throws { diff --git a/Sources/WeChatTweak/Patcher.swift b/Sources/WeChatTweak/Patcher.swift index 142d339..afa277d 100644 --- a/Sources/WeChatTweak/Patcher.swift +++ b/Sources/WeChatTweak/Patcher.swift @@ -17,12 +17,11 @@ struct Patcher { case noArchMatched } - static func patch(binary: URL, config: Config) throws { + static func patch(binary: URL, entries: [Target.Entry]) throws { guard FileManager.default.fileExists(atPath: binary.path) else { throw Error.invalidFile } - let entries = config.targets.flatMap { $0.entries } guard !entries.isEmpty else { throw Error.noArchMatched } let fh = try FileHandle(forUpdating: binary) diff --git a/Sources/WeChatTweak/Config.swift b/Sources/WeChatTweak/Target.swift similarity index 63% rename from Sources/WeChatTweak/Config.swift rename to Sources/WeChatTweak/Target.swift index 375ba59..dce54d6 100644 --- a/Sources/WeChatTweak/Config.swift +++ b/Sources/WeChatTweak/Target.swift @@ -1,29 +1,28 @@ // -// Config.swift +// Target.swift // WeChatTweak // // Created by Sunny Young on 2025/12/5. // import Foundation -import MachO -struct Config: Decodable { - enum Arch: String, Decodable { - case arm64 - case x86_64 +struct Target: Decodable { + struct Entry: Decodable { + enum Arch: String, Decodable { + case arm64 + case x86_64 - var cpu: UInt32 { - switch self { - case .arm64: - return UInt32(CPU_TYPE_ARM64) - case .x86_64: - return UInt32(CPU_TYPE_X86_64) + var cpu: UInt32 { + switch self { + case .arm64: + return UInt32(CPU_TYPE_ARM64) + case .x86_64: + return UInt32(CPU_TYPE_X86_64) + } } } - } - struct Entry: Decodable { let arch: Arch let addr: UInt64 let asm: Data @@ -62,38 +61,8 @@ struct Config: Decodable { } } - struct Target: Decodable { - let identifier: String - let entries: [Entry] - - private enum CodingKeys: CodingKey { - case identifier - case entries - } - - init(from decoder: any Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.identifier = try container.decode(String.self, forKey: .identifier) - self.entries = try container.decode([Entry].self, forKey: .entries) - } - } - - let version: String - let targets: [Target] - - static func load(url: URL) async throws -> [Config] { - if url.isFileURL { - return try JSONDecoder().decode( - [Config].self, - from: Data(contentsOf: url) - ) - } else { - return try JSONDecoder().decode( - [Config].self, - from: try await URLSession.shared.data(from: url).0 - ) - } - } + let identifier: String + let entries: [Entry] } private extension Data { diff --git a/Sources/WeChatTweak/main.swift b/Sources/WeChatTweak/main.swift index e853721..d820556 100644 --- a/Sources/WeChatTweak/main.swift +++ b/Sources/WeChatTweak/main.swift @@ -20,7 +20,7 @@ extension Tweak { print("------ Current version ------") print(try await Command.version(app: options.app) ?? "unknown") print("------ Supported versions ------") - try await Config.load(url: options.config).forEach({ print($0.version) }) + try await Tweak.versions().forEach { print($0) } Darwin.exit(EXIT_SUCCESS) } } @@ -36,25 +36,21 @@ extension Tweak { mutating func run() async throws { print("------ Version ------") - let version = try await Command.version(app: options.app) - print("WeChat version: \(version ?? "unknown")") - - print("------ Config ------") - guard let config = (try await Config.load(url: options.config)).first(where: { $0.version == version }) else { + guard let version = try await Command.version(app: self.options.app), let targets = try await Tweak.load(version: version) else { throw Error.unsupportedVersion } - print("Matched config: \(config)") + print("\(version)") print("------ Patch ------") try await Command.patch( - app: options.app, - config: config + app: self.options.app, + targets: targets ) print("Done!") print("------ Resign ------") try await Command.resign( - app: options.app + app: self.options.app ) print("Done!") @@ -101,7 +97,7 @@ struct Tweak: AsyncParsableCommand { @Option( name: .shortAndLong, - help: "Local path or Remote URL of config.json", + help: "Local path or Remote URL of config (default: derived from app version)", transform: { if FileManager.default.fileExists(atPath: $0) { return URL(fileURLWithPath: $0) @@ -113,7 +109,7 @@ struct Tweak: AsyncParsableCommand { } } ) - var config: URL = URL(string:"https://raw.githubusercontent.com/sunnyyoung/WeChatTweak/refs/heads/master/config.json")! + var config: URL? = nil } static let configuration = CommandConfiguration( @@ -131,6 +127,35 @@ struct Tweak: AsyncParsableCommand { } } +extension Tweak { + static func load(url: URL) async throws -> [Target] { + let data = try await { + if url.isFileURL { + return try Data(contentsOf: url) + } else { + return try await URLSession.shared.data(from: url).0 + } + }() + return try JSONDecoder().decode([Target].self, from: data) + } + + static func load(version: String) async throws -> [Target]? { + guard try await Tweak.versions().contains(version) else { + return nil + } + return try await Self.load(url: URL(string: "https://raw.githubusercontent.com/sunnyyoung/WeChatTweak/master/Versions/\(version).json")!) + } + + static func versions() async throws -> [String] { + let data = try await URLSession.shared.data( + for: URLRequest( + url: URL(string: "https://api.github.com/repos/sunnyyoung/WeChatTweak/contents/Versions")! + ) + ) + return (try JSONSerialization.jsonObject(with: data.0) as? [[String: Any]])?.compactMap { ($0["name"] as? NSString)?.deletingPathExtension } ?? [] + } +} + Task { await Tweak.main() } diff --git a/Versions/31927.json b/Versions/31927.json new file mode 100644 index 0000000..3a2ba30 --- /dev/null +++ b/Versions/31927.json @@ -0,0 +1,82 @@ +[ + { + "identifier": "revoke", + "entries": [ + { + "arch": "arm64", + "addr": "103dba3d0", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "startUpdater", + "entries": [ + { + "arch": "arm64", + "addr": "1001ea41c", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "startBackgroundUpdatesCheck", + "entries": [ + { + "arch": "arm64", + "addr": "1001ed3b4", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "checkForUpdates", + "entries": [ + { + "arch": "arm64", + "addr": "1001ecf9c", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "enableAutoUpdate", + "entries": [ + { + "arch": "arm64", + "addr": "1001ed920", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "automaticallyDownloadsUpdates", + "entries": [ + { + "arch": "arm64", + "addr": "1001f6ee8", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "canCheckForUpdate", + "entries": [ + { + "arch": "arm64", + "addr": "1001f6ef8", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "multiInstance", + "entries": [ + { + "arch": "arm64", + "addr": "1001e1c10", + "asm": "20008052C0035FD6" + } + ] + } +] diff --git a/Versions/31960.json b/Versions/31960.json new file mode 100644 index 0000000..b9725f8 --- /dev/null +++ b/Versions/31960.json @@ -0,0 +1,22 @@ +[ + { + "identifier": "revoke", + "entries": [ + { + "arch": "arm64", + "addr": "10408a408", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "multiInstance", + "entries": [ + { + "arch": "arm64", + "addr": "1001e4a38", + "asm": "20008052C0035FD6" + } + ] + } +] diff --git a/Versions/32281.json b/Versions/32281.json new file mode 100644 index 0000000..da9c25c --- /dev/null +++ b/Versions/32281.json @@ -0,0 +1,82 @@ +[ + { + "identifier": "revoke", + "entries": [ + { + "arch": "arm64", + "addr": "103db33e0", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "startUpdater", + "entries": [ + { + "arch": "arm64", + "addr": "1001e9ed0", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "startBackgroundUpdatesCheck", + "entries": [ + { + "arch": "arm64", + "addr": "1001ecb10", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "checkForUpdates", + "entries": [ + { + "arch": "arm64", + "addr": "1001ec73c", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "enableAutoUpdate", + "entries": [ + { + "arch": "arm64", + "addr": "1001ecfc0", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "automaticallyDownloadsUpdates", + "entries": [ + { + "arch": "arm64", + "addr": "1001f59e0", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "canCheckForUpdate", + "entries": [ + { + "arch": "arm64", + "addr": "1001f59e0", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "multiInstance", + "entries": [ + { + "arch": "arm64", + "addr": "1001e1a74", + "asm": "20008052C0035FD6" + } + ] + } +] diff --git a/Versions/32288.json b/Versions/32288.json new file mode 100644 index 0000000..b22886c --- /dev/null +++ b/Versions/32288.json @@ -0,0 +1,82 @@ +[ + { + "identifier": "revoke", + "entries": [ + { + "arch": "arm64", + "addr": "103db34c0", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "startUpdater", + "entries": [ + { + "arch": "arm64", + "addr": "1001e9ed0", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "startBackgroundUpdatesCheck", + "entries": [ + { + "arch": "arm64", + "addr": "1001ecb10", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "checkForUpdates", + "entries": [ + { + "arch": "arm64", + "addr": "1001ec73c", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "enableAutoUpdate", + "entries": [ + { + "arch": "arm64", + "addr": "1001ecfc0", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "automaticallyDownloadsUpdates", + "entries": [ + { + "arch": "arm64", + "addr": "1001f59e0", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "canCheckForUpdate", + "entries": [ + { + "arch": "arm64", + "addr": "1001f59e0", + "asm": "00008052C0035FD6" + } + ] + }, + { + "identifier": "multiInstance", + "entries": [ + { + "arch": "arm64", + "addr": "1001e1a74", + "asm": "20008052C0035FD6" + } + ] + } +]