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  }