2021-10-30 13:17:47 +03:00
|
|
|
package responsehighlighter
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"regexp"
|
|
|
|
|
"strings"
|
|
|
|
|
"unicode"
|
|
|
|
|
|
|
|
|
|
"github.com/projectdiscovery/gologger"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// [0-9a-fA-F]{8} {2} - hexdump indexes (8 character hex value followed by two spaces)
|
|
|
|
|
// [0-9a-fA-F]{2} + - 2 character long hex values followed by one or two space (potentially wrapped with an ASCII color code, see below)
|
|
|
|
|
// \x1b\[(\d;?)+m - ASCII color code pattern
|
|
|
|
|
// \x1b\[0m - ASCII color code reset
|
|
|
|
|
// \|(.*)\|\n - ASCII representation of the input delimited by pipe characters
|
|
|
|
|
var hexDumpParsePattern = regexp.MustCompile(`([0-9a-fA-F]{8} {2})((?:(?:\x1b\[(?:\d;?)+m)?[0-9a-fA-F]{2}(?:\x1b\[0m)? +)+)\|(.*)\|\n`)
|
|
|
|
|
var hexValuePattern = regexp.MustCompile(`([a-fA-F0-9]{2})`)
|
|
|
|
|
|
|
|
|
|
type HighlightableHexDump struct {
|
|
|
|
|
index []string
|
|
|
|
|
hex []string
|
|
|
|
|
ascii []string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewHighlightableHexDump(rowSize int) HighlightableHexDump {
|
|
|
|
|
return HighlightableHexDump{index: make([]string, 0, rowSize), hex: make([]string, 0, rowSize), ascii: make([]string, 0, rowSize)}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (hexDump HighlightableHexDump) len() int {
|
|
|
|
|
return len(hexDump.index)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (hexDump HighlightableHexDump) String() string {
|
|
|
|
|
var result string
|
|
|
|
|
for i := 0; i < hexDump.len(); i++ {
|
|
|
|
|
result += hexDump.index[i] + hexDump.hex[i] + "|" + hexDump.ascii[i] + "|\n"
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func toHighLightedHexDump(hexDump, snippetToHighlight string) (HighlightableHexDump, error) {
|
|
|
|
|
hexDumpRowValues := hexDumpParsePattern.FindAllStringSubmatch(hexDump, -1)
|
|
|
|
|
if hexDumpRowValues == nil || len(hexDumpRowValues) != strings.Count(hexDump, "\n") {
|
|
|
|
|
message := "could not parse hexdump"
|
2024-08-16 08:01:23 -07:00
|
|
|
gologger.Warning().Msg(message)
|
|
|
|
|
|
2021-10-30 13:17:47 +03:00
|
|
|
return HighlightableHexDump{}, errors.New(message)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result := NewHighlightableHexDump(len(hexDumpRowValues))
|
|
|
|
|
for _, currentHexDumpRowValues := range hexDumpRowValues {
|
|
|
|
|
result.index = append(result.index, currentHexDumpRowValues[1])
|
|
|
|
|
result.hex = append(result.hex, currentHexDumpRowValues[2])
|
|
|
|
|
result.ascii = append(result.ascii, currentHexDumpRowValues[3])
|
|
|
|
|
}
|
|
|
|
|
return result.highlight(snippetToHighlight), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (hexDump HighlightableHexDump) highlight(snippetToColor string) HighlightableHexDump {
|
|
|
|
|
return highlightAsciiSection(highlightHexSection(hexDump, snippetToColor), snippetToColor)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func highlightHexSection(hexDump HighlightableHexDump, snippetToColor string) HighlightableHexDump {
|
|
|
|
|
var snippetHexCharactersMatchPattern string
|
|
|
|
|
for _, char := range snippetToColor {
|
|
|
|
|
snippetHexCharactersMatchPattern += fmt.Sprintf(`(%02x[ \n]+)`, char)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hexDump.hex = highlight(hexDump.hex, snippetHexCharactersMatchPattern, func(v string) string {
|
|
|
|
|
return hexValuePattern.ReplaceAllString(v, addColor("$1"))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return hexDump
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func highlightAsciiSection(hexDump HighlightableHexDump, snippetToColor string) HighlightableHexDump {
|
|
|
|
|
var snippetCharactersMatchPattern string
|
|
|
|
|
for _, v := range snippetToColor {
|
2021-11-01 20:44:55 +02:00
|
|
|
var value string
|
|
|
|
|
if IsASCIIPrintable(v) {
|
|
|
|
|
value = regexp.QuoteMeta(string(v))
|
|
|
|
|
} else {
|
|
|
|
|
value = "."
|
|
|
|
|
}
|
|
|
|
|
snippetCharactersMatchPattern += fmt.Sprintf(`(%s\n*)`, value)
|
2021-10-30 13:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hexDump.ascii = highlight(hexDump.ascii, snippetCharactersMatchPattern, func(v string) string {
|
|
|
|
|
if len(v) > 1 {
|
|
|
|
|
return addColor(string(v[0])) + v[1:] // do not color new line characters
|
|
|
|
|
}
|
|
|
|
|
return addColor(v)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return hexDump
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func highlight(values []string, snippetCharactersMatchPattern string, replaceToFunc func(v string) string) []string {
|
|
|
|
|
rows := strings.Join(values, "\n")
|
|
|
|
|
compiledPattern := regexp.MustCompile(snippetCharactersMatchPattern)
|
|
|
|
|
for _, submatch := range compiledPattern.FindAllStringSubmatch(rows, -1) {
|
|
|
|
|
var replaceTo string
|
|
|
|
|
var replaceFrom string
|
|
|
|
|
for _, matchedValueWithSuffix := range submatch[1:] {
|
|
|
|
|
replaceFrom += matchedValueWithSuffix
|
|
|
|
|
replaceTo += replaceToFunc(matchedValueWithSuffix)
|
|
|
|
|
}
|
|
|
|
|
rows = strings.ReplaceAll(rows, replaceFrom, replaceTo)
|
|
|
|
|
}
|
|
|
|
|
return strings.Split(rows, "\n")
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-01 20:44:55 +02:00
|
|
|
func HasBinaryContent(input string) bool {
|
|
|
|
|
return !IsASCII(input)
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-30 13:17:47 +03:00
|
|
|
// IsASCII tests whether a string consists only of ASCII characters or not
|
|
|
|
|
func IsASCII(input string) bool {
|
|
|
|
|
for i := 0; i < len(input); i++ {
|
|
|
|
|
if input[i] > unicode.MaxASCII {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
2021-11-01 20:44:55 +02:00
|
|
|
|
|
|
|
|
func IsASCIIPrintable(input rune) bool {
|
|
|
|
|
return input > 32 && input < unicode.MaxASCII
|
|
|
|
|
}
|