1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use keyhog_core::{Chunk, ChunkMetadata};
/// Extract strings from specific binary sections (ELF .rodata/.data, PE .rdata/.data).
/// These sections are the most likely to contain embedded secrets.
pub(crate) fn extract_sections(bytes: &[u8], path: &str) -> Option<Vec<Chunk>> {
use goblin::Object;
let obj = match Object::parse(bytes) {
Ok(o) => o,
Err(_) => return None,
};
let mut chunks = Vec::new();
// High-value section names where secrets are commonly embedded
let target_sections = &[
".rodata",
".rdata",
".data",
".const",
".cstring",
"__cstring",
"__const",
"__data",
];
match obj {
Object::Elf(elf) => {
for sh in &elf.section_headers {
let name = elf.shdr_strtab.get_at(sh.sh_name).unwrap_or("");
if target_sections.contains(&name) {
let start = sh.sh_offset as usize;
let end = (start + sh.sh_size as usize).min(bytes.len());
if start < end {
let section_bytes = &bytes[start..end];
let strings = crate::binary::extract_printable_strings(
section_bytes,
crate::binary::MIN_STRING_LEN,
);
if !strings.is_empty() {
chunks.push(Chunk {
data: strings.join("\n"),
metadata: ChunkMetadata {
source_type: format!("binary:elf:{name}"),
path: Some(path.to_string()),
commit: None,
author: None,
date: None,
},
});
}
}
}
}
}
Object::PE(pe) => {
for section in &pe.sections {
let name = std::str::from_utf8(§ion.name)
.unwrap_or("")
.trim_end_matches('\0');
if target_sections.contains(&name) {
let start = section.pointer_to_raw_data as usize;
let end = (start + section.size_of_raw_data as usize).min(bytes.len());
if start < end {
let section_bytes = &bytes[start..end];
let strings = crate::binary::extract_printable_strings(
section_bytes,
crate::binary::MIN_STRING_LEN,
);
if !strings.is_empty() {
chunks.push(Chunk {
data: strings.join("\n"),
metadata: ChunkMetadata {
source_type: format!("binary:pe:{name}"),
path: Some(path.to_string()),
commit: None,
author: None,
date: None,
},
});
}
}
}
}
}
Object::Mach(goblin::mach::Mach::Binary(macho)) => {
for seg in &macho.segments {
for (section, _) in seg.sections().unwrap_or_default() {
let name = section.name().unwrap_or("");
if target_sections.contains(&name) {
let start = section.offset as usize;
let end = (start + section.size as usize).min(bytes.len());
if start < end {
let section_bytes = &bytes[start..end];
let strings = crate::binary::extract_printable_strings(
section_bytes,
crate::binary::MIN_STRING_LEN,
);
if !strings.is_empty() {
chunks.push(Chunk {
data: strings.join("\n"),
metadata: ChunkMetadata {
source_type: format!("binary:macho:{name}"),
path: Some(path.to_string()),
commit: None,
author: None,
date: None,
},
});
}
}
}
}
}
}
_ => {}
}
if chunks.is_empty() {
None
} else {
Some(chunks)
}
}