1use super::{MemoryReader, SectionRef};
7
8pub struct MachOSections {
10 pub unwind_info: Option<SectionRef>,
11 pub eh_frame: Option<SectionRef>,
12 pub text: Option<SectionRef>,
13}
14
15const MH_MAGIC_64: u32 = 0xFEED_FACF;
17const MH_MAGIC: u32 = 0xFEED_FACE;
18const LC_SEGMENT_64: u32 = 0x19;
19const LC_SEGMENT: u32 = 0x01;
20const LC_UUID: u32 = 0x1B;
21
22pub fn find_sections(
24 reader: &dyn MemoryReader,
25 load_address: u64,
26 is_64_bit: bool,
27) -> MachOSections {
28 let mut result = MachOSections {
29 unwind_info: None,
30 eh_frame: None,
31 text: None,
32 };
33
34 if is_64_bit {
35 find_sections_64(reader, load_address, &mut result);
36 } else {
37 find_sections_32(reader, load_address, &mut result);
38 }
39
40 result
41}
42
43pub fn find_uuid(
45 reader: &dyn MemoryReader,
46 load_address: u64,
47 is_64_bit: bool,
48) -> Option<[u8; 16]> {
49 let magic = reader.read_u32(load_address)?;
50
51 let (header_size, expected_magic, lc_segment) = if is_64_bit {
52 (32u64, MH_MAGIC_64, LC_SEGMENT_64)
53 } else {
54 (28u64, MH_MAGIC, LC_SEGMENT)
55 };
56
57 if magic != expected_magic {
58 return None;
59 }
60
61 let ncmds = reader.read_u32(load_address + 16)? as u64;
62 let _ = lc_segment; let mut offset = header_size;
65 for _ in 0..ncmds {
66 let cmd = reader.read_u32(load_address + offset)?;
67 let cmd_size = reader.read_u32(load_address + offset + 4)? as u64;
68
69 if cmd == LC_UUID && cmd_size >= 24 {
70 let uuid_bytes = reader.read_memory(load_address + offset + 8, 16)?;
71 let mut uuid = [0u8; 16];
72 uuid.copy_from_slice(&uuid_bytes);
73 return Some(uuid);
74 }
75
76 offset += cmd_size;
77 }
78
79 None
80}
81
82fn find_sections_64(reader: &dyn MemoryReader, load_address: u64, result: &mut MachOSections) {
83 let Some(magic) = reader.read_u32(load_address) else {
84 return;
85 };
86 if magic != MH_MAGIC_64 {
87 return;
88 }
89
90 let Some(ncmds) = reader.read_u32(load_address + 16) else {
91 return;
92 };
93
94 let mut offset = 32u64;
96
97 for _ in 0..ncmds {
98 let Some(cmd) = reader.read_u32(load_address + offset) else {
99 return;
100 };
101 let Some(cmd_size) = reader.read_u32(load_address + offset + 4) else {
102 return;
103 };
104
105 if cmd == LC_SEGMENT_64 {
106 let Some(seg_name_bytes) = reader.read_memory(load_address + offset + 8, 16) else {
108 offset += cmd_size as u64;
109 continue;
110 };
111 let seg_name = bytes_to_name(&seg_name_bytes);
112
113 if seg_name == "__TEXT" {
114 let Some(nsects) = reader.read_u32(load_address + offset + 64) else {
117 offset += cmd_size as u64;
118 continue;
119 };
120
121 let mut sect_offset = offset + 72;
123 for _ in 0..nsects {
124 parse_section_64(reader, load_address, sect_offset, result);
125 sect_offset += 80; }
127 }
128 }
129
130 offset += cmd_size as u64;
131 }
132}
133
134fn find_sections_32(reader: &dyn MemoryReader, load_address: u64, result: &mut MachOSections) {
135 let Some(magic) = reader.read_u32(load_address) else {
136 return;
137 };
138 if magic != MH_MAGIC {
139 return;
140 }
141
142 let Some(ncmds) = reader.read_u32(load_address + 16) else {
143 return;
144 };
145
146 let mut offset = 28u64;
148
149 for _ in 0..ncmds {
150 let Some(cmd) = reader.read_u32(load_address + offset) else {
151 return;
152 };
153 let Some(cmd_size) = reader.read_u32(load_address + offset + 4) else {
154 return;
155 };
156
157 if cmd == LC_SEGMENT {
158 let Some(seg_name_bytes) = reader.read_memory(load_address + offset + 8, 16) else {
159 offset += cmd_size as u64;
160 continue;
161 };
162 let seg_name = bytes_to_name(&seg_name_bytes);
163
164 if seg_name == "__TEXT" {
165 let Some(nsects) = reader.read_u32(load_address + offset + 48) else {
166 offset += cmd_size as u64;
167 continue;
168 };
169
170 let mut sect_offset = offset + 56; for _ in 0..nsects {
172 parse_section_32(reader, load_address, sect_offset, result);
173 sect_offset += 68; }
175 }
176 }
177
178 offset += cmd_size as u64;
179 }
180}
181
182fn parse_section_64(
183 reader: &dyn MemoryReader,
184 load_address: u64,
185 sect_offset: u64,
186 result: &mut MachOSections,
187) {
188 let Some(sect_name_bytes) = reader.read_memory(load_address + sect_offset, 16) else {
190 return;
191 };
192 let sect_name = bytes_to_name(§_name_bytes);
193
194 let Some(addr) = reader.read_u64(load_address + sect_offset + 32) else {
195 return;
196 };
197 let Some(size) = reader.read_u64(load_address + sect_offset + 40) else {
198 return;
199 };
200
201 match sect_name.as_str() {
202 "__unwind_info" => {
203 result.unwind_info = Some(SectionRef {
204 vm_addr: addr,
205 size,
206 });
207 }
208 "__eh_frame" => {
209 result.eh_frame = Some(SectionRef {
210 vm_addr: addr,
211 size,
212 });
213 }
214 "__text" => {
215 result.text = Some(SectionRef {
216 vm_addr: addr,
217 size,
218 });
219 }
220 _ => {}
221 }
222}
223
224fn parse_section_32(
225 reader: &dyn MemoryReader,
226 load_address: u64,
227 sect_offset: u64,
228 result: &mut MachOSections,
229) {
230 let Some(sect_name_bytes) = reader.read_memory(load_address + sect_offset, 16) else {
231 return;
232 };
233 let sect_name = bytes_to_name(§_name_bytes);
234
235 let Some(addr) = reader.read_u32(load_address + sect_offset + 32) else {
237 return;
238 };
239 let Some(size) = reader.read_u32(load_address + sect_offset + 36) else {
240 return;
241 };
242
243 match sect_name.as_str() {
244 "__unwind_info" => {
245 result.unwind_info = Some(SectionRef {
246 vm_addr: addr as u64,
247 size: size as u64,
248 });
249 }
250 "__eh_frame" => {
251 result.eh_frame = Some(SectionRef {
252 vm_addr: addr as u64,
253 size: size as u64,
254 });
255 }
256 "__text" => {
257 result.text = Some(SectionRef {
258 vm_addr: addr as u64,
259 size: size as u64,
260 });
261 }
262 _ => {}
263 }
264}
265
266fn bytes_to_name(bytes: &[u8]) -> String {
268 let len = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len());
269 String::from_utf8_lossy(&bytes[..len]).to_string()
270}
271
272#[cfg(test)]
273mod tests {
274 use super::super::SliceMemoryReader;
275 use super::*;
276
277 fn build_test_macho_64() -> Vec<u8> {
280 let mut data = Vec::new();
281
282 data.extend_from_slice(&MH_MAGIC_64.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(&1u32.to_le_bytes()); let sizeofcmds = 72 + 80 * 3; data.extend_from_slice(&(sizeofcmds as u32).to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); assert_eq!(data.len(), 32);
293
294 let cmd_size = 72 + 80 * 3;
296 data.extend_from_slice(&LC_SEGMENT_64.to_le_bytes()); data.extend_from_slice(&(cmd_size as u32).to_le_bytes()); let mut segname = [0u8; 16];
300 segname[..6].copy_from_slice(b"__TEXT");
301 data.extend_from_slice(&segname);
302 data.extend_from_slice(&0x1000u64.to_le_bytes()); data.extend_from_slice(&0x3000u64.to_le_bytes()); data.extend_from_slice(&0u64.to_le_bytes()); data.extend_from_slice(&0u64.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(&3u32.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); assert_eq!(data.len(), 104); write_section_64(&mut data, "__text", "__TEXT", 0x1100, 0x500);
314 write_section_64(&mut data, "__unwind_info", "__TEXT", 0x2000, 0x200);
316 write_section_64(&mut data, "__eh_frame", "__TEXT", 0x2200, 0x400);
318
319 data
320 }
321
322 fn write_section_64(data: &mut Vec<u8>, sectname: &str, segname: &str, addr: u64, size: u64) {
323 let mut sn = [0u8; 16];
325 let bytes = sectname.as_bytes();
326 sn[..bytes.len().min(16)].copy_from_slice(&bytes[..bytes.len().min(16)]);
327 data.extend_from_slice(&sn);
328
329 let mut sg = [0u8; 16];
330 let bytes = segname.as_bytes();
331 sg[..bytes.len().min(16)].copy_from_slice(&bytes[..bytes.len().min(16)]);
332 data.extend_from_slice(&sg);
333
334 data.extend_from_slice(&addr.to_le_bytes()); data.extend_from_slice(&size.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); data.extend_from_slice(&0u32.to_le_bytes()); }
345
346 #[test]
347 fn find_sections_64bit() {
348 let macho_data = build_test_macho_64();
349 let reader = SliceMemoryReader {
350 data: macho_data,
351 base_address: 0,
352 };
353
354 let sections = find_sections(&reader, 0, true);
355
356 let text = sections.text.unwrap();
357 assert_eq!(text.vm_addr, 0x1100);
358 assert_eq!(text.size, 0x500);
359
360 let unwind = sections.unwind_info.unwrap();
361 assert_eq!(unwind.vm_addr, 0x2000);
362 assert_eq!(unwind.size, 0x200);
363
364 let eh = sections.eh_frame.unwrap();
365 assert_eq!(eh.vm_addr, 0x2200);
366 assert_eq!(eh.size, 0x400);
367 }
368
369 #[test]
370 fn find_sections_bad_magic() {
371 let reader = SliceMemoryReader {
372 data: vec![0u8; 64],
373 base_address: 0,
374 };
375 let sections = find_sections(&reader, 0, true);
376 assert!(sections.text.is_none());
377 assert!(sections.unwind_info.is_none());
378 assert!(sections.eh_frame.is_none());
379 }
380
381 #[test]
382 fn bytes_to_name_with_null() {
383 let b = b"__TEXT\0\0\0\0\0\0\0\0\0\0\0";
384 assert_eq!(bytes_to_name(b), "__TEXT");
385 }
386
387 #[test]
388 fn bytes_to_name_full() {
389 let b = b"__longerthan16ch";
390 assert_eq!(bytes_to_name(b), "__longerthan16ch");
391 }
392}