forensics.c (14022B)
1 /* 2 This file is part of libextractor. 3 Copyright (C) 2026 Vidyut Samanta and Christian Grothoff 4 5 libextractor is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 libextractor is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libextractor; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 /** 21 * @file plugins/forensics.c 22 * @brief helpers shared by the plugins that read provenance out of 23 * binary headers 24 * @author Christian Grothoff 25 */ 26 #include "platform.h" 27 #include "forensics.h" 28 29 #include <math.h> 30 #include <stdarg.h> 31 32 33 int 34 EXTRACTOR_forensic_read_ (struct EXTRACTOR_ExtractContext *ec, 35 int64_t offset, 36 void *buf, 37 size_t len) 38 { 39 unsigned char *out = buf; 40 size_t got = 0; 41 42 if (0 == len) 43 return 1; 44 if ( (0 <= offset) && 45 (offset != ec->seek (ec->cls, 46 offset, 47 SEEK_SET)) ) 48 return 0; 49 while (got < len) 50 { 51 void *data; 52 ssize_t ret; 53 54 ret = ec->read (ec->cls, 55 &data, 56 len - got); 57 if (0 >= ret) 58 return 0; 59 if (((size_t) ret) > len - got) 60 return 0; /* the IPC layer is misbehaving; do not overrun */ 61 memcpy (&out[got], 62 data, 63 (size_t) ret); 64 got += (size_t) ret; 65 } 66 return 1; 67 } 68 69 70 int 71 EXTRACTOR_forensic_utf8_valid_ (const char *data, 72 size_t len) 73 { 74 const unsigned char *p = (const unsigned char *) data; 75 size_t i = 0; 76 77 while (i < len) 78 { 79 unsigned int extra; 80 uint32_t cp; 81 82 if (p[i] < 0x80) 83 { 84 i++; 85 continue; 86 } 87 if ((0xC2 <= p[i]) && (p[i] <= 0xDF)) 88 { 89 extra = 1; 90 cp = p[i] & 0x1F; 91 } 92 else if ((0xE0 <= p[i]) && (p[i] <= 0xEF)) 93 { 94 extra = 2; 95 cp = p[i] & 0x0F; 96 } 97 else if ((0xF0 <= p[i]) && (p[i] <= 0xF4)) 98 { 99 extra = 3; 100 cp = p[i] & 0x07; 101 } 102 else 103 { 104 return 0; /* continuation byte or overlong lead */ 105 } 106 if (i + extra >= len) 107 return 0; /* truncated multi-byte sequence */ 108 for (unsigned int k = 1; k <= extra; k++) 109 { 110 if (0x80 != (p[i + k] & 0xC0)) 111 return 0; 112 cp = (cp << 6) | (p[i + k] & 0x3F); 113 } 114 /* reject overlong encodings, surrogates and out-of-range values */ 115 if ( (1 == extra) && (cp < 0x80) ) 116 return 0; 117 if ( (2 == extra) && (cp < 0x800) ) 118 return 0; 119 if ( (3 == extra) && (cp < 0x10000) ) 120 return 0; 121 if ( (0xD800 <= cp) && (cp <= 0xDFFF) ) 122 return 0; 123 if (cp > 0x10FFFF) 124 return 0; 125 i += extra + 1; 126 } 127 return 1; 128 } 129 130 131 size_t 132 EXTRACTOR_forensic_trim_ (const char *data, 133 size_t len) 134 { 135 while ( (0 < len) && 136 ( ('\0' == data[len - 1]) || 137 (' ' == data[len - 1]) || 138 ('\t' == data[len - 1]) || 139 ('\r' == data[len - 1]) || 140 ('\n' == data[len - 1]) ) ) 141 len--; 142 return len; 143 } 144 145 146 int 147 EXTRACTOR_forensic_parse_octal_ (const char *data, 148 size_t len, 149 uint64_t *value) 150 { 151 uint64_t v = 0; 152 size_t i = 0; 153 int digits = 0; 154 155 while ( (i < len) && 156 (' ' == data[i]) ) 157 i++; 158 while ( (i < len) && 159 ('0' <= data[i]) && 160 ('7' >= data[i]) ) 161 { 162 if (v > (UINT64_MAX >> 3)) 163 return 0; /* would overflow */ 164 v = (v << 3) | (uint64_t) (data[i] - '0'); 165 digits++; 166 i++; 167 } 168 if (0 == digits) 169 return 0; 170 /* what follows must be padding, not more number */ 171 while (i < len) 172 { 173 if ( ('\0' != data[i]) && 174 (' ' != data[i]) ) 175 return 0; 176 i++; 177 } 178 *value = v; 179 return 1; 180 } 181 182 183 double 184 EXTRACTOR_forensic_entropy_ (const unsigned char *data, 185 size_t len) 186 { 187 unsigned long counts[256]; 188 double entropy = 0.0; 189 190 if (0 == len) 191 return 0.0; 192 memset (counts, 193 0, 194 sizeof (counts)); 195 for (size_t i = 0; i < len; i++) 196 counts[data[i]]++; 197 for (unsigned int i = 0; i < 256; i++) 198 { 199 double p; 200 201 if (0 == counts[i]) 202 continue; 203 p = ((double) counts[i]) / ((double) len); 204 entropy -= p * log2 (p); 205 } 206 return entropy; 207 } 208 209 210 int 211 EXTRACTOR_forensic_emit_ (struct EXTRACTOR_ExtractContext *ec, 212 const char *plugin, 213 enum EXTRACTOR_MetaType type, 214 const char *fmt, 215 ...) 216 { 217 char buf[EXTRACTOR_FORENSIC_MAX_STRING]; 218 va_list ap; 219 int len; 220 221 va_start (ap, fmt); 222 len = vsnprintf (buf, 223 sizeof (buf), 224 fmt, 225 ap); 226 va_end (ap); 227 if (0 >= len) 228 return 0; 229 if (((size_t) len) >= sizeof (buf)) 230 len = sizeof (buf) - 1; 231 return (0 != ec->proc (ec->cls, 232 plugin, 233 type, 234 EXTRACTOR_METAFORMAT_UTF8, 235 "text/plain", 236 buf, 237 (size_t) len + 1)) ? 1 : 0; 238 } 239 240 241 int 242 EXTRACTOR_forensic_emit_text_ (struct EXTRACTOR_ExtractContext *ec, 243 const char *plugin, 244 enum EXTRACTOR_MetaType type, 245 const char *data, 246 size_t len) 247 { 248 char buf[EXTRACTOR_FORENSIC_MAX_STRING]; 249 size_t out = 0; 250 251 /* a NUL inside the field ends the string: these are C strings in 252 fixed-width slots far more often than they are counted strings */ 253 for (size_t i = 0; i < len; i++) 254 if ('\0' == data[i]) 255 { 256 len = i; 257 break; 258 } 259 len = EXTRACTOR_forensic_trim_ (data, 260 len); 261 if (0 == len) 262 return 0; 263 if (len > sizeof (buf) - 1) 264 len = sizeof (buf) - 1; 265 for (size_t i = 0; i < len; i++) 266 { 267 unsigned char c = (unsigned char) data[i]; 268 269 /* Collapse control characters rather than dropping the value: a 270 path with a stray tab in it is still the path we want to show, 271 but it must not be able to forge line structure in the output. */ 272 if ( (c < 0x20) || (0x7F == c) ) 273 buf[out++] = ' '; 274 else 275 buf[out++] = (char) c; 276 } 277 out = EXTRACTOR_forensic_trim_ (buf, 278 out); 279 if (0 == out) 280 return 0; 281 buf[out] = '\0'; 282 if (! EXTRACTOR_forensic_utf8_valid_ (buf, 283 out)) 284 return 0; /* not text we can hand out; the caller may know better */ 285 return (0 != ec->proc (ec->cls, 286 plugin, 287 type, 288 EXTRACTOR_METAFORMAT_UTF8, 289 "text/plain", 290 buf, 291 out + 1)) ? 1 : 0; 292 } 293 294 295 int 296 EXTRACTOR_forensic_emit_utf16le_ (struct EXTRACTOR_ExtractContext *ec, 297 const char *plugin, 298 enum EXTRACTOR_MetaType type, 299 const unsigned char *data, 300 size_t bytes) 301 { 302 char buf[EXTRACTOR_FORENSIC_MAX_STRING]; 303 size_t out = 0; 304 size_t i = 0; 305 306 while (i + 1 < bytes) 307 { 308 uint32_t cp = EXTRACTOR_forensic_le16_ (&data[i]); 309 310 i += 2; 311 if (0 == cp) 312 break; /* NUL terminator */ 313 if ( (0xD800 <= cp) && (cp <= 0xDBFF) ) 314 { 315 uint32_t lo; 316 317 if (i + 1 >= bytes) 318 break; /* truncated surrogate pair */ 319 lo = EXTRACTOR_forensic_le16_ (&data[i]); 320 if ( (lo < 0xDC00) || (lo > 0xDFFF) ) 321 break; /* unpaired surrogate; stop rather than guess */ 322 i += 2; 323 cp = 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00); 324 } 325 else if ( (0xDC00 <= cp) && (cp <= 0xDFFF) ) 326 { 327 break; /* stray low surrogate */ 328 } 329 if ( (cp < 0x20) || (0x7F == cp) ) 330 cp = ' '; 331 if (out + 4 >= sizeof (buf)) 332 break; 333 if (cp < 0x80) 334 { 335 buf[out++] = (char) cp; 336 } 337 else if (cp < 0x800) 338 { 339 buf[out++] = (char) (0xC0 | (cp >> 6)); 340 buf[out++] = (char) (0x80 | (cp & 0x3F)); 341 } 342 else if (cp < 0x10000) 343 { 344 buf[out++] = (char) (0xE0 | (cp >> 12)); 345 buf[out++] = (char) (0x80 | ((cp >> 6) & 0x3F)); 346 buf[out++] = (char) (0x80 | (cp & 0x3F)); 347 } 348 else 349 { 350 buf[out++] = (char) (0xF0 | (cp >> 18)); 351 buf[out++] = (char) (0x80 | ((cp >> 12) & 0x3F)); 352 buf[out++] = (char) (0x80 | ((cp >> 6) & 0x3F)); 353 buf[out++] = (char) (0x80 | (cp & 0x3F)); 354 } 355 } 356 out = EXTRACTOR_forensic_trim_ (buf, 357 out); 358 if (0 == out) 359 return 0; 360 buf[out] = '\0'; 361 return (0 != ec->proc (ec->cls, 362 plugin, 363 type, 364 EXTRACTOR_METAFORMAT_UTF8, 365 "text/plain", 366 buf, 367 out + 1)) ? 1 : 0; 368 } 369 370 371 int 372 EXTRACTOR_forensic_emit_unix_time_ (struct EXTRACTOR_ExtractContext *ec, 373 const char *plugin, 374 enum EXTRACTOR_MetaType type, 375 int64_t when) 376 { 377 char buf[32]; 378 struct tm tm; 379 time_t t = (time_t) when; 380 381 /* Zero and 0xFFFFFFFF are how these fields look when they were never 382 filled in; anything outside a plausible range is a misparse. */ 383 if ( (when < 315532800LL) || /* 1980-01-01 */ 384 (when > 4102444800LL) ) /* 2100-01-01 */ 385 return 0; 386 if (NULL == gmtime_r (&t, 387 &tm)) 388 return 0; 389 if (0 == strftime (buf, 390 sizeof (buf), 391 "%Y-%m-%dT%H:%M:%SZ", 392 &tm)) 393 return 0; 394 return (0 != ec->proc (ec->cls, 395 plugin, 396 type, 397 EXTRACTOR_METAFORMAT_UTF8, 398 "text/plain", 399 buf, 400 strlen (buf) + 1)) ? 1 : 0; 401 } 402 403 404 int 405 EXTRACTOR_forensic_emit_filetime_ (struct EXTRACTOR_ExtractContext *ec, 406 const char *plugin, 407 enum EXTRACTOR_MetaType type, 408 uint64_t filetime) 409 { 410 /* FILETIME counts 100ns intervals from 1601-01-01; 11644473600 is the 411 number of seconds from there to the Unix epoch. */ 412 if (0 == filetime) 413 return 0; 414 if (filetime > (uint64_t) 200000000000000000ULL) 415 return 0; /* well past year 2234; a misparse */ 416 return EXTRACTOR_forensic_emit_unix_time_ (ec, 417 plugin, 418 type, 419 (int64_t) (filetime / 10000000ULL) 420 - 11644473600LL); 421 } 422 423 424 int 425 EXTRACTOR_forensic_emit_size_ (struct EXTRACTOR_ExtractContext *ec, 426 const char *plugin, 427 enum EXTRACTOR_MetaType type, 428 uint64_t bytes) 429 { 430 return EXTRACTOR_forensic_emit_ (ec, 431 plugin, 432 type, 433 "%llu", 434 (unsigned long long) bytes); 435 } 436 437 438 int 439 EXTRACTOR_forensic_emit_hex_ (struct EXTRACTOR_ExtractContext *ec, 440 const char *plugin, 441 enum EXTRACTOR_MetaType type, 442 const unsigned char *data, 443 size_t len) 444 { 445 static const char hex[] = "0123456789abcdef"; 446 char buf[129]; 447 448 if ( (0 == len) || 449 (len > (sizeof (buf) - 1) / 2) ) 450 return 0; 451 for (size_t i = 0; i < len; i++) 452 { 453 buf[2 * i] = hex[data[i] >> 4]; 454 buf[2 * i + 1] = hex[data[i] & 0x0F]; 455 } 456 buf[2 * len] = '\0'; 457 return (0 != ec->proc (ec->cls, 458 plugin, 459 type, 460 EXTRACTOR_METAFORMAT_UTF8, 461 "text/plain", 462 buf, 463 2 * len + 1)) ? 1 : 0; 464 } 465 466 467 int 468 EXTRACTOR_forensic_emit_guid_ (struct EXTRACTOR_ExtractContext *ec, 469 const char *plugin, 470 enum EXTRACTOR_MetaType type, 471 const unsigned char *guid, 472 int mixed_endian) 473 { 474 uint32_t d1; 475 uint16_t d2; 476 uint16_t d3; 477 int all_zero = 1; 478 479 for (unsigned int i = 0; i < 16; i++) 480 if (0 != guid[i]) 481 { 482 all_zero = 0; 483 break; 484 } 485 if (all_zero) 486 return 0; 487 if (mixed_endian) 488 { 489 d1 = EXTRACTOR_forensic_le32_ (guid); 490 d2 = EXTRACTOR_forensic_le16_ (&guid[4]); 491 d3 = EXTRACTOR_forensic_le16_ (&guid[6]); 492 } 493 else 494 { 495 d1 = EXTRACTOR_forensic_be32_ (guid); 496 d2 = EXTRACTOR_forensic_be16_ (&guid[4]); 497 d3 = EXTRACTOR_forensic_be16_ (&guid[6]); 498 } 499 return EXTRACTOR_forensic_emit_ (ec, 500 plugin, 501 type, 502 "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", 503 (unsigned int) d1, 504 (unsigned int) d2, 505 (unsigned int) d3, 506 guid[8], guid[9], 507 guid[10], guid[11], guid[12], 508 guid[13], guid[14], guid[15]); 509 } 510 511 512 /* end of forensics.c */