1fn u16le(b: &[u8], off: usize) -> Option<u16> {
14 let s = b.get(off..off + 2)?;
15 Some(u16::from_le_bytes([s[0], s[1]]))
16}
17
18fn u32le(b: &[u8], off: usize) -> Option<u32> {
19 let s = b.get(off..off + 4)?;
20 Some(u32::from_le_bytes([s[0], s[1], s[2], s[3]]))
21}
22
23fn uleb128(b: &[u8], off: &mut usize) -> Option<u32> {
24 let mut result = 0u32;
25 let mut shift = 0u32;
26 loop {
27 let byte = *b.get(*off)?;
28 *off += 1;
29 result |= ((byte & 0x7f) as u32) << shift;
30 if byte & 0x80 == 0 {
31 return Some(result);
32 }
33 shift += 7;
34 if shift >= 35 {
35 return None;
36 }
37 }
38}
39
40const ZIP_EOCD_SIG: [u8; 4] = [0x50, 0x4b, 0x05, 0x06];
43const ZIP_CD_SIG: [u8; 4] = [0x50, 0x4b, 0x01, 0x02];
44const ZIP_LOCAL_SIG: [u8; 4] = [0x50, 0x4b, 0x03, 0x04];
45const ZIP_STORED: u16 = 0;
46
47struct ZipEntry {
48 local_off: usize,
49 comp_size: usize,
50}
51
52fn zip_find_dex_entries(data: &[u8]) -> Vec<ZipEntry> {
53 let scan_start = data.len().saturating_sub(65558);
55 let eocd_pos = match data[scan_start..]
56 .windows(4)
57 .rposition(|w| w == ZIP_EOCD_SIG)
58 {
59 Some(p) => scan_start + p,
60 None => return Vec::new(),
61 };
62
63 let cd_size = match u32le(data, eocd_pos + 12) {
64 Some(v) => v as usize,
65 None => return Vec::new(),
66 };
67 let cd_off = match u32le(data, eocd_pos + 16) {
68 Some(v) => v as usize,
69 None => return Vec::new(),
70 };
71
72 let mut pos = cd_off;
73 let cd_end = cd_off.saturating_add(cd_size);
74 let mut entries = Vec::new();
75
76 while pos + 46 <= cd_end && pos + 46 <= data.len() {
77 if data.get(pos..pos + 4) != Some(&ZIP_CD_SIG) {
78 break;
79 }
80
81 let compression = match u16le(data, pos + 10) {
82 Some(v) => v,
83 None => break,
84 };
85 let comp_size = match u32le(data, pos + 20) {
86 Some(v) => v as usize,
87 None => break,
88 };
89 let local_off = match u32le(data, pos + 42) {
90 Some(v) => v as usize,
91 None => break,
92 };
93 let fname_len = match u16le(data, pos + 28) {
94 Some(v) => v as usize,
95 None => break,
96 };
97 let extra_len = match u16le(data, pos + 30) {
98 Some(v) => v as usize,
99 None => break,
100 };
101 let comment_len = match u16le(data, pos + 32) {
102 Some(v) => v as usize,
103 None => break,
104 };
105
106 let fname_end = pos + 46 + fname_len;
107 if fname_end > data.len() {
108 break;
109 }
110 let fname = &data[pos + 46..fname_end];
111
112 let is_dex = fname.starts_with(b"classes") && fname.ends_with(b".dex");
114 if is_dex && compression == ZIP_STORED {
115 entries.push(ZipEntry {
116 local_off,
117 comp_size,
118 });
119 }
120
121 pos = match pos.checked_add(46 + fname_len + extra_len + comment_len) {
122 Some(v) => v,
123 None => break,
124 };
125 }
126
127 entries.sort_by_key(|e| std::cmp::Reverse(e.comp_size));
130 entries
131}
132
133fn zip_entry_data<'a>(data: &'a [u8], entry: &ZipEntry) -> Option<&'a [u8]> {
134 let lh = entry.local_off;
135 if data.get(lh..lh + 4) != Some(&ZIP_LOCAL_SIG) {
136 return None;
137 }
138 let fname_len = u16le(data, lh + 26)? as usize;
139 let extra_len = u16le(data, lh + 28)? as usize;
140 let data_start = lh + 30 + fname_len + extra_len;
141 data.get(data_start..data_start + entry.comp_size)
142}
143
144const DEX_MAGIC: &[u8] = b"dex\n";
147
148const MAX_DEX_STRING_LEN: usize = 8192;
155
156fn dex_string(dex: &[u8], string_ids_off: usize, idx: usize) -> Option<&[u8]> {
157 if string_ids_off
161 .checked_add(idx.saturating_mul(4))
162 .is_none_or(|end| end + 4 > dex.len())
163 {
164 return None;
165 }
166 let str_data_off = u32le(dex, string_ids_off + idx * 4)? as usize;
167 let mut off = str_data_off;
170 let uleb_budget = off.checked_add(5)?;
171 loop {
172 let b = *dex.get(off)?;
173 off += 1;
174 if b & 0x80 == 0 {
175 break;
176 }
177 if off >= uleb_budget {
178 return None;
179 }
180 }
181 let start = off;
183 loop {
184 let b = *dex.get(off)?;
185 if b == 0 {
186 break;
187 }
188 off += 1;
189 if off.saturating_sub(start) >= MAX_DEX_STRING_LEN {
190 return None;
191 }
192 }
193 dex.get(start..off)
194}
195
196const MAX_ENCODED_VALUE_DEPTH: usize = 64;
200
201fn skip_encoded_value(dex: &[u8], off: &mut usize) -> Option<()> {
202 skip_encoded_value_depth(dex, off, 0)
203}
204
205fn skip_encoded_value_depth(dex: &[u8], off: &mut usize, depth: usize) -> Option<()> {
206 if depth > MAX_ENCODED_VALUE_DEPTH {
207 return None;
208 }
209 let vbyte = *dex.get(*off)?;
210 *off += 1;
211 let vtype = vbyte & 0x1f;
212 let varg = (vbyte >> 5) as usize;
213 match vtype {
214 0x00 | 0x02 | 0x03 | 0x04 | 0x06 | 0x10 | 0x11 | 0x15 | 0x16 | 0x17 | 0x18 | 0x19
216 | 0x1a | 0x1b => {
217 *off = off.checked_add(varg + 1)?;
218 }
219 0x1c => {
220 let size = uleb128(dex, off)?;
222 for _ in 0..size {
223 skip_encoded_value_depth(dex, off, depth + 1)?;
224 }
225 }
226 0x1d => {
227 uleb128(dex, off)?; let size = uleb128(dex, off)?;
230 for _ in 0..size {
231 uleb128(dex, off)?; skip_encoded_value_depth(dex, off, depth + 1)?;
233 }
234 }
235 0x1e | 0x1f => {} _ => return None,
237 }
238 Some(())
239}
240
241fn read_int_encoded_value(dex: &[u8], off: &mut usize) -> Option<u32> {
242 let vbyte = *dex.get(*off)?;
243 *off += 1;
244 let vtype = vbyte & 0x1f;
245 let varg = (vbyte >> 5) as usize;
246 if vtype != 0x04 {
248 return None;
249 }
250 let size = varg + 1;
251 if size > 4 {
252 return None;
253 }
254 let mut val = 0u32;
255 for i in 0..size {
256 val |= (*dex.get(*off + i)? as u32) << (i * 8);
257 }
258 *off += size;
259 Some(val)
260}
261
262fn cap_section_size(dex: &[u8], off: usize, elem_size: usize, declared: usize) -> usize {
267 let room = dex.len().saturating_sub(off);
268 declared.min(room / elem_size)
269}
270
271fn find_in_dex(dex: &[u8], class_desc: &[u8], field_name: &[u8]) -> Option<u32> {
272 if !dex.starts_with(DEX_MAGIC) || dex.len() < 112 {
273 return None;
274 }
275
276 let string_ids_size =
277 cap_section_size(dex, u32le(dex, 60)? as usize, 4, u32le(dex, 56)? as usize);
278 let string_ids_off = u32le(dex, 60)? as usize;
279 let type_ids_size =
280 cap_section_size(dex, u32le(dex, 68)? as usize, 4, u32le(dex, 64)? as usize);
281 let type_ids_off = u32le(dex, 68)? as usize;
282 let field_ids_size =
283 cap_section_size(dex, u32le(dex, 84)? as usize, 8, u32le(dex, 80)? as usize);
284 let field_ids_off = u32le(dex, 84)? as usize;
285 let class_defs_size =
286 cap_section_size(dex, u32le(dex, 100)? as usize, 32, u32le(dex, 96)? as usize);
287 let class_defs_off = u32le(dex, 100)? as usize;
288
289 let mut class_str_idx: Option<usize> = None;
291 for i in 0..string_ids_size {
292 if dex_string(dex, string_ids_off, i) == Some(class_desc) {
293 class_str_idx = Some(i);
294 break;
295 }
296 }
297 let class_str_idx = class_str_idx?;
298
299 let mut class_type_idx: Option<usize> = None;
301 for i in 0..type_ids_size {
302 if u32le(dex, type_ids_off + i * 4)? as usize == class_str_idx {
303 class_type_idx = Some(i);
304 break;
305 }
306 }
307 let class_type_idx = class_type_idx?;
308
309 let mut field_str_idx: Option<u32> = None;
311 for i in 0..string_ids_size {
312 if dex_string(dex, string_ids_off, i) == Some(field_name) {
313 field_str_idx = Some(i as u32);
314 break;
315 }
316 }
317 let field_str_idx = field_str_idx?;
318
319 let mut target_field_idx: Option<u32> = None;
321 for i in 0..field_ids_size {
322 let foff = field_ids_off + i * 8;
323 let fclass = u16le(dex, foff)? as usize;
324 let fname = u32le(dex, foff + 4)?;
325 if fclass == class_type_idx && fname == field_str_idx {
326 target_field_idx = Some(i as u32);
327 break;
328 }
329 }
330 let target_field_idx = target_field_idx?;
331
332 let mut class_data_off = None;
334 let mut static_vals_off = None;
335 for i in 0..class_defs_size {
336 let coff = class_defs_off + i * 32;
337 if u32le(dex, coff)? as usize == class_type_idx {
338 class_data_off = Some(u32le(dex, coff + 24)? as usize);
339 static_vals_off = Some(u32le(dex, coff + 28)? as usize);
340 break;
341 }
342 }
343 let class_data_off = class_data_off?;
344 let static_vals_off = static_vals_off?;
345 if class_data_off == 0 || static_vals_off == 0 {
346 return None;
347 }
348
349 let mut off = class_data_off;
351 let static_fields_size = uleb128(dex, &mut off)?;
352 let _instance_fields = uleb128(dex, &mut off)?;
353 let _direct_methods = uleb128(dex, &mut off)?;
354 let _virtual_methods = uleb128(dex, &mut off)?;
355
356 let mut field_pos: Option<usize> = None;
357 let mut cur_field_idx = 0u32;
358 for i in 0..static_fields_size as usize {
359 let diff = uleb128(dex, &mut off)?;
360 let _access_flags = uleb128(dex, &mut off)?;
361 cur_field_idx += diff;
362 if cur_field_idx == target_field_idx {
363 field_pos = Some(i);
364 break;
365 }
366 }
367 let field_pos = field_pos?;
368
369 let mut sv = static_vals_off;
371 let sv_size = uleb128(dex, &mut sv)? as usize;
372 if field_pos >= sv_size {
373 return None;
374 }
375
376 for i in 0..=field_pos {
377 if i == field_pos {
378 return read_int_encoded_value(dex, &mut sv);
379 }
380 skip_encoded_value(dex, &mut sv)?;
381 }
382 None
383}
384
385fn method_param_types<'a>(
395 dex: &'a [u8],
396 class_desc: &[u8],
397 method_name: &[u8],
398) -> Option<Vec<&'a [u8]>> {
399 if !dex.starts_with(DEX_MAGIC) || dex.len() < 112 {
400 return None;
401 }
402
403 let string_ids_size =
404 cap_section_size(dex, u32le(dex, 60)? as usize, 4, u32le(dex, 56)? as usize);
405 let string_ids_off = u32le(dex, 60)? as usize;
406 let type_ids_size =
407 cap_section_size(dex, u32le(dex, 68)? as usize, 4, u32le(dex, 64)? as usize);
408 let type_ids_off = u32le(dex, 68)? as usize;
409 let proto_ids_size =
410 cap_section_size(dex, u32le(dex, 76)? as usize, 12, u32le(dex, 72)? as usize);
411 let proto_ids_off = u32le(dex, 76)? as usize;
412 let method_ids_size =
413 cap_section_size(dex, u32le(dex, 92)? as usize, 8, u32le(dex, 88)? as usize);
414 let method_ids_off = u32le(dex, 92)? as usize;
415
416 let mut class_str_idx: Option<usize> = None;
418 for i in 0..string_ids_size {
419 if dex_string(dex, string_ids_off, i) == Some(class_desc) {
420 class_str_idx = Some(i);
421 break;
422 }
423 }
424 let class_str_idx = class_str_idx?;
425
426 let mut class_type_idx: Option<usize> = None;
428 for i in 0..type_ids_size {
429 if u32le(dex, type_ids_off + i * 4)? as usize == class_str_idx {
430 class_type_idx = Some(i);
431 break;
432 }
433 }
434 let class_type_idx = class_type_idx?;
435
436 let mut method_str_idx: Option<u32> = None;
438 for i in 0..string_ids_size {
439 if dex_string(dex, string_ids_off, i) == Some(method_name) {
440 method_str_idx = Some(i as u32);
441 break;
442 }
443 }
444 let method_str_idx = method_str_idx?;
445
446 let mut proto_idx: Option<usize> = None;
448 for i in 0..method_ids_size {
449 let moff = method_ids_off + i * 8;
450 let fclass = u16le(dex, moff)? as usize;
451 let fproto = u16le(dex, moff + 2)? as usize;
452 let fname = u32le(dex, moff + 4)?;
453 if fclass == class_type_idx && fname == method_str_idx {
454 proto_idx = Some(fproto);
455 break;
456 }
457 }
458 let proto_idx = proto_idx?;
459 if proto_idx >= proto_ids_size {
460 return None;
461 }
462
463 let poff = proto_ids_off + proto_idx * 12;
465 let params_off = u32le(dex, poff + 8)? as usize;
466 if params_off == 0 {
467 return Some(Vec::new());
468 }
469
470 let param_count = u32le(dex, params_off)? as usize;
474 let room = dex.len().saturating_sub(params_off + 4);
475 let param_count = param_count.min(room / 2);
476 let mut params = Vec::with_capacity(param_count);
477 for i in 0..param_count {
478 let tidx = u16le(dex, params_off + 4 + i * 2)? as usize;
479 if tidx >= type_ids_size {
484 return None;
485 }
486 let desc_str_idx = u32le(dex, type_ids_off + tidx * 4)? as usize;
487 if desc_str_idx >= string_ids_size {
488 return None;
489 }
490 let desc = dex_string(dex, string_ids_off, desc_str_idx)?;
491 params.push(desc);
492 }
493 Some(params)
494}
495
496pub fn find_transaction_code(jar_path: &str, class_desc: &str, field_name: &str) -> Option<u32> {
506 let data = std::fs::read(jar_path).ok()?;
507 let entries = zip_find_dex_entries(&data);
508 for entry in &entries {
509 if let Some(dex) = zip_entry_data(&data, entry)
510 && let Some(code) = find_in_dex(dex, class_desc.as_bytes(), field_name.as_bytes())
511 {
512 return Some(code);
513 }
514 }
515 None
516}
517
518pub fn resolve_tx_codes_from_dex() -> Option<(u32, u32, u8, u32)> {
526 const JAR: &str = "/system/framework/framework.jar";
527 const AM_STUB: &str = "Landroid/app/IActivityManager$Stub;";
528 const OBS_STUB: &str = "Landroid/app/IProcessObserver$Stub;";
529
530 let observer_code = find_transaction_code(JAR, AM_STUB, "TRANSACTION_registerProcessObserver")?;
531 let fg_code =
532 find_transaction_code(JAR, OBS_STUB, "TRANSACTION_onForegroundActivitiesChanged")?;
533
534 if let Some(query_code) =
535 find_transaction_code(JAR, AM_STUB, "TRANSACTION_getFocusedRootTaskInfo")
536 {
537 return Some((observer_code, query_code, 1, fg_code));
538 }
539 let query_code = find_transaction_code(JAR, AM_STUB, "TRANSACTION_getFocusedStackInfo")?;
541 Some((observer_code, query_code, 2, fg_code))
542}
543
544pub fn resolve_display_register_callback_tx() -> Option<u32> {
551 const JAR: &str = "/system/framework/framework.jar";
552 find_transaction_code(
553 JAR,
554 "Landroid/hardware/display/IDisplayManager$Stub;",
555 "TRANSACTION_registerCallback",
556 )
557}
558
559pub fn resolve_display_register_callback_mask_tx() -> Option<u32> {
568 const JAR: &str = "/system/framework/framework.jar";
569 find_transaction_code(
570 JAR,
571 "Landroid/hardware/display/IDisplayManager$Stub;",
572 "TRANSACTION_registerCallbackWithEventMask",
573 )
574}
575
576pub fn resolve_display_info_tx() -> Option<u32> {
584 const JAR: &str = "/system/framework/framework.jar";
585 find_transaction_code(
586 JAR,
587 "Landroid/hardware/display/IDisplayManager$Stub;",
588 "TRANSACTION_getDisplayInfo",
589 )
590}
591
592pub fn resolve_is_interactive_tx() -> Option<u32> {
593 const JAR: &str = "/system/framework/framework.jar";
594 find_transaction_code(
595 JAR,
596 "Landroid/os/IPowerManager$Stub;",
597 "TRANSACTION_isInteractive",
598 )
599}
600
601pub fn resolve_set_device_idle_mode_tx() -> Option<u32> {
613 const JAR: &str = "/system/framework/framework.jar";
614 find_transaction_code(
615 JAR,
616 "Landroid/net/INetworkPolicyManager$Stub;",
617 "TRANSACTION_setDeviceIdleMode",
618 )
619}
620
621pub fn resolve_get_running_app_processes_tx() -> Option<u32> {
633 const JAR: &str = "/system/framework/framework.jar";
634 find_transaction_code(
635 JAR,
636 "Landroid/app/IActivityManager$Stub;",
637 "TRANSACTION_getRunningAppProcesses",
638 )
639}
640
641pub fn resolve_active_playback_configurations_tx() -> Option<u32> {
651 const JAR: &str = "/system/framework/framework.jar";
652 find_transaction_code(
653 JAR,
654 "Landroid/media/IAudioService$Stub;",
655 "TRANSACTION_getActivePlaybackConfigurations",
656 )
657}
658
659pub fn resolve_fgproc_codes() -> Option<(u32, u32)> {
665 const JAR: &str = "/system/framework/framework.jar";
666 const AM_STUB: &str = "Landroid/app/IActivityManager$Stub;";
667 const FGPROC_STUB: &str = "Landroid/app/IForegroundProcessObserver$Stub;";
668
669 let register_code = find_transaction_code(
670 JAR,
671 AM_STUB,
672 "TRANSACTION_registerForegroundProcessObserver",
673 )?;
674 let on_change_code =
675 find_transaction_code(JAR, FGPROC_STUB, "TRANSACTION_onForegroundProcessChanged")?;
676 Some((register_code, on_change_code))
677}
678
679pub fn resolve_fgproc_codes_fallback() -> Option<(u32, u32)> {
694 const JAR: &str = "/system/framework/framework.jar";
695 const AM_STUB: &str = "Landroid/app/IActivityManager$Stub;";
696 const OBS_STUB: &str = "Landroid/app/IProcessObserver$Stub;";
697 const OBS_IFACE: &str = "Landroid/app/IProcessObserver;";
698
699 if resolve_fgproc_codes().is_some() {
701 return None;
702 }
703
704 let data = std::fs::read(JAR).ok()?;
705 let entries = zip_find_dex_entries(&data);
706
707 let mut pid_first = false;
710 for entry in &entries {
711 if let Some(dex) = zip_entry_data(&data, entry)
712 && let Some(params) =
713 method_param_types(dex, OBS_IFACE.as_bytes(), b"onForegroundActivitiesChanged")
714 {
715 pid_first = params.first().map(|t| *t == b"I").unwrap_or(false);
716 break;
717 }
718 }
719 if !pid_first {
720 return None;
721 }
722
723 let register_code = find_transaction_code(JAR, AM_STUB, "TRANSACTION_registerProcessObserver")?;
724 let on_change_code =
725 find_transaction_code(JAR, OBS_STUB, "TRANSACTION_onForegroundActivitiesChanged")?;
726 Some((register_code, on_change_code))
727}
728
729pub fn resolve_fps_codes() -> Option<(u32, u32, u32)> {
736 const JAR: &str = "/system/framework/framework.jar";
737 const IWM_STUB: &str = "Landroid/view/IWindowManager$Stub;";
738 const FPS_STUB: &str = "Landroid/window/ITaskFpsCallback$Stub;";
739
740 let register_code =
741 find_transaction_code(JAR, IWM_STUB, "TRANSACTION_registerTaskFpsCallback")?;
742 let unregister_code =
743 find_transaction_code(JAR, IWM_STUB, "TRANSACTION_unregisterTaskFpsCallback")?;
744 let on_fps_code = find_transaction_code(JAR, FPS_STUB, "TRANSACTION_onFpsReported")?;
745 Some((register_code, unregister_code, on_fps_code))
746}
747
748pub fn resolve_handoff_codes() -> Option<(u32, u32)> {
760 const JAR: &str = "/system/framework/framework.jar";
761 const AM_STUB: &str = "Landroid/app/IActivityManager$Stub;";
762
763 let get_code = find_transaction_code(JAR, AM_STUB, "TRANSACTION_getContentProviderExternal")?;
764 let remove_code = find_transaction_code(
765 JAR,
766 AM_STUB,
767 "TRANSACTION_removeContentProviderExternalAsUser",
768 )?;
769 Some((get_code, remove_code))
770}
771
772pub fn resolve_task_stack_codes() -> Option<(u32, u32)> {
783 const JAR: &str = "/system/framework/framework.jar";
784 const ATM_STUB: &str = "Landroid/app/IActivityTaskManager$Stub;";
785
786 let register_code =
787 find_transaction_code(JAR, ATM_STUB, "TRANSACTION_registerTaskStackListener")?;
788 let unregister_code =
789 find_transaction_code(JAR, ATM_STUB, "TRANSACTION_unregisterTaskStackListener")?;
790 Some((register_code, unregister_code))
791}
792
793pub struct UidObserverCodes {
810 pub register_code: u32,
812 pub unregister_code: u32,
814 pub register_for_uids_code: Option<u32>,
817 pub on_gone_code: u32,
819 pub on_active_code: u32,
821 pub on_idle_code: u32,
823 pub on_cached_code: u32,
825 pub on_state_code: u32,
827 pub on_adj_code: u32,
829}
830
831pub fn resolve_uid_observer_codes() -> Option<UidObserverCodes> {
833 const JAR: &str = "/system/framework/framework.jar";
834 const AM_STUB: &str = "Landroid/app/IActivityManager$Stub;";
835 const UID_OBS_STUB: &str = "Landroid/app/IUidObserver$Stub;";
836
837 let register_code = find_transaction_code(JAR, AM_STUB, "TRANSACTION_registerUidObserver")?;
838 let unregister_code = find_transaction_code(JAR, AM_STUB, "TRANSACTION_unregisterUidObserver")?;
839 let register_for_uids_code =
840 find_transaction_code(JAR, AM_STUB, "TRANSACTION_registerUidObserverForUids");
841 let on_gone_code = find_transaction_code(JAR, UID_OBS_STUB, "TRANSACTION_onUidGone")?;
842 let on_active_code = find_transaction_code(JAR, UID_OBS_STUB, "TRANSACTION_onUidActive")?;
843 let on_idle_code = find_transaction_code(JAR, UID_OBS_STUB, "TRANSACTION_onUidIdle")?;
844 let on_cached_code =
845 find_transaction_code(JAR, UID_OBS_STUB, "TRANSACTION_onUidCachedChanged")?;
846 let on_state_code = find_transaction_code(JAR, UID_OBS_STUB, "TRANSACTION_onUidStateChanged")?;
847 let on_adj_code = find_transaction_code(JAR, UID_OBS_STUB, "TRANSACTION_onUidProcAdjChanged")?;
848
849 Some(UidObserverCodes {
850 register_code,
851 unregister_code,
852 register_for_uids_code,
853 on_gone_code,
854 on_active_code,
855 on_idle_code,
856 on_cached_code,
857 on_state_code,
858 on_adj_code,
859 })
860}
861
862#[cfg(test)]
863mod tests {
864 use super::*;
865
866 fn u32put(b: &mut [u8], off: usize, v: u32) {
867 b[off..off + 4].copy_from_slice(&v.to_le_bytes());
868 }
869
870 fn build_dex(class_desc: &[u8], method_name: &[u8], params: &[&[u8]]) -> Vec<u8> {
874 let mut out = vec![0u8; 112];
875 out[0..4].copy_from_slice(DEX_MAGIC);
876 out[4..8].copy_from_slice(b"035\0");
877
878 let mut strings: Vec<Vec<u8>> = vec![class_desc.to_vec(), method_name.to_vec()];
879 for p in params {
880 if !strings.iter().any(|s| s == p) {
881 strings.push(p.to_vec());
882 }
883 }
884 let n_string = strings.len();
885 let mut types: Vec<Vec<u8>> = vec![class_desc.to_vec()];
886 for p in params {
887 if !types.iter().any(|t| t == p) {
888 types.push(p.to_vec());
889 }
890 }
891 let n_type = types.len();
892
893 let off_string_ids = 112;
894 let off_type_ids = off_string_ids + n_string * 4;
895 let off_proto_ids = off_type_ids + n_type * 4;
896 let off_method_ids = off_proto_ids + 12; out.resize(off_method_ids + 8, 0); let mut string_offs = Vec::with_capacity(n_string);
903 for s in &strings {
904 string_offs.push(out.len());
905 let n = s.len() as u8;
906 out.push(n); out.extend_from_slice(s);
908 out.push(0);
909 }
910
911 let params_off = out.len();
913 out.extend_from_slice(&(params.len() as u32).to_le_bytes());
914 for p in params {
915 let ti = types.iter().position(|t| t == p).unwrap();
916 out.extend_from_slice(&(ti as u16).to_le_bytes());
917 }
918
919 u32put(&mut out, 56, n_string as u32);
921 u32put(&mut out, 60, off_string_ids as u32);
922 u32put(&mut out, 64, n_type as u32);
923 u32put(&mut out, 68, off_type_ids as u32);
924 u32put(&mut out, 72, 1); u32put(&mut out, 76, off_proto_ids as u32);
926 u32put(&mut out, 80, 0); u32put(&mut out, 84, 0);
928 u32put(&mut out, 88, 1); u32put(&mut out, 92, off_method_ids as u32);
930 u32put(&mut out, 96, 0); u32put(&mut out, 100, 0);
932
933 for (i, &so) in string_offs.iter().enumerate() {
935 u32put(&mut out, off_string_ids + i * 4, so as u32);
936 }
937 for (i, t) in types.iter().enumerate() {
939 let si = strings.iter().position(|s| s == t).unwrap();
940 u32put(&mut out, off_type_ids + i * 4, si as u32);
941 }
942 u32put(&mut out, off_proto_ids, 0);
944 u32put(&mut out, off_proto_ids + 4, 0);
945 u32put(&mut out, off_proto_ids + 8, params_off as u32);
946 let m = off_method_ids;
948 out[m..m + 2].copy_from_slice(&0u16.to_le_bytes());
949 out[m + 2..m + 4].copy_from_slice(&0u16.to_le_bytes());
950 u32put(&mut out, m + 4, 1);
951
952 out
953 }
954
955 #[test]
956 fn parses_int_first_proto() {
957 let dex = build_dex(
958 b"Landroid/app/IProcessObserver;",
959 b"onForegroundActivitiesChanged",
960 &[b"I", b"I", b"Z"],
961 );
962 let params = method_param_types(
963 &dex,
964 b"Landroid/app/IProcessObserver;",
965 b"onForegroundActivitiesChanged",
966 )
967 .expect("method present");
968 assert_eq!(params, [&b"I"[..], &b"I"[..], &b"Z"[..]]);
969 }
970
971 #[test]
972 fn rejects_string_first_proto() {
973 let dex = build_dex(
975 b"Landroid/app/IProcessObserver;",
976 b"onForegroundActivitiesChanged",
977 &[b"Ljava/lang/String;", b"I", b"Z"],
978 );
979 let params = method_param_types(
980 &dex,
981 b"Landroid/app/IProcessObserver;",
982 b"onForegroundActivitiesChanged",
983 )
984 .expect("method present");
985 assert_eq!(params.first().map(|t| *t == b"I"), Some(false));
986 }
987
988 #[test]
989 fn absent_method_returns_none() {
990 let dex = build_dex(
991 b"Landroid/app/IProcessObserver;",
992 b"onForegroundActivitiesChanged",
993 &[b"I"],
994 );
995 assert!(
996 method_param_types(&dex, b"Landroid/app/IProcessObserver;", b"onProcessDied").is_none()
997 );
998 }
999
1000 #[test]
1001 fn capped_param_count_does_not_allocate_huge() {
1002 let mut dex = vec![0u8; 136];
1006 dex[0..4].copy_from_slice(DEX_MAGIC);
1007 dex[56..60].copy_from_slice(&1u32.to_le_bytes());
1009 dex[60..64].copy_from_slice(&112u32.to_le_bytes());
1010 dex[64..68].copy_from_slice(&1u32.to_le_bytes());
1011 dex[68..72].copy_from_slice(&116u32.to_le_bytes());
1012 dex[72..76].copy_from_slice(&1u32.to_le_bytes());
1014 dex[76..80].copy_from_slice(&120u32.to_le_bytes());
1015 dex[88..92].copy_from_slice(&1u32.to_le_bytes());
1017 dex[92..96].copy_from_slice(&124u32.to_le_bytes());
1018 dex[128..132].copy_from_slice(&u32::MAX.to_le_bytes());
1020 let params = method_param_types(
1023 &dex,
1024 b"Landroid/app/IProcessObserver;",
1025 b"onForegroundActivitiesChanged",
1026 );
1027 assert!(params.is_none());
1028 }
1029
1030 #[test]
1031 fn deep_encoded_value_recursion_is_bounded() {
1032 let mut bytes = vec![0u8; 2 + 2 * (MAX_ENCODED_VALUE_DEPTH + 2)];
1035 bytes[0] = 0x1c; bytes[1] = 1; let mut off = 2;
1038 for _ in 0..=MAX_ENCODED_VALUE_DEPTH + 1 {
1039 bytes[off] = 0x1c; bytes[off + 1] = 1;
1041 off += 2;
1042 }
1043 let mut pos = 0;
1044 let r = skip_encoded_value(&bytes, &mut pos);
1045 assert!(r.is_none());
1046 }
1047}