github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/protocols/common/helpers/responsehighlighter/hexdump.go (about) 1 package responsehighlighter 2 3 import ( 4 "errors" 5 "fmt" 6 "regexp" 7 "strings" 8 "unicode" 9 10 "github.com/projectdiscovery/gologger" 11 ) 12 13 // [0-9a-fA-F]{8} {2} - hexdump indexes (8 character hex value followed by two spaces) 14 // [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) 15 // \x1b\[(\d;?)+m - ASCII color code pattern 16 // \x1b\[0m - ASCII color code reset 17 // \|(.*)\|\n - ASCII representation of the input delimited by pipe characters 18 var hexDumpParsePattern = regexp.MustCompile(`([0-9a-fA-F]{8} {2})((?:(?:\x1b\[(?:\d;?)+m)?[0-9a-fA-F]{2}(?:\x1b\[0m)? +)+)\|(.*)\|\n`) 19 var hexValuePattern = regexp.MustCompile(`([a-fA-F0-9]{2})`) 20 21 type HighlightableHexDump struct { 22 index []string 23 hex []string 24 ascii []string 25 } 26 27 func NewHighlightableHexDump(rowSize int) HighlightableHexDump { 28 return HighlightableHexDump{index: make([]string, 0, rowSize), hex: make([]string, 0, rowSize), ascii: make([]string, 0, rowSize)} 29 } 30 31 func (hexDump HighlightableHexDump) len() int { 32 return len(hexDump.index) 33 } 34 35 func (hexDump HighlightableHexDump) String() string { 36 var result string 37 for i := 0; i < hexDump.len(); i++ { 38 result += hexDump.index[i] + hexDump.hex[i] + "|" + hexDump.ascii[i] + "|\n" 39 } 40 return result 41 } 42 43 func toHighLightedHexDump(hexDump, snippetToHighlight string) (HighlightableHexDump, error) { 44 hexDumpRowValues := hexDumpParsePattern.FindAllStringSubmatch(hexDump, -1) 45 if hexDumpRowValues == nil || len(hexDumpRowValues) != strings.Count(hexDump, "\n") { 46 message := "could not parse hexdump" 47 gologger.Warning().Msgf(message) 48 return HighlightableHexDump{}, errors.New(message) 49 } 50 51 result := NewHighlightableHexDump(len(hexDumpRowValues)) 52 for _, currentHexDumpRowValues := range hexDumpRowValues { 53 result.index = append(result.index, currentHexDumpRowValues[1]) 54 result.hex = append(result.hex, currentHexDumpRowValues[2]) 55 result.ascii = append(result.ascii, currentHexDumpRowValues[3]) 56 } 57 return result.highlight(snippetToHighlight), nil 58 } 59 60 func (hexDump HighlightableHexDump) highlight(snippetToColor string) HighlightableHexDump { 61 return highlightAsciiSection(highlightHexSection(hexDump, snippetToColor), snippetToColor) 62 } 63 64 func highlightHexSection(hexDump HighlightableHexDump, snippetToColor string) HighlightableHexDump { 65 var snippetHexCharactersMatchPattern string 66 for _, char := range snippetToColor { 67 snippetHexCharactersMatchPattern += fmt.Sprintf(`(%02x[ \n]+)`, char) 68 } 69 70 hexDump.hex = highlight(hexDump.hex, snippetHexCharactersMatchPattern, func(v string) string { 71 return hexValuePattern.ReplaceAllString(v, addColor("$1")) 72 }) 73 74 return hexDump 75 } 76 77 func highlightAsciiSection(hexDump HighlightableHexDump, snippetToColor string) HighlightableHexDump { 78 var snippetCharactersMatchPattern string 79 for _, v := range snippetToColor { 80 var value string 81 if IsASCIIPrintable(v) { 82 value = regexp.QuoteMeta(string(v)) 83 } else { 84 value = "." 85 } 86 snippetCharactersMatchPattern += fmt.Sprintf(`(%s\n*)`, value) 87 } 88 89 hexDump.ascii = highlight(hexDump.ascii, snippetCharactersMatchPattern, func(v string) string { 90 if len(v) > 1 { 91 return addColor(string(v[0])) + v[1:] // do not color new line characters 92 } 93 return addColor(v) 94 }) 95 96 return hexDump 97 } 98 99 func highlight(values []string, snippetCharactersMatchPattern string, replaceToFunc func(v string) string) []string { 100 rows := strings.Join(values, "\n") 101 compiledPattern := regexp.MustCompile(snippetCharactersMatchPattern) 102 for _, submatch := range compiledPattern.FindAllStringSubmatch(rows, -1) { 103 var replaceTo string 104 var replaceFrom string 105 for _, matchedValueWithSuffix := range submatch[1:] { 106 replaceFrom += matchedValueWithSuffix 107 replaceTo += replaceToFunc(matchedValueWithSuffix) 108 } 109 rows = strings.ReplaceAll(rows, replaceFrom, replaceTo) 110 } 111 return strings.Split(rows, "\n") 112 } 113 114 func HasBinaryContent(input string) bool { 115 return !IsASCII(input) 116 } 117 118 // IsASCII tests whether a string consists only of ASCII characters or not 119 func IsASCII(input string) bool { 120 for i := 0; i < len(input); i++ { 121 if input[i] > unicode.MaxASCII { 122 return false 123 } 124 } 125 return true 126 } 127 128 func IsASCIIPrintable(input rune) bool { 129 return input > 32 && input < unicode.MaxASCII 130 }