mediatek_optical/receiver/
analysis.rs1use super::{
7 decoding::{self, Call, Value},
8 programming::{self, IntegritySelection, PlainSignature, Selection},
9};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct SelectedRange {
14 pub selection: Selection,
16 pub signature: Option<PlainSignature>,
18 pub acceptance: Option<programming::AcceptanceTail>,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct BulkDecode {
27 pub header: Call,
29 pub from_zero: Call,
31 pub preserving_boot: Call,
34}
35
36#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct ApplicationAnalysis {
39 ranges: Vec<SelectedRange>,
40 integrity: Vec<IntegritySelection>,
41 scratch: Vec<Call>,
42 bulk: Vec<BulkDecode>,
43 conflicting_arguments: bool,
44}
45
46impl ApplicationAnalysis {
47 pub fn inspect(bytes: &[u8], base: u32) -> Option<Self> {
50 if base & 1 != 0 {
51 return None;
52 }
53 let end = base.checked_add(u32::try_from(bytes.len()).ok()?)?;
54 let mut ranges = Vec::new();
55 let mut integrity = Vec::new();
56 for address in (base..end).step_by(2) {
57 if let Some(selection) = programming::inspect_selection(bytes, base, address) {
58 let signature = programming::inspect_plain_signature(bytes, base, &selection);
59 ranges.push(SelectedRange {
60 selection,
61 signature,
62 acceptance: None,
63 });
64 }
65 if let Some(proof) = programming::inspect_legacy_integrity(bytes, base, address) {
66 integrity.push(proof);
67 }
68 }
69 let mut scratch: Vec<Call> = Vec::new();
70 let mut conflicting_arguments = false;
71 for range in &mut ranges {
72 let Some(signature) = range.signature else {
73 continue;
74 };
75 let mut candidates = integrity.iter().filter_map(|checks| {
76 if checks.address < signature.success
77 || checks.address.checked_sub(signature.success)? > 1024
78 {
79 return None;
80 }
81 programming::inspect_acceptance_tail(
82 bytes,
83 base,
84 &range.selection,
85 &signature,
86 checks,
87 )
88 });
89 let first = candidates.next();
90 if candidates.next().is_some() {
91 conflicting_arguments = true;
92 } else {
93 range.acceptance = first;
94 }
95 }
96 for range in &ranges {
97 let Some(signature) = range.signature else {
98 continue;
99 };
100 let first = range.selection.address.saturating_sub(512).max(base);
104 for start in (first..range.selection.address).step_by(2) {
105 let Some(call) =
106 decoding::trace_call(bytes, base, start, [Value::Unknown; 8], |a| {
107 (a == Value::Constant(signature.staging_cell)).then_some(Value::Staging(0))
108 })
109 else {
110 continue;
111 };
112 if call.address >= range.selection.address
113 || call.length != 16
114 || call.source != Value::Staging(0x3000)
115 || !matches!(call.destination, Value::Stack(_))
116 {
117 continue;
118 }
119 let Some(offset) = call
120 .target
121 .checked_sub(base)
122 .and_then(|x| usize::try_from(x).ok())
123 else {
124 continue;
125 };
126 let Some(routine) = bytes.get(offset..) else {
127 continue;
128 };
129 if !super::matches(routine, super::AES) {
130 continue;
131 }
132 if let Some(previous) = scratch.iter().find(|c| c.address == call.address) {
133 conflicting_arguments |= previous.target != call.target
136 || previous.source != call.source
137 || previous.destination != call.destination
138 || previous.length != call.length;
139 } else {
140 scratch.push(call);
141 }
142 }
143 }
144 scratch.sort_by_key(|c| c.address);
145 let mut bulk = Vec::new();
146 for range in &ranges {
147 let trace = |start| {
148 decoding::trace_call(bytes, base, range.selection.end, [Value::Unknown; 8], |a| {
149 match a {
150 Value::Constant(cell) if cell == range.selection.start_cell => {
151 Some(Value::Constant(start))
152 }
153 Value::Constant(cell)
154 if cell & 3 == 0 && (0x02000000..0x02010000).contains(&cell) =>
155 {
156 Some(Value::Loaded { cell, offset: 0 })
157 }
158 _ => None,
159 }
160 })
161 };
162 let (Some(from_zero), Some(preserving_boot)) = (trace(0), trace(0x10000)) else {
163 continue;
164 };
165 let Value::Loaded { cell, offset: 0 } = from_zero.source else {
166 continue;
167 };
168 if from_zero.destination != from_zero.source
169 || from_zero.length != 0x200000
170 || preserving_boot.address != from_zero.address
171 || preserving_boot.target != from_zero.target
172 || preserving_boot.length != from_zero.length
173 || preserving_boot.source
174 != (Value::Loaded {
175 cell,
176 offset: 0x10000,
177 })
178 || preserving_boot.destination != preserving_boot.source
179 {
180 continue;
181 }
182 let Some(offset) = from_zero
183 .target
184 .checked_sub(base)
185 .and_then(|x| usize::try_from(x).ok())
186 else {
187 continue;
188 };
189 if !bytes
190 .get(offset..)
191 .is_some_and(|routine| super::matches(routine, super::AES))
192 {
193 continue;
194 }
195 let first = range.selection.address.saturating_sub(512).max(base);
196 let mut header: Option<Call> = None;
197 let mut ambiguous = false;
198 for start in (first..range.selection.address).step_by(2) {
199 let Some(call) =
200 decoding::trace_call(bytes, base, start, [Value::Unknown; 8], |a| {
201 (a == Value::Constant(cell)).then_some(Value::Loaded { cell, offset: 0 })
202 })
203 else {
204 continue;
205 };
206 if call.address >= range.selection.address
207 || call.target != from_zero.target
208 || call.source
209 != (Value::Loaded {
210 cell,
211 offset: 0x3000,
212 })
213 || call.length != 16
214 || !matches!(call.destination, Value::Stack(_))
215 {
216 continue;
217 }
218 if let Some(previous) = header {
219 ambiguous |= previous.address != call.address
220 || previous.destination != call.destination;
221 } else {
222 header = Some(call);
223 }
224 }
225 if ambiguous {
226 conflicting_arguments = true;
227 } else if let Some(header) = header {
228 bulk.push(BulkDecode {
229 header,
230 from_zero,
231 preserving_boot,
232 });
233 }
234 }
235 Some(Self {
236 ranges,
237 integrity,
238 scratch,
239 bulk,
240 conflicting_arguments,
241 })
242 }
243
244 pub fn ranges(&self) -> &[SelectedRange] {
246 &self.ranges
247 }
248 pub fn integrity_selections(&self) -> &[IntegritySelection] {
250 &self.integrity
251 }
252 pub fn scratch_decoders(&self) -> &[Call] {
254 &self.scratch
255 }
256 pub fn conflicting_arguments(&self) -> bool {
258 self.conflicting_arguments
259 }
260 pub fn bulk_decoders(&self) -> &[BulkDecode] {
263 &self.bulk
264 }
265}