1use std::collections::HashMap;
4
5use crate::annotations::{self, EncodedValue};
6use crate::code::CodeItem;
7use crate::reader::Cursor;
8
9pub const NO_INDEX: u32 = 0xffff_ffff;
11
12#[derive(Debug, Clone)]
13pub struct ProtoId {
14 pub shorty_idx: u32,
16 pub return_type_idx: u32,
18 pub parameters_off: u32,
20}
21
22#[derive(Debug, Clone)]
23pub struct FieldId {
24 pub class_idx: u32,
25 pub type_idx: u32,
26 pub name_idx: u32,
27}
28
29#[derive(Debug, Clone)]
30pub struct MethodId {
31 pub class_idx: u32,
32 pub proto_idx: u32,
33 pub name_idx: u32,
34}
35
36#[derive(Debug, Clone)]
38pub struct ClassDef {
39 pub class_idx: u32,
41 pub access_flags: u32,
42 pub superclass_idx: u32,
43 pub interfaces_off: u32,
44 pub source_file_idx: u32,
45 pub annotations_off: u32,
46 pub class_data_off: u32,
47 pub static_values_off: u32,
48}
49
50#[derive(Debug, Clone)]
52pub struct EncodedField {
53 pub field_idx: u32,
54 pub access_flags: u32,
55}
56
57#[derive(Debug, Clone)]
59pub struct EncodedMethod {
60 pub method_idx: u32,
61 pub access_flags: u32,
62 pub code_off: u32,
63}
64
65#[derive(Debug, Clone, Default)]
67pub struct ClassData {
68 pub static_fields: Vec<EncodedField>,
69 pub instance_fields: Vec<EncodedField>,
70 pub direct_methods: Vec<EncodedMethod>,
71 pub virtual_methods: Vec<EncodedMethod>,
72}
73
74pub struct DexFile {
80 data: std::sync::Mutex<Option<Vec<u8>>>,
86 released: std::sync::atomic::AtomicBool,
89 strings: Vec<String>,
90 types: Vec<u32>,
92 protos: Vec<ProtoId>,
93 fields: Vec<FieldId>,
94 methods: Vec<MethodId>,
95 pub class_defs: Vec<ClassDef>,
96 pub version: String,
97 by_type: HashMap<u32, usize>,
99 method_handles: Vec<MethodHandleItem>,
101 call_sites: Vec<CallSiteInfo>,
103}
104
105#[derive(Debug, Clone)]
107pub struct MethodHandleItem {
108 pub kind: u16,
112 pub target_id: u32,
114 pub is_field: bool,
115}
116
117#[derive(Debug, Clone)]
119pub struct CallSiteInfo {
120 pub bootstrap_handle: u32,
122 pub name_idx: u32,
124 pub proto_idx: u32,
126 pub linker_args: Vec<crate::annotations::EncodedValue>,
128}
129
130impl Drop for DexFile {
131 fn drop(&mut self) {
132 *self.data.lock().unwrap() = None;
133 }
134}
135
136impl DexFile {
137 pub fn parse(data: Vec<u8>) -> Result<DexFile, String> {
138 if data.len() < crate::DEX_HEADER_SIZE {
139 return Err("file too small for DEX header".into());
140 }
141 let magic = &data[..8];
142 if &magic[..4] != b"dex\n" || magic[7] != 0 {
143 return Err(format!("not a DEX file: magic {:02x?}", magic));
144 }
145 let version = String::from_utf8_lossy(&magic[4..7]).into_owned();
146 if !matches!(
148 version.as_str(),
149 "035" | "037" | "038" | "039" | "040" | "041"
150 ) {
151 return Err(format!("unsupported DEX version {}", version));
152 }
153 let endian = u32::from_le_bytes(data[40..44].try_into().unwrap());
154 if endian != 0x1234_5678 {
155 return Err(format!("unexpected endian tag {endian:#x}"));
156 }
157
158 let u32at = |off: usize| -> u32 {
159 if off + 4 > data.len() {
160 return 0;
161 }
162 u32::from_le_bytes(data[off..off + 4].try_into().unwrap())
163 };
164 let string_ids_size = u32at(0x38) as usize;
165 let string_ids_off = u32at(0x3c) as usize;
166 let type_ids_size = u32at(0x40) as usize;
167 let type_ids_off = u32at(0x44) as usize;
168 let proto_ids_size = u32at(0x48) as usize;
169 let proto_ids_off = u32at(0x4c) as usize;
170 let field_ids_size = u32at(0x50) as usize;
171 let field_ids_off = u32at(0x54) as usize;
172 let method_ids_size = u32at(0x58) as usize;
173 let method_ids_off = u32at(0x5c) as usize;
174 let class_defs_size = u32at(0x60) as usize;
175 let class_defs_off = u32at(0x64) as usize;
176
177 let mut strings = Vec::with_capacity(string_ids_size);
179 if string_ids_off + 4 * string_ids_size <= data.len() {
180 for i in 0..string_ids_size {
181 let so = u32at(string_ids_off + 4 * i) as usize;
182 let mut c = Cursor::at(&data, so);
183 let s = c
184 .read_uleb128()
185 .and_then(|len| c.read_mutf8(c.pos, len))
186 .unwrap_or_default();
187 strings.push(s);
188 }
189 }
190
191 let mut types = Vec::with_capacity(type_ids_size);
193 if type_ids_off + 4 * type_ids_size <= data.len() {
194 for i in 0..type_ids_size {
195 types.push(u32at(type_ids_off + 4 * i));
196 }
197 }
198
199 let mut protos = Vec::with_capacity(proto_ids_size);
201 if proto_ids_off + 12 * proto_ids_size <= data.len() {
202 for i in 0..proto_ids_size {
203 let base = proto_ids_off + 12 * i;
204 protos.push(ProtoId {
205 shorty_idx: u32at(base),
206 return_type_idx: {
207 let v = u16::from_le_bytes(data[base + 4..base + 6].try_into().unwrap());
208 v as u32
209 },
210 parameters_off: u32at(base + 8),
211 });
212 }
213 }
214
215 let mut fields = Vec::with_capacity(field_ids_size);
217 if field_ids_off + 8 * field_ids_size <= data.len() {
218 for i in 0..field_ids_size {
219 let base = field_ids_off + 8 * i;
220 fields.push(FieldId {
221 class_idx: u16::from_le_bytes(data[base..base + 2].try_into().unwrap()) as u32,
222 type_idx: u16::from_le_bytes(data[base + 2..base + 4].try_into().unwrap())
223 as u32,
224 name_idx: u32at(base + 4),
225 });
226 }
227 }
228
229 let mut methods = Vec::with_capacity(method_ids_size);
231 if method_ids_off + 8 * method_ids_size <= data.len() {
232 for i in 0..method_ids_size {
233 let base = method_ids_off + 8 * i;
234 methods.push(MethodId {
235 class_idx: u16::from_le_bytes(data[base..base + 2].try_into().unwrap()) as u32,
236 proto_idx: u16::from_le_bytes(data[base + 2..base + 4].try_into().unwrap())
237 as u32,
238 name_idx: u32at(base + 4),
239 });
240 }
241 }
242
243 let mut class_defs = Vec::with_capacity(class_defs_size);
245 if class_defs_off + 32 * class_defs_size <= data.len() {
246 for i in 0..class_defs_size {
247 let base = class_defs_off + 32 * i;
248 class_defs.push(ClassDef {
249 class_idx: u32at(base),
250 access_flags: u32at(base + 4),
251 superclass_idx: u32at(base + 8),
252 interfaces_off: u32at(base + 12),
253 source_file_idx: u32at(base + 16),
254 annotations_off: u32at(base + 20),
255 class_data_off: u32at(base + 24),
256 static_values_off: u32at(base + 28),
257 });
258 }
259 }
260
261 let mut by_type = HashMap::with_capacity(class_defs.len());
262 for (i, cd) in class_defs.iter().enumerate() {
263 by_type.insert(cd.class_idx, i);
264 }
265
266 let (method_handles, call_sites) = parse_map_tables(&data);
269
270 Ok(DexFile {
271 data: std::sync::Mutex::new(Some(data)),
272 released: std::sync::atomic::AtomicBool::new(false),
273 strings,
274 types,
275 protos,
276 fields,
277 methods,
278 class_defs,
279 version,
280 by_type,
281 method_handles,
282 call_sites,
283 })
284 }
285
286 pub fn raw(&self) -> &[u8] {
297 let guard = self.data.lock().unwrap();
298 match guard.as_ref() {
299 Some(bytes) => unsafe { std::mem::transmute::<&[u8], &[u8]>(bytes.as_slice()) },
300 None => &[],
301 }
302 }
303
304 pub fn release_data(&mut self) {
307 *self.data.lock().unwrap() = None;
308 self.released
309 .store(true, std::sync::atomic::Ordering::Release);
310 }
311
312 pub fn mark_released(&self) {
317 self.released
318 .store(true, std::sync::atomic::Ordering::Release);
319 *self.data.lock().unwrap() = None;
321 }
322
323 #[inline]
324 #[allow(dead_code)] fn is_released(&self) -> bool {
326 self.released.load(std::sync::atomic::Ordering::Acquire)
327 }
328
329 pub fn string(&self, idx: u32) -> &str {
330 self.strings
331 .get(idx as usize)
332 .map(|s| s.as_str())
333 .unwrap_or("")
334 }
335
336 pub fn string_count(&self) -> usize {
337 self.strings.len()
338 }
339
340 pub fn class_name(&self, idx: u32) -> String {
343 let d = self.type_name(idx);
344 if d.len() > 2 && d.starts_with('L') && d.ends_with(';') {
345 d[1..d.len() - 1].to_string()
346 } else {
347 d.to_string()
348 }
349 }
350
351 pub fn type_name(&self, idx: u32) -> &str {
353 match self.types.get(idx as usize) {
354 Some(&si) => self.string(si),
355 None => "",
356 }
357 }
358
359 pub fn type_count(&self) -> usize {
360 self.types.len()
361 }
362
363 pub fn proto(&self, idx: u32) -> &ProtoId {
364 static EMPTY: std::sync::OnceLock<ProtoId> = std::sync::OnceLock::new();
365 self.protos.get(idx as usize).unwrap_or_else(|| {
366 EMPTY.get_or_init(|| ProtoId {
367 shorty_idx: 0,
368 return_type_idx: 0,
369 parameters_off: 0,
370 })
371 })
372 }
373
374 pub fn proto_params(&self, idx: u32) -> Vec<u32> {
376 let off = self.proto(idx).parameters_off;
377 if off == 0 {
378 return Vec::new();
379 }
380 self.read_type_list(off).unwrap_or_default()
381 }
382
383 fn read_type_list(&self, off: u32) -> Option<Vec<u32>> {
384 let off = off as usize;
385 if off + 4 > self.raw().len() {
386 return None;
387 }
388 let size = u32::from_le_bytes(self.raw()[off..off + 4].try_into().unwrap()) as usize;
389 let mut out = Vec::with_capacity(size);
390 let mut p = off + 4;
391 for _ in 0..size {
392 if p + 2 > self.raw().len() {
393 break;
394 }
395 out.push(u16::from_le_bytes(self.raw()[p..p + 2].try_into().unwrap()) as u32);
396 p += 2;
397 }
398 Some(out)
399 }
400
401 pub fn field(&self, idx: u32) -> &FieldId {
402 static EMPTY: std::sync::OnceLock<FieldId> = std::sync::OnceLock::new();
403 self.fields.get(idx as usize).unwrap_or_else(|| {
404 EMPTY.get_or_init(|| FieldId {
405 class_idx: 0,
406 type_idx: 0,
407 name_idx: 0,
408 })
409 })
410 }
411
412 pub fn field_count(&self) -> usize {
413 self.fields.len()
414 }
415
416 pub fn method(&self, idx: u32) -> &MethodId {
417 static EMPTY: std::sync::OnceLock<MethodId> = std::sync::OnceLock::new();
418 self.methods.get(idx as usize).unwrap_or_else(|| {
419 EMPTY.get_or_init(|| MethodId {
420 class_idx: 0,
421 proto_idx: 0,
422 name_idx: 0,
423 })
424 })
425 }
426
427 pub fn method_count(&self) -> usize {
428 self.methods.len()
429 }
430
431 pub fn class_def_of(&self, type_idx: u32) -> Option<usize> {
433 self.by_type.get(&type_idx).copied()
434 }
435
436 pub fn interfaces_of(&self, cd: &ClassDef) -> Vec<u32> {
438 if cd.interfaces_off == 0 {
439 return Vec::new();
440 }
441 self.read_type_list(cd.interfaces_off).unwrap_or_default()
442 }
443
444 pub fn class_data(&self, cd: &ClassDef) -> ClassData {
445 if cd.class_data_off == 0 {
446 return ClassData::default();
447 }
448 self.read_class_data(cd.class_data_off).unwrap_or_default()
449 }
450
451 fn read_class_data(&self, off: u32) -> Option<ClassData> {
452 let mut c = Cursor::at(self.raw(), off as usize);
453 let sf = c.read_uleb128()? as usize;
454 let inf = c.read_uleb128()? as usize;
455 let dm = c.read_uleb128()? as usize;
456 let vm = c.read_uleb128()? as usize;
457
458 let read_fields = |n: usize, cur: &mut Cursor| -> Option<Vec<EncodedField>> {
459 let mut out = Vec::with_capacity(n);
460 let mut idx: u64 = 0;
461 for _ in 0..n {
462 idx += cur.read_uleb128()?;
463 out.push(EncodedField {
464 field_idx: idx as u32,
465 access_flags: cur.read_uleb128()? as u32,
466 });
467 }
468 Some(out)
469 };
470 let static_fields = read_fields(sf, &mut c)?;
471 let instance_fields = read_fields(inf, &mut c)?;
472
473 let read_methods = |n: usize, cur: &mut Cursor| -> Option<Vec<EncodedMethod>> {
474 let mut out = Vec::with_capacity(n);
475 let mut idx: u64 = 0;
476 for _ in 0..n {
477 idx += cur.read_uleb128()?;
478 out.push(EncodedMethod {
479 method_idx: idx as u32,
480 access_flags: cur.read_uleb128()? as u32,
481 code_off: cur.read_uleb128()? as u32,
482 });
483 }
484 Some(out)
485 };
486 let direct_methods = read_methods(dm, &mut c)?;
487 let virtual_methods = read_methods(vm, &mut c)?;
488
489 Some(ClassData {
490 static_fields,
491 instance_fields,
492 direct_methods,
493 virtual_methods,
494 })
495 }
496
497 pub fn code_stats(&self, off: u32) -> Option<(u16, u32)> {
500 if off == 0 {
501 return None;
502 }
503 let o = off as usize;
504 if o + 16 > self.raw().len() {
505 return None;
506 }
507 let regs = u16::from_le_bytes([self.raw()[o], self.raw()[o + 1]]);
508 let insns = u32::from_le_bytes([
509 self.raw()[o + 12],
510 self.raw()[o + 13],
511 self.raw()[o + 14],
512 self.raw()[o + 15],
513 ]);
514 Some((regs, insns))
515 }
516
517 pub fn debug_info_off_at(&self, off: u32) -> Option<u32> {
519 if off == 0 {
520 return None;
521 }
522 let o = off as usize;
523 if o + 12 > self.raw().len() {
524 return None;
525 }
526 Some(u32::from_le_bytes([
527 self.raw()[o + 8],
528 self.raw()[o + 9],
529 self.raw()[o + 10],
530 self.raw()[o + 11],
531 ]))
532 }
533
534 pub fn code_at(&self, off: u32) -> Option<CodeItem> {
535 if off == 0 {
536 return None;
537 }
538 CodeItem::parse(self.raw(), off as usize)
539 }
540
541 pub fn class_names_from_image(image: &[u8]) -> Vec<String> {
546 if image.len() < 0x70 || &image[..4] != b"dex\n" {
547 return Vec::new();
548 }
549 let u4 = |o: usize| -> u32 {
550 u32::from_le_bytes([image[o], image[o + 1], image[o + 2], image[o + 3]])
551 };
552 let strings_size = u4(0x38) as usize;
553 let strings_off = u4(0x3c) as usize;
554 let types_size = u4(0x40) as usize;
555 let types_off = u4(0x44) as usize;
556 let classes_size = u4(0x60) as usize;
557 let classes_off = u4(0x64) as usize;
558 if strings_off + 4 * strings_size > image.len()
559 || types_off + 4 * types_size > image.len()
560 || classes_off + 32 * classes_size > image.len()
561 {
562 return Vec::new();
563 }
564 let u4at = |o: usize| -> u32 {
565 u32::from_le_bytes([image[o], image[o + 1], image[o + 2], image[o + 3]])
566 };
567 let string_at = |idx: u32| -> Option<String> {
568 let so = u4at(strings_off + 4 * idx as usize) as usize;
569 if so >= image.len() {
570 return None;
571 }
572 let mut p = so;
574 let mut len = 0u64;
576 let mut shift = 0;
577 loop {
578 if p >= image.len() {
579 return None;
580 }
581 let b = image[p];
582 p += 1;
583 len |= ((b & 0x7f) as u64) << shift;
584 shift += 7;
585 if b & 0x80 == 0 {
586 break;
587 }
588 }
589 let end = image[p..].iter().position(|&b| b == 0)? + p;
590 crate::reader::mutf8_decode(&image[p..end], len)
591 };
592 let mut out = Vec::with_capacity(classes_size);
593 for i in 0..classes_size {
594 let cd = classes_off + 32 * i;
595 let class_idx = u4at(cd) as usize;
596 if class_idx >= types_size {
597 continue;
598 }
599 let string_idx = u4at(types_off + 4 * class_idx);
600 if let Some(name) = string_at(string_idx) {
601 let plain = name
604 .strip_prefix('L')
605 .and_then(|s| s.strip_suffix(';'))
606 .map(str::to_string)
607 .unwrap_or(name);
608 out.push(plain);
609 }
610 }
611 out
612 }
613
614 pub fn code_insns_bytes_at(&self, off: u32) -> Option<&[u8]> {
617 if off == 0 {
618 return None;
619 }
620 let off = off as usize;
621 let d = self.raw();
622 if off + 16 > d.len() {
623 return None;
624 }
625 let insns_size =
627 u32::from_le_bytes([d[off + 12], d[off + 13], d[off + 14], d[off + 15]]) as usize;
628 let start = off + 16;
629 let end = start + 2 * insns_size;
630 if end > d.len() {
631 return None;
632 }
633 Some(&d[start..end])
634 }
635
636 pub fn static_values(&self, off: u32) -> Vec<EncodedValue> {
638 if off == 0 {
639 return Vec::new();
640 }
641 annotations::read_encoded_array(self.raw(), off as usize).unwrap_or_default()
642 }
643
644 pub fn method_handle(&self, idx: u32) -> Option<&MethodHandleItem> {
646 self.method_handles.get(idx as usize)
647 }
648
649 pub fn method_handle_count(&self) -> usize {
650 self.method_handles.len()
651 }
652
653 pub fn call_site(&self, idx: u32) -> Option<&CallSiteInfo> {
655 self.call_sites.get(idx as usize)
656 }
657
658 pub fn call_site_count(&self) -> usize {
659 self.call_sites.len()
660 }
661
662 pub fn parameter_names(&self, debug_info_off: u32) -> Vec<Option<String>> {
666 if debug_info_off == 0 {
667 return Vec::new();
668 }
669 let mut c = Cursor::at(self.raw(), debug_info_off as usize);
670 let _line_start = match c.read_uleb128() {
671 Some(v) => v,
672 None => return Vec::new(),
673 };
674 let n = match c.read_uleb128() {
675 Some(v) => v as usize,
676 None => return Vec::new(),
677 };
678 let mut out = Vec::with_capacity(n);
679 for _ in 0..n {
680 let idx = c.read_uleb128p1().unwrap_or(-1);
681 if idx < 0 {
682 out.push(None);
683 } else {
684 out.push(Some(self.string(idx as u32).to_string()));
685 }
686 }
687 out
688 }
689}
690
691impl std::fmt::Debug for DexFile {
692 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
693 f.debug_struct("DexFile")
694 .field("version", &self.version)
695 .field("strings", &self.strings.len())
696 .field("types", &self.types.len())
697 .field("protos", &self.protos.len())
698 .field("fields", &self.fields.len())
699 .field("methods", &self.methods.len())
700 .field("class_defs", &self.class_defs.len())
701 .finish()
702 }
703}
704
705fn parse_map_tables(data: &[u8]) -> (Vec<MethodHandleItem>, Vec<CallSiteInfo>) {
711 let mut handles = Vec::new();
712 let mut sites = Vec::new();
713 if data.len() < 0x70 {
714 return (handles, sites);
715 }
716 let map_off = u32le(data, 0x34) as usize;
717 if map_off + 4 > data.len() {
718 return (handles, sites);
719 }
720 let n = u32le(data, map_off) as usize;
721 let mut p = map_off + 4;
722 for _ in 0..n {
723 if p + 12 > data.len() {
724 break;
725 }
726 let t = u16le(data, p);
727 let size = u32le(data, p + 4) as usize;
728 let off = u32le(data, p + 8) as usize;
729 p += 12;
730 match t {
731 0x0007 => {
732 for i in 0..size {
734 let q = off + 4 * i;
735 if q + 4 > data.len() {
736 break;
737 }
738 let cs_off = u32le(data, q) as usize;
739 if let Some(info) = parse_call_site(data, cs_off) {
740 sites.push(info);
741 }
742 }
743 }
744 0x0008 => {
745 for i in 0..size {
747 let q = off + 8 * i;
748 if q + 8 > data.len() {
749 break;
750 }
751 let kind = u16le(data, q);
752 let id = u16le(data, q + 4);
753 handles.push(MethodHandleItem {
754 kind,
755 target_id: id as u32,
756 is_field: kind <= 3,
757 });
758 }
759 }
760 _ => {}
761 }
762 }
763 (handles, sites)
764}
765
766fn parse_call_site(data: &[u8], off: usize) -> Option<CallSiteInfo> {
767 let vals = crate::annotations::read_encoded_array(data, off)?;
768 if vals.len() < 3 {
769 return None;
770 }
771 let bootstrap_handle = match &vals[0] {
772 crate::annotations::EncodedValue::MethodHandle(h) => *h,
773 _ => return None,
774 };
775 let name_idx = match &vals[1] {
776 crate::annotations::EncodedValue::String(s) => *s,
777 _ => return None,
778 };
779 let proto_idx = match &vals[2] {
780 crate::annotations::EncodedValue::MethodType(p) => *p,
781 _ => return None,
782 };
783 Some(CallSiteInfo {
784 bootstrap_handle,
785 name_idx,
786 proto_idx,
787 linker_args: vals[3..].to_vec(),
788 })
789}
790
791fn u16le(data: &[u8], off: usize) -> u16 {
792 if off + 2 > data.len() {
793 return 0;
794 }
795 u16::from_le_bytes([data[off], data[off + 1]])
796}
797
798fn u32le(data: &[u8], off: usize) -> u32 {
799 if off + 4 > data.len() {
800 return 0;
801 }
802 u32::from_le_bytes([data[off], data[off + 1], data[off + 2], data[off + 3]])
803}
804
805#[cfg(test)]
806mod tests {
807 use super::*;
808
809 #[test]
810 fn rejects_non_dex() {
811 assert!(DexFile::parse(vec![0u8; 128]).is_err());
812 }
813}