strings-file-triage.md (26439B)
1 --- 2 title: "strings & File Triage" 3 description: "Triage an unknown file or a blob of unallocated space: strings in depth (-n, -t x, -e l for UTF-16, piping into IOC regexes), file/magic bytes, xxd and hexdump, exiftool metadata, binwalk, carving with foremost/scalpel/photorec, bulk_extractor, hashing and ssdeep, PDF and Office maldoc checks, YARA — with the Windows equivalents (Sysinternals strings, Get-FileHash, certutil, Format-Hex) alongside." 4 category: dfir 5 subcategory: "Analysis" 6 tags: [dfir, forensics, strings, file-analysis, carving, hexdump, magic-bytes, metadata, yara, malware-triage, windows, linux] 7 tools: ["strings / bstrings / Sysinternals strings.exe", "file / libmagic", "xxd / hexdump / Format-Hex", "exiftool", "binwalk", "foremost / scalpel / photorec", "bulk_extractor", "ssdeep / hashdeep", "pdfid / pdf-parser / peepdf", "oletools (olevba, oleid, oledump)", "YARA", "Detect It Easy (DiE)", "pestudio / pev / readpe", "HxD / ImHex"] 8 difficulty: intermediate 9 updated: "2026-09-26" 10 --- 11 12 # strings & File Triage 13 14 Before a disassembler, before a sandbox, there is the ten-minute pass every unknown file gets: **what is it really** (`file`, magic bytes), **what is written inside it** (`strings`, in the right encoding), **what does it carry** (`exiftool`, `binwalk`, embedded objects), **what does it match** (hashes, `ssdeep`, YARA). The same tools, pointed at a memory image, a swap file or the unallocated blocks of a disk, become **carving** — pulling files and indicators out of space no filesystem points to. This card is those tools, Linux/macOS first with the **Windows equivalent** next to each, and the patterns that make `strings` output readable instead of a 40,000-line wall. 15 16 > [!warning] Handle malware like malware 17 > Work on a copy, in a VM with no shared clipboard or network, with the file renamed to `sample.bin` so nothing double-clicks it. `strings`, `file` and `exiftool` do not execute anything — but `binwalk -e`, `7z x` and PDF tools that render *can* trigger parser bugs. Snapshot first. 18 19 ## Identify: what is it really? 20 21 ```bash 22 file sample.bin # libmagic: type, arch, linker, packer hints 23 file -b --mime-type sample.bin # bare MIME 24 file -z archive.gz # look inside compressed 25 file -k sample.bin # keep going, report all matches (polyglots) 26 file * # a whole directory, quickly 27 xxd sample.bin | head -4 # eyeball the magic yourself 28 ``` 29 30 ```powershell 31 # Windows: no `file`, so read the magic 32 Format-Hex .\sample.bin -Count 32 33 certutil -dump .\sample.bin | Select -First 5 # for certs/PKCS 34 # or install: winget install GnuWin32.File / use TrID (trid.exe sample.bin) 35 ``` 36 37 | Magic (hex) | ASCII | Type | 38 |---|---|---| 39 | `4D 5A` | `MZ` | Windows PE (exe/dll/sys) — `PE\0\0` follows at offset in `0x3C` | 40 | `7F 45 4C 46` | `.ELF` | Linux/Unix executable | 41 | `CF FA ED FE` / `CA FE BA BE` | | Mach-O 64-bit / universal (fat) | 42 | `50 4B 03 04` | `PK..` | ZIP — also **docx/xlsx/pptx, jar, apk, ipa, odt** | 43 | `D0 CF 11 E0 A1 B1 1A E1` | | OLE2 / CFB — legacy **doc/xls/ppt, msi, msg** | 44 | `25 50 44 46` | `%PDF` | PDF | 45 | `7B 5C 72 74 66` | `{\rtf` | RTF (CVE-2017-11882 territory) | 46 | `1F 8B` | | gzip | 47 | `42 5A 68` | `BZh` | bzip2 | 48 | `FD 37 7A 58 5A 00` | `.7zXZ.` | xz | 49 | `37 7A BC AF 27 1C` | `7z..'.` | 7-Zip | 50 | `52 61 72 21 1A 07` | `Rar!..` | RAR | 51 | `75 73 74 61 72` @257 | `ustar` | tar | 52 | `FF D8 FF` | | JPEG | 53 | `89 50 4E 47 0D 0A 1A 0A` | `.PNG` | PNG | 54 | `47 49 46 38` | `GIF8` | GIF | 55 | `52 49 46 46` | `RIFF` | WAV/AVI/WebP | 56 | `53 51 4C 69 74 65` | `SQLite` | SQLite DB (browser history, Windows Timeline, mobile apps) | 57 | `72 65 67 66` | `regf` | Windows registry hive | 58 | `45 6C 66 46 69 6C 65` | `ElfFile` | Windows EVTX | 59 | `4C 00 00 00 01 14 02 00` | `L...` | Windows LNK | 60 | `4D 41 4D 04` / `53 43 43 41` @4 | `MAM.` / `SCCA` | Prefetch (compressed Win10 / raw) | 61 | `EF BB BF` / `FF FE` / `FE FF` | | UTF-8 BOM / UTF-16 LE / BE text | 62 | `23 21` | `#!` | script with shebang | 63 | `4D 53 43 46` | `MSCF` | CAB | 64 | `43 57 53` / `46 57 53` | `CWS`/`FWS` | Flash | 65 | `00 00 00 xx 66 74 79 70` | `ftyp` @4 | MP4/MOV/HEIC | 66 | `4B 44 4D` | `KDM` | VMware VMDK | 67 | `63 6F 6E 65 63 74 69 78` | `conectix` | VHD | 68 | `45 56 46 09 0D 0A FF 00` | `EVF` | EnCase E01 | 69 | `4C 69 4D 45` | `LiME` | LiME memory dump | 70 71 > [!tip] Extension lies, magic mostly doesn't 72 > `invoice.pdf.exe`, `report.docm` renamed to `.doc`, a JPEG with a PHP webshell appended (`file` says JPEG, `strings` says `<?php`), a ZIP that is also a valid PDF. `file -k` and looking at **both ends** of the file (`xxd | head`, `xxd | tail`) catch most polyglots and appended payloads. Trailing data after the PNG `IEND` chunk or after the ZIP central directory is never innocent. 73 74 ## `strings` in depth 75 76 GNU `strings` (binutils) prints runs of printable characters ≥ 4 bytes. The defaults hide half of what a Windows binary contains. 77 78 ```bash 79 strings sample.bin # ASCII (and single-byte) runs ≥ 4 chars 80 strings -n 8 sample.bin # minimum length 8 — cuts the noise dramatically 81 strings -a sample.bin # -a: scan the WHOLE file, not just loaded sections (default on modern binutils, be explicit) 82 strings -e l sample.bin # 16-bit little-endian = UTF-16LE: Windows API strings, paths, registry keys, PowerShell in .NET binaries 83 strings -e b sample.bin # UTF-16 big-endian (rare: Java, some Mac formats) 84 strings -e S sample.bin # single 8-bit incl. high-ASCII (Latin-1 lures, some packers) 85 strings -t x sample.bin # prefix each string with its hex offset (-t d decimal, -t o octal) → jump there in a hex editor 86 strings -t x -e l sample.bin | grep -i 'cmd\|powershell\|http' 87 strings -a -n 6 sample.bin; strings -a -n 6 -e l sample.bin # ALWAYS run both — ASCII and UTF-16 — on Windows samples 88 strings -f *.bin # -f: print filename before each string (many files) 89 strings sample.bin | sort | uniq -c | sort -rn | head # repeated strings = table entries, decoy padding 90 strings sample.bin | awk 'length > 60' # long strings: base64 blobs, embedded scripts, URLs with parameters 91 ``` 92 93 | Platform | Equivalent | 94 |---|---| 95 | macOS | `strings` is from LLVM/cctools: `strings -a sample.bin`; **no `-e l`** — use `iconv`/`python` below, or `brew install binutils` → `gstrings -e l` | 96 | Windows | **Sysinternals** `strings.exe -a -n 6 sample.bin` scans ASCII **and** Unicode by default (`-u` Unicode only, `-a` ASCII only, `-o` offsets, `-s` recurse); **bstrings** (EZ Tools) adds regex and built-in IOC patterns — see [EZ Tools](/sheets/dfir/ez-tools-kape#bstrings--better-strings) | 97 | PowerShell | `Select-String -Path sample.bin -Pattern 'http' -Encoding Unicode` for a quick UTF-16 grep, or `[IO.File]::ReadAllBytes` + regex | 98 | Any | FLOSS (Mandiant `floss sample.exe`) — recovers **obfuscated** and **stack** strings from PE files that plain `strings` never sees; run it after `strings` disappoints | 99 100 ```bash 101 # UTF-16 strings on macOS/Windows-less environments without -e l 102 python3 -c "import re,sys;d=open(sys.argv[1],'rb').read();print('\n'.join(m.group().decode('utf-16le') for m in re.finditer(rb'(?:[\x20-\x7e]\x00){6,}',d)))" sample.bin 103 ``` 104 105 ### Making `strings` output useful: the IOC pass 106 107 Save the full output once (`strings -a -n 6 sample.bin > s.txt; strings -a -n 6 -e l sample.bin >> s.txt`) and grep the file instead of re-running. 108 109 ```bash 110 S=s.txt 111 grep -Eio 'https?://[a-z0-9./?=_%:-]+' $S | sort -u # URLs 112 grep -Eo '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' $S | sort -u | grep -Ev '^(0|127|255)\.' # IPv4 (drops version-number noise imperfectly — eyeball it) 113 grep -Eio '\b[a-z0-9.-]+\.(com|net|org|io|ru|cn|top|xyz|info|biz|onion|pw|cc|su)\b' $S | sort -u # domains 114 grep -Eio '[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}' $S | sort -u # emails 115 grep -Ei '\\\\[^\\ ]+\\[^\\ ]+' $S | sort -u # UNC paths 116 grep -Ei 'HKEY_|HKLM|HKCU|CurrentVersion\\Run|Software\\Microsoft' $S | sort -u # registry 117 grep -Ei '\.(exe|dll|bat|ps1|vbs|js|hta|scr|lnk|tmp|dat|log)\b' $S | sort -u # filenames 118 grep -Ei 'C:\\|%APPDATA%|%TEMP%|\\Temp\\|\\Users\\|/tmp/|/dev/shm|/var/tmp' $S | sort -u # paths 119 grep -Ei 'powershell|cmd\.exe|/c |-enc|-nop|-w hidden|IEX|Invoke-|DownloadString|FromBase64|wscript|cscript|mshta|rundll32|regsvr32|certutil|bitsadmin|schtasks|sc \|net user|whoami' $S # LOLBins & PS 120 grep -Ei 'CreateRemoteThread|VirtualAlloc|WriteProcessMemory|NtUnmapViewOfSection|SetWindowsHookEx|IsDebuggerPresent|GetTickCount|CryptEncrypt|InternetOpen|WinHttp|URLDownloadToFile|ShellExecute|WinExec|RegSetValue|AdjustTokenPrivileges|OpenProcess|LoadLibrary|GetProcAddress' $S | sort -u # suspicious imports 121 grep -Ei 'user-agent|mozilla/|accept:|content-type' $S # hardcoded HTTP client 122 grep -Ei 'password|passwd|pwd=|token|secret|api[_-]?key|BEGIN (RSA|OPENSSH|EC) PRIVATE|AKIA[0-9A-Z]{16}' $S # credentials 123 grep -Ei 'mutex|Global\\|Local\\' $S # mutex names → hunt on other hosts 124 grep -Ei 'pdb$|\.pdb\b|[A-Z]:\\Users\\[^\\]+\\(source|Desktop|Documents)' $S # PDB path = developer username & project name 125 grep -Ei 'upx|aspack|themida|vmprotect|mpress|petite|\.packed|this program cannot be run' $S # packer strings 126 grep -Eo '[A-Za-z0-9+/]{40,}={0,2}' $S | head # base64 blobs → decode below 127 grep -Eo '[0-9a-f]{32}\b|[0-9a-f]{40}\b|[0-9a-f]{64}\b' $S | sort -u | head # hashes / hex keys 128 grep -Eio '(bitcoin|btc|monero|xmr|wallet|\.onion|ransom|decrypt|your files)' $S # ransomware notes 129 ``` 130 131 ```bash 132 # Decode what you found 133 echo 'SQBFAFgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQA' | base64 -d | iconv -f utf-16le -t utf-8 # PowerShell -enc is UTF-16LE base64 134 echo 'aHR0cDov...' | base64 -d | file - # what is inside the blob? 135 printf '\x68\x74\x74\x70' | xxd; echo 68747470 | xxd -r -p # hex ↔ bytes 136 python3 -c "import sys;print(''.join(chr(ord(c)^0x41) for c in open('blob','rb').read().decode('latin1')))" # single-byte XOR guess 137 # Brute-force single-byte XOR / ROT / base64 layers: CyberChef "Magic" op, or `xortool -c 20 blob`, or `unxor` 138 ``` 139 140 > [!tip] Strings are cheap for the attacker to fake 141 > Decoy strings (legitimate-looking URLs, fake PDB paths, whole English dictionaries) are common in packed samples. A binary with **almost no** strings, or only library boilerplate, is *more* suspicious than one full of them: it is packed or the strings are built at runtime — run FLOSS, or dump it from memory after it unpacks (see [Volatility](/sheets/dfir/volatility) `windows.dumpfiles` / `malfind`). 142 143 ## Hex: `xxd`, `hexdump`, `od`, editors 144 145 ```bash 146 xxd sample.bin | less # canonical: offset, hex, ASCII 147 xxd -s 0x3C -l 4 sample.bin # -s seek, -l length: read the PE header offset 148 xxd -s -64 sample.bin # last 64 bytes (appended data?) 149 xxd -c 32 -g 1 sample.bin | head # 32 bytes per line, grouped by 1 150 xxd -p sample.bin | head -c 200 # plain hex, no formatting (for grep / diff) 151 xxd -r -p hex.txt > out.bin # hex → binary 152 hexdump -C sample.bin | head # BSD-style canonical (macOS default) 153 od -A x -t x1z -v sample.bin | head # od: hex offsets, 1-byte hex, ASCII, no * for repeats 154 cmp -l a.bin b.bin | head # byte-level diff; `vbindiff a b`, `radiff2 -x a b` for visual 155 dd if=sample.bin bs=1 skip=$((0x1000)) count=512 2>/dev/null | xxd | head # cut a region 156 ``` 157 158 ```powershell 159 Format-Hex .\sample.bin -Offset 0x3C -Count 4 160 Format-Hex .\sample.bin | Select -Last 4 161 [BitConverter]::ToString((Get-Content .\sample.bin -Encoding Byte -TotalCount 16)) -replace '-',' ' 162 ``` 163 164 GUI hex editors: **HxD** (Windows, free, huge files, disk/RAM editing, data inspector), **ImHex** (cross-platform, pattern language decodes structures for you, hex + entropy + strings + yara in one), **010 Editor** (paid, binary templates for every format), `hexyl` (terminal, coloured). Registry hive, PE and NTFS record templates exist for all three — worth it when you need to read a structure by hand. 165 166 ## Metadata: `exiftool` 167 168 ```bash 169 exiftool sample.jpg # everything: camera, GPS, timestamps, software, author 170 exiftool -G1 -a -s sample.docx # group names, all duplicates, tag names as they are in the file 171 exiftool -a -u -g1 -ee sample.pdf # unknown tags, embedded streams 172 exiftool -gps:all -n photo.jpg # coordinates in decimal 173 exiftool -Author -Creator -Producer -CreateDate -ModifyDate *.pdf *.doc* # who made the docs, with what 174 exiftool -r -csv -common -CreateDate -Author /mnt/img/home/*/Documents > docs.csv # recurse, CSV for Timeline Explorer 175 exiftool -time:all -a -G0:1 -s sample.docx # every timestamp incl. Office internal ones vs filesystem 176 exiftool -if '$gpslatitude' -r -p '$filename,$gpslatitude,$gpslongitude' . # only geotagged files 177 exiftool -X sample.jpg > meta.xml; exiftool -j sample.jpg > meta.json 178 exiftool -all= copy.jpg # strip (never on evidence — for your own redaction) 179 ``` 180 181 What to read: **Office** `Creator`/`LastModifiedBy`/`Company`/`RevisionNumber`/`TotalEditTime`/`Template` (`normal.dotm` vs something odd), `Application` and `AppVersion` (a "2019 Word" doc from a 2010 build); **PDF** `Producer`/`Creator` (`LibreOffice`, `Microsoft: Print To PDF`, `mPDF` from a webapp, `Ghostscript`), `CreateDate` vs `ModifyDate`, XMP history; **images** `Make`/`Model`/`Software`/`GPSPosition`/`DateTimeOriginal`, thumbnail that does not match the image (edited photo); **LNK/EXE** `exiftool` reads PE version info (`CompanyName`, `OriginalFileName` — a `svchost.exe` whose original name is `stealer.exe`). 182 183 ## Archives, containers, embedded objects 184 185 ```bash 186 # Office docs are ZIPs — look inside without Office 187 unzip -l sample.docx; unzip -p sample.docx word/vbaProject.bin | file - # macro container present? 188 unzip -p sample.docx word/_rels/document.xml.rels | grep -io 'target="http[^"]*"' # remote template injection 189 unzip -p sample.docx docProps/core.xml docProps/app.xml # metadata XML 190 7z l sample.bin; 7z x -osample_extracted sample.bin # 7z opens almost anything: zip, cab, msi, iso, vhd, wim, chm, pe resources 191 binwalk sample.bin # signature scan for embedded files (firmware, but also polyglots and stego) 192 binwalk -e sample.bin # extract them (into _sample.bin.extracted/) 193 binwalk -E sample.bin # entropy graph — flat high entropy = encrypted/compressed/packed 194 binwalk -R '\x50\x4b\x03\x04' sample.bin # raw byte search with offsets 195 foremost -t all -i sample.bin -o carved/ # carve known types out of ANY blob (same tool as disk carving below) 196 ``` 197 198 ```bash 199 # PE quick look (Linux) — imports, sections, entropy, timestamps 200 readpe -h sample.exe; readpe -i sample.exe | head -60 # pev 201 pescan sample.exe # pev heuristics 202 python3 -c "import pefile;p=pefile.PE('sample.exe');print(p.FILE_HEADER.dump());[print(e.dll,[i.name for i in e.imports][:8]) for e in p.DIRECTORY_ENTRY_IMPORT]" 203 diec sample.exe # Detect It Easy: compiler, packer, protector 204 # Windows GUI: pestudio (indicators, imports scored, strings, VT lookup), DiE, CFF Explorer, PE-bear 205 ``` 206 207 ## Documents: PDF and Office maldoc triage 208 209 ```bash 210 # PDF 211 pdfid.py sample.pdf # counts of /JS /JavaScript /OpenAction /AA /Launch /EmbeddedFile /URI /ObjStm — any of the first five = open it further 212 pdf-parser.py -a sample.pdf # stats: objects by type 213 pdf-parser.py --search JavaScript sample.pdf # find the object 214 pdf-parser.py -o 12 -f -w sample.pdf # dump object 12, filters applied, raw 215 pdf-parser.py --search /Launch sample.pdf; pdf-parser.py --search /URI sample.pdf 216 peepdf -i sample.pdf # interactive; `js_analyse` / `extract js` 217 qpdf --qdf --object-streams=disable sample.pdf out.pdf # decompress everything so grep/strings work on it 218 strings out.pdf | grep -Ei 'javascript|/JS|/Launch|/URI|/EmbeddedFile|/OpenAction|cmd|powershell' 219 220 # Office (OLE and OOXML) — oletools 221 oleid sample.doc # summary: encrypted? VBA? XLM? external relationships? flash? 222 olevba sample.docm # dump macros + auto-exec triggers + suspicious keywords + IOCs table 223 olevba --decode --deobf sample.docm # decode hex/base64/StrReverse, attempt deobfuscation 224 olevba --reveal sample.docm # macro source with obfuscated strings replaced 225 oledump.py sample.doc # streams; 'M' = macro stream; -s 7 -v = decompress stream 7 226 oledump.py -p plugin_http_heuristics sample.doc 227 mraptor sample.doc # AutoExec + Write/Execute = suspicious, one line 228 rtfobj sample.rtf # embedded OLE objects in RTF (Equation Editor exploits) 229 msodde sample.docx # DDE field attacks 230 xlmdeobfuscator -f sample.xls # Excel 4.0 (XLM) macros 231 ``` 232 233 ## Hashing, fuzzy hashing, known-good/bad 234 235 ```bash 236 sha256sum sample.bin; md5sum sample.bin; sha1sum sample.bin 237 hashdeep -c md5,sha1,sha256 -r ./samples/ > hashes.txt # recursive multi-hash 238 hashdeep -c sha256 -r -a -k known-good.txt ./bin/ # audit dir against a known set (-a audit, -k known) 239 hashdeep -c sha256 -r -m -k nsrl.txt /mnt/img/usr/bin # -m: show only files that MATCH known (whitelist noise) 240 hashdeep -c sha256 -r -x -k nsrl.txt /mnt/img/usr/bin # -x: show only UNKNOWN files — the review list 241 ssdeep -b sample.bin # context-triggered piecewise hash 242 ssdeep -bl -r ./samples/ > fuzzy.txt; ssdeep -bm fuzzy.txt new.bin # match a new sample against a set (score 0–100) 243 ssdeep -d -r ./samples/ # cluster near-duplicates within a directory 244 tlsh -f sample.bin # TLSH — better on small/heavily-modified files 245 # Look up, don't upload (an upload tells the attacker you found it): 246 # VirusTotal hash search, MalwareBazaar, Hybrid Analysis, `vt file <sha256>` CLI, ANY.RUN search 247 ``` 248 249 ```powershell 250 Get-FileHash .\sample.bin -Algorithm SHA256 # MD5, SHA1, SHA256, SHA384, SHA512 251 Get-ChildItem -Recurse | Get-FileHash -Algorithm SHA256 | Export-Csv hashes.csv 252 certutil -hashfile .\sample.bin SHA256 # no PowerShell 253 certutil -hashfile .\sample.bin MD5 254 sigcheck64.exe -a -h -vt .\sample.exe # Sysinternals: signature, version, hashes, VirusTotal (-vt requires accepting ToS) 255 sigcheck64.exe -e -u -vr -s C:\Windows\System32 > unsigned.txt # unsigned exes in a tree 256 ``` 257 258 ## Carving: files from unallocated space, swap, memory 259 260 ```bash 261 # Get the blob: unallocated blocks of a partition (Sleuth Kit), or the whole image, or pagefile/hiberfil/mem 262 blkls -o 2048 disk.dd > unalloc.raw 263 264 # foremost — header/footer carving, config-driven, fast 265 foremost -t jpg,png,pdf,doc,zip,exe -i unalloc.raw -o carved-foremost/ # -t types (or all), writes audit.txt with offsets 266 foremost -c /etc/foremost.conf -i disk.dd -o carved/ # custom signatures in the conf 267 # scalpel — same idea, faster, edit /etc/scalpel/scalpel.conf to UNCOMMENT the types you want first 268 scalpel -c /etc/scalpel/scalpel.conf -o carved-scalpel/ unalloc.raw 269 # photorec — best for media (400+ formats), interactive TUI, works on damaged fs; /cmd for scripting 270 photorec /log /d carved-photorec/ /cmd disk.dd fileopt,everything,enable,search 271 # bulk_extractor — does NOT carve files; it scans EVERY byte for FEATURES: emails, URLs, IPs, phone numbers, credit cards, 272 # JSON, base64, EXIF, zip/gzip/rar contents (recursively decompressed!), Windows PE headers, hex keys, domains 273 bulk_extractor -o be-out/ disk.dd # 10–30 min per 100 GB; multi-threaded 274 bulk_extractor -o be-out/ -E email -E url -E domain -E exif unalloc.raw # -E enable only listed scanners 275 ls be-out/ # email.txt url.txt domain.txt ip.txt telephone.txt ccn.txt exif.txt zip.txt json.txt *_histogram.txt 276 head be-out/url_histogram.txt be-out/email_histogram.txt # ranked — top hits first 277 bulk_extractor -o be-out/ -F iocs.txt disk.dd # -F: search for your own list of strings (offsets to features.txt) 278 bulk_extractor -o be-out/ -f 'Invoke-|-enc ' disk.dd # -f: regex 279 # Every hit carries a byte offset (or "offset-ZIP-offset" for compressed) → dd/xxd there, or `fiwalk`/`identify_filenames.py` to map offset → filename via the fs 280 identify_filenames.py --all be-out/ be-out-annotated/ --image disk.dd 281 ``` 282 283 ```bash 284 # Manual carving when you know the header: find offsets, cut 285 grep -obUaP '\xFF\xD8\xFF\xE0' unalloc.raw | head # -o offsets, -b byte offset, -U binary, -a text, -P perl regex 286 python3 - <<'PY' 287 import re 288 d=open('unalloc.raw','rb').read() 289 for i,m in enumerate(re.finditer(rb'%PDF-1\.\d.*?\x25\x25EOF', d, re.S)): 290 open(f'carved_{i}.pdf','wb').write(m.group()); print(i, m.start(), len(m.group())) 291 PY 292 # Strings over the whole blob, with offsets, into the IOC pass above 293 strings -a -t x -n 8 unalloc.raw > unalloc-strings.txt; strings -a -t x -n 8 -e l unalloc.raw >> unalloc-strings.txt 294 # Windows pagefile/hiberfil: strings + bstrings work directly; decompress hiberfil first for real analysis 295 # hibr2bin.exe hiberfil.sys hiber.raw (Comae/Magnet) then Volatility / strings on hiber.raw 296 ``` 297 298 > [!info] What carving cannot do 299 > Fragmented files come out corrupt (only the first fragment, or garbage joined on). SSDs with TRIM zero deleted blocks quickly — carving an SSD imaged days after deletion mostly yields nothing. Encrypted/compressed containers carve as one opaque blob. Carved files have **no name, no path, no timestamps**: to get those back, match the carved file's hash/offset to `$MFT`/`fls -d` deleted entries or the USN journal. 300 301 ## YARA: match what you already know 302 303 ```bash 304 yara -r rules/ sample.bin # recurse a rules dir 305 yara -s rule.yar sample.bin # -s print matching strings + offsets 306 yara -m -s rules.yar sample.bin # -m print rule metadata 307 yara -r -w -f rules/ /mnt/img/home/ # -w no warnings, -f fast mode 308 yara -C compiled.yarc sample.bin; yarac rules/*.yar compiled.yarc # precompile big rule sets 309 yara rules.yar 1234 # scan a live PID (Linux, root) 310 yara -D sample.bin # dump module data (pe, elf, hash, math) 311 yara -d filename=sample.bin -d filesize=$(stat -c%s sample.bin) rules.yar sample.bin # external variables 312 yara-x scan rules/ sample.bin # yara-x: the Rust rewrite, same syntax, faster, better errors 313 ``` 314 315 ```yara 316 rule triage_suspicious_pe_strings 317 { 318 meta: 319 author = "DAEMON" 320 description = "Quick triage: PE with injection APIs + a URL + a PowerShell flag" 321 strings: 322 $api1 = "VirtualAllocEx" ascii 323 $api2 = "WriteProcessMemory" ascii 324 $api3 = "CreateRemoteThread" ascii 325 $url = /https?:\/\/[a-z0-9.\-]+\/[a-z0-9\/._-]*/ ascii wide nocase 326 $ps = "-enc" ascii wide nocase 327 $ps2 = "FromBase64String" ascii wide 328 condition: 329 uint16(0) == 0x5A4D and filesize < 5MB and 330 2 of ($api*) and ($url or any of ($ps*)) 331 } 332 ``` 333 334 Rule sources: **Neo23x0/signature-base** (Loki/Thor), **Yara-Rules/rules**, **ReversingLabs**, **Elastic protections-artifacts**, **InQuest awesome-yara** index, ESET, Malpedia (login), plus the packer/crypto/capability rules in **mandiant/capa** (`capa sample.exe` gives you ATT&CK capabilities without writing rules). Keep an `iocs.yar` per case with the hashes, mutexes, PDB paths and URLs you pulled out of `strings` — then scan every other host's triage collection with it. 335 336 ## Ten-minute triage checklist 337 338 ```bash 339 f=sample.bin 340 file -k $f; sha256sum $f; ssdeep -b $f; exiftool -G1 -s $f | head -40 341 xxd $f | head -4; xxd $f | tail -4 342 strings -a -n 6 $f > s.txt; strings -a -n 6 -e l $f >> s.txt; wc -l s.txt 343 grep -Eio 'https?://[^ "]+|\b([0-9]{1,3}\.){3}[0-9]{1,3}\b|[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}' s.txt | sort -u 344 grep -Ei 'powershell|cmd\.exe|-enc|VirtualAlloc|WriteProcessMemory|CreateRemoteThread|HKCU|HKLM|\\Temp\\|\.pdb' s.txt | sort -u | head -40 345 binwalk $f | head; binwalk -E $f >/dev/null && echo "see entropy png" 346 diec $f 2>/dev/null || pescan $f 2>/dev/null 347 yara -r ~/rules/ $f; capa $f 2>/dev/null | head -40 348 # then decide: benign / known-bad (hash hit) / needs sandbox (ANY.RUN, CAPE, Joe) / needs a reverser 349 ``` 350 351 1. **Identity**: `file -k`, magic head/tail, size, entropy. Packed? Polyglot? Appended data? 352 2. **Hashes**: SHA-256 to VT/MalwareBazaar *search* (do not upload yet); `ssdeep` against your case corpus. 353 3. **Metadata**: `exiftool` — author, tool, timestamps, PDB path, version info vs filename. 354 4. **Strings**, both encodings, then the IOC greps. Save everything to the case notes with offsets. 355 5. **Structure**: imports/sections (PE), objects (PDF), macros (Office), embedded files (`binwalk`, `7z l`). 356 6. **Match**: YARA rule sets, capa. Write the case rule from what you found. 357 7. **Decide** the next stop: sandbox, [Volatility](/sheets/dfir/volatility) for a memory-resident stage, or a reverser. 358 359 ## Related 360 361 - [EZ Tools & KAPE](/sheets/dfir/ez-tools-kape) — `bstrings`, `Hasher`, `EZViewer` on Windows 362 - [Linux forensics](/sheets/dfir/linux-forensics) — where unallocated space comes from (`blkls`), Sleuth Kit 363 - [Acquisition](/sheets/dfir/disk-imaging) — getting the disk/memory image to carve 364 - [Volatility 3](/sheets/dfir/volatility) — dumping unpacked binaries from memory for a second `strings` pass 365 - [Hashing](/sheets/cryptography/hashing) — algorithm reference