1use std::collections::BTreeMap;
9
10use serde::{Deserialize, Serialize};
11
12pub type Address = u64;
14
15#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
17pub struct ResultDocument {
18 pub analysis: Analysis,
19 pub metadata: Metadata,
20 pub strings: Strings,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
25#[non_exhaustive]
26pub enum StringEncoding {
27 #[serde(rename = "ASCII")]
28 Ascii,
29 #[serde(rename = "UTF-16LE")]
30 Utf16Le,
31 #[serde(rename = "UTF-8")]
32 Utf8,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
37#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
38#[non_exhaustive]
39pub enum AddressType {
40 Stack,
41 Global,
42 Heap,
43}
44
45#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
47#[derive(Eq)]
48pub struct StackString {
49 pub encoding: StringEncoding,
50 pub frame_offset: i64,
51 pub function: Address,
52 pub offset: i64,
53 pub original_stack_pointer: Address,
54 pub program_counter: Address,
55 pub stack_pointer: Address,
56 pub string: String,
57}
58
59#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
61#[derive(Eq)]
62pub struct DecodedString {
63 pub address: Address,
64 pub address_type: AddressType,
65 pub decoded_at: Address,
66 pub decoding_routine: Address,
67 pub encoding: StringEncoding,
68 pub string: String,
69}
70
71#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
73#[derive(Eq)]
74pub struct StaticString {
75 pub encoding: StringEncoding,
76 pub offset: Address,
77 pub string: String,
78}
79
80#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
82pub struct Runtime {
83 #[serde(default)]
84 pub decoded_strings: f64,
85 #[serde(default)]
86 pub find_features: f64,
87 #[serde(default)]
88 pub language_strings: f64,
89 #[serde(default)]
90 pub stack_strings: f64,
91 #[serde(default)]
92 pub start_date: String,
93 #[serde(default)]
94 pub static_strings: f64,
95 #[serde(default)]
96 pub tight_strings: f64,
97 #[serde(default)]
98 pub total: f64,
99 #[serde(default)]
100 pub vivisect: f64,
101}
102
103#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
105pub struct DecodingFunctionScore {
106 pub score: f64,
107 #[serde(deserialize_with = "deserialize_u64_from_number")]
108 pub xrefs_to: u64,
109}
110
111#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
113pub struct Functions {
114 #[serde(default)]
115 pub analyzed_decoded_strings: u64,
116 #[serde(default)]
117 pub analyzed_stack_strings: u64,
118 #[serde(default)]
119 pub analyzed_tight_strings: u64,
120 #[serde(default, deserialize_with = "deserialize_address_key_map")]
121 pub decoding_function_scores: BTreeMap<Address, DecodingFunctionScore>,
122 #[serde(default)]
123 pub discovered: u64,
124 #[serde(default)]
125 pub library: u64,
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
130#[serde(transparent)]
131pub struct Enabled(pub bool);
132
133impl Enabled {
134 #[must_use]
135 pub const fn get(self) -> bool {
136 self.0
137 }
138}
139
140impl From<Enabled> for bool {
141 fn from(value: Enabled) -> Self {
142 value.0
143 }
144}
145
146#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
148pub struct Analysis {
149 #[serde(default = "default_enabled")]
150 pub enable_decoded_strings: Enabled,
151 #[serde(default = "default_enabled")]
152 pub enable_stack_strings: Enabled,
153 #[serde(default = "default_enabled")]
154 pub enable_static_strings: Enabled,
155 #[serde(default = "default_enabled")]
156 pub enable_tight_strings: Enabled,
157 #[serde(default)]
158 pub functions: Functions,
159}
160
161#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
163pub struct Metadata {
164 pub file_path: String,
165 #[serde(default)]
166 pub imagebase: Address,
167 #[serde(default)]
168 pub language: String,
169 #[serde(default)]
170 pub language_selected: String,
171 #[serde(default)]
172 pub language_version: String,
173 #[serde(default)]
174 pub min_length: u64,
175 #[serde(default)]
176 pub runtime: Runtime,
177 #[serde(default)]
178 pub version: String,
179}
180
181#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
183#[derive(Eq)]
184pub struct Strings {
185 #[serde(default)]
186 pub decoded_strings: Vec<DecodedString>,
187 #[serde(default)]
188 pub language_strings: Vec<StaticString>,
189 #[serde(default)]
190 pub language_strings_missed: Vec<StaticString>,
191 #[serde(default)]
192 pub stack_strings: Vec<StackString>,
193 #[serde(default)]
194 pub static_strings: Vec<StaticString>,
195 #[serde(default)]
196 pub tight_strings: Vec<StackString>,
197}
198
199const fn default_enabled() -> Enabled {
200 Enabled(true)
201}
202
203fn deserialize_address_key_map<'de, D>(
204 deserializer: D,
205) -> Result<BTreeMap<Address, DecodingFunctionScore>, D::Error>
206where
207 D: serde::Deserializer<'de>,
208{
209 struct MapVisitor;
210
211 impl<'de> serde::de::Visitor<'de> for MapVisitor {
212 type Value = BTreeMap<Address, DecodingFunctionScore>;
213
214 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
215 formatter.write_str("包含十进制或 0x 十六进制地址 key 的 map")
216 }
217
218 fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
219 where
220 M: serde::de::MapAccess<'de>,
221 {
222 let mut map = BTreeMap::new();
223 while let Some((key, value)) =
224 access.next_entry::<AddressKey, DecodingFunctionScore>()?
225 {
226 map.insert(key.0, value);
227 }
228 Ok(map)
229 }
230 }
231
232 deserializer.deserialize_map(MapVisitor)
233}
234
235struct AddressKey(Address);
236
237impl<'de> Deserialize<'de> for AddressKey {
238 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
239 where
240 D: serde::Deserializer<'de>,
241 {
242 struct KeyVisitor;
243
244 impl serde::de::Visitor<'_> for KeyVisitor {
245 type Value = AddressKey;
246
247 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
248 formatter.write_str("十进制或 0x 十六进制地址")
249 }
250
251 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
252 where
253 E: serde::de::Error,
254 {
255 Ok(AddressKey(value))
256 }
257
258 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
259 where
260 E: serde::de::Error,
261 {
262 u64::try_from(value)
263 .map(AddressKey)
264 .map_err(|_error| E::custom("期望非负整数地址"))
265 }
266
267 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
268 where
269 E: serde::de::Error,
270 {
271 if value.is_finite() && value.fract() == 0.0 && value >= 0.0 {
272 value
273 .to_string()
274 .parse::<u64>()
275 .map(AddressKey)
276 .map_err(|_error| E::custom("期望非负整数地址"))
277 } else {
278 Err(E::custom("期望非负整数地址"))
279 }
280 }
281
282 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
283 where
284 E: serde::de::Error,
285 {
286 parse_address_key(value)
287 .map(AddressKey)
288 .map_err(E::custom)
289 }
290
291 fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
292 where
293 E: serde::de::Error,
294 {
295 self.visit_str(&value)
296 }
297 }
298
299 deserializer.deserialize_any(KeyVisitor)
300 }
301}
302
303fn parse_address_key(value: &str) -> Result<Address, &'static str> {
304 if let Some(hex) = value.strip_prefix("0x").or_else(|| value.strip_prefix("0X")) {
305 if hex.is_empty() {
306 return Err("十六进制地址不能为空");
307 }
308 u64::from_str_radix(hex, 16).map_err(|_error| "无效的十六进制地址")
309 } else if value.is_empty() {
310 Err("地址不能为空")
311 } else {
312 value.parse::<u64>().map_err(|_error| "无效的十进制地址")
313 }
314}
315
316fn deserialize_u64_from_number<'de, D>(deserializer: D) -> Result<u64, D::Error>
317where
318 D: serde::Deserializer<'de>,
319{
320 struct NumberVisitor;
321
322 impl serde::de::Visitor<'_> for NumberVisitor {
323 type Value = u64;
324
325 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
326 formatter.write_str("a non-negative integer")
327 }
328
329 fn visit_u64<E>(self, value: u64) -> Result<u64, E>
330 where
331 E: serde::de::Error,
332 {
333 Ok(value)
334 }
335
336 fn visit_i64<E>(self, value: i64) -> Result<u64, E>
337 where
338 E: serde::de::Error,
339 {
340 u64::try_from(value).map_err(|_error| E::custom("expected a non-negative integer"))
341 }
342
343 fn visit_f64<E>(self, value: f64) -> Result<u64, E>
344 where
345 E: serde::de::Error,
346 {
347 if value.is_finite() && value.fract() == 0.0 && value >= 0.0 {
348 value
349 .to_string()
350 .parse::<u64>()
351 .map_err(|_error| E::custom("expected a non-negative integer"))
352 } else {
353 Err(E::custom("expected a non-negative integer"))
354 }
355 }
356
357 fn visit_str<E>(self, value: &str) -> Result<u64, E>
358 where
359 E: serde::de::Error,
360 {
361 value
362 .parse::<u64>()
363 .map_err(|_error| E::custom("expected a non-negative integer"))
364 }
365 }
366
367 deserializer.deserialize_any(NumberVisitor)
368}
369
370#[cfg(test)]
371mod tests {
372 use super::*;
373
374 #[test]
375 fn decoding_function_scores_accepts_hex_keys() {
376 let json = r#"{"analysis":{"functions":{"decoding_function_scores":{"0x1000":{"score":1.5,"xrefs_to":2}}}},"metadata":{"file_path":"sample.bin"},"strings":{}}"#;
377 let doc: ResultDocument = serde_json::from_str(json).expect("解析 JSON 失败");
378 let mut expected = BTreeMap::new();
379 expected.insert(
380 0x1000,
381 DecodingFunctionScore {
382 score: 1.5,
383 xrefs_to: 2,
384 },
385 );
386 assert_eq!(doc.analysis.functions.decoding_function_scores, expected);
387 }
388
389 #[test]
390 fn decoding_function_scores_accepts_decimal_keys() {
391 let json = r#"{"analysis":{"functions":{"decoding_function_scores":{"4096":{"score":1.5,"xrefs_to":2}}}},"metadata":{"file_path":"sample.bin"},"strings":{}}"#;
392 let doc: ResultDocument = serde_json::from_str(json).expect("解析 JSON 失败");
393 assert!(doc
394 .analysis
395 .functions
396 .decoding_function_scores
397 .contains_key(&4096));
398 }
399}