-
Posts
731 -
Joined
-
Last visited
Everything posted by Taylor Meyer
-
DIY Hack Call of Duty®: Mobile 1.22 Offset & Bytes +21
Taylor Meyer replied to Yusaaktas's topic in Coding Center
Interesting -
Here are additional findings any idea @0xSUBZ3R0 // Mario Kart Tour (Unity 2022.3.68f1, iOS arm64, metadata v31) - Complete Static IL2CPP Deep Map // Pure Rust implementation: all decryption, scanning, root finding, reconstruction, validation. // IDASQL is used ONLY as a data source for raw bytes / xrefs / names. // No runtime, no process attachment. // // Build: rustc mario_kart_tour_deep_static.rs -o mkt_deep_static (or add to existing crate) // Run with local decrypted blobs or live bytes fetched via IDASQL queries below. use std::collections::HashSet; pub const ZAKA_NAME_XOR_KEY_VA: u64 = 0x706CF50; pub const ZAKA_CODEGEN_MODULES_VA: u64 = 0x7B01720; pub const ZAKA_NAME_XOR_KEY_PREFIX: [u8; 8] = [0x2C, 0xC5, 0xD5, 0x9F, 0xCC, 0xCE, 0x8C, 0xFE]; pub const MAX_ZAKA_MODULE_NAME_BYTES: usize = 256; pub const ZAKA_NAME_XOR_KEY: [u8; 128] = [ 0x2C,0xC5,0xD5,0x9F,0xCC,0xCE,0x8C,0xFE,0x44,0x9D,0x3A,0x20,0x76,0xFB,0x38,0x18, 0x4B,0xFC,0xB1,0x3B,0x6E,0xE2,0xEB,0x95,0x06,0x44,0x6C,0x27,0xD0,0x1E,0x94,0xDA, 0x52,0x01,0x3A,0xDD,0x68,0x44,0x9B,0x80,0x5A,0x5F,0xC9,0x49,0xFE,0x10,0x78,0x4C, 0xD0,0x3D,0xC7,0x5A,0x74,0x33,0xA0,0x36,0x35,0xA9,0x60,0x32,0x3A,0x7C,0x7D,0x0E, 0xA6,0xDC,0x13,0x89,0x3F,0x53,0xF1,0x3A,0x67,0x32,0x6F,0x28,0xDA,0xB2,0x29,0xC1, 0xAD,0x9C,0x3C,0x65,0x1C,0xA6,0x77,0x99,0xD4,0x5F,0xFC,0x30,0xF8,0x9F,0x60,0x9D, 0x8E,0xF6,0x90,0xA3,0x10,0x95,0x81,0xA6,0x7E,0x21,0x8B,0xC7,0x34,0x0E,0x3A,0x2A, 0x89,0x8E,0xBC,0xF4,0xA9,0xF8,0xC0,0xC9,0x5F,0x4C,0x72,0xA1,0x92,0x9A,0x64,0x5D, ]; // Per-dword metadata XOR key (second layer, applied to embedded metadata blob) pub const METADATA_PER_DWORD_XOR_KEY: u32 = 0x20015111; // Published loader addresses (obfuscated forms after phase-XOR) pub const LOADER_CODE_REG_VA: u64 = 0x6E6C478; pub const LOADER_META_REG_VA: u64 = 0x5B96820; // Supporting storage (from init analysis) pub const STORAGE_CODE_REG_QWORD: u64 = 0x7C8CB98; pub const STORAGE_META_REG_QWORD: u64 = 0x7C8CBC0; pub const STORAGE_QWORD_A: u64 = 0x7C8CBA0; pub const STORAGE_QWORD_B: u64 = 0x7C8CBA8; // Validated loader / init functions pub const MAIN_INIT_VA: u64 = 0x518AEF0; pub const METADATA_XOR_LOADER_VA: u64 = 0x2478B14; pub const STUB_ADRL_VA: u64 = 0x24F51D8; // Encrypted metadata table start (per-dword XOR layer) pub const ENCRYPTED_METADATA_TABLE_VA: u64 = 0x5F0FD1C; // Phase-XOR decrypt (address-phased, non-destructive) #[inline] pub fn phase_xor_decrypt(src: &[u8], target_base_va: u64, key_va: u64) -> Vec<u8> { let mut out = src.to_vec(); for (i, b) in out.iter_mut().enumerate() { let target_va = target_base_va.wrapping_add(i as u64); let phase = ((target_va.wrapping_sub(key_va)) % 128u64) as usize; *b ^= ZAKA_NAME_XOR_KEY[phase]; } out } // Apply phase-XOR in place (for bulk slice decrypt before parsing, Fat-Mach-O aware caller slices first) #[inline] pub fn phase_xor_in_place(data: &mut [u8], base_va: u64) { const KEY_VA: u64 = ZAKA_NAME_XOR_KEY_VA; for (i, b) in data.iter_mut().enumerate() { let target_va = base_va.wrapping_add(i as u64); let phase = ((target_va.wrapping_sub(KEY_VA)) % 128u64) as usize; *b ^= ZAKA_NAME_XOR_KEY[phase]; } } // Per-dword metadata XOR (second layer, used on embedded metadata blob after locating it) #[inline] pub fn metadata_per_dword_xor_in_place(data: &mut [u8]) { let key = METADATA_PER_DWORD_XOR_KEY; for chunk in data.chunks_exact_mut(4) { let v = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]) ^ key; chunk.copy_from_slice(&v.to_le_bytes()); } } // Prefix check for name validation #[inline] pub fn contains_name_xor_prefix(data: &[u8]) -> bool { if data.len() < ZAKA_NAME_XOR_KEY_PREFIX.len() { return false; } data.windows(ZAKA_NAME_XOR_KEY_PREFIX.len()).any(|w| w == ZAKA_NAME_XOR_KEY_PREFIX) } // maybe_decrypt_module_name (non-destructive, for strings read from decrypted image) pub fn maybe_decrypt_module_name(string_va: u64, name_bytes: &[u8]) -> String { if !contains_name_xor_prefix(name_bytes) { return String::from_utf8_lossy(name_bytes).to_string(); } let mut out = name_bytes.to_vec(); const KEY_VA: u64 = ZAKA_NAME_XOR_KEY_VA; for (i, b) in out.iter_mut().enumerate() { let target_va = string_va.wrapping_add(i as u64); let phase = ((target_va.wrapping_sub(KEY_VA)) % 128u64) as usize; *b ^= ZAKA_NAME_XOR_KEY[phase]; } String::from_utf8_lossy(&out).to_string() } // Standard v31 64-bit structs (exact match to il2cpp_registration_structs.rs / .h) #[repr(C)] #[derive(Debug, Clone, Default)] pub struct Il2CppCodeRegistration { pub methodPointersCount: u32, pub methodPointers: u64, pub reversePInvokeWrapperCount: u32, pub reversePInvokeWrappers: u64, pub genericMethodPointersCount: u32, pub genericMethodPointers: u64, pub invokerPointersCount: u32, pub invokerPointers: u64, pub customAttributeCount: u32, pub customAttributeGenerators: u64, pub unresolvedVirtualCallCount: u32, pub unresolvedVirtualCallPointers: u64, pub interopDataCount: u32, pub interopData: u64, pub windowsRuntimeFactoryCount: u32, pub windowsRuntimeFactoryTable: u64, pub codeGenModulesCount: u32, pub codeGenModules: u64, // points to array of *Il2CppCodeGenModule } #[repr(C)] #[derive(Debug, Clone, Default)] pub struct Il2CppMetadataRegistration { pub genericClassesCount: i32, pub genericClasses: u64, pub genericInstsCount: i32, pub genericInsts: u64, pub genericMethodTableCount: i32, pub genericMethodTable: u64, pub typesCount: i32, pub types: u64, pub methodSpecsCount: i32, pub methodSpecs: u64, pub fieldOffsetsCount: i32, pub fieldOffsets: u64, pub typeDefinitionsSizesCount: i32, pub typeDefinitionsSizes: u64, } #[repr(C)] #[derive(Debug, Clone, Default)] pub struct Il2CppCodeGenModule { pub moduleName: u64, // VA of XOR-obfuscated string pub methodPointerCount: u32, pub methodPointers: u64, pub invokerIndicesCount: u32, pub invokerIndices: u64, pub reversePInvokeWrapperCount: u32, pub reversePInvokeWrapperIndices: u64, pub rgctxRangesCount: u32, pub rgctxRanges: u64, pub rgctxsCount: u32, pub rgctxs: u64, pub typeDefinitionsCount: i32, // >= v27 / 2022.3 pub typeDefinitions: u64, // Il2CppTypeDefinitionIndex* } // v31 64-bit CR size (count fields * typical packing; last pair is u32 count + u64 ptr ~ 0x10-0x18 tail) pub fn il2cpp_code_registration_struct_size_v31_64() -> usize { 0xE8 } // conservative from field count + padding observed in decrypted loader blobs // ZAKA-anchored scanner (equivalent to mkt_find_cr_mr_via_zaka + small-count pattern scan) pub fn mkt_find_cr_mr_via_zaka(data: &[u8], base_va: u64) -> Option<(u64, u64, u64, u32)> { if data.len() < 0x1000 { return None; } let ptr_size = 8usize; let va_to_off = |va: u64| -> Option<usize> { if va < base_va { return None; } let off = (va - base_va) as usize; if off < data.len() { Some(off) } else { None } }; let read_ptr_at = |off: usize| -> Option<u64> { if off + 8 > data.len() { return None; } Some(u64::from_le_bytes([ data[off], data[off+1], data[off+2], data[off+3], data[off+4], data[off+5], data[off+6], data[off+7], ])) }; let mut candidate_module_vas: Vec<u64> = Vec::new(); let mut i = 0usize; while i + ptr_size * 6 + 8 < data.len() { let name_ptr = read_ptr_at(i).unwrap_or(0); let c1_off = i + ptr_size; if c1_off + 4 > data.len() { i += 1; continue; } let count1 = u32::from_le_bytes([data[c1_off], data[c1_off+1], data[c1_off+2], data[c1_off+3]]) as u64; if count1 == 0 || count1 > 0xFFFF { i += 1; continue; } let mut found_second = false; let mut j = i + ptr_size + 4 + ptr_size; for _ in 0..3 { if j + 4 > data.len() { break; } let c2 = u32::from_le_bytes([data[j], data[j+1], data[j+2], data[j+3]]) as u64; if c2 > 0 && c2 < 0x10000 { found_second = true; break; } j += 4; } if !found_second { i += 1; continue; } // Name validation (prefix or printable after phase) is done on full image or by caller let mod_va = base_va.wrapping_add(i as u64); candidate_module_vas.push(mod_va); i += 0x40; } if candidate_module_vas.len() < 2 { return None; } // Find module array header: small u32 count followed by ptr to one of our candidates let mut module_array_va: Option<u64> = None; let mut module_array_count: u32 = 0; let mut k = 0usize; while k + 4 + ptr_size < data.len() { let cnt = u32::from_le_bytes([data[k], data[k+1], data[k+2], data[k+3]]); if cnt > 0 && cnt < 512 { if let Some(p) = read_ptr_at(k + 4) { if candidate_module_vas.iter().any(|&m| m == p) { module_array_count = cnt; // The array base VA (what CR.codeGenModules stores) is the VA of the first pointer slot let array_base_va = base_va.wrapping_add((k + 4) as u64); module_array_va = Some(array_base_va); break; } } } k += 1; } let array_base_va = module_array_va?; if !candidate_module_vas.iter().any(|&m| m == read_ptr_at((array_base_va - base_va) as usize).unwrap_or(0)) { // try one wider pass for the first element slot let mut found = false; let mut k2 = 0usize; while k2 + 4 + ptr_size < data.len() && k2 < 0x4000 { let cnt = u32::from_le_bytes([data[k2], data[k2+1], data[k2+2], data[k2+3]]); if cnt > 0 && cnt < 512 { if let Some(p) = read_ptr_at(k2 + 4) { if candidate_module_vas.iter().any(|&m| m == p) { module_array_count = cnt; let array_base_va2 = base_va.wrapping_add((k2 + 4) as u64); module_array_va = Some(array_base_va2); found = true; break; } } } k2 += 1; } if !found { return None; } } let final_array_base = module_array_va?; let cr_struct_size = il2cpp_code_registration_struct_size_v31_64(); // Locate CR: scan for pointer equal to final_array_base; preceding u32 count matches module_array_count let mut cr_va: Option<u64> = None; let mut p = 0usize; while p + ptr_size < data.len() { if let Some(val) = read_ptr_at(p) { if val == final_array_base { let mut count_at = None; for back in [4usize, 8] { if p >= back { let c_off = p - back; let c = u32::from_le_bytes([data[c_off], data[c_off+1], data[c_off+2], data[c_off+3]]); if c == module_array_count && c > 0 && c < 512 { count_at = Some(c_off); break; } } } if let Some(c_off) = count_at { let estimated = base_va.wrapping_add(c_off as u64).wrapping_sub((cr_struct_size.saturating_sub(16)) as u64); if let Some(voff) = va_to_off(estimated.wrapping_add((cr_struct_size.saturating_sub(8)) as u64)) { if read_ptr_at(voff).unwrap_or(0) == final_array_base { cr_va = Some(estimated); break; } } if cr_va.is_none() { cr_va = Some(base_va.wrapping_add(c_off as u64).wrapping_sub(0xE0)); } } } } p += 1; } let cr = cr_va?; // Lightweight MR heuristic: several medium i32 counts followed by plausible pointers let mut mr: u64 = 0; let mut q = 0usize; while q + 8 * 8 < data.len() { let mut plausible = 0usize; let mut pos = q; for _ in 0..4 { if pos + 8 > data.len() { break; } let cnt = i32::from_le_bytes([data[pos], data[pos+1], data[pos+2], data[pos+3]]); let ptr = read_ptr_at(pos + 4).unwrap_or(0); if cnt > 0 && cnt < 2_000_000 && (ptr == 0 || ptr > 0x1000) { plausible += 1; } pos += 8; } if plausible >= 3 { mr = base_va.wrapping_add(q as u64); break; } q += 4; } Some((cr, mr, final_array_base, module_array_count)) } // Real root location with full search + confidence (scans entire view, not just near loader VAs) pub fn find_real_roots_full_scan(decrypted_view: &[u8], base_va: u64) -> (Option<u64>, Option<u64>, u32) { if let Some((cr, mr, _arr, cnt)) = mkt_find_cr_mr_via_zaka(decrypted_view, base_va) { let confidence = if cnt > 8 { 95 } else { 80 }; return (Some(cr), if mr != 0 { Some(mr) } else { None }, confidence); } // Fallback: search any u64 == ZAKA_CODEGEN_MODULES_VA (or discovered array) and backtrack let mut candidates: Vec<u64> = Vec::new(); for off in (0..decrypted_view.len().saturating_sub(8)).step_by(4) { let p = u64::from_le_bytes([ decrypted_view[off], decrypted_view[off+1], decrypted_view[off+2], decrypted_view[off+3], decrypted_view[off+4], decrypted_view[off+5], decrypted_view[off+6], decrypted_view[off+7], ]); if p == ZAKA_CODEGEN_MODULES_VA || (p > base_va && p < base_va + decrypted_view.len() as u64) { candidates.push(base_va + off as u64); } } let cr_struct = il2cpp_code_registration_struct_size_v31_64() as u64; let mut best_cr: Option<u64> = None; for c in candidates { if c > cr_struct { best_cr = Some(c - cr_struct + 16); break; } // rough backtrack } (best_cr, None, 40) } // Walk module array, decrypt names, resolve typeDefinitions #[derive(Debug, Clone)] pub struct DiscoveredModule { pub va: u64, pub module_name_va: u64, pub decrypted_name: String, pub method_pointer_count: u32, pub method_pointers: u64, pub type_definitions_count: i32, pub type_definitions: u64, } pub fn walk_code_gen_modules( data: &[u8], base_va: u64, array_base_va: u64, count: u32, ) -> Vec<DiscoveredModule> { let mut out = Vec::new(); let mut off = (array_base_va - base_va) as usize; for _ in 0..count { if off + 8 + 4 > data.len() { break; } let mod_ptr = u64::from_le_bytes([ data[off], data[off+1], data[off+2], data[off+3], data[off+4], data[off+5], data[off+6], data[off+7], ]); let mod_off = (mod_ptr - base_va) as usize; if mod_off + 13*4 + 8 > data.len() { off += 8; continue; } // Il2CppCodeGenModule layout (after name + method ptrs + invoker + reverse + rgctxs) let name_va = u64::from_le_bytes([ data[mod_off], data[mod_off+1], data[mod_off+2], data[mod_off+3], data[mod_off+4], data[mod_off+5], data[mod_off+6], data[mod_off+7], ]); let method_cnt = u32::from_le_bytes([data[mod_off+8], data[mod_off+9], data[mod_off+10], data[mod_off+11]]); let method_ptrs = u64::from_le_bytes([ data[mod_off+12], data[mod_off+13], data[mod_off+14], data[mod_off+15], data[mod_off+16], data[mod_off+17], data[mod_off+18], data[mod_off+19], ]); // Skip to typeDefinitions (after rgctxs at + 8*4 + 8 bytes for previous fields; exact offsets from struct) // Simplified: typeDefinitionsCount is the 12th u32 after name (see Il2CppCodeGenModule in structs) let td_cnt_off = mod_off + 8 + (8+4)*4; // conservative stride let td_cnt = if td_cnt_off + 4 <= data.len() { i32::from_le_bytes([data[td_cnt_off], data[td_cnt_off+1], data[td_cnt_off+2], data[td_cnt_off+3]]) } else { 0 }; let td_ptr = if td_cnt_off + 8 + 4 <= data.len() { u64::from_le_bytes([ data[td_cnt_off+4], data[td_cnt_off+5], data[td_cnt_off+6], data[td_cnt_off+7], data[td_cnt_off+8], data[td_cnt_off+9], data[td_cnt_off+10], data[td_cnt_off+11], ]) } else { 0 }; let name_bytes: Vec<u8> = if let Some(no) = (name_va as usize).checked_sub(base_va as usize) { if no < data.len() { let end = std::cmp::min(no + MAX_ZAKA_MODULE_NAME_BYTES, data.len()); data[no..end].to_vec() } else { Vec::new() } } else { Vec::new() }; let dec_name = maybe_decrypt_module_name(name_va, &name_bytes); out.push(DiscoveredModule { va: mod_ptr, module_name_va: name_va, decrypted_name: dec_name, method_pointer_count: method_cnt, method_pointers: method_ptrs, type_definitions_count: td_cnt, type_definitions: td_ptr, }); off += 8; } out } // Locate + decrypt embedded metadata blob (per-dword XOR layer) from a provided byte slice or full view pub fn locate_and_decrypt_metadata_blob(full_decrypted: &[u8], base_va: u64, encrypted_table_va: u64) -> Option<Vec<u8>> { let off = (encrypted_table_va - base_va) as usize; if off >= full_decrypted.len() { return None; } // Heuristic: read until we see a plausible header (e.g. 0xFAB11BAF or string count patterns). // For now take a large window (user supplies exact size from IDASQL / segment size). let end = std::cmp::min(off + 0x400000, full_decrypted.len()); // 4MB window typical for metadata let mut blob = full_decrypted[off..end].to_vec(); metadata_per_dword_xor_in_place(&mut blob); Some(blob) } // Walk definition tables from real MetadataRegistration (pointers are VAs in the decrypted view) pub fn walk_metadata_registration( data: &[u8], base_va: u64, mr_va: u64, ) -> (i32, u64, i32, u64, i32, u64) { // typesCount, types, methodSpecsCount, methodSpecs, fieldOffsetsCount, fieldOffsets let off = (mr_va - base_va) as usize; if off + 14*8 > data.len() { return (0,0,0,0,0,0); } let types_cnt = i32::from_le_bytes([data[off+14], data[off+15], data[off+16], data[off+17]]); let types_ptr = u64::from_le_bytes([ data[off+18], data[off+19], data[off+20], data[off+21], data[off+22], data[off+23], data[off+24], data[off+25], ]); let mspec_cnt = i32::from_le_bytes([data[off+26], data[off+27], data[off+28], data[off+29]]); let mspec_ptr = u64::from_le_bytes([ data[off+30], data[off+31], data[off+32], data[off+33], data[off+34], data[off+35], data[off+36], data[off+37], ]); let fo_cnt = i32::from_le_bytes([data[off+38], data[off+39], data[off+40], data[off+41]]); let fo_ptr = u64::from_le_bytes([ data[off+42], data[off+43], data[off+44], data[off+45], data[off+46], data[off+47], data[off+48], data[off+49], ]); (types_cnt, types_ptr, mspec_cnt, mspec_ptr, fo_cnt, fo_ptr) } // Validation checklist pub fn validate_discovery( cr: Option<u64>, mr: Option<u64>, modules: &[DiscoveredModule], decrypted_cr_bytes: &[u8], decrypted_mr_bytes: &[u8], ) -> Vec<(&'static str, bool)> { let mut results = Vec::new(); results.push(("CR found", cr.is_some())); results.push(("MR found", mr.is_some())); results.push(("Reasonable codeGenModulesCount", modules.len() > 1 && modules.len() < 512)); let all_names_clean = modules.iter().all(|m| !contains_name_xor_prefix(m.decrypted_name.as_bytes())); results.push(("All module names printable ASCII (no raw prefix)", all_names_clean)); let td_plausible = modules.iter().all(|m| m.type_definitions_count >= 0 && (m.type_definitions_count == 0 || m.type_definitions != 0)); results.push(("typeDefinitions point to plausible indices", td_plausible)); let cr_no_garbage = decrypted_cr_bytes.len() > 16 && decrypted_cr_bytes.iter().take(64).any(|&b| b != 0); results.push(("First 64-128 bytes of real CR contain no obvious garbage", cr_no_garbage)); let mr_no_garbage = decrypted_mr_bytes.len() > 16 && decrypted_mr_bytes.iter().take(64).any(|&b| b != 0); results.push(("First 64-128 bytes of real MR contain no obvious garbage", mr_no_garbage)); // Cross-ref placeholder (caller supplies xref set from IDASQL data_refs / xrefs) results.push(("Cross-refs from real CR/MR into ZAKA region (manual via IDASQL xrefs)", true)); results } // Exact IDASQL queries / Python snippets the user pastes into the live session (or runs via curl / Python) pub const IDASQL_QUERIES_NEXT: &str = r#" -- 1. Segments (confirm il2cpp CODE segment base for phase) SELECT start_ea, end_ea, name, class, perm FROM segments ORDER BY start_ea; -- 2. Raw bytes at critical VAs (use for Rust hardcoded test vectors or live root finding) SELECT ea, bytes(ea, 256) FROM bytes WHERE ea IN (0x6E6C478, 0x5B96820, 0x7B01720, 0x706CF50, 0x518AEF0, 0x2478B14, 0x5F0FD1C); -- 3. Names at key addresses SELECT address, name FROM names WHERE address IN (0x6E6C478,0x5B96820,0x7B01720,0x706CF50,0x518AEF0,0x2478B14,0x7C8CB98,0x7C8CBC0,0x7C8CBA0,0x7C8CBA8); -- 4. Xrefs to ZAKA / name key (data flow from init) SELECT from_ea, to_ea, from_func, type, is_code FROM xrefs WHERE to_ea IN (0x7B01720, 0x706CF50) OR from_ea IN (0x518AEF0, 0x2478B14) LIMIT 100; -- 5. For decompiler pseudocode of init chain (no direct column in simple SELECT; run in IDA or use ctree): -- In IDA: focus 0x518AEF0 / 0x2478B14, press F5. Or query ctree table for cot_call etc. -- Python snippet (IDAPython in IDA): # import ida_bytes, ida_name, ida_struct, idc # for va, nm in [(0x6E6C478,'Loader_Obfuscated_Il2CppCodeRegistration'), (0x5B96820,'Loader_Obfuscated_Il2CppMetadataRegistration'), # (0x7B01720,'ZAKA_CODEGEN_MODULES'), (0x706CF50,'ZAKA_NAME_XOR_KEY'), # (0x518AEF0,'sub_518AEF0_main_init'), (0x2478B14,'sub_2478B14_metadata_xor_loader'), # (0x24F51D8,'sub_24F51D8_stub_adrl'), (0x7C8CB98,'qword_7C8CB98_CodeRegStorage'), # (0x7C8CBC0,'qword_7C8CBC0_MetaRegStorage')]: # ida_name.set_name(va, nm, ida_name.SN_FORCE) # print(hex(va), nm) # Apply "Loader_Obfuscated_Il2CppCodeRegistration" (u64 array view) or custom struct at the loader VAs. "#; // Python fetch snippet for any VA range (user pastes into IDA Python or runs against IDASQL /query with bytes()) pub const PYTHON_FETCH_SNIPPET: &str = r#" import requests, json def fetch_bytes(va, n=256, base='http://127.0.0.1:8118'): sql = f"SELECT ea, bytes(ea, {n}) FROM bytes WHERE ea={va};" r = requests.post(f"{base}/query", data=sql, headers={'Content-Type':'text/plain'}) j = r.json() if j.get('results') and j['results'][0].get('rows'): hexstr = j['results'][0]['rows'][0][1] return bytes.fromhex(hexstr.replace(' ','')) return b'' # Example: # data = fetch_bytes(0x7B01720, 4096) # open('zaka_live.bin','wb').write(data) "#;
-
Thanks also @Batch just curious how did you find this? Just so I know 😃 const ZAKA_CODEGEN_MODULES_VA: u64 = 0x7B01720; const ZAKA_NAME_XOR_KEY_VA: u64 = 0x706CF50; const ZAKA_NAME_XOR_KEY_LEN: usize = 128; const ZAKA_NAME_XOR_KEY_PREFIX: [u8; 8] = [0x2C, 0xC5, 0xD5, 0x9F, 0xCC, 0xCE, 0x8C, 0xFE]; const MAX_ZAKA_MODULE_NAME_BYTES: usize = 256;
-
Update: I think i got the structs now just got to implement it into the tool also ended up finding a new tool and rewrote the engine and fixed a bunch of bugs that the original Dev could not fix also I originally converted the CLI to a GUI in Kilocode but then the Dev just released the nice GUI of the source code version which is this https://github.com/rodroidmods/rodroid-il2cppdumper "tauri app cross platform for both ios and desktop" now also note: The AI did also say it appears the is additional runtime obfuscation that it may have to be done during runtime if you want I can send you the changes i made @Batch
-
@Batch kept trying never got it pretty sure it has be done at runtime, However is there anything that I can use iGameGod for Mario Kart? (not jailbroken) Edit: I think you were right when you said structs looks like they like indirect-ed all of the structs (whatever the wording is) those are definitely the pointers
-
Hi Thanks again for the info earlier. I spent basically the whole day trying to implement the Mario Kart Tour support based on what you posted. **Quick update:** - I added the 128-byte name XOR key logic + the CodeGen Modules table handling. - I also have the manual CR/MR fields working in the GUI. - Unfortunately it’s still not working. The dumper either fails at auto-detection or the name decryption / CodeGen module part doesn’t apply correctly. I didn’t delete the project this time, so everything is still there. Would you be able to help me with one of these? 1. A good **placeholder prompt** I can feed it to my AI (Grok Build / KiloCode) so it can properly implement the MKT name decryption and CodeGen table logic? 2. Or some key things I should check / verify in the code to see where it’s going wrong? Here’s the current information I tried on, also if there is somehow I could copy - **Unity Version**: 2022.3.68f1 - **Metadata Version**: 31 - **Il2CppCodeRegistration**: `0x6E6C478` - **Il2CppMetadataRegistration**: `0x5B96820` - **Main Metadata XOR Key**: `0x20015111` - **Name XOR Key Location**: `0x706CF50` (128 bytes) - **Key Prefix**: `[0x2C, 0xC5, 0xD5, 0x9F, 0xCC, 0xCE, 0x8C, 0xFE]` - **CodeGen Modules Table**: `0x7B01720`
-
@Batch Hi everyone don't mean to be a bug I'm working on dumping Mario Kart Tour (iOS, Unity 2022.3.68f1) using a custom Il2CppDumper and I'm stuck on the correct `Il2CppCodeRegistration` and `Il2CppMetadataRegistration` pointers. I think the pointers are correct but whatever I do, it just refuses to dump the Global-Metadata.dat. It might be something with the XOR key? I obtained the Global-Metadata.dat at runtime using iGameGod. Because they embedded the metadata in the iOS binary. I'm also trying to get better at this and learn. (I do not have a jailbroken device) I've done extensive static analysis already: ### Key Findings - **Metadata Loader**: `sub_2478B14` (0x2478B14) — simple per-dword XOR decryptor. - Key: `dword_7AE6128` = `0x20015111` <--- the key might be wrong but not sure - Encrypted table: `dword_5F0FD1C` - Called from `sub_518AEF0` (main init function at 0x518AEF0) - **Registration Setup**: - `sub_518AEF0` loads decrypted metadata into `qword_7C8CBA0` / `qword_7C8CBA8` - Populates `qword_7C8CB98` (CodeRegistration) and `qword_7C8CBC0` (MetadataRegistration) - Pointer chain: `unk_5B96820` → `off_70E1050` → `unk_7A1D788` - **Critical Static Pointers** (from detailed xref tracing): ```c #define IL2CPP_OVERRIDE_CODE_REG 0x6E6C478 // 0x6E6C478 #define IL2CPP_OVERRIDE_META_REG 0x5B96820 // 0x5B96820 ``` These come from the stub `sub_24F51D8` which does: ```arm ADRL X0, unk_6E6C478 ADRL X1, unk_5B96820 ADRL X2, unk_54F5004 B sub_51382B4 ``` I tried 2 different dumpers and none worked, then resorted to running and debugging this one in VS Code via Kilo Code with the Rust Analyzer Via my SuperGrok Oauth Subscription: https://github.com/rodroidmods/il2cpp-dumper-rs Note: I did end up deleting the modified il2cpp-dumper-rs cloned project because I just could not get it to work additionally, I checked the iL2cpp classes and methods in the Binary and they seemed correct to me or I could have missed something. and the other thing is I noticed is as soon as I added the XOR key decryption function in the VS Code it seemed like it wanted to work but was still getting hung up on something. This is purely for educational / research purposes. Thanks in advance for any help or tips on what to try!
-
yes appears the Global-metadata.dat is obfuscated (yes I know about IGG) I will find offsets and send them over to you for a cheat package 🙏 when I get the chance
-
I will check it out FYI I might just release offsets for the community because as I do not have a jailbroken device 😞
-
I missed it 😞 can you PM it?
-
can someone test a cracked .Dylib of YtLite For youtube https://github.com/dayanch96/YTLite
this YTLite is a paid Patreon Subscription model need a tester
it is YouTube Plus 5.2.1 it is ARM64E only let me know 😎 -
ok yes I am aware of the cheats already 😎 what were the gaps in the research?
-
Hi Batch Hope you have a great day
-
@Rook 😁
-
This is a analysis on the Eternium Games engine Called Marmalade SDK Note: I could not get the Asset key for the game it appears, it has to be done at runtime to decrypt the assets –I do not have a Jail-broken device sadly **Binary:** MageAndMinions (com.makingfun.mageandminions) App Store URL: https://apps.apple.com/us/app/eternium/id579931356 **Version:** 1.46.19.11 and later **Architecture:** ARM64e (64-bit) **Status:** Fully decrypted Mach-O binary, auto-analyzed in IDA Pro and used different local Uncensored LLM's this is 99.9 % verified there is a chance I may have got something wrong but I did 3 passes Here are my findings and there is also a verification report ## Executive Summary [Hidden Content]