1use memf_core::object_reader::ObjectReader;
10use memf_format::PhysicalMemoryProvider;
11use serde::Serialize;
12
13use crate::Result;
14
15const AF_PACKET: u16 = 17;
17const SOCK_RAW: u16 = 3;
19const IFF_PROMISC: u32 = 0x100;
21
22#[derive(Debug, Clone, Serialize)]
24pub struct RawSocketInfo {
25 pub pid: u32,
27 pub comm: String,
29 pub socket_type: String,
31 pub protocol: u16,
33 pub is_promiscuous: bool,
35 pub is_suspicious: bool,
37}
38
39pub use crate::heuristics::classify_raw_socket;
46
47pub fn walk_raw_sockets<P: PhysicalMemoryProvider>(
56 reader: &ObjectReader<P>,
57) -> Result<Vec<RawSocketInfo>> {
58 let init_task_addr = match reader.symbols().symbol_address("init_task") {
60 Some(a) => a,
61 None => return Ok(vec![]),
62 };
63 let tasks_offset = match reader.symbols().field_offset("task_struct", "tasks") {
64 Some(o) => o,
65 None => return Ok(vec![]),
66 };
67 if reader
69 .symbols()
70 .field_offset("task_struct", "files")
71 .is_none()
72 {
73 return Ok(vec![]);
74 }
75
76 let head_vaddr = init_task_addr + tasks_offset;
77 let task_addrs = reader.walk_list(head_vaddr, "task_struct", "tasks")?;
78
79 let mut results: Vec<RawSocketInfo> = Vec::new();
80
81 collect_raw_sockets_for_task(reader, init_task_addr, &mut results);
82 for &task_addr in &task_addrs {
83 collect_raw_sockets_for_task(reader, task_addr, &mut results);
84 }
85
86 results.sort_by_key(|r| r.pid);
87 Ok(results)
88}
89
90fn collect_raw_sockets_for_task<P: PhysicalMemoryProvider>(
92 reader: &ObjectReader<P>,
93 task_addr: u64,
94 out: &mut Vec<RawSocketInfo>,
95) {
96 let pid: u32 = match reader.read_field(task_addr, "task_struct", "pid") {
97 Ok(v) => v,
98 Err(_) => return,
99 };
100 let comm = reader
101 .read_field_string(task_addr, "task_struct", "comm", 16)
102 .unwrap_or_default();
103
104 let files_ptr: u64 = match reader.read_field(task_addr, "task_struct", "files") {
106 Ok(v) => v,
107 Err(_) => return,
108 };
109 if files_ptr == 0 {
110 return;
111 }
112
113 let fdt_ptr: u64 = match reader.read_field(files_ptr, "files_struct", "fdt") {
115 Ok(v) => v,
116 Err(_) => return,
117 };
118 if fdt_ptr == 0 {
119 return;
120 }
121
122 let fd_array_ptr: u64 = match reader.read_field(fdt_ptr, "fdtable", "fd") {
124 Ok(v) => v,
125 Err(_) => return,
126 };
127 if fd_array_ptr == 0 {
128 return;
129 }
130
131 for fd_index in 0u64..256 {
133 let file_slot_addr = fd_array_ptr + fd_index * 8;
134 let file_ptr_raw = match reader.read_bytes(file_slot_addr, 8) {
135 Ok(b) => b,
136 Err(_) => break,
137 };
138 let file_ptr = u64::from_le_bytes(match file_ptr_raw.try_into() {
139 Ok(b) => b,
140 Err(_) => break,
141 });
142 if file_ptr == 0 {
143 continue;
144 }
145
146 if let Some(info) = try_read_raw_socket(reader, pid, &comm, file_ptr) {
147 out.push(info);
148 }
149 }
150}
151
152fn try_read_raw_socket<P: PhysicalMemoryProvider>(
158 reader: &ObjectReader<P>,
159 pid: u32,
160 comm: &str,
161 file_ptr: u64,
162) -> Option<RawSocketInfo> {
163 let sock_ptr: u64 = reader.read_field(file_ptr, "file", "private_data").ok()?;
165 if sock_ptr == 0 {
166 return None;
167 }
168
169 let sock_type: u16 = reader.read_field(sock_ptr, "socket", "type").ok()?;
171
172 let sk_ptr: u64 = reader.read_field(sock_ptr, "socket", "sk").ok()?;
174 if sk_ptr == 0 {
175 return None;
176 }
177
178 let sk_family: u16 = reader.read_field(sk_ptr, "sock", "sk_family").ok()?;
180 let protocol: u16 = reader
182 .read_field::<u16>(sk_ptr, "sock", "sk_protocol")
183 .unwrap_or(0);
184
185 let socket_type_str = if sk_family == AF_PACKET {
186 "AF_PACKET"
187 } else if sock_type == SOCK_RAW {
188 "SOCK_RAW"
189 } else {
190 return None; };
192
193 let is_promiscuous = try_read_promisc(reader, sk_ptr);
196
197 let is_suspicious = classify_raw_socket(comm, socket_type_str, is_promiscuous);
198
199 Some(RawSocketInfo {
200 pid,
201 comm: comm.to_string(),
202 socket_type: socket_type_str.to_string(),
203 protocol,
204 is_promiscuous,
205 is_suspicious,
206 })
207}
208
209fn try_read_promisc<P: PhysicalMemoryProvider>(reader: &ObjectReader<P>, sk_ptr: u64) -> bool {
213 let prot_hook_offset = match reader.symbols().field_offset("packet_sock", "prot_hook") {
215 Some(o) => o,
216 None => return false,
217 };
218 let dev_in_hook = match reader.symbols().field_offset("packet_type", "dev") {
219 Some(o) => o,
220 None => return false,
221 };
222
223 let dev_ptr_addr = sk_ptr + prot_hook_offset + dev_in_hook;
224 let dev_raw = match reader.read_bytes(dev_ptr_addr, 8) {
225 Ok(b) => b,
226 Err(_) => return false,
227 };
228 let dev_ptr = u64::from_le_bytes(match dev_raw.try_into() {
229 Ok(b) => b,
230 Err(_) => return false,
231 });
232 if dev_ptr == 0 {
233 return false;
234 }
235
236 let flags: u32 = match reader.read_field(dev_ptr, "net_device", "flags") {
237 Ok(v) => v,
238 Err(_) => return false,
239 };
240
241 (flags & IFF_PROMISC) != 0
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247 use memf_core::object_reader::ObjectReader;
248 use memf_core::test_builders::{PageTableBuilder, SyntheticPhysMem};
249 use memf_core::vas::{TranslationMode, VirtualAddressSpace};
250 use memf_symbols::isf::IsfResolver;
251 use memf_symbols::test_builders::IsfBuilder;
252
253 #[test]
258 fn classify_raw_socket_promiscuous_is_suspicious() {
259 assert!(
260 classify_raw_socket("tcpdump", "AF_PACKET", true),
261 "promiscuous mode must always be suspicious regardless of comm"
262 );
263 }
264
265 #[test]
266 fn classify_raw_socket_af_packet_unknown_comm_suspicious() {
267 assert!(
268 classify_raw_socket("malware", "AF_PACKET", false),
269 "AF_PACKET socket held by unknown process must be suspicious"
270 );
271 }
272
273 #[test]
274 fn classify_raw_socket_tcpdump_benign() {
275 assert!(
276 !classify_raw_socket("tcpdump", "AF_PACKET", false),
277 "non-promiscuous AF_PACKET socket by tcpdump must not be suspicious"
278 );
279 }
280
281 #[test]
282 fn classify_raw_socket_ping_benign() {
283 assert!(
284 !classify_raw_socket("ping", "SOCK_RAW", false),
285 "SOCK_RAW socket by ping must not be suspicious"
286 );
287 }
288
289 #[test]
290 fn classify_raw_socket_sock_raw_unknown_comm_suspicious() {
291 assert!(
292 classify_raw_socket("implant", "SOCK_RAW", false),
293 "SOCK_RAW socket held by unknown process must be suspicious"
294 );
295 }
296
297 fn make_reader_no_init_task() -> ObjectReader<SyntheticPhysMem> {
302 let isf = IsfBuilder::new()
303 .add_struct("task_struct", 128)
304 .add_field("task_struct", "pid", 0, "int")
305 .add_struct("list_head", 16)
306 .add_field("list_head", "next", 0, "pointer")
307 .add_field("list_head", "prev", 8, "pointer")
308 .build_json();
309
310 let resolver = IsfResolver::from_value(&isf).unwrap();
311 let (cr3, mem) = PageTableBuilder::new().build();
312 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
313 ObjectReader::new(vas, Box::new(resolver))
314 }
315
316 #[test]
317 fn walk_raw_sockets_missing_init_task_returns_empty() {
318 let reader = make_reader_no_init_task();
319 let result = walk_raw_sockets(&reader).expect("should not error");
320 assert!(
321 result.is_empty(),
322 "missing init_task must yield empty results (graceful degradation)"
323 );
324 }
325
326 #[test]
329 fn classify_raw_socket_unknown_type_benign() {
330 assert!(
332 !classify_raw_socket("someproc", "UNKNOWN_TYPE", false),
333 "unknown socket type, not promiscuous must not be suspicious"
334 );
335 }
336
337 #[test]
338 fn classify_raw_socket_wireshark_af_packet_benign() {
339 assert!(
340 !classify_raw_socket("wireshark", "AF_PACKET", false),
341 "wireshark AF_PACKET must not be suspicious"
342 );
343 }
344
345 #[test]
346 fn classify_raw_socket_dumpcap_af_packet_benign() {
347 assert!(
348 !classify_raw_socket("dumpcap", "AF_PACKET", false),
349 "dumpcap AF_PACKET must not be suspicious"
350 );
351 }
352
353 #[test]
354 fn classify_raw_socket_dhclient_af_packet_benign() {
355 assert!(
356 !classify_raw_socket("dhclient", "AF_PACKET", false),
357 "dhclient AF_PACKET must not be suspicious"
358 );
359 }
360
361 #[test]
362 fn classify_raw_socket_dhcpcd_af_packet_benign() {
363 assert!(
364 !classify_raw_socket("dhcpcd", "AF_PACKET", false),
365 "dhcpcd AF_PACKET must not be suspicious"
366 );
367 }
368
369 #[test]
370 fn classify_raw_socket_arping_af_packet_benign() {
371 assert!(
372 !classify_raw_socket("arping", "AF_PACKET", false),
373 "arping AF_PACKET must not be suspicious"
374 );
375 }
376
377 #[test]
378 fn classify_raw_socket_ping_af_packet_benign() {
379 assert!(
380 !classify_raw_socket("ping", "AF_PACKET", false),
381 "ping AF_PACKET must not be suspicious"
382 );
383 }
384
385 #[test]
386 fn classify_raw_socket_ping6_af_packet_benign() {
387 assert!(
388 !classify_raw_socket("ping6", "AF_PACKET", false),
389 "ping6 AF_PACKET must not be suspicious"
390 );
391 }
392
393 #[test]
394 fn classify_raw_socket_traceroute_sock_raw_benign() {
395 assert!(
396 !classify_raw_socket("traceroute", "SOCK_RAW", false),
397 "traceroute SOCK_RAW must not be suspicious"
398 );
399 }
400
401 #[test]
402 fn classify_raw_socket_traceroute6_sock_raw_benign() {
403 assert!(
404 !classify_raw_socket("traceroute6", "SOCK_RAW", false),
405 "traceroute6 SOCK_RAW must not be suspicious"
406 );
407 }
408
409 #[test]
410 fn classify_raw_socket_arping_sock_raw_benign() {
411 assert!(
412 !classify_raw_socket("arping", "SOCK_RAW", false),
413 "arping SOCK_RAW must not be suspicious"
414 );
415 }
416
417 #[test]
418 fn classify_raw_socket_ping6_sock_raw_benign() {
419 assert!(
420 !classify_raw_socket("ping6", "SOCK_RAW", false),
421 "ping6 SOCK_RAW must not be suspicious"
422 );
423 }
424
425 #[test]
426 fn classify_raw_socket_uppercase_comm_not_benign() {
427 assert!(
429 !classify_raw_socket("TCPDUMP", "AF_PACKET", false),
430 "TCPDUMP (uppercase) AF_PACKET must not be suspicious (case-folded)"
431 );
432 }
433
434 #[test]
435 fn classify_raw_socket_promisc_overrides_benign_comm() {
436 assert!(
438 classify_raw_socket("wireshark", "AF_PACKET", true),
439 "promiscuous wireshark must still be suspicious"
440 );
441 }
442
443 fn make_reader_with_init_task_no_tasks() -> ObjectReader<SyntheticPhysMem> {
446 let isf = IsfBuilder::new()
447 .add_symbol("init_task", 0xFFFF_FFFF_8260_0000)
448 .add_struct("task_struct", 512)
449 .add_field("task_struct", "pid", 0, "int")
450 .build_json();
451 let resolver = IsfResolver::from_value(&isf).unwrap();
452 let (cr3, mem) = PageTableBuilder::new().build();
453 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
454 ObjectReader::new(vas, Box::new(resolver))
455 }
456
457 #[test]
458 fn walk_raw_sockets_missing_tasks_offset_returns_empty() {
459 let reader = make_reader_with_init_task_no_tasks();
460 let result = walk_raw_sockets(&reader).expect("should not error");
461 assert!(
462 result.is_empty(),
463 "missing task_struct.tasks offset must yield empty results"
464 );
465 }
466
467 fn make_reader_with_tasks_no_files() -> ObjectReader<SyntheticPhysMem> {
470 let isf = IsfBuilder::new()
471 .add_symbol("init_task", 0xFFFF_FFFF_8260_0000)
472 .add_struct("task_struct", 512)
473 .add_field("task_struct", "pid", 0, "int")
474 .add_field("task_struct", "tasks", 8, "pointer")
475 .add_struct("list_head", 16)
476 .add_field("list_head", "next", 0, "pointer")
477 .add_field("list_head", "prev", 8, "pointer")
478 .build_json();
479 let resolver = IsfResolver::from_value(&isf).unwrap();
480 let (cr3, mem) = PageTableBuilder::new().build();
481 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
482 ObjectReader::new(vas, Box::new(resolver))
483 }
484
485 #[test]
486 fn walk_raw_sockets_missing_files_field_returns_empty() {
487 let reader = make_reader_with_tasks_no_files();
488 let result = walk_raw_sockets(&reader).expect("should not error");
489 assert!(
490 result.is_empty(),
491 "missing task_struct.files field must yield empty results"
492 );
493 }
494
495 #[test]
498 fn walk_raw_sockets_symbol_present_files_null_returns_empty() {
499 use memf_core::test_builders::flags as ptf;
500 use memf_core::vas::{TranslationMode, VirtualAddressSpace};
501 use memf_symbols::isf::IsfResolver;
502 use memf_symbols::test_builders::IsfBuilder;
503
504 let tasks_offset: u64 = 0x10;
506 let sym_vaddr: u64 = 0xFFFF_8800_0090_0000;
507 let sym_paddr: u64 = 0x0090_0000; let isf = IsfBuilder::new()
510 .add_symbol("init_task", sym_vaddr)
511 .add_struct("list_head", 0x10)
512 .add_field("list_head", "next", 0x00, "pointer")
513 .add_struct("task_struct", 0x400)
514 .add_field("task_struct", "tasks", tasks_offset, "pointer")
515 .add_field("task_struct", "pid", 0x00, "unsigned int")
516 .add_field("task_struct", "comm", 0x20, "char")
517 .add_field("task_struct", "files", 0x30, "pointer")
518 .build_json();
519 let resolver = IsfResolver::from_value(&isf).unwrap();
520
521 let mut page = [0u8; 4096];
523 let self_ptr = sym_vaddr + tasks_offset;
524 page[tasks_offset as usize..tasks_offset as usize + 8]
525 .copy_from_slice(&self_ptr.to_le_bytes());
526 let (cr3, mem) = PageTableBuilder::new()
529 .map_4k(sym_vaddr, sym_paddr, ptf::WRITABLE)
530 .write_phys(sym_paddr, &page)
531 .build();
532
533 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
534 let reader: ObjectReader<SyntheticPhysMem> = ObjectReader::new(vas, Box::new(resolver));
535
536 let result = walk_raw_sockets(&reader).expect("should not error");
537 assert!(
538 result.is_empty(),
539 "null files ptr → no fd table → no raw sockets"
540 );
541 }
542
543 #[test]
546 fn walk_raw_sockets_fdt_ptr_null_returns_empty() {
547 use memf_core::test_builders::flags as ptf;
548
549 let tasks_offset: u64 = 0x10;
550 let files_offset: u64 = 0x30;
551
552 let sym_vaddr: u64 = 0xFFFF_8800_0091_0000;
553 let sym_paddr: u64 = 0x0091_0000;
554
555 let files_vaddr: u64 = 0xFFFF_8800_0092_0000;
557 let files_paddr: u64 = 0x0092_0000;
558
559 let isf = IsfBuilder::new()
560 .add_symbol("init_task", sym_vaddr)
561 .add_struct("list_head", 0x10)
562 .add_field("list_head", "next", 0x00, "pointer")
563 .add_struct("task_struct", 0x400)
564 .add_field("task_struct", "tasks", tasks_offset, "pointer")
565 .add_field("task_struct", "pid", 0x00, "unsigned int")
566 .add_field("task_struct", "comm", 0x20, "char")
567 .add_field("task_struct", "files", files_offset, "pointer")
568 .add_struct("files_struct", 0x100)
569 .add_field("files_struct", "fdt", 0x00, "pointer")
570 .build_json();
571 let resolver = IsfResolver::from_value(&isf).unwrap();
572
573 let mut task_page = [0u8; 4096];
575 let self_ptr = sym_vaddr + tasks_offset;
576 task_page[tasks_offset as usize..tasks_offset as usize + 8]
577 .copy_from_slice(&self_ptr.to_le_bytes());
578 task_page[files_offset as usize..files_offset as usize + 8]
579 .copy_from_slice(&files_vaddr.to_le_bytes());
580
581 let files_page = [0u8; 4096]; let (cr3, mem) = PageTableBuilder::new()
585 .map_4k(sym_vaddr, sym_paddr, ptf::WRITABLE)
586 .write_phys(sym_paddr, &task_page)
587 .map_4k(files_vaddr, files_paddr, ptf::WRITABLE)
588 .write_phys(files_paddr, &files_page)
589 .build();
590
591 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
592 let reader: ObjectReader<SyntheticPhysMem> = ObjectReader::new(vas, Box::new(resolver));
593
594 let result = walk_raw_sockets(&reader).expect("should not error");
595 assert!(
596 result.is_empty(),
597 "fdt_ptr == 0 → early return → no raw sockets"
598 );
599 }
600
601 #[test]
604 fn walk_raw_sockets_fd_array_null_returns_empty() {
605 use memf_core::test_builders::flags as ptf;
606
607 let tasks_offset: u64 = 0x10;
608 let files_offset: u64 = 0x30;
609
610 let sym_vaddr: u64 = 0xFFFF_8800_0093_0000;
611 let sym_paddr: u64 = 0x0093_0000;
612
613 let files_vaddr: u64 = 0xFFFF_8800_0094_0000;
614 let files_paddr: u64 = 0x0094_0000;
615
616 let fdt_vaddr: u64 = 0xFFFF_8800_0095_0000;
617 let fdt_paddr: u64 = 0x0095_0000;
618
619 let isf = IsfBuilder::new()
620 .add_symbol("init_task", sym_vaddr)
621 .add_struct("list_head", 0x10)
622 .add_field("list_head", "next", 0x00, "pointer")
623 .add_struct("task_struct", 0x400)
624 .add_field("task_struct", "tasks", tasks_offset, "pointer")
625 .add_field("task_struct", "pid", 0x00, "unsigned int")
626 .add_field("task_struct", "comm", 0x20, "char")
627 .add_field("task_struct", "files", files_offset, "pointer")
628 .add_struct("files_struct", 0x100)
629 .add_field("files_struct", "fdt", 0x00, "pointer")
630 .add_struct("fdtable", 0x40)
631 .add_field("fdtable", "fd", 0x00, "pointer")
632 .build_json();
633 let resolver = IsfResolver::from_value(&isf).unwrap();
634
635 let mut task_page = [0u8; 4096];
636 let self_ptr = sym_vaddr + tasks_offset;
637 task_page[tasks_offset as usize..tasks_offset as usize + 8]
638 .copy_from_slice(&self_ptr.to_le_bytes());
639 task_page[files_offset as usize..files_offset as usize + 8]
640 .copy_from_slice(&files_vaddr.to_le_bytes());
641
642 let mut files_page = [0u8; 4096];
644 files_page[0..8].copy_from_slice(&fdt_vaddr.to_le_bytes());
645
646 let fdt_page = [0u8; 4096];
648
649 let (cr3, mem) = PageTableBuilder::new()
650 .map_4k(sym_vaddr, sym_paddr, ptf::WRITABLE)
651 .write_phys(sym_paddr, &task_page)
652 .map_4k(files_vaddr, files_paddr, ptf::WRITABLE)
653 .write_phys(files_paddr, &files_page)
654 .map_4k(fdt_vaddr, fdt_paddr, ptf::WRITABLE)
655 .write_phys(fdt_paddr, &fdt_page)
656 .build();
657
658 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
659 let reader: ObjectReader<SyntheticPhysMem> = ObjectReader::new(vas, Box::new(resolver));
660
661 let result = walk_raw_sockets(&reader).expect("should not error");
662 assert!(
663 result.is_empty(),
664 "fd_array_ptr == 0 → early return → no raw sockets"
665 );
666 }
667
668 #[test]
670 fn raw_socket_info_serializes() {
671 let info = RawSocketInfo {
672 pid: 99,
673 comm: "sniffer".to_string(),
674 socket_type: "AF_PACKET".to_string(),
675 protocol: 0x0300,
676 is_promiscuous: false,
677 is_suspicious: true,
678 };
679 let cloned = info.clone();
680 let json = serde_json::to_string(&cloned).unwrap();
681 assert!(json.contains("\"pid\":99"));
682 assert!(json.contains("AF_PACKET"));
683 let dbg = format!("{cloned:?}");
684 assert!(dbg.contains("sniffer"));
685 }
686
687 #[test]
690 fn walk_raw_sockets_all_fd_slots_null_returns_empty() {
691 use memf_core::test_builders::flags as ptf;
692
693 let tasks_offset: u64 = 0x10;
694 let files_offset: u64 = 0x30;
695
696 let sym_vaddr: u64 = 0xFFFF_8800_0096_0000;
697 let sym_paddr: u64 = 0x0096_0000;
698
699 let files_vaddr: u64 = 0xFFFF_8800_0097_0000;
700 let files_paddr: u64 = 0x0097_0000;
701
702 let fdt_vaddr: u64 = 0xFFFF_8800_0098_0000;
703 let fdt_paddr: u64 = 0x0098_0000;
704
705 let fd_array_vaddr: u64 = 0xFFFF_8800_0099_0000;
706 let fd_array_paddr: u64 = 0x0099_0000;
707
708 let isf = IsfBuilder::new()
709 .add_symbol("init_task", sym_vaddr)
710 .add_struct("list_head", 0x10)
711 .add_field("list_head", "next", 0x00, "pointer")
712 .add_struct("task_struct", 0x400)
713 .add_field("task_struct", "tasks", tasks_offset, "pointer")
714 .add_field("task_struct", "pid", 0x00, "unsigned int")
715 .add_field("task_struct", "comm", 0x20, "char")
716 .add_field("task_struct", "files", files_offset, "pointer")
717 .add_struct("files_struct", 0x100)
718 .add_field("files_struct", "fdt", 0x00, "pointer")
719 .add_struct("fdtable", 0x40)
720 .add_field("fdtable", "fd", 0x00, "pointer")
721 .build_json();
722 let resolver = IsfResolver::from_value(&isf).unwrap();
723
724 let mut task_page = [0u8; 4096];
725 let self_ptr = sym_vaddr + tasks_offset;
726 task_page[tasks_offset as usize..tasks_offset as usize + 8]
727 .copy_from_slice(&self_ptr.to_le_bytes());
728 task_page[files_offset as usize..files_offset as usize + 8]
729 .copy_from_slice(&files_vaddr.to_le_bytes());
730
731 let mut files_page = [0u8; 4096];
732 files_page[0..8].copy_from_slice(&fdt_vaddr.to_le_bytes());
733
734 let mut fdt_page = [0u8; 4096];
735 fdt_page[0..8].copy_from_slice(&fd_array_vaddr.to_le_bytes());
736
737 let fd_array_page = [0u8; 4096];
739
740 let (cr3, mem) = PageTableBuilder::new()
741 .map_4k(sym_vaddr, sym_paddr, ptf::WRITABLE)
742 .write_phys(sym_paddr, &task_page)
743 .map_4k(files_vaddr, files_paddr, ptf::WRITABLE)
744 .write_phys(files_paddr, &files_page)
745 .map_4k(fdt_vaddr, fdt_paddr, ptf::WRITABLE)
746 .write_phys(fdt_paddr, &fdt_page)
747 .map_4k(fd_array_vaddr, fd_array_paddr, ptf::WRITABLE)
748 .write_phys(fd_array_paddr, &fd_array_page)
749 .build();
750
751 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
752 let reader: ObjectReader<SyntheticPhysMem> = ObjectReader::new(vas, Box::new(resolver));
753
754 let result = walk_raw_sockets(&reader).expect("should not error");
755 assert!(result.is_empty(), "all-zero fd slots → no raw sockets");
756 }
757
758 #[test]
763 fn walk_raw_sockets_af_packet_sock_detected() {
764 use memf_core::test_builders::flags as ptf;
765
766 let tasks_offset: u64 = 0x10;
767 let files_offset: u64 = 0x30;
768
769 let sym_vaddr: u64 = 0xFFFF_8800_009A_0000;
770 let sym_paddr: u64 = 0x009A_0000;
771
772 let files_vaddr: u64 = 0xFFFF_8800_009B_0000;
773 let files_paddr: u64 = 0x009B_0000;
774
775 let fdt_vaddr: u64 = 0xFFFF_8800_009C_0000;
776 let fdt_paddr: u64 = 0x009C_0000;
777
778 let fd_array_vaddr: u64 = 0xFFFF_8800_009D_0000;
779 let fd_array_paddr: u64 = 0x009D_0000;
780
781 let file_vaddr: u64 = 0xFFFF_8800_009E_0000;
783 let file_paddr: u64 = 0x009E_0000;
784
785 let sock_vaddr: u64 = 0xFFFF_8800_009F_0000;
787 let sock_paddr: u64 = 0x009F_0000;
788
789 let sk_vaddr: u64 = 0xFFFF_8800_00C0_0000;
791 let sk_paddr: u64 = 0x00C0_0000;
792
793 let isf = IsfBuilder::new()
794 .add_symbol("init_task", sym_vaddr)
795 .add_struct("list_head", 0x10)
796 .add_field("list_head", "next", 0x00, "pointer")
797 .add_struct("task_struct", 0x400)
798 .add_field("task_struct", "tasks", tasks_offset, "pointer")
799 .add_field("task_struct", "pid", 0x00, "unsigned int")
800 .add_field("task_struct", "comm", 0x20, "char")
801 .add_field("task_struct", "files", files_offset, "pointer")
802 .add_struct("files_struct", 0x100)
803 .add_field("files_struct", "fdt", 0x00, "pointer")
804 .add_struct("fdtable", 0x40)
805 .add_field("fdtable", "fd", 0x00, "pointer")
806 .add_struct("file", 0x100)
807 .add_field("file", "private_data", 0x00, "pointer")
808 .add_struct("socket", 0x80)
809 .add_field("socket", "type", 0x00, "unsigned short")
810 .add_field("socket", "sk", 0x08, "pointer")
811 .add_struct("sock", 0x100)
812 .add_field("sock", "sk_family", 0x00, "unsigned short")
813 .add_field("sock", "sk_protocol", 0x02, "unsigned short")
814 .build_json();
816 let resolver = IsfResolver::from_value(&isf).unwrap();
817
818 let mut task_page = [0u8; 4096];
820 let self_ptr = sym_vaddr + tasks_offset;
821 task_page[tasks_offset as usize..tasks_offset as usize + 8]
822 .copy_from_slice(&self_ptr.to_le_bytes());
823 task_page[0x20..0x28].copy_from_slice(b"sniffer\0");
824 task_page[0x00..0x04].copy_from_slice(&200u32.to_le_bytes()); task_page[files_offset as usize..files_offset as usize + 8]
826 .copy_from_slice(&files_vaddr.to_le_bytes());
827
828 let mut files_page = [0u8; 4096];
830 files_page[0..8].copy_from_slice(&fdt_vaddr.to_le_bytes());
831
832 let mut fdt_page = [0u8; 4096];
834 fdt_page[0..8].copy_from_slice(&fd_array_vaddr.to_le_bytes());
835
836 let mut fd_array_page = [0u8; 4096];
838 fd_array_page[0..8].copy_from_slice(&file_vaddr.to_le_bytes());
839 let mut file_page = [0u8; 4096];
844 file_page[0..8].copy_from_slice(&sock_vaddr.to_le_bytes());
845
846 let mut socket_page = [0u8; 4096];
848 socket_page[0..2].copy_from_slice(&3u16.to_le_bytes()); socket_page[8..16].copy_from_slice(&sk_vaddr.to_le_bytes());
850
851 let mut sk_page = [0u8; 4096];
853 sk_page[0..2].copy_from_slice(&17u16.to_le_bytes()); sk_page[2..4].copy_from_slice(&0x0300u16.to_le_bytes()); let (cr3, mem) = PageTableBuilder::new()
857 .map_4k(sym_vaddr, sym_paddr, ptf::WRITABLE)
858 .write_phys(sym_paddr, &task_page)
859 .map_4k(files_vaddr, files_paddr, ptf::WRITABLE)
860 .write_phys(files_paddr, &files_page)
861 .map_4k(fdt_vaddr, fdt_paddr, ptf::WRITABLE)
862 .write_phys(fdt_paddr, &fdt_page)
863 .map_4k(fd_array_vaddr, fd_array_paddr, ptf::WRITABLE)
864 .write_phys(fd_array_paddr, &fd_array_page)
865 .map_4k(file_vaddr, file_paddr, ptf::WRITABLE)
866 .write_phys(file_paddr, &file_page)
867 .map_4k(sock_vaddr, sock_paddr, ptf::WRITABLE)
868 .write_phys(sock_paddr, &socket_page)
869 .map_4k(sk_vaddr, sk_paddr, ptf::WRITABLE)
870 .write_phys(sk_paddr, &sk_page)
871 .build();
872
873 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
874 let reader: ObjectReader<SyntheticPhysMem> = ObjectReader::new(vas, Box::new(resolver));
875
876 let result = walk_raw_sockets(&reader).expect("should not error");
877 assert_eq!(result.len(), 1, "one AF_PACKET socket should be detected");
878 assert_eq!(result[0].socket_type, "AF_PACKET");
879 assert_eq!(result[0].pid, 200);
880 assert!(
881 !result[0].is_promiscuous,
882 "promisc false when packet_sock missing"
883 );
884 }
885
886 #[test]
888 fn walk_raw_sockets_sk_ptr_null_no_entry() {
889 use memf_core::test_builders::flags as ptf;
890
891 let tasks_offset: u64 = 0x10;
892 let files_offset: u64 = 0x30;
893 let sym_vaddr: u64 = 0xFFFF_8800_00D0_0000;
894 let sym_paddr: u64 = 0x00D0_0000;
895 let files_vaddr: u64 = 0xFFFF_8800_00D1_0000;
896 let files_paddr: u64 = 0x00D1_0000;
897 let fdt_vaddr: u64 = 0xFFFF_8800_00D2_0000;
898 let fdt_paddr: u64 = 0x00D2_0000;
899 let fd_array_vaddr: u64 = 0xFFFF_8800_00D3_0000;
900 let fd_array_paddr: u64 = 0x00D3_0000;
901 let file_vaddr: u64 = 0xFFFF_8800_00D4_0000;
902 let file_paddr: u64 = 0x00D4_0000;
903 let sock_vaddr: u64 = 0xFFFF_8800_00D5_0000;
905 let sock_paddr: u64 = 0x00D5_0000;
906
907 let isf = IsfBuilder::new()
908 .add_symbol("init_task", sym_vaddr)
909 .add_struct("list_head", 0x10)
910 .add_field("list_head", "next", 0x00, "pointer")
911 .add_struct("task_struct", 0x400)
912 .add_field("task_struct", "tasks", tasks_offset, "pointer")
913 .add_field("task_struct", "pid", 0x00, "unsigned int")
914 .add_field("task_struct", "comm", 0x20, "char")
915 .add_field("task_struct", "files", files_offset, "pointer")
916 .add_struct("files_struct", 0x100)
917 .add_field("files_struct", "fdt", 0x00, "pointer")
918 .add_struct("fdtable", 0x40)
919 .add_field("fdtable", "fd", 0x00, "pointer")
920 .add_struct("file", 0x100)
921 .add_field("file", "private_data", 0x00, "pointer")
922 .add_struct("socket", 0x80)
923 .add_field("socket", "type", 0x00, "unsigned short")
924 .add_field("socket", "sk", 0x08, "pointer")
925 .add_struct("sock", 0x100)
926 .add_field("sock", "sk_family", 0x00, "unsigned short")
927 .add_field("sock", "sk_protocol", 0x02, "unsigned short")
928 .build_json();
929 let resolver = IsfResolver::from_value(&isf).unwrap();
930
931 let mut task_page = [0u8; 4096];
932 let self_ptr = sym_vaddr + tasks_offset;
933 task_page[tasks_offset as usize..tasks_offset as usize + 8]
934 .copy_from_slice(&self_ptr.to_le_bytes());
935 task_page[files_offset as usize..files_offset as usize + 8]
936 .copy_from_slice(&files_vaddr.to_le_bytes());
937
938 let mut files_page = [0u8; 4096];
939 files_page[0..8].copy_from_slice(&fdt_vaddr.to_le_bytes());
940
941 let mut fdt_page = [0u8; 4096];
942 fdt_page[0..8].copy_from_slice(&fd_array_vaddr.to_le_bytes());
943
944 let mut fd_array_page = [0u8; 4096];
945 fd_array_page[0..8].copy_from_slice(&file_vaddr.to_le_bytes());
946
947 let mut file_page = [0u8; 4096];
948 file_page[0..8].copy_from_slice(&sock_vaddr.to_le_bytes()); let mut socket_page = [0u8; 4096];
952 socket_page[0..2].copy_from_slice(&3u16.to_le_bytes()); socket_page[8..16].copy_from_slice(&0u64.to_le_bytes()); let (cr3, mem) = PageTableBuilder::new()
956 .map_4k(sym_vaddr, sym_paddr, ptf::WRITABLE)
957 .write_phys(sym_paddr, &task_page)
958 .map_4k(files_vaddr, files_paddr, ptf::WRITABLE)
959 .write_phys(files_paddr, &files_page)
960 .map_4k(fdt_vaddr, fdt_paddr, ptf::WRITABLE)
961 .write_phys(fdt_paddr, &fdt_page)
962 .map_4k(fd_array_vaddr, fd_array_paddr, ptf::WRITABLE)
963 .write_phys(fd_array_paddr, &fd_array_page)
964 .map_4k(file_vaddr, file_paddr, ptf::WRITABLE)
965 .write_phys(file_paddr, &file_page)
966 .map_4k(sock_vaddr, sock_paddr, ptf::WRITABLE)
967 .write_phys(sock_paddr, &socket_page)
968 .build();
969
970 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
971 let reader: ObjectReader<SyntheticPhysMem> = ObjectReader::new(vas, Box::new(resolver));
972
973 let result = walk_raw_sockets(&reader).expect("should not error");
974 assert!(
975 result.is_empty(),
976 "sk_ptr == 0 → returns None → no raw socket entry"
977 );
978 }
979
980 #[test]
983 fn walk_raw_sockets_sock_raw_family_detected() {
984 use memf_core::test_builders::flags as ptf;
985
986 let tasks_offset: u64 = 0x10;
987 let files_offset: u64 = 0x30;
988 let sym_vaddr: u64 = 0xFFFF_8800_00E0_0000;
989 let sym_paddr: u64 = 0x00E0_0000;
990 let files_vaddr: u64 = 0xFFFF_8800_00E1_0000;
991 let files_paddr: u64 = 0x00E1_0000;
992 let fdt_vaddr: u64 = 0xFFFF_8800_00E2_0000;
993 let fdt_paddr: u64 = 0x00E2_0000;
994 let fd_array_vaddr: u64 = 0xFFFF_8800_00E3_0000;
995 let fd_array_paddr: u64 = 0x00E3_0000;
996 let file_vaddr: u64 = 0xFFFF_8800_00E4_0000;
997 let file_paddr: u64 = 0x00E4_0000;
998 let sock_vaddr: u64 = 0xFFFF_8800_00E5_0000;
999 let sock_paddr: u64 = 0x00E5_0000;
1000 let sk_vaddr: u64 = 0xFFFF_8800_00E6_0000;
1001 let sk_paddr: u64 = 0x00E6_0000;
1002
1003 let isf = IsfBuilder::new()
1004 .add_symbol("init_task", sym_vaddr)
1005 .add_struct("list_head", 0x10)
1006 .add_field("list_head", "next", 0x00, "pointer")
1007 .add_struct("task_struct", 0x400)
1008 .add_field("task_struct", "tasks", tasks_offset, "pointer")
1009 .add_field("task_struct", "pid", 0x00, "unsigned int")
1010 .add_field("task_struct", "comm", 0x20, "char")
1011 .add_field("task_struct", "files", files_offset, "pointer")
1012 .add_struct("files_struct", 0x100)
1013 .add_field("files_struct", "fdt", 0x00, "pointer")
1014 .add_struct("fdtable", 0x40)
1015 .add_field("fdtable", "fd", 0x00, "pointer")
1016 .add_struct("file", 0x100)
1017 .add_field("file", "private_data", 0x00, "pointer")
1018 .add_struct("socket", 0x80)
1019 .add_field("socket", "type", 0x00, "unsigned short")
1020 .add_field("socket", "sk", 0x08, "pointer")
1021 .add_struct("sock", 0x100)
1022 .add_field("sock", "sk_family", 0x00, "unsigned short")
1023 .add_field("sock", "sk_protocol", 0x02, "unsigned short")
1024 .build_json();
1025 let resolver = IsfResolver::from_value(&isf).unwrap();
1026
1027 let mut task_page = [0u8; 4096];
1028 let self_ptr = sym_vaddr + tasks_offset;
1029 task_page[tasks_offset as usize..tasks_offset as usize + 8]
1030 .copy_from_slice(&self_ptr.to_le_bytes());
1031 task_page[0x20..0x28].copy_from_slice(b"implant\0");
1032 task_page[0x00..0x04].copy_from_slice(&300u32.to_le_bytes());
1033 task_page[files_offset as usize..files_offset as usize + 8]
1034 .copy_from_slice(&files_vaddr.to_le_bytes());
1035
1036 let mut files_page = [0u8; 4096];
1037 files_page[0..8].copy_from_slice(&fdt_vaddr.to_le_bytes());
1038
1039 let mut fdt_page = [0u8; 4096];
1040 fdt_page[0..8].copy_from_slice(&fd_array_vaddr.to_le_bytes());
1041
1042 let mut fd_array_page = [0u8; 4096];
1043 fd_array_page[0..8].copy_from_slice(&file_vaddr.to_le_bytes());
1044
1045 let mut file_page = [0u8; 4096];
1046 file_page[0..8].copy_from_slice(&sock_vaddr.to_le_bytes());
1047
1048 let mut socket_page = [0u8; 4096];
1050 socket_page[0..2].copy_from_slice(&3u16.to_le_bytes()); socket_page[8..16].copy_from_slice(&sk_vaddr.to_le_bytes());
1052
1053 let mut sk_page = [0u8; 4096];
1055 sk_page[0..2].copy_from_slice(&2u16.to_le_bytes()); sk_page[2..4].copy_from_slice(&255u16.to_le_bytes()); let (cr3, mem) = PageTableBuilder::new()
1059 .map_4k(sym_vaddr, sym_paddr, ptf::WRITABLE)
1060 .write_phys(sym_paddr, &task_page)
1061 .map_4k(files_vaddr, files_paddr, ptf::WRITABLE)
1062 .write_phys(files_paddr, &files_page)
1063 .map_4k(fdt_vaddr, fdt_paddr, ptf::WRITABLE)
1064 .write_phys(fdt_paddr, &fdt_page)
1065 .map_4k(fd_array_vaddr, fd_array_paddr, ptf::WRITABLE)
1066 .write_phys(fd_array_paddr, &fd_array_page)
1067 .map_4k(file_vaddr, file_paddr, ptf::WRITABLE)
1068 .write_phys(file_paddr, &file_page)
1069 .map_4k(sock_vaddr, sock_paddr, ptf::WRITABLE)
1070 .write_phys(sock_paddr, &socket_page)
1071 .map_4k(sk_vaddr, sk_paddr, ptf::WRITABLE)
1072 .write_phys(sk_paddr, &sk_page)
1073 .build();
1074
1075 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
1076 let reader: ObjectReader<SyntheticPhysMem> = ObjectReader::new(vas, Box::new(resolver));
1077
1078 let result = walk_raw_sockets(&reader).expect("should not error");
1079 assert_eq!(result.len(), 1, "SOCK_RAW socket should be detected");
1080 assert_eq!(result[0].socket_type, "SOCK_RAW");
1081 assert_eq!(result[0].pid, 300);
1082 assert!(
1083 result[0].is_suspicious,
1084 "unknown comm + SOCK_RAW → suspicious"
1085 );
1086 }
1087
1088 #[test]
1091 fn walk_raw_sockets_not_raw_socket_returns_none() {
1092 use memf_core::test_builders::flags as ptf;
1093
1094 let tasks_offset: u64 = 0x10;
1095 let files_offset: u64 = 0x30;
1096 let sym_vaddr: u64 = 0xFFFF_8800_00F0_0000;
1097 let sym_paddr: u64 = 0x00F0_0000;
1098 let files_vaddr: u64 = 0xFFFF_8800_00F1_0000;
1099 let files_paddr: u64 = 0x00F1_0000;
1100 let fdt_vaddr: u64 = 0xFFFF_8800_00F2_0000;
1101 let fdt_paddr: u64 = 0x00F2_0000;
1102 let fd_array_vaddr: u64 = 0xFFFF_8800_00F3_0000;
1103 let fd_array_paddr: u64 = 0x00F3_0000;
1104 let file_vaddr: u64 = 0xFFFF_8800_00F4_0000;
1105 let file_paddr: u64 = 0x00F4_0000;
1106 let sock_vaddr: u64 = 0xFFFF_8800_00F5_0000;
1107 let sock_paddr: u64 = 0x00F5_0000;
1108 let sk_vaddr: u64 = 0xFFFF_8800_00F6_0000;
1109 let sk_paddr: u64 = 0x00F6_0000;
1110
1111 let isf = IsfBuilder::new()
1112 .add_symbol("init_task", sym_vaddr)
1113 .add_struct("list_head", 0x10)
1114 .add_field("list_head", "next", 0x00, "pointer")
1115 .add_struct("task_struct", 0x400)
1116 .add_field("task_struct", "tasks", tasks_offset, "pointer")
1117 .add_field("task_struct", "pid", 0x00, "unsigned int")
1118 .add_field("task_struct", "comm", 0x20, "char")
1119 .add_field("task_struct", "files", files_offset, "pointer")
1120 .add_struct("files_struct", 0x100)
1121 .add_field("files_struct", "fdt", 0x00, "pointer")
1122 .add_struct("fdtable", 0x40)
1123 .add_field("fdtable", "fd", 0x00, "pointer")
1124 .add_struct("file", 0x100)
1125 .add_field("file", "private_data", 0x00, "pointer")
1126 .add_struct("socket", 0x80)
1127 .add_field("socket", "type", 0x00, "unsigned short")
1128 .add_field("socket", "sk", 0x08, "pointer")
1129 .add_struct("sock", 0x100)
1130 .add_field("sock", "sk_family", 0x00, "unsigned short")
1131 .add_field("sock", "sk_protocol", 0x02, "unsigned short")
1132 .build_json();
1133 let resolver = IsfResolver::from_value(&isf).unwrap();
1134
1135 let mut task_page = [0u8; 4096];
1136 let self_ptr = sym_vaddr + tasks_offset;
1137 task_page[tasks_offset as usize..tasks_offset as usize + 8]
1138 .copy_from_slice(&self_ptr.to_le_bytes());
1139 task_page[files_offset as usize..files_offset as usize + 8]
1140 .copy_from_slice(&files_vaddr.to_le_bytes());
1141
1142 let mut files_page = [0u8; 4096];
1143 files_page[0..8].copy_from_slice(&fdt_vaddr.to_le_bytes());
1144
1145 let mut fdt_page = [0u8; 4096];
1146 fdt_page[0..8].copy_from_slice(&fd_array_vaddr.to_le_bytes());
1147
1148 let mut fd_array_page = [0u8; 4096];
1149 fd_array_page[0..8].copy_from_slice(&file_vaddr.to_le_bytes());
1150
1151 let mut file_page = [0u8; 4096];
1152 file_page[0..8].copy_from_slice(&sock_vaddr.to_le_bytes());
1153
1154 let mut socket_page = [0u8; 4096];
1156 socket_page[0..2].copy_from_slice(&1u16.to_le_bytes()); socket_page[8..16].copy_from_slice(&sk_vaddr.to_le_bytes());
1158
1159 let mut sk_page = [0u8; 4096];
1161 sk_page[0..2].copy_from_slice(&2u16.to_le_bytes()); let (cr3, mem) = PageTableBuilder::new()
1164 .map_4k(sym_vaddr, sym_paddr, ptf::WRITABLE)
1165 .write_phys(sym_paddr, &task_page)
1166 .map_4k(files_vaddr, files_paddr, ptf::WRITABLE)
1167 .write_phys(files_paddr, &files_page)
1168 .map_4k(fdt_vaddr, fdt_paddr, ptf::WRITABLE)
1169 .write_phys(fdt_paddr, &fdt_page)
1170 .map_4k(fd_array_vaddr, fd_array_paddr, ptf::WRITABLE)
1171 .write_phys(fd_array_paddr, &fd_array_page)
1172 .map_4k(file_vaddr, file_paddr, ptf::WRITABLE)
1173 .write_phys(file_paddr, &file_page)
1174 .map_4k(sock_vaddr, sock_paddr, ptf::WRITABLE)
1175 .write_phys(sock_paddr, &socket_page)
1176 .map_4k(sk_vaddr, sk_paddr, ptf::WRITABLE)
1177 .write_phys(sk_paddr, &sk_page)
1178 .build();
1179
1180 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
1181 let reader: ObjectReader<SyntheticPhysMem> = ObjectReader::new(vas, Box::new(resolver));
1182
1183 let result = walk_raw_sockets(&reader).expect("should not error");
1184 assert!(
1185 result.is_empty(),
1186 "SOCK_STREAM is not a raw socket → None → empty"
1187 );
1188 }
1189
1190 #[test]
1192 fn walk_raw_sockets_private_data_null_no_entry() {
1193 use memf_core::test_builders::flags as ptf;
1194
1195 let tasks_offset: u64 = 0x10;
1196 let files_offset: u64 = 0x30;
1197 let sym_vaddr: u64 = 0xFFFF_8800_00C1_0000;
1198 let sym_paddr: u64 = 0x00C1_0000;
1199 let files_vaddr: u64 = 0xFFFF_8800_00C2_0000;
1200 let files_paddr: u64 = 0x00C2_0000;
1201 let fdt_vaddr: u64 = 0xFFFF_8800_00C3_0000;
1202 let fdt_paddr: u64 = 0x00C3_0000;
1203 let fd_array_vaddr: u64 = 0xFFFF_8800_00C4_0000;
1204 let fd_array_paddr: u64 = 0x00C4_0000;
1205 let file_vaddr: u64 = 0xFFFF_8800_00C5_0000;
1206 let file_paddr: u64 = 0x00C5_0000;
1207
1208 let isf = IsfBuilder::new()
1209 .add_symbol("init_task", sym_vaddr)
1210 .add_struct("list_head", 0x10)
1211 .add_field("list_head", "next", 0x00, "pointer")
1212 .add_struct("task_struct", 0x400)
1213 .add_field("task_struct", "tasks", tasks_offset, "pointer")
1214 .add_field("task_struct", "pid", 0x00, "unsigned int")
1215 .add_field("task_struct", "comm", 0x20, "char")
1216 .add_field("task_struct", "files", files_offset, "pointer")
1217 .add_struct("files_struct", 0x100)
1218 .add_field("files_struct", "fdt", 0x00, "pointer")
1219 .add_struct("fdtable", 0x40)
1220 .add_field("fdtable", "fd", 0x00, "pointer")
1221 .add_struct("file", 0x100)
1222 .add_field("file", "private_data", 0x00, "pointer")
1223 .add_struct("socket", 0x80)
1224 .add_field("socket", "type", 0x00, "unsigned short")
1225 .add_field("socket", "sk", 0x08, "pointer")
1226 .add_struct("sock", 0x100)
1227 .add_field("sock", "sk_family", 0x00, "unsigned short")
1228 .add_field("sock", "sk_protocol", 0x02, "unsigned short")
1229 .build_json();
1230 let resolver = IsfResolver::from_value(&isf).unwrap();
1231
1232 let mut task_page = [0u8; 4096];
1233 let self_ptr = sym_vaddr + tasks_offset;
1234 task_page[tasks_offset as usize..tasks_offset as usize + 8]
1235 .copy_from_slice(&self_ptr.to_le_bytes());
1236 task_page[files_offset as usize..files_offset as usize + 8]
1237 .copy_from_slice(&files_vaddr.to_le_bytes());
1238
1239 let mut files_page = [0u8; 4096];
1240 files_page[0..8].copy_from_slice(&fdt_vaddr.to_le_bytes());
1241
1242 let mut fdt_page = [0u8; 4096];
1243 fdt_page[0..8].copy_from_slice(&fd_array_vaddr.to_le_bytes());
1244
1245 let mut fd_array_page = [0u8; 4096];
1246 fd_array_page[0..8].copy_from_slice(&file_vaddr.to_le_bytes());
1247
1248 let file_page = [0u8; 4096];
1250
1251 let (cr3, mem) = PageTableBuilder::new()
1252 .map_4k(sym_vaddr, sym_paddr, ptf::WRITABLE)
1253 .write_phys(sym_paddr, &task_page)
1254 .map_4k(files_vaddr, files_paddr, ptf::WRITABLE)
1255 .write_phys(files_paddr, &files_page)
1256 .map_4k(fdt_vaddr, fdt_paddr, ptf::WRITABLE)
1257 .write_phys(fdt_paddr, &fdt_page)
1258 .map_4k(fd_array_vaddr, fd_array_paddr, ptf::WRITABLE)
1259 .write_phys(fd_array_paddr, &fd_array_page)
1260 .map_4k(file_vaddr, file_paddr, ptf::WRITABLE)
1261 .write_phys(file_paddr, &file_page)
1262 .build();
1263
1264 let vas = VirtualAddressSpace::new(mem, cr3, TranslationMode::X86_64FourLevel);
1265 let reader: ObjectReader<SyntheticPhysMem> = ObjectReader::new(vas, Box::new(resolver));
1266
1267 let result = walk_raw_sockets(&reader).expect("should not error");
1268 assert!(result.is_empty(), "private_data=0 → no raw socket entry");
1269 }
1270}