v3 candidate
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
package analysis
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Inspect interprets bytes starting at `buf[0]` as many candidate
|
||||
// types. It is resilient: types that don't have enough bytes simply
|
||||
// return empty strings.
|
||||
func Inspect(buf []byte, offset int64) map[string]any {
|
||||
out := map[string]any{
|
||||
"offset": offset,
|
||||
"length": len(buf),
|
||||
}
|
||||
|
||||
// 8-bit
|
||||
if len(buf) >= 1 {
|
||||
out["u8"] = int(buf[0])
|
||||
out["i8"] = int(int8(buf[0]))
|
||||
out["char"] = printable(buf[0])
|
||||
out["bin8"] = fmt.Sprintf("%08b", buf[0])
|
||||
}
|
||||
|
||||
// 16-bit
|
||||
if len(buf) >= 2 {
|
||||
le16 := binary.LittleEndian.Uint16(buf)
|
||||
be16 := binary.BigEndian.Uint16(buf)
|
||||
out["u16_le"] = int(le16)
|
||||
out["u16_be"] = int(be16)
|
||||
out["i16_le"] = int(int16(le16))
|
||||
out["i16_be"] = int(int16(be16))
|
||||
}
|
||||
|
||||
// 32-bit
|
||||
if len(buf) >= 4 {
|
||||
le32 := binary.LittleEndian.Uint32(buf)
|
||||
be32 := binary.BigEndian.Uint32(buf)
|
||||
out["u32_le"] = uint(le32)
|
||||
out["u32_be"] = uint(be32)
|
||||
out["i32_le"] = int(int32(le32))
|
||||
out["i32_be"] = int(int32(be32))
|
||||
out["f32_le"] = float64(math.Float32frombits(le32))
|
||||
out["f32_be"] = float64(math.Float32frombits(be32))
|
||||
}
|
||||
|
||||
// 64-bit
|
||||
if len(buf) >= 8 {
|
||||
le64 := binary.LittleEndian.Uint64(buf)
|
||||
be64 := binary.BigEndian.Uint64(buf)
|
||||
out["u64_le"] = float64(le64)
|
||||
out["u64_be"] = float64(be64)
|
||||
out["i64_le"] = float64(int64(le64))
|
||||
out["i64_be"] = float64(int64(be64))
|
||||
out["f64_le"] = math.Float64frombits(le64)
|
||||
out["f64_be"] = math.Float64frombits(be64)
|
||||
}
|
||||
|
||||
// Timestamp guesses
|
||||
if len(buf) >= 4 {
|
||||
le := binary.LittleEndian.Uint32(buf)
|
||||
out["unix_le"] = formatUnix(int64(le))
|
||||
}
|
||||
if len(buf) >= 8 {
|
||||
le := binary.LittleEndian.Uint64(buf)
|
||||
out["filetime_le"] = formatFiletime(le)
|
||||
out["webkit_le"] = formatWebkit(le)
|
||||
out["oletime_le"] = formatOLEDate(math.Float64frombits(le))
|
||||
}
|
||||
|
||||
// ASCII preview (up to 32 bytes)
|
||||
n := len(buf)
|
||||
if n > 32 {
|
||||
n = 32
|
||||
}
|
||||
ascii := make([]byte, n)
|
||||
for i := 0; i < n; i++ {
|
||||
if buf[i] >= 0x20 && buf[i] <= 0x7e {
|
||||
ascii[i] = buf[i]
|
||||
} else {
|
||||
ascii[i] = '.'
|
||||
}
|
||||
}
|
||||
out["ascii"] = string(ascii)
|
||||
|
||||
// UTF-8 attempt
|
||||
if utf8.Valid(buf[:n]) {
|
||||
out["utf8"] = string(buf[:n])
|
||||
}
|
||||
|
||||
// UTF-16 LE/BE preview
|
||||
if len(buf) >= 2 {
|
||||
lu := n / 2
|
||||
if lu > 0 {
|
||||
u := make([]uint16, lu)
|
||||
for i := 0; i < lu; i++ {
|
||||
u[i] = uint16(buf[i*2]) | uint16(buf[i*2+1])<<8
|
||||
}
|
||||
out["utf16le"] = string(utf16.Decode(u))
|
||||
for i := 0; i < lu; i++ {
|
||||
u[i] = uint16(buf[i*2])<<8 | uint16(buf[i*2+1])
|
||||
}
|
||||
out["utf16be"] = string(utf16.Decode(u))
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
func printable(b byte) string {
|
||||
if b >= 0x20 && b <= 0x7e {
|
||||
return string([]byte{b})
|
||||
}
|
||||
return "."
|
||||
}
|
||||
|
||||
func formatUnix(sec int64) string {
|
||||
if sec < 0 || sec > 4102444800 { // past 2100
|
||||
return ""
|
||||
}
|
||||
return time.Unix(sec, 0).UTC().Format(time.RFC3339)
|
||||
}
|
||||
|
||||
// Windows FILETIME epoch 1601-01-01 in 100ns ticks.
|
||||
const filetimeEpochDelta = 11644473600
|
||||
|
||||
func formatFiletime(v uint64) string {
|
||||
if v == 0 {
|
||||
return ""
|
||||
}
|
||||
sec := int64(v/10_000_000) - filetimeEpochDelta
|
||||
if sec < 0 || sec > 4102444800 {
|
||||
return ""
|
||||
}
|
||||
return time.Unix(sec, 0).UTC().Format(time.RFC3339)
|
||||
}
|
||||
|
||||
// WebKit/Chrome epoch 1601-01-01 in microseconds.
|
||||
func formatWebkit(v uint64) string {
|
||||
if v == 0 {
|
||||
return ""
|
||||
}
|
||||
sec := int64(v/1_000_000) - filetimeEpochDelta
|
||||
if sec < 0 || sec > 4102444800 {
|
||||
return ""
|
||||
}
|
||||
return time.Unix(sec, 0).UTC().Format(time.RFC3339)
|
||||
}
|
||||
|
||||
// OLE Automation date: days since 1899-12-30, as float64.
|
||||
func formatOLEDate(days float64) string {
|
||||
if days < 2 || days > 75000 {
|
||||
return ""
|
||||
}
|
||||
base := time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)
|
||||
sec := int64(days * 86400)
|
||||
return base.Add(time.Duration(sec) * time.Second).Format(time.RFC3339)
|
||||
}
|
||||
Reference in New Issue
Block a user