mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 14:36:16 +01:00
Implement TSS support
This commit is contained in:
@@ -22,7 +22,7 @@ log = { version = "0.4" }
|
||||
env_logger = { version = "0.11" }
|
||||
|
||||
indexmap = { version = "2.7", features = ["serde"], optional = true }
|
||||
uuid = { version = "1.12", features = ["serde"], optional = true }
|
||||
uuid = { version = "1.12", features = ["serde", "v4"], optional = true }
|
||||
async-recursion = { version = "1.1", optional = true }
|
||||
base64 = { version = "0.22", optional = true }
|
||||
|
||||
@@ -30,6 +30,8 @@ serde_json = { version = "1", optional = true }
|
||||
json = { version = "0.12", optional = true }
|
||||
byteorder = { version = "1.5", optional = true }
|
||||
|
||||
reqwest = { version = "0.12", features = ["json"], optional = true }
|
||||
|
||||
|
||||
[features]
|
||||
core_device_proxy = ["dep:serde_json", "dep:json", "dep:byteorder"]
|
||||
@@ -38,6 +40,7 @@ installation_proxy = []
|
||||
mounter = []
|
||||
usbmuxd = []
|
||||
tcp = ["tokio/net"]
|
||||
tss = ["dep:uuid", "dep:reqwest"]
|
||||
xpc = [
|
||||
"tokio/full",
|
||||
"dep:indexmap",
|
||||
@@ -53,6 +56,7 @@ full = [
|
||||
"usbmuxd",
|
||||
"xpc",
|
||||
"tcp",
|
||||
"tss",
|
||||
]
|
||||
|
||||
# Why: https://github.com/rust-lang/cargo/issues/1197
|
||||
|
||||
@@ -13,8 +13,11 @@ pub mod lockdownd;
|
||||
pub mod mounter;
|
||||
pub mod pairing_file;
|
||||
pub mod provider;
|
||||
#[cfg(feature = "tss")]
|
||||
pub mod tss;
|
||||
#[cfg(feature = "usbmuxd")]
|
||||
pub mod usbmuxd;
|
||||
mod util;
|
||||
#[cfg(feature = "xpc")]
|
||||
pub mod xpc;
|
||||
|
||||
@@ -216,6 +219,13 @@ pub enum IdeviceError {
|
||||
#[error("usb bad version")]
|
||||
UsbBadVersion,
|
||||
|
||||
#[error("bad build manifest")]
|
||||
BadBuildManifest,
|
||||
|
||||
#[cfg(feature = "tss")]
|
||||
#[error("http reqwest error")]
|
||||
Reqwest(#[from] reqwest::Error),
|
||||
|
||||
#[error("unknown error `{0}` returned from device")]
|
||||
UnknownErrorType(String),
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Abstractions for lockdownd
|
||||
|
||||
use log::error;
|
||||
use plist::Value;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{pairing_file, Idevice, IdeviceError, IdeviceService};
|
||||
@@ -37,7 +38,7 @@ impl LockdowndClient {
|
||||
pub fn new(idevice: Idevice) -> Self {
|
||||
Self { idevice }
|
||||
}
|
||||
pub async fn get_value(&mut self, value: impl Into<String>) -> Result<String, IdeviceError> {
|
||||
pub async fn get_value(&mut self, value: impl Into<String>) -> Result<Value, IdeviceError> {
|
||||
let req = LockdowndRequest {
|
||||
label: self.idevice.label.clone(),
|
||||
key: Some(value.into()),
|
||||
@@ -47,7 +48,7 @@ impl LockdowndClient {
|
||||
self.idevice.send_plist(message).await?;
|
||||
let message: plist::Dictionary = self.idevice.read_plist().await?;
|
||||
match message.get("Value") {
|
||||
Some(m) => Ok(plist::from_value(m)?),
|
||||
Some(m) => Ok(m.to_owned()),
|
||||
None => Err(IdeviceError::UnexpectedResponse),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use crate::{lockdownd::LockdowndClient, Idevice, IdeviceError, IdeviceService};
|
||||
use crate::{
|
||||
lockdownd::LockdowndClient, tss::TSSRequest, util::hashmap_to_dictionary, Idevice,
|
||||
IdeviceError, IdeviceService,
|
||||
};
|
||||
|
||||
pub struct ImageMounter {
|
||||
idevice: Idevice,
|
||||
@@ -51,6 +54,26 @@ impl ImageMounter {
|
||||
}
|
||||
}
|
||||
|
||||
/// Looks up an image and returns the signature
|
||||
pub async fn lookup_image(
|
||||
&mut self,
|
||||
image_type: impl Into<String>,
|
||||
) -> Result<Vec<u8>, IdeviceError> {
|
||||
let image_type = image_type.into();
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Command".into(), "LookupImage".into());
|
||||
req.insert("ImageType".into(), image_type.into());
|
||||
self.idevice
|
||||
.send_plist(plist::Value::Dictionary(req))
|
||||
.await?;
|
||||
|
||||
let res = self.idevice.read_plist().await?;
|
||||
match res.get("ImageSignature") {
|
||||
Some(plist::Value::Data(signature)) => Ok(signature.clone()),
|
||||
_ => Err(IdeviceError::NotFound),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn upload_image(
|
||||
&mut self,
|
||||
image_type: impl Into<String>,
|
||||
@@ -129,6 +152,28 @@ impl ImageMounter {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unmounts an image at a specified path.
|
||||
/// Use ``/Developer`` for pre-iOS 17 developer images.
|
||||
/// Use ``/System/Developer`` for personalized images.
|
||||
pub async fn unmount_image(
|
||||
&mut self,
|
||||
mount_path: impl Into<String>,
|
||||
) -> Result<(), IdeviceError> {
|
||||
let mount_path = mount_path.into();
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Command".into(), "UnmountImage".into());
|
||||
req.insert("MountPath".into(), mount_path.into());
|
||||
self.idevice
|
||||
.send_plist(plist::Value::Dictionary(req))
|
||||
.await?;
|
||||
|
||||
let res = self.idevice.read_plist().await?;
|
||||
match res.get("Status") {
|
||||
Some(plist::Value::String(s)) if s.as_str() == "Complete" => Ok(()),
|
||||
_ => Err(IdeviceError::UnexpectedResponse),
|
||||
}
|
||||
}
|
||||
|
||||
/// Queries the personalization manifest from the device.
|
||||
/// On failure, the socket must be closed and reestablished.
|
||||
pub async fn query_personalization_manifest(
|
||||
@@ -153,4 +198,334 @@ impl ImageMounter {
|
||||
_ => Err(IdeviceError::NotFound),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn query_developer_mode_status(&mut self) -> Result<bool, IdeviceError> {
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Command".into(), "QueryDeveloperModeStatus".into());
|
||||
self.idevice
|
||||
.send_plist(plist::Value::Dictionary(req))
|
||||
.await?;
|
||||
|
||||
let res = self.idevice.read_plist().await?;
|
||||
match res.get("DeveloperModeStatus") {
|
||||
Some(plist::Value::Boolean(status)) => Ok(*status),
|
||||
_ => Err(IdeviceError::UnexpectedResponse),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn query_nonce(
|
||||
&mut self,
|
||||
personalized_image_type: Option<String>,
|
||||
) -> Result<Vec<u8>, IdeviceError> {
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Command".into(), "QueryNonce".into());
|
||||
if let Some(image_type) = personalized_image_type {
|
||||
req.insert("PersonalizedImageType".into(), image_type.into());
|
||||
}
|
||||
self.idevice
|
||||
.send_plist(plist::Value::Dictionary(req))
|
||||
.await?;
|
||||
|
||||
let res = self.idevice.read_plist().await?;
|
||||
match res.get("PersonalizationNonce") {
|
||||
Some(plist::Value::Data(nonce)) => Ok(nonce.clone()),
|
||||
_ => Err(IdeviceError::UnexpectedResponse),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn query_personalization_identifiers(
|
||||
&mut self,
|
||||
image_type: Option<String>,
|
||||
) -> Result<plist::Dictionary, IdeviceError> {
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Command".into(), "QueryPersonalizationIdentifiers".into());
|
||||
if let Some(image_type) = image_type {
|
||||
req.insert("PersonalizedImageType".into(), image_type.into());
|
||||
}
|
||||
self.idevice
|
||||
.send_plist(plist::Value::Dictionary(req))
|
||||
.await?;
|
||||
|
||||
let res = self.idevice.read_plist().await?;
|
||||
match res.get("PersonalizationIdentifiers") {
|
||||
Some(plist::Value::Dictionary(identifiers)) => Ok(identifiers.clone()),
|
||||
_ => Err(IdeviceError::UnexpectedResponse),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn roll_personalization_nonce(&mut self) -> Result<(), IdeviceError> {
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Command".into(), "RollPersonalizationNonce".into());
|
||||
self.idevice
|
||||
.send_plist(plist::Value::Dictionary(req))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn roll_cryptex_nonce(&mut self) -> Result<(), IdeviceError> {
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Command".into(), "RollCryptexNonce".into());
|
||||
self.idevice
|
||||
.send_plist(plist::Value::Dictionary(req))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn mount_developer(
|
||||
&mut self,
|
||||
image: &[u8],
|
||||
signature: Vec<u8>,
|
||||
) -> Result<(), IdeviceError> {
|
||||
self.upload_image("Developer", &image, signature.clone())
|
||||
.await?;
|
||||
self.mount_image(
|
||||
"Developer",
|
||||
signature,
|
||||
Vec::new(),
|
||||
plist::Value::Dictionary(plist::Dictionary::new()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn mount_personalized(
|
||||
&mut self,
|
||||
image: Vec<u8>,
|
||||
trust_cache: Vec<u8>,
|
||||
build_manifest: &[u8],
|
||||
info_plist: Option<plist::Value>,
|
||||
unique_chip_id: u64,
|
||||
) -> Result<(), IdeviceError> {
|
||||
// Try to fetch personalization manifest
|
||||
let manifest = match self
|
||||
.query_personalization_manifest("DeveloperDiskImage", image.clone()) // TODO:
|
||||
.await
|
||||
{
|
||||
Ok(manifest) => manifest,
|
||||
Err(IdeviceError::NotFound) => {
|
||||
// Get manifest from TSS
|
||||
let manifest_dict: plist::Dictionary = plist::from_bytes(build_manifest)?;
|
||||
self.get_manifest_from_tss(&manifest_dict, unique_chip_id)
|
||||
.await?
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
self.upload_image("Personalized", &image, manifest.clone())
|
||||
.await?;
|
||||
|
||||
let mut extras = plist::Dictionary::new();
|
||||
if let Some(info) = info_plist {
|
||||
extras.insert("ImageInfoPlist".into(), info);
|
||||
}
|
||||
extras.insert(
|
||||
"ImageTrustCache".into(),
|
||||
plist::Value::Data(trust_cache.clone()),
|
||||
);
|
||||
|
||||
self.mount_image(
|
||||
"Personalized",
|
||||
manifest,
|
||||
trust_cache,
|
||||
plist::Value::Dictionary(extras),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "tss")]
|
||||
pub async fn get_manifest_from_tss(
|
||||
&mut self,
|
||||
build_manifest: &plist::Dictionary,
|
||||
unique_chip_id: u64,
|
||||
) -> Result<Vec<u8>, IdeviceError> {
|
||||
use log::{debug, warn};
|
||||
|
||||
let mut request = TSSRequest::new();
|
||||
|
||||
let personalization_identifiers = self.query_personalization_identifiers(None).await?;
|
||||
for (key, val) in &personalization_identifiers {
|
||||
if key.starts_with("Ap,") {
|
||||
request.insert(key, val.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let board_id = match personalization_identifiers.get("BoardId") {
|
||||
Some(plist::Value::Integer(b)) => match b.as_unsigned() {
|
||||
Some(b) => b,
|
||||
None => return Err(IdeviceError::UnexpectedResponse),
|
||||
},
|
||||
_ => {
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
};
|
||||
let chip_id = match personalization_identifiers.get("ChipID") {
|
||||
Some(plist::Value::Integer(b)) => match b.as_unsigned() {
|
||||
Some(b) => b,
|
||||
None => return Err(IdeviceError::UnexpectedResponse),
|
||||
},
|
||||
_ => {
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
};
|
||||
|
||||
request.insert("@ApImg4Ticket", true);
|
||||
request.insert("@BBTicket", true);
|
||||
request.insert("ApBoardID", board_id);
|
||||
request.insert("ApChipID", chip_id);
|
||||
request.insert("ApECID", unique_chip_id);
|
||||
request.insert(
|
||||
"ApNonce",
|
||||
plist::Value::Data(
|
||||
self.query_nonce(Some("DeveloperDiskImage".to_string()))
|
||||
.await?,
|
||||
),
|
||||
);
|
||||
request.insert("ApProductionMode", true);
|
||||
request.insert("ApSecurityDomain", 1);
|
||||
request.insert("ApSecurityMode", true);
|
||||
request.insert("SepNonce", plist::Value::Data(vec![0; 20]));
|
||||
request.insert("UID_MODE", false);
|
||||
|
||||
let identities = match build_manifest.get("BuildIdentities") {
|
||||
Some(plist::Value::Array(i)) => i,
|
||||
_ => {
|
||||
return Err(IdeviceError::BadBuildManifest);
|
||||
}
|
||||
};
|
||||
let mut build_identity = None;
|
||||
for id in identities {
|
||||
let id = match id {
|
||||
plist::Value::Dictionary(id) => id,
|
||||
_ => {
|
||||
debug!("build identity wasn't a dictionary");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let ap_board_id = match id.get("ApBoardID") {
|
||||
Some(plist::Value::String(a)) => a,
|
||||
_ => {
|
||||
debug!("Build identity contained no ApBoardID");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let ap_board_id = match u64::from_str_radix(ap_board_id.trim_start_matches("0x"), 16) {
|
||||
Ok(a) => a,
|
||||
Err(_) => {
|
||||
debug!("Could not parse {ap_board_id} as usize");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if ap_board_id != board_id {
|
||||
continue;
|
||||
}
|
||||
let ap_chip_id = match id.get("ApChipID") {
|
||||
Some(plist::Value::String(a)) => a,
|
||||
_ => {
|
||||
debug!("Build identity contained no ApChipID");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let ap_chip_id = match u64::from_str_radix(ap_chip_id.trim_start_matches("0x"), 16) {
|
||||
Ok(a) => a,
|
||||
Err(_) => {
|
||||
debug!("Could not parse {ap_board_id} as usize");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if ap_chip_id != chip_id {
|
||||
continue;
|
||||
}
|
||||
build_identity = Some(id.to_owned());
|
||||
break;
|
||||
}
|
||||
|
||||
let build_identity = match build_identity {
|
||||
Some(b) => b,
|
||||
None => {
|
||||
return Err(IdeviceError::BadBuildManifest);
|
||||
}
|
||||
};
|
||||
|
||||
let manifest = match build_identity.get("Manifest") {
|
||||
Some(plist::Value::Dictionary(m)) => m,
|
||||
_ => {
|
||||
return Err(IdeviceError::BadBuildManifest);
|
||||
}
|
||||
};
|
||||
|
||||
let mut parameters = plist::Dictionary::new();
|
||||
parameters.insert("ApProductionMode".into(), true.into());
|
||||
parameters.insert("ApSecurityDomain".into(), 1.into());
|
||||
parameters.insert("ApSecurityMode".into(), true.into());
|
||||
parameters.insert("ApSupportsImg4".into(), true.into());
|
||||
|
||||
for (key, manifest_item) in manifest {
|
||||
let manifest_item = match manifest_item {
|
||||
plist::Value::Dictionary(m) => m,
|
||||
_ => {
|
||||
debug!("Manifest item wasn't a dictionary");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let info = match manifest_item.get("Info") {
|
||||
Some(plist::Value::Dictionary(i)) => i,
|
||||
_ => {
|
||||
debug!("Manifest item didn't contain info");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
match info.get("Trusted") {
|
||||
Some(plist::Value::Boolean(t)) => {
|
||||
if !t {
|
||||
debug!("Info item isn't trusted");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
debug!("Info didn't contain trusted bool");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let mut tss_entry = manifest_item.clone();
|
||||
tss_entry.remove("Info");
|
||||
|
||||
if let Some(plist::Value::Dictionary(l)) = manifest.get("LoadableTrustCache") {
|
||||
if let Some(plist::Value::Dictionary(i)) = l.get("Info") {
|
||||
if let Some(plist::Value::Array(rules)) = i.get("RestoreRequestRules") {
|
||||
crate::tss::apply_restore_request_rules(&mut tss_entry, ¶meters, rules);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if manifest_item.get("Digest").is_none() {
|
||||
tss_entry.insert("Digest".into(), plist::Value::Data(vec![]));
|
||||
}
|
||||
|
||||
request.insert(key, tss_entry);
|
||||
}
|
||||
let res = request.send().await?;
|
||||
let mut res = match res {
|
||||
plist::Value::Dictionary(r) => r,
|
||||
_ => {
|
||||
warn!("Apple returned a non-dictionary plist");
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
};
|
||||
|
||||
match res.remove("ApImg4Ticket") {
|
||||
Some(plist::Value::Data(d)) => Ok(d),
|
||||
_ => {
|
||||
warn!("TSS response didn't contain ApImg4Ticket data");
|
||||
Err(IdeviceError::UnexpectedResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
140
idevice/src/tss.rs
Normal file
140
idevice/src/tss.rs
Normal file
@@ -0,0 +1,140 @@
|
||||
// Jackson Coxson
|
||||
// Thanks pymobiledevice3
|
||||
|
||||
use log::{debug, warn};
|
||||
use plist::Value;
|
||||
|
||||
use crate::{util::plist_to_bytes, IdeviceError};
|
||||
|
||||
const TSS_CLIENT_VERSION_STRING: &str = "libauthinstall-1033.0.2";
|
||||
const TSS_CONTROLLER_ACTION_URL: &str = "http://gs.apple.com/TSS/controller?action=2";
|
||||
|
||||
pub struct TSSRequest {
|
||||
inner: plist::Dictionary,
|
||||
}
|
||||
|
||||
impl TSSRequest {
|
||||
pub fn new() -> Self {
|
||||
let mut inner = plist::Dictionary::new();
|
||||
inner.insert("@HostPlatformInfo".into(), "mac".into());
|
||||
inner.insert("@VersionInfo".into(), TSS_CLIENT_VERSION_STRING.into());
|
||||
inner.insert(
|
||||
"@UUID".into(),
|
||||
uuid::Uuid::new_v4().to_string().to_uppercase().into(),
|
||||
);
|
||||
Self {
|
||||
inner: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, key: impl Into<String>, val: impl Into<Value>) {
|
||||
let key = key.into();
|
||||
let val = val.into();
|
||||
self.inner.insert(key, val);
|
||||
}
|
||||
|
||||
pub async fn send(&self) -> Result<plist::Value, IdeviceError> {
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let res = client
|
||||
.post(TSS_CONTROLLER_ACTION_URL)
|
||||
.header("Cache-Control", "no-cache")
|
||||
.header("Content-type", "text/xml; charset=\"utf-8\"")
|
||||
.header("User-Agent", "InetURL/1.0")
|
||||
.header("Expect", "")
|
||||
.body(plist_to_bytes(&self.inner))
|
||||
.send()
|
||||
.await?
|
||||
.text()
|
||||
.await?;
|
||||
|
||||
debug!("Apple responeded with {res}");
|
||||
let res = res.trim_start_matches("MESSAGE=");
|
||||
if !res.starts_with("SUCCESS") {
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
let res = res.split("REQUEST_STRING=").collect::<Vec<&str>>();
|
||||
if res.len() < 2 {
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
Ok(plist::from_bytes(res[1].as_bytes())?)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TSSRequest {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_restore_request_rules(
|
||||
input: &mut plist::Dictionary,
|
||||
parameters: &plist::Dictionary,
|
||||
rules: &Vec<plist::Value>,
|
||||
) {
|
||||
for rule in rules {
|
||||
if let plist::Value::Dictionary(rule) = rule {
|
||||
let mut conditions_fulfulled = true;
|
||||
let conditions = match rule.get("Conditions") {
|
||||
Some(plist::Value::Dictionary(c)) => c,
|
||||
_ => {
|
||||
warn!("Conditions doesn't exist or wasn't a dictionary!!");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
for (key, value) in conditions {
|
||||
let value2 = match key.as_str() {
|
||||
"ApRawProductionMode" => parameters.get("ApProductionMode"),
|
||||
"ApCurrentProductionMode" => parameters.get("ApProductionMode"),
|
||||
"ApRawSecurityMode" => parameters.get("ApSecurityMode"),
|
||||
"ApRequiresImage4" => parameters.get("ApSupportsImg4"),
|
||||
"ApDemotionPolicyOverride" => parameters.get("DemotionPolicy"),
|
||||
"ApInRomDFU" => parameters.get("ApInRomDFU"),
|
||||
_ => {
|
||||
warn!("Unhandled key {key}");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
conditions_fulfulled = match value2 {
|
||||
Some(value2) => value == value2,
|
||||
None => false,
|
||||
};
|
||||
|
||||
if !conditions_fulfulled {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !conditions_fulfulled {
|
||||
continue;
|
||||
}
|
||||
|
||||
let actions = match rule.get("Actions") {
|
||||
Some(plist::Value::Dictionary(a)) => a,
|
||||
_ => {
|
||||
warn!("Actions doesn't exist or wasn't a dictionary!!");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
for (key, value) in actions {
|
||||
if let Some(i) = value.as_unsigned_integer() {
|
||||
if i == 255 {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if let Some(i) = value.as_signed_integer() {
|
||||
if i == 255 {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
input.insert(key.to_owned(), value.to_owned());
|
||||
}
|
||||
} else {
|
||||
warn!("Rule wasn't a dictionary");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use crate::util::plist_to_bytes;
|
||||
use log::warn;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -11,14 +12,6 @@ pub struct RawPacket {
|
||||
pub plist: plist::Dictionary,
|
||||
}
|
||||
|
||||
fn plist_to_bytes(p: &plist::Dictionary) -> Vec<u8> {
|
||||
let buf = Vec::new();
|
||||
let mut writer = std::io::BufWriter::new(buf);
|
||||
plist::to_writer_xml(&mut writer, &p).unwrap();
|
||||
|
||||
writer.into_inner().unwrap()
|
||||
}
|
||||
|
||||
impl RawPacket {
|
||||
pub fn new(plist: plist::Dictionary, version: u32, message: u32, tag: u32) -> RawPacket {
|
||||
let plist_bytes = plist_to_bytes(&plist);
|
||||
|
||||
9
idevice/src/util.rs
Normal file
9
idevice/src/util.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
// Jackson Coxson
|
||||
|
||||
pub fn plist_to_bytes(p: &plist::Dictionary) -> Vec<u8> {
|
||||
let buf = Vec::new();
|
||||
let mut writer = std::io::BufWriter::new(buf);
|
||||
plist::to_writer_xml(&mut writer, &p).unwrap();
|
||||
|
||||
writer.into_inner().unwrap()
|
||||
}
|
||||
Reference in New Issue
Block a user