diff --git a/Cargo.lock b/Cargo.lock index f63dcf7..ff64adf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -430,6 +430,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "iana-time-zone" version = "0.1.62" @@ -804,6 +810,7 @@ dependencies = [ "env_logger", "fatfs", "gpt", + "hex", "imago", "log", "open", diff --git a/Cargo.toml b/Cargo.toml index 8cfb3f5..9537f9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ clap-verbosity-flag = "3.0.2" env_logger = "0.11.7" fatfs = "0.3.6" gpt = "4.1.0" +hex = "0.4" imago = { version = "0.1.4", features = ["sync-wrappers"] } log = "0.4.27" open = "5.3.2" diff --git a/src/main.rs b/src/main.rs index dd1d0f5..c6f010e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ mod qcow2; mod serial; use std::{ - io::{Seek, SeekFrom, Write}, + io::{stdin, stdout, Seek, SeekFrom, Write}, path::PathBuf, }; @@ -48,9 +48,47 @@ fn main() -> Result<()> { let mut plist: MacPlist = plist::from_reader(&mut conf_plist)?; - let serial = serial::find_desired(plist.get_product_name())?; - let uuid = Uuid::new_v4(); - let rom = { + let mut needs_update = false; + + // Check if valid serials already exist + if plist.has_valid_serials() && !args.force_regenerate { + println!("Valid serial numbers already configured:"); + println!(" Serial Number: {}", plist.get_serial_number()); + println!(" MLB: {}", plist.get_mlb()); + println!(); + + if !args.dry_run { + print!("Do you want to regenerate new serial numbers? (y/N) "); + stdout().flush()?; + let mut buffer = String::new(); + stdin().read_line(&mut buffer)?; + if !buffer.trim().eq_ignore_ascii_case("y") && !buffer.trim().eq_ignore_ascii_case("yes") { + println!("Keeping existing serial numbers."); + } else { + needs_update = true; + } + } + } else { + needs_update = true; + } + + let serial = if needs_update { + serial::find_desired(plist.get_product_name())? + } else { + serial::Serial { + serial_number: plist.get_serial_number().to_string(), + board_serial: plist.get_mlb().to_string(), + } + }; + + let uuid = if needs_update { + Uuid::new_v4() + } else { + // Keep existing UUID + Uuid::new_v4() // We'll keep this for now; ideally we'd parse the existing one + }; + + let rom = if needs_update { let mut rom = [0; 12]; let mut rng = rand::rng(); @@ -69,37 +107,92 @@ fn main() -> Result<()> { } rom + } else { + [0; 12] // Keep existing ROM }; + // Check and add Sequoia patches if requested + let mut patches_added = false; + if args.add_sequoia_patches || args.force_sequoia_patches { + if !plist.has_sequoia_patches() || args.force_sequoia_patches { + println!(); + println!("Adding macOS Sequoia kernel patches for VM detection bypass..."); + plist.add_sequoia_kernel_patches(); + patches_added = true; + } else { + println!(); + println!("Sequoia kernel patches already present."); + } + } else if !plist.has_sequoia_patches() { + println!(); + println!("Note: Sequoia kernel patches not found in config.plist."); + println!("For macOS Sequoia 15.7.1+, you need kernel patches to enable Apple ID login."); + print!("Would you like to add them now? (Y/n) "); + stdout().flush()?; + let mut buffer = String::new(); + stdin().read_line(&mut buffer)?; + let answer = buffer.trim(); + if answer.is_empty() || answer.eq_ignore_ascii_case("y") || answer.eq_ignore_ascii_case("yes") { + plist.add_sequoia_kernel_patches(); + patches_added = true; + } + } + if args.dry_run { println!(); - println!("Set serial number to {}", serial.serial_number); - println!("Set MLB to {}", serial.board_serial); - println!("Set UUID to {}", uuid); - println!( - "Set ROM to {:?}", - std::str::from_utf8(&rom).context("ROM should always be valid UTF-8")? - ); + if needs_update { + println!("Would set serial number to {}", serial.serial_number); + println!("Would set MLB to {}", serial.board_serial); + println!("Would set UUID to {}", uuid); + println!( + "Would set ROM to {:?}", + std::str::from_utf8(&rom).context("ROM should always be valid UTF-8")? + ); + } + if patches_added { + println!("Would add Sequoia kernel patches"); + } return Ok(()); } - plist.set_serial_number(serial.serial_number); - plist.set_mlb(serial.board_serial); - plist.set_uuid(uuid); - plist.set_rom(rom); + // Only update if changes were made + if needs_update || patches_added { + if needs_update { + plist.set_serial_number(serial.serial_number); + plist.set_mlb(serial.board_serial); + plist.set_uuid(uuid); + plist.set_rom(rom); + } - plist.debug(); - conf_plist - .truncate() - .context("Failed to truncate config.plist")?; - conf_plist.seek(SeekFrom::Start(0))?; - plist::to_writer_xml(&mut conf_plist, &plist).context("Failed to write config.plist")?; + plist.debug(); + conf_plist + .truncate() + .context("Failed to truncate config.plist")?; + conf_plist.seek(SeekFrom::Start(0))?; + plist::to_writer_xml(&mut conf_plist, &plist).context("Failed to write config.plist")?; - conf_plist.flush()?; - drop(conf_plist); - fs.unmount()?; - first_partition.flush()?; - qcow2.flush()?; + conf_plist.flush()?; + drop(conf_plist); + fs.unmount()?; + first_partition.flush()?; + qcow2.flush()?; + + println!(); + println!("✓ Configuration updated successfully!"); + if patches_added { + println!("✓ Sequoia kernel patches added"); + println!(); + println!("IMPORTANT: These patches may prevent macOS system updates."); + println!("To update macOS:"); + println!(" 1. Temporarily disable the patches in OpenCore boot menu"); + println!(" 2. Reset NVRAM"); + println!(" 3. Install the update"); + println!(" 4. Re-enable the patches after updating"); + } + } else { + println!(); + println!("No changes needed."); + } Ok(()) } @@ -120,8 +213,19 @@ fn first_partition_subset(mut qcow2: &mut Qcow2) -> Result> struct Args { #[clap(long, help = "Path to the bootloader ('OpenCore.qcow2')")] bootloader: PathBuf, + #[clap(short, long, help = "Don't commit changes to disk")] dry_run: bool, + + #[clap(short = 'f', long, help = "Force regeneration of serial numbers even if valid ones exist")] + force_regenerate: bool, + + #[clap(short = 's', long, help = "Add Sequoia kernel patches for VM detection bypass")] + add_sequoia_patches: bool, + + #[clap(long, help = "Force add Sequoia patches even if they already exist")] + force_sequoia_patches: bool, + #[clap(flatten)] verbose: clap_verbosity_flag::Verbosity, } diff --git a/src/modelinfo.rs b/src/modelinfo.rs index c46c0e5..ee88383 100644 --- a/src/modelinfo.rs +++ b/src/modelinfo.rs @@ -1,12 +1,156 @@ /* automatically generated by rust-bindgen 0.71.1 */ +#[repr(C)] +#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct __BindgenBitfieldUnit { + storage: Storage, +} +impl __BindgenBitfieldUnit { + #[inline] + pub const fn new(storage: Storage) -> Self { + Self { storage } + } +} +impl __BindgenBitfieldUnit +where + Storage: AsRef<[u8]> + AsMut<[u8]>, +{ + #[inline] + fn extract_bit(byte: u8, index: usize) -> bool { + let bit_index = if cfg!(target_endian = "big") { + 7 - (index % 8) + } else { + index % 8 + }; + let mask = 1 << bit_index; + byte & mask == mask + } + #[inline] + pub fn get_bit(&self, index: usize) -> bool { + debug_assert!(index / 8 < self.storage.as_ref().len()); + let byte_index = index / 8; + let byte = self.storage.as_ref()[byte_index]; + Self::extract_bit(byte, index) + } + #[inline] + pub unsafe fn raw_get_bit(this: *const Self, index: usize) -> bool { + debug_assert!(index / 8 < core::mem::size_of::()); + let byte_index = index / 8; + let byte = *(core::ptr::addr_of!((*this).storage) as *const u8).offset(byte_index as isize); + Self::extract_bit(byte, index) + } + #[inline] + fn change_bit(byte: u8, index: usize, val: bool) -> u8 { + let bit_index = if cfg!(target_endian = "big") { + 7 - (index % 8) + } else { + index % 8 + }; + let mask = 1 << bit_index; + if val { + byte | mask + } else { + byte & !mask + } + } + #[inline] + pub fn set_bit(&mut self, index: usize, val: bool) { + debug_assert!(index / 8 < self.storage.as_ref().len()); + let byte_index = index / 8; + let byte = &mut self.storage.as_mut()[byte_index]; + *byte = Self::change_bit(*byte, index, val); + } + #[inline] + pub unsafe fn raw_set_bit(this: *mut Self, index: usize, val: bool) { + debug_assert!(index / 8 < core::mem::size_of::()); + let byte_index = index / 8; + let byte = + (core::ptr::addr_of_mut!((*this).storage) as *mut u8).offset(byte_index as isize); + *byte = Self::change_bit(*byte, index, val); + } + #[inline] + pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 { + debug_assert!(bit_width <= 64); + debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); + debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len()); + let mut val = 0; + for i in 0..(bit_width as usize) { + if self.get_bit(i + bit_offset) { + let index = if cfg!(target_endian = "big") { + bit_width as usize - 1 - i + } else { + i + }; + val |= 1 << index; + } + } + val + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + debug_assert!(bit_width <= 64); + debug_assert!(bit_offset / 8 < core::mem::size_of::()); + debug_assert!((bit_offset + (bit_width as usize)) / 8 <= core::mem::size_of::()); + let mut val = 0; + for i in 0..(bit_width as usize) { + if Self::raw_get_bit(this, i + bit_offset) { + let index = if cfg!(target_endian = "big") { + bit_width as usize - 1 - i + } else { + i + }; + val |= 1 << index; + } + } + val + } + #[inline] + pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) { + debug_assert!(bit_width <= 64); + debug_assert!(bit_offset / 8 < self.storage.as_ref().len()); + debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len()); + for i in 0..(bit_width as usize) { + let mask = 1 << i; + let val_bit_is_set = val & mask == mask; + let index = if cfg!(target_endian = "big") { + bit_width as usize - 1 - i + } else { + i + }; + self.set_bit(index + bit_offset, val_bit_is_set); + } + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + debug_assert!(bit_width <= 64); + debug_assert!(bit_offset / 8 < core::mem::size_of::()); + debug_assert!((bit_offset + (bit_width as usize)) / 8 <= core::mem::size_of::()); + for i in 0..(bit_width as usize) { + let mask = 1 << i; + let val_bit_is_set = val & mask == mask; + let index = if cfg!(target_endian = "big") { + bit_width as usize - 1 - i + } else { + i + }; + Self::raw_set_bit(this, index + bit_offset, val_bit_is_set); + } + } +} +#[derive(PartialEq, Copy, Clone, Hash, Debug, Default)] +#[repr(C)] +pub struct __BindgenComplex { + pub re: T, + pub im: T, +} +pub const __bool_true_false_are_defined: u32 = 1; pub const true_: u32 = 1; pub const false_: u32 = 0; -pub const __bool_true_false_are_defined: u32 = 1; pub const _STDINT_H: u32 = 1; pub const _FEATURES_H: u32 = 1; pub const _DEFAULT_SOURCE: u32 = 1; -pub const __GLIBC_USE_ISOC2X: u32 = 0; +pub const __GLIBC_USE_ISOC2Y: u32 = 0; +pub const __GLIBC_USE_ISOC23: u32 = 0; pub const __USE_ISOC11: u32 = 1; pub const __USE_ISOC99: u32 = 1; pub const __USE_ISOC95: u32 = 1; @@ -24,11 +168,13 @@ pub const __WORDSIZE: u32 = 64; pub const __WORDSIZE_TIME64_COMPAT32: u32 = 1; pub const __SYSCALL_WORDSIZE: u32 = 64; pub const __TIMESIZE: u32 = 64; +pub const __USE_TIME_BITS64: u32 = 1; pub const __USE_MISC: u32 = 1; pub const __USE_ATFILE: u32 = 1; pub const __USE_FORTIFY_LEVEL: u32 = 0; pub const __GLIBC_USE_DEPRECATED_GETS: u32 = 0; pub const __GLIBC_USE_DEPRECATED_SCANF: u32 = 0; +pub const __GLIBC_USE_C23_STRTOL: u32 = 0; pub const _STDC_PREDEF_H: u32 = 1; pub const __STDC_IEC_559__: u32 = 1; pub const __STDC_IEC_60559_BFP__: u32 = 201404; @@ -37,17 +183,17 @@ pub const __STDC_IEC_60559_COMPLEX__: u32 = 201404; pub const __STDC_ISO_10646__: u32 = 201706; pub const __GNU_LIBRARY__: u32 = 6; pub const __GLIBC__: u32 = 2; -pub const __GLIBC_MINOR__: u32 = 35; +pub const __GLIBC_MINOR__: u32 = 42; pub const _SYS_CDEFS_H: u32 = 1; pub const __glibc_c99_flexarr_available: u32 = 1; pub const __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI: u32 = 0; pub const __HAVE_GENERIC_SELECTION: u32 = 1; pub const __GLIBC_USE_LIB_EXT2: u32 = 0; pub const __GLIBC_USE_IEC_60559_BFP_EXT: u32 = 0; -pub const __GLIBC_USE_IEC_60559_BFP_EXT_C2X: u32 = 0; +pub const __GLIBC_USE_IEC_60559_BFP_EXT_C23: u32 = 0; pub const __GLIBC_USE_IEC_60559_EXT: u32 = 0; pub const __GLIBC_USE_IEC_60559_FUNCS_EXT: u32 = 0; -pub const __GLIBC_USE_IEC_60559_FUNCS_EXT_C2X: u32 = 0; +pub const __GLIBC_USE_IEC_60559_FUNCS_EXT_C23: u32 = 0; pub const __GLIBC_USE_IEC_60559_TYPES_EXT: u32 = 0; pub const _BITS_TYPES_H: u32 = 1; pub const _BITS_TYPESIZES_H: u32 = 1; @@ -61,6 +207,7 @@ pub const _BITS_TIME64_H: u32 = 1; pub const _BITS_WCHAR_H: u32 = 1; pub const _BITS_STDINT_INTN_H: u32 = 1; pub const _BITS_STDINT_UINTN_H: u32 = 1; +pub const _BITS_STDINT_LEAST_H: u32 = 1; pub const INT8_MIN: i32 = -128; pub const INT16_MIN: i32 = -32768; pub const INT32_MIN: i32 = -2147483648; @@ -206,7 +353,6 @@ pub const LINE_MAX: u32 = 2048; pub const CHARCLASS_NAME_MAX: u32 = 2048; pub const RE_DUP_MAX: u32 = 32767; pub const _STDIO_H: u32 = 1; -pub const __GNUC_VA_LIST: u32 = 1; pub const _____fpos_t_defined: u32 = 1; pub const ____mbstate_t_defined: u32 = 1; pub const _____fpos64_t_defined: u32 = 1; @@ -216,6 +362,7 @@ pub const __struct_FILE_defined: u32 = 1; pub const _IO_EOF_SEEN: u32 = 16; pub const _IO_ERR_SEEN: u32 = 32; pub const _IO_USER_LOCK: u32 = 32768; +pub const __cookie_io_functions_t_defined: u32 = 1; pub const _IOFBF: u32 = 0; pub const _IOLBF: u32 = 1; pub const _IONBF: u32 = 2; @@ -225,14 +372,14 @@ pub const SEEK_SET: u32 = 0; pub const SEEK_CUR: u32 = 1; pub const SEEK_END: u32 = 2; pub const P_tmpdir: &[u8; 5] = b"/tmp\0"; -pub const _BITS_STDIO_LIM_H: u32 = 1; pub const L_tmpnam: u32 = 20; pub const TMP_MAX: u32 = 238328; +pub const _BITS_STDIO_LIM_H: u32 = 1; pub const FILENAME_MAX: u32 = 4096; pub const L_ctermid: u32 = 9; pub const FOPEN_MAX: u32 = 16; -pub const __HAVE_FLOAT128: u32 = 0; -pub const __HAVE_DISTINCT_FLOAT128: u32 = 0; +pub const __HAVE_FLOAT128: u32 = 1; +pub const __HAVE_DISTINCT_FLOAT128: u32 = 1; pub const __HAVE_FLOAT64X: u32 = 1; pub const __HAVE_FLOAT64X_LONG_DOUBLE: u32 = 1; pub const __HAVE_FLOAT16: u32 = 0; @@ -645,7 +792,6 @@ unsafe extern "C" { unsafe extern "C" { pub static mut AppleModelDesc: [APPLE_MODEL_DESC; 9042usize]; } -pub type va_list = __builtin_va_list; pub type __gnuc_va_list = __builtin_va_list; #[repr(C)] #[derive(Copy, Clone)] @@ -744,7 +890,9 @@ pub struct _IO_FILE { pub _markers: *mut _IO_marker, pub _chain: *mut _IO_FILE, pub _fileno: ::std::os::raw::c_int, - pub _flags2: ::std::os::raw::c_int, + pub _bitfield_align_1: [u32; 0], + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 3usize]>, + pub _short_backupbuf: [::std::os::raw::c_char; 1usize], pub _old_offset: __off_t, pub _cur_column: ::std::os::raw::c_ushort, pub _vtable_offset: ::std::os::raw::c_schar, @@ -755,9 +903,11 @@ pub struct _IO_FILE { pub _wide_data: *mut _IO_wide_data, pub _freeres_list: *mut _IO_FILE, pub _freeres_buf: *mut ::std::os::raw::c_void, - pub __pad5: usize, + pub _prevchain: *mut *mut _IO_FILE, pub _mode: ::std::os::raw::c_int, - pub _unused2: [::std::os::raw::c_char; 20usize], + pub _unused3: ::std::os::raw::c_int, + pub _total_written: __uint64_t, + pub _unused2: [::std::os::raw::c_char; 8usize], } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { @@ -789,7 +939,8 @@ const _: () = { ["Offset of field: _IO_FILE::_markers"][::std::mem::offset_of!(_IO_FILE, _markers) - 96usize]; ["Offset of field: _IO_FILE::_chain"][::std::mem::offset_of!(_IO_FILE, _chain) - 104usize]; ["Offset of field: _IO_FILE::_fileno"][::std::mem::offset_of!(_IO_FILE, _fileno) - 112usize]; - ["Offset of field: _IO_FILE::_flags2"][::std::mem::offset_of!(_IO_FILE, _flags2) - 116usize]; + ["Offset of field: _IO_FILE::_short_backupbuf"] + [::std::mem::offset_of!(_IO_FILE, _short_backupbuf) - 119usize]; ["Offset of field: _IO_FILE::_old_offset"] [::std::mem::offset_of!(_IO_FILE, _old_offset) - 120usize]; ["Offset of field: _IO_FILE::_cur_column"] @@ -807,10 +958,107 @@ const _: () = { [::std::mem::offset_of!(_IO_FILE, _freeres_list) - 168usize]; ["Offset of field: _IO_FILE::_freeres_buf"] [::std::mem::offset_of!(_IO_FILE, _freeres_buf) - 176usize]; - ["Offset of field: _IO_FILE::__pad5"][::std::mem::offset_of!(_IO_FILE, __pad5) - 184usize]; + ["Offset of field: _IO_FILE::_prevchain"] + [::std::mem::offset_of!(_IO_FILE, _prevchain) - 184usize]; ["Offset of field: _IO_FILE::_mode"][::std::mem::offset_of!(_IO_FILE, _mode) - 192usize]; - ["Offset of field: _IO_FILE::_unused2"][::std::mem::offset_of!(_IO_FILE, _unused2) - 196usize]; + ["Offset of field: _IO_FILE::_unused3"][::std::mem::offset_of!(_IO_FILE, _unused3) - 196usize]; + ["Offset of field: _IO_FILE::_total_written"] + [::std::mem::offset_of!(_IO_FILE, _total_written) - 200usize]; + ["Offset of field: _IO_FILE::_unused2"][::std::mem::offset_of!(_IO_FILE, _unused2) - 208usize]; +}; +impl _IO_FILE { + #[inline] + pub fn _flags2(&self) -> ::std::os::raw::c_int { + unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 24u8) as u32) } + } + #[inline] + pub fn set__flags2(&mut self, val: ::std::os::raw::c_int) { + unsafe { + let val: u32 = ::std::mem::transmute(val); + self._bitfield_1.set(0usize, 24u8, val as u64) + } + } + #[inline] + pub unsafe fn _flags2_raw(this: *const Self) -> ::std::os::raw::c_int { + unsafe { + ::std::mem::transmute(<__BindgenBitfieldUnit<[u8; 3usize]>>::raw_get( + ::std::ptr::addr_of!((*this)._bitfield_1), + 0usize, + 24u8, + ) as u32) + } + } + #[inline] + pub unsafe fn set__flags2_raw(this: *mut Self, val: ::std::os::raw::c_int) { + unsafe { + let val: u32 = ::std::mem::transmute(val); + <__BindgenBitfieldUnit<[u8; 3usize]>>::raw_set( + ::std::ptr::addr_of_mut!((*this)._bitfield_1), + 0usize, + 24u8, + val as u64, + ) + } + } + #[inline] + pub fn new_bitfield_1(_flags2: ::std::os::raw::c_int) -> __BindgenBitfieldUnit<[u8; 3usize]> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 3usize]> = Default::default(); + __bindgen_bitfield_unit.set(0usize, 24u8, { + let _flags2: u32 = unsafe { ::std::mem::transmute(_flags2) }; + _flags2 as u64 + }); + __bindgen_bitfield_unit + } +} +pub type cookie_read_function_t = ::std::option::Option< + unsafe extern "C" fn( + __cookie: *mut ::std::os::raw::c_void, + __buf: *mut ::std::os::raw::c_char, + __nbytes: usize, + ) -> __ssize_t, +>; +pub type cookie_write_function_t = ::std::option::Option< + unsafe extern "C" fn( + __cookie: *mut ::std::os::raw::c_void, + __buf: *const ::std::os::raw::c_char, + __nbytes: usize, + ) -> __ssize_t, +>; +pub type cookie_seek_function_t = ::std::option::Option< + unsafe extern "C" fn( + __cookie: *mut ::std::os::raw::c_void, + __pos: *mut __off64_t, + __w: ::std::os::raw::c_int, + ) -> ::std::os::raw::c_int, +>; +pub type cookie_close_function_t = ::std::option::Option< + unsafe extern "C" fn(__cookie: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int, +>; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct _IO_cookie_io_functions_t { + pub read: cookie_read_function_t, + pub write: cookie_write_function_t, + pub seek: cookie_seek_function_t, + pub close: cookie_close_function_t, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of _IO_cookie_io_functions_t"] + [::std::mem::size_of::<_IO_cookie_io_functions_t>() - 32usize]; + ["Alignment of _IO_cookie_io_functions_t"] + [::std::mem::align_of::<_IO_cookie_io_functions_t>() - 8usize]; + ["Offset of field: _IO_cookie_io_functions_t::read"] + [::std::mem::offset_of!(_IO_cookie_io_functions_t, read) - 0usize]; + ["Offset of field: _IO_cookie_io_functions_t::write"] + [::std::mem::offset_of!(_IO_cookie_io_functions_t, write) - 8usize]; + ["Offset of field: _IO_cookie_io_functions_t::seek"] + [::std::mem::offset_of!(_IO_cookie_io_functions_t, seek) - 16usize]; + ["Offset of field: _IO_cookie_io_functions_t::close"] + [::std::mem::offset_of!(_IO_cookie_io_functions_t, close) - 24usize]; }; +pub type cookie_io_functions_t = _IO_cookie_io_functions_t; +pub type va_list = __gnuc_va_list; pub type off_t = __off_t; pub type fpos_t = __fpos_t; unsafe extern "C" { @@ -880,6 +1128,13 @@ unsafe extern "C" { pub fn fdopen(__fd: ::std::os::raw::c_int, __modes: *const ::std::os::raw::c_char) -> *mut FILE; } +unsafe extern "C" { + pub fn fopencookie( + __magic_cookie: *mut ::std::os::raw::c_void, + __modes: *const ::std::os::raw::c_char, + __io_funcs: cookie_io_functions_t, + ) -> *mut FILE; +} unsafe extern "C" { pub fn fmemopen( __s: *mut ::std::os::raw::c_void, @@ -963,6 +1218,27 @@ unsafe extern "C" { __arg: *mut __va_list_tag, ) -> ::std::os::raw::c_int; } +unsafe extern "C" { + pub fn vasprintf( + __ptr: *mut *mut ::std::os::raw::c_char, + __f: *const ::std::os::raw::c_char, + __arg: *mut __va_list_tag, + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn __asprintf( + __ptr: *mut *mut ::std::os::raw::c_char, + __fmt: *const ::std::os::raw::c_char, + ... + ) -> ::std::os::raw::c_int; +} +unsafe extern "C" { + pub fn asprintf( + __ptr: *mut *mut ::std::os::raw::c_char, + __fmt: *const ::std::os::raw::c_char, + ... + ) -> ::std::os::raw::c_int; +} unsafe extern "C" { pub fn vdprintf( __fd: ::std::os::raw::c_int, @@ -994,6 +1270,8 @@ unsafe extern "C" { ... ) -> ::std::os::raw::c_int; } +pub type __cfloat128 = __BindgenComplex; +pub type _Float128 = u128; pub type _Float32 = f32; pub type _Float64 = f64; pub type _Float32x = f64; @@ -1634,11 +1912,12 @@ const _: () = { pub struct __pthread_cond_s { pub __wseq: __atomic_wide_counter, pub __g1_start: __atomic_wide_counter, - pub __g_refs: [::std::os::raw::c_uint; 2usize], pub __g_size: [::std::os::raw::c_uint; 2usize], pub __g1_orig_size: ::std::os::raw::c_uint, pub __wrefs: ::std::os::raw::c_uint, pub __g_signals: [::std::os::raw::c_uint; 2usize], + pub __unused_initialized_1: ::std::os::raw::c_uint, + pub __unused_initialized_2: ::std::os::raw::c_uint, } #[allow(clippy::unnecessary_operation, clippy::identity_op)] const _: () = { @@ -1648,16 +1927,18 @@ const _: () = { [::std::mem::offset_of!(__pthread_cond_s, __wseq) - 0usize]; ["Offset of field: __pthread_cond_s::__g1_start"] [::std::mem::offset_of!(__pthread_cond_s, __g1_start) - 8usize]; - ["Offset of field: __pthread_cond_s::__g_refs"] - [::std::mem::offset_of!(__pthread_cond_s, __g_refs) - 16usize]; ["Offset of field: __pthread_cond_s::__g_size"] - [::std::mem::offset_of!(__pthread_cond_s, __g_size) - 24usize]; + [::std::mem::offset_of!(__pthread_cond_s, __g_size) - 16usize]; ["Offset of field: __pthread_cond_s::__g1_orig_size"] - [::std::mem::offset_of!(__pthread_cond_s, __g1_orig_size) - 32usize]; + [::std::mem::offset_of!(__pthread_cond_s, __g1_orig_size) - 24usize]; ["Offset of field: __pthread_cond_s::__wrefs"] - [::std::mem::offset_of!(__pthread_cond_s, __wrefs) - 36usize]; + [::std::mem::offset_of!(__pthread_cond_s, __wrefs) - 28usize]; ["Offset of field: __pthread_cond_s::__g_signals"] - [::std::mem::offset_of!(__pthread_cond_s, __g_signals) - 40usize]; + [::std::mem::offset_of!(__pthread_cond_s, __g_signals) - 32usize]; + ["Offset of field: __pthread_cond_s::__unused_initialized_1"] + [::std::mem::offset_of!(__pthread_cond_s, __unused_initialized_1) - 40usize]; + ["Offset of field: __pthread_cond_s::__unused_initialized_2"] + [::std::mem::offset_of!(__pthread_cond_s, __unused_initialized_2) - 44usize]; }; pub type __tss_t = ::std::os::raw::c_uint; pub type __thrd_t = ::std::os::raw::c_ulong; @@ -1998,6 +2279,15 @@ unsafe extern "C" { __buffer: *mut drand48_data, ) -> ::std::os::raw::c_int; } +unsafe extern "C" { + pub fn arc4random() -> __uint32_t; +} +unsafe extern "C" { + pub fn arc4random_buf(__buf: *mut ::std::os::raw::c_void, __size: usize); +} +unsafe extern "C" { + pub fn arc4random_uniform(__upper_bound: __uint32_t) -> __uint32_t; +} unsafe extern "C" { pub fn malloc(__size: ::std::os::raw::c_ulong) -> *mut ::std::os::raw::c_void; } @@ -2443,6 +2733,12 @@ unsafe extern "C" { __c: ::std::os::raw::c_int, ) -> *mut ::std::os::raw::c_char; } +unsafe extern "C" { + pub fn strchrnul( + __s: *const ::std::os::raw::c_char, + __c: ::std::os::raw::c_int, + ) -> *mut ::std::os::raw::c_char; +} unsafe extern "C" { pub fn strcspn( __s: *const ::std::os::raw::c_char, @@ -2487,6 +2783,34 @@ unsafe extern "C" { __save_ptr: *mut *mut ::std::os::raw::c_char, ) -> *mut ::std::os::raw::c_char; } +unsafe extern "C" { + pub fn strcasestr( + __haystack: *const ::std::os::raw::c_char, + __needle: *const ::std::os::raw::c_char, + ) -> *mut ::std::os::raw::c_char; +} +unsafe extern "C" { + pub fn memmem( + __haystack: *const ::std::os::raw::c_void, + __haystacklen: usize, + __needle: *const ::std::os::raw::c_void, + __needlelen: usize, + ) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + pub fn __mempcpy( + __dest: *mut ::std::os::raw::c_void, + __src: *const ::std::os::raw::c_void, + __n: usize, + ) -> *mut ::std::os::raw::c_void; +} +unsafe extern "C" { + pub fn mempcpy( + __dest: *mut ::std::os::raw::c_void, + __src: *const ::std::os::raw::c_void, + __n: ::std::os::raw::c_ulong, + ) -> *mut ::std::os::raw::c_void; +} unsafe extern "C" { pub fn strlen(__s: *const ::std::os::raw::c_char) -> ::std::os::raw::c_ulong; } @@ -2521,7 +2845,7 @@ unsafe extern "C" { pub fn bcopy( __src: *const ::std::os::raw::c_void, __dest: *mut ::std::os::raw::c_void, - __n: usize, + __n: ::std::os::raw::c_ulong, ); } unsafe extern "C" { @@ -2614,6 +2938,20 @@ unsafe extern "C" { __n: ::std::os::raw::c_ulong, ) -> *mut ::std::os::raw::c_char; } +unsafe extern "C" { + pub fn strlcpy( + __dest: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> ::std::os::raw::c_ulong; +} +unsafe extern "C" { + pub fn strlcat( + __dest: *mut ::std::os::raw::c_char, + __src: *const ::std::os::raw::c_char, + __n: ::std::os::raw::c_ulong, + ) -> ::std::os::raw::c_ulong; +} #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct tm { diff --git a/src/plist_data.rs b/src/plist_data.rs index 89d7ba0..5836fd4 100644 --- a/src/plist_data.rs +++ b/src/plist_data.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use plist::Value; +use plist::{Dictionary, Value}; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -21,6 +21,25 @@ impl MacPlist { &self.platform_info.generic.system_product_name } + pub fn get_serial_number(&self) -> &str { + &self.platform_info.generic.system_serial_number + } + + pub fn get_mlb(&self) -> &str { + &self.platform_info.generic.mlb + } + + pub fn has_valid_serials(&self) -> bool { + let serial = self.get_serial_number(); + let mlb = self.get_mlb(); + + // Check if serials are not empty and not default values + !serial.is_empty() && + !mlb.is_empty() && + serial != "NO_DEVICE_SN" && + mlb != "NO_LOGIC_BOARD_SN" + } + pub fn set_serial_number(&mut self, serial_number: String) { self.platform_info.generic.system_serial_number = serial_number; } @@ -36,6 +55,92 @@ impl MacPlist { pub fn set_rom(&mut self, rom: [u8; 12]) { self.platform_info.generic.rom = Value::Data(rom.to_vec()); } + + pub fn add_sequoia_kernel_patches(&mut self) { + // Get or create the Kernel section + let kernel = self.other + .entry("Kernel".to_string()) + .or_insert_with(|| Value::Dictionary(Dictionary::new())); + + if let Value::Dictionary(kernel_dict) = kernel { + // Get or create the Patch array + let patch_array = if let Some(existing) = kernel_dict.get("Patch") { + kernel_dict.get_mut("Patch").unwrap() + } else { + kernel_dict.insert("Patch".to_string(), Value::Array(Vec::new())); + kernel_dict.get_mut("Patch").unwrap() + }; + + if let Value::Array(patches) = patch_array { + // Check if patches already exist + let has_vmm_patch = patches.iter().any(|p| { + if let Value::Dictionary(d) = p { + if let Some(Value::String(comment)) = d.get("Comment") { + return comment.contains("kern.hv_vmm_present") || + comment.contains("VM detection"); + } + } + false + }); + + if !has_vmm_patch { + // Patch 1: Rename kern.hv_vmm_present to hibernatecount + let mut patch1 = Dictionary::new(); + patch1.insert("Arch".to_string(), Value::String("x86_64".to_string())); + patch1.insert("Base".to_string(), Value::String("".to_string())); + patch1.insert("Comment".to_string(), Value::String("Disable VM detection (kern.hv_vmm_present -> hibernatecount) for Sequoia".to_string())); + patch1.insert("Count".to_string(), Value::Integer(1.into())); + patch1.insert("Enabled".to_string(), Value::Boolean(true)); + patch1.insert("Find".to_string(), Value::Data(hex::decode("68696265726E61746568696472656164790068696265726E617465636F756E7400").unwrap())); + patch1.insert("Replace".to_string(), Value::Data(hex::decode("68696265726E61746568696472656164790068765F766D6D5F70726573656E7400").unwrap())); + patch1.insert("Identifier".to_string(), Value::String("kernel".to_string())); + patch1.insert("MinKernel".to_string(), Value::String("24.0.0".to_string())); + patch1.insert("MaxKernel".to_string(), Value::String("".to_string())); + patch1.insert("Mask".to_string(), Value::Data(Vec::new())); + patch1.insert("ReplaceMask".to_string(), Value::Data(Vec::new())); + patch1.insert("Skip".to_string(), Value::Integer(0.into())); + + // Patch 2: Rename back (second patch) + let mut patch2 = Dictionary::new(); + patch2.insert("Arch".to_string(), Value::String("x86_64".to_string())); + patch2.insert("Base".to_string(), Value::String("".to_string())); + patch2.insert("Comment".to_string(), Value::String("Disable VM detection (hibernatecount -> hv_vmm_present) for Sequoia".to_string())); + patch2.insert("Count".to_string(), Value::Integer(1.into())); + patch2.insert("Enabled".to_string(), Value::Boolean(true)); + patch2.insert("Find".to_string(), Value::Data(hex::decode("626F6F742073657373696F6E20555549440068765F766D6D5F70726573656E7400").unwrap())); + patch2.insert("Replace".to_string(), Value::Data(hex::decode("626F6F742073657373696F6E20555549440068696265726E617465636F756E7400").unwrap())); + patch2.insert("Identifier".to_string(), Value::String("kernel".to_string())); + patch2.insert("MinKernel".to_string(), Value::String("24.0.0".to_string())); + patch2.insert("MaxKernel".to_string(), Value::String("".to_string())); + patch2.insert("Mask".to_string(), Value::Data(Vec::new())); + patch2.insert("ReplaceMask".to_string(), Value::Data(Vec::new())); + patch2.insert("Skip".to_string(), Value::Integer(0.into())); + + patches.push(Value::Dictionary(patch1)); + patches.push(Value::Dictionary(patch2)); + + log::info!("Added Sequoia kernel patches for VM detection bypass"); + } + } + } + } + + pub fn has_sequoia_patches(&self) -> bool { + if let Some(Value::Dictionary(kernel_dict)) = self.other.get("Kernel") { + if let Some(Value::Array(patches)) = kernel_dict.get("Patch") { + return patches.iter().any(|p| { + if let Value::Dictionary(d) = p { + if let Some(Value::String(comment)) = d.get("Comment") { + return comment.contains("kern.hv_vmm_present") || + comment.contains("VM detection"); + } + } + false + }); + } + } + false + } } #[derive(Debug, Clone, Deserialize, Serialize)]