Data hosted with ♥ by Pastebin.com - Download Raw - See Original
  1. import sys
  2. import struct
  3.  
  4. # I'm trying not to bury the magic number...
  5. CHECKSUM_MASK = 0x536e6144 # DanS (actuall SnaD)
  6. RICH_TEXT = 'Rich'
  7. RICH_TEXT_LENGTH = len(RICH_TEXT)
  8. PE_START = 0x3c
  9. PE_FIELD_LENGTH = 4
  10.  
  11. ##
  12. # A convenient exception to raise if the Rich Header doesn't exist.
  13. class RichHeaderNotFoundException(Exception):
  14.     def __init__(self):
  15.         Exception.__init__(self, "Rich footer does not appear to exist")
  16.  
  17. ##
  18. # Locate the body of the data that contains the rich header This will be
  19. # (roughly) between 0x3c and the beginning of the PE header, but the entire
  20. # thing up to the last checksum will be needed in order to verify the header.
  21. def get_file_header(file_name):
  22.     f = open(file_name,'rb')
  23.  
  24.     #start with 0x3c
  25.     f.seek(PE_START)
  26.     data = f.read(PE_FIELD_LENGTH)
  27.  
  28.     if data == '': #File is empty, bail
  29.         raise RichHeaderNotFoundException()
  30.     end = struct.unpack('<L',data)[0] # get the value at 0x3c
  31.  
  32.     f.seek(0)
  33.     data = f.read( end ) # read until that value is reached
  34.     f.close()
  35.  
  36.     return data
  37.  
  38. ##
  39. # This class assists in parsing the Rich Header from PE Files.
  40. # The Rich Header is the section in the PE file following the dos stub but
  41. # preceding the lfa_new header which is inserted by link.exe when building with
  42. # the Microsoft Compilers.  The Rich Heder contains the following:
  43. # <pre>
  44. # marker, checksum, checksum, checksum,
  45. # R_compid_i, R_occurrence_i,
  46. # R_compid_i+1, R_occurrence_i+1, ...  
  47. # R_compid_N-1, R_occurrence_N-1, Rich, marker
  48. #
  49. # marker = checksum XOR 0x536e6144
  50. # R_compid_i is the ith compid XORed with the checksum
  51. # R_occurrence_i is the ith occurrence  XORed with the checksum
  52. # Rich = the text string 'Rich'
  53. # The checksum is the sum of all the PE Header values rotated by their
  54. # offset and the sum of all compids rotated by their occurrence counts.  
  55. # </pre>
  56. # @see _validate_checksum code for checksum calculation
  57. class ParsedRichHeader:
  58.     ##
  59.     # Creates a ParsedRichHeader from the specified PE File.
  60.     # @throws RichHeaderNotFoundException if the file does not contain a rich header
  61.     # @param file_name The PE File to be parsed
  62.     def __init__(self, file_name):
  63.         ## The file that was parsed
  64.         self.file_name = file_name
  65.         self._parse( file_name )
  66.  
  67.     ##
  68.     # Used internally to parse the PE File and extract Rich Header data.
  69.     # Initializes self.compids and self.valid_checksum.
  70.     # @param file_name The PE File to be parsed
  71.     # @throws RichHeaderNotFoundException if the file does not contain a rich header
  72.     def _parse(self,file_name):
  73.         #make sure there is a header:
  74.         data = get_file_header( file_name )
  75.  
  76.         compid_end_index = data.find(RICH_TEXT)
  77.         if compid_end_index == -1:
  78.             raise RichHeaderNotFoundException()
  79.  
  80.         rich_offset = compid_end_index + RICH_TEXT_LENGTH
  81.  
  82.         checksum_text = data[rich_offset:rich_offset+4]
  83.         checksum_value = struct.unpack('<L', checksum_text)[0]
  84.         #start marker denotes the beginning of the rich header
  85.         start_marker = struct.pack('<LLLL',checksum_value ^ CHECKSUM_MASK, checksum_value, checksum_value, checksum_value )[0]
  86.  
  87.         rich_header_start = data.find(start_marker)
  88.         if rich_header_start == -1:
  89.             raise RichHeaderNotFoundException()
  90.  
  91.         compid_start_index = rich_header_start + 16 # move past the marker and 3 checksums
  92.  
  93.         compids = dict()
  94.         for i in range(compid_start_index, compid_end_index, 8):
  95.             compid = struct.unpack('<L',data[i:i+4])[0] ^ checksum_value
  96.             count = struct.unpack('<L',data[i+4:i+8])[0] ^ checksum_value
  97.             compids[compid]=count
  98.        
  99.         ## A dictionary of compids and their occurrence counts
  100.         self.compids = compids
  101.         ## A value for later reference to see if the checksum was valid
  102.         self.valid_checksum = self._validate_checksum( data, rich_header_start, checksum_value )
  103.  
  104.     ##
  105.     # Compute the checksum value and see if it matches the checksum stored in
  106.     # the Rich Header.
  107.     # The checksum is the sum of all the PE Header values rotated by their
  108.     # offset and the sum of all compids rotated by their occurrence counts
  109.     # @param data A blob of binary data that corresponds to the PE Header data
  110.     # @param rich_header_start The offset to marker, checksum, checksum, checksum
  111.     # @returns True if the checksum is valid, false otherwise
  112.     def _validate_checksum(self, data, rich_header_start, checksum):
  113.  
  114.         #initialize the checksum offset at which the rich header is located
  115.         cksum = rich_header_start
  116.  
  117.         #add the value from the pe header after rotating the value by its offset in the pe header
  118.         for i in range(0,rich_header_start):
  119.             if PE_START <= i <= PE_START+PE_FIELD_LENGTH-1:
  120.                 continue
  121.             temp = ord(data[i])
  122.             cksum+= ((temp << (i%32)) | (temp >> (32-(i%32))) & 0xff)
  123.             cksum &=0xffffffff
  124.  
  125.         #add each compid to the checksum after rotating it by its occurrence count
  126.         for k in self.compids.keys():
  127.             cksum += (k << self.compids[k]%32 | k >> ( 32 - (self.compids[k]%32)))
  128.             cksum &=0xffffffff
  129.  
  130.         ## A convenient place for storing the checksum that was computing during checksum validation
  131.         self.checksum = cksum
  132.  
  133.         return cksum == checksum
  134.  
  135. if __name__ == "__main__":
  136.     ph = ParsedRichHeader(sys.argv[1])
  137.     for key in ph.compids.keys():
  138.         print ('compid: %08x\tcount: %d' % (key, ph.compids[key]))
  139.     if ph.valid_checksum:
  140.         print ("Checksum valid")
  141.     else:
  142.         print("Checksum not valid!")