Files
Leeksov 4647310322 GLEGram 12.5 — Initial public release
Based on Swiftgram 12.5 (Telegram iOS 12.5).
All GLEGram features ported and organized in GLEGram/ folder.

Features: Ghost Mode, Saved Deleted Messages, Content Protection Bypass,
Font Replacement, Fake Profile, Chat Export, Plugin System, and more.

See CHANGELOG_12.5.md for full details.
2026-04-06 09:48:12 +03:00

279 lines
12 KiB
Swift

import Foundation
import MachO
import CryptoKit
import SGLogging
// MARK: - Runtime integrity verification
//
// Three independent layers that must ALL pass for access to be granted:
// 1. Text-segment checksum detects ANY patched byte in __TEXT,__text
// 2. Anti-stub detection recognises common crack patterns (mov w0,#1;ret, nop, b+N)
// 3. Accumulator-based access access derived from a sum of fragments contributed by
// multiple independent code paths; patching any single path breaks the total
//
// The access check is NOT a single "if bool" it is the result of XOR-ing
// multiple fragment values. The expected XOR depends on which access flags
// the server granted, making it impossible to bypass by patching one comparison.
public enum SupportersIntegrity {
//
// 1. __TEXT,__text checksum
//
private static var _sealedHash: UInt64 = 0
private static var _sealed = false
/// Sample ~512 points across the main executable's __TEXT,__text section
/// and compute a rolling hash. Any single patched instruction changes the result.
@inline(never)
public static func textChecksum() -> UInt64 {
guard let header = _dyld_get_image_header(0) else { return 0 }
var size: UInt = 0
let h64 = UnsafeRawPointer(header).assumingMemoryBound(to: mach_header_64.self)
guard let ptr = getsectiondata(h64, "__TEXT", "__text", &size), size > 64 else { return 0 }
var h: UInt64 = 0x736F6D65_70736575 // SipHash-like seed
let step = max(8, Int(size) / 512)
var off = 0
while off + 8 <= Int(size) {
let v = ptr.advanced(by: off)
.withMemoryRebound(to: UInt64.self, capacity: 1) { $0.pointee }
h ^= v
h &*= 0x517CC1B7_27220A95
h = (h << 13) | (h >> 51)
off += step
}
return h
}
/// Call once after the first successful encrypted API validation in a session.
/// Records the "known-good" checksum for later comparison.
public static func seal() {
guard !_sealed else { return }
_sealedHash = textChecksum()
_sealed = true
SGLogger.shared.log("SGIntegrity", "sealed text checksum")
}
/// Returns `false` if __TEXT has been modified after sealing (= runtime patch).
/// If not yet sealed, returns `true` (don't block cold start from cache).
@inline(__always)
public static func textOK() -> Bool {
guard _sealed else { return true }
return textChecksum() == _sealedHash
}
//
// 2. Anti-stub detection
//
/// Recognises ARM64 instruction patterns commonly injected by crackers.
///
/// Covered patterns (little-endian):
/// - `mov wN, #imm16; ret` (0x5280xxxx 0xD65F03C0)
/// - `nop; nop` (0xD503201F 0xD503201F)
/// - `b +N` first instr (0x14xxxxxx)
@inline(__always)
public static func isStubbed(_ ptr: UnsafeRawPointer) -> Bool {
let pair = ptr.load(as: UInt64.self)
// second instruction == ret?
let hi32 = UInt32(truncatingIfNeeded: pair >> 32)
if hi32 == 0xD65F03C0 { // ret
let lo32 = UInt32(truncatingIfNeeded: pair)
if lo32 & 0xFF800000 == 0x52800000 { // mov wN, #imm16
return true
}
}
// nop; nop
if pair == 0xD503201F_D503201F { return true }
// unconditional branch as first instruction
let first = UInt32(truncatingIfNeeded: pair)
if first & 0xFC000000 == 0x14000000 { return true }
return false
}
//
// 3. Accumulator / fragment-based access derivation
//
// Design:
// Several independent code paths each contribute a deterministic "fragment".
// Fragments are XOR'd together; the final value is compared to an "expected"
// that encodes the actual access flags.
//
// expected = F_crypto ^ F_cache ^ F_text ^ F_access(flags)
//
// If any code path was stubbed (returns early / wrong value), its fragment
// will differ and the final XOR won't match access denied.
//
// The "expected" value is recomputed from the stored per-user seed, so the
// cracker cannot hard-code it it changes per user/session.
// Fixed fragment keys (compile-time constants, deliberately non-round).
// These are combined with runtime data to produce the actual fragments.
public static let kCryptoOK: UInt64 = 0xA3B7_C2D1_E5F6_0718
public static let kCacheOK: UInt64 = 0x4E5F_6A7B_8C9D_0E1F
public static let kTextOK: UInt64 = 0x1C2D_3E4F_5A6B_7C8D
public static let kAccessOn: UInt64 = 0x9F8E_7D6C_5B4A_3928
public static let kBetaOn: UInt64 = 0x72F1_843A_6BD5_9EC0
private static var _fragments = [UInt64]()
private static let _lock = NSLock()
/// Reset at the beginning of a new validation cycle.
public static func resetFragments() {
_lock.lock()
_fragments.removeAll()
_lock.unlock()
}
/// A verified code path contributes its fragment.
@inline(__always)
public static func contribute(_ fragment: UInt64) {
_lock.lock()
_fragments.append(fragment)
_lock.unlock()
}
/// XOR of all contributed fragments.
@inline(__always)
public static func accumulatedXOR() -> UInt64 {
_lock.lock()
let v = _fragments.reduce(0 as UInt64) { $0 ^ $1 }
_lock.unlock()
return v
}
/// Number of contributed fragments (sanity: must be expected count).
@inline(__always)
public static func fragmentCount() -> Int {
_lock.lock()
let c = _fragments.count
_lock.unlock()
return c
}
//
// 4. Composite access derivation
//
/// Derive glegramTab access from accumulated fragments.
///
/// Expected XOR when all integrity checks pass AND glegramTab == true:
/// kCryptoOK ^ kCacheOK ^ kTextOK ^ kAccessOn
///
/// If any layer is missing or returns the wrong fragment, the XOR differs
/// from the expected value access denied.
///
/// This function is `@inline(__always)` so it is duplicated at every call site.
/// The cracker must patch EVERY call site, not just one function.
@inline(__always)
public static func deriveGlegramTab() -> Bool {
let expected = kCryptoOK ^ kCacheOK ^ kTextOK ^ kAccessOn
return accumulatedXOR() == expected && fragmentCount() >= 3
}
/// Same for betaBuilds uses a different expected value.
@inline(__always)
public static func deriveBetaBuilds() -> Bool {
let expected = kCryptoOK ^ kCacheOK ^ kTextOK ^ kBetaOn
return accumulatedXOR() == expected && fragmentCount() >= 3
}
//
// 5. Per-user access token (HMAC-signed)
//
/// Compute a per-user access token: HMAC-SHA256(derivedKey, userId|flags).
/// The server can also generate this token so the client can verify it.
public static func computeAccessToken(
userId: String,
glegramTab: Bool,
betaBuilds: Bool,
hmacKeyData: Data
) -> Data {
let integrityKey = deriveIntegrityKey(from: hmacKeyData)
let payload = "\(userId)|\(glegramTab ? "1" : "0")|\(betaBuilds ? "1" : "0")"
let auth = HMAC<SHA256>.authenticationCode(
for: Data(payload.utf8),
using: SymmetricKey(data: integrityKey)
)
return Data(auth)
}
/// Verify a stored access token against recomputed expected value.
@inline(__always)
public static func verifyAccessToken(
_ token: Data,
userId: String,
glegramTab: Bool,
betaBuilds: Bool,
hmacKeyData: Data
) -> Bool {
let expected = computeAccessToken(
userId: userId,
glegramTab: glegramTab,
betaBuilds: betaBuilds,
hmacKeyData: hmacKeyData
)
guard expected.count == token.count, !token.isEmpty else { return false }
var diff: UInt8 = 0
for i in 0..<expected.count {
diff |= expected[i] ^ token[i]
}
return diff == 0
}
/// Derive a separate key for access tokens (so it differs from the API HMAC key).
private static func deriveIntegrityKey(from masterKey: Data) -> Data {
let salt = Data("glegram-integrity-v1".utf8)
let auth = HMAC<SHA256>.authenticationCode(for: salt, using: SymmetricKey(data: masterKey))
return Data(auth)
}
//
// 6. Full validation entry point
//
/// Run all integrity layers and contribute their fragments to the accumulator.
/// Call this during / after encrypted API validation.
///
/// - `cryptoSucceeded`: set `true` after a successful decrypt+HMAC verify
/// - `cacheDecrypted`: set `true` after Keychain data was decrypted successfully
/// - `glegramTab` / `betaBuilds`: raw access flags from the server response
///
/// After calling this, `deriveGlegramTab()` / `deriveBetaBuilds()` return
/// the integrity-verified access.
public static func validate(
cryptoSucceeded: Bool,
cacheDecrypted: Bool,
glegramTab: Bool,
betaBuilds: Bool
) {
resetFragments()
// Fragment 1: crypto verification passed
if cryptoSucceeded {
contribute(kCryptoOK)
}
// Fragment 2: cache decryption passed
if cacheDecrypted {
contribute(kCacheOK)
}
// Fragment 3: text segment not patched
if textOK() {
contribute(kTextOK)
}
// Fragment 4: access flag (determines which derive* returns true)
if glegramTab {
contribute(kAccessOn)
} else if betaBuilds {
contribute(kBetaOn)
}
}
}