def set_in_fat(self, value: int) -> None: """ Sets cluster in FAT to certain value. Firstly, we split the target value into 3 half bytes (max value is 0xfff). Then we could encounter two situations: 1. if the cluster index (indexed from zero) is even, we set the full byte computed by self.cluster_id_to_logical_position_in_bits and the second half of the consequent byte. Order of half bytes is 2, 1, 3. 2. if the cluster index is odd, we set the first half of the computed byte and the full consequent byte. Order of half bytes is 1, 3, 2. """ # value must fit into number of bits of the fat (12, 16 or 32) assert value <= (1 << self.fatfs_state.fatfs_type) - 1 half_bytes = split_by_half_byte_12_bit_little_endian(value) if self.fatfs_state.fatfs_type == FAT12: if self.fat_cluster_address % 8 == 0: self.fatfs_state.binary_image[ self.real_cluster_address] = build_byte( half_bytes[1], half_bytes[0]) self._set_second_half_byte(self.real_cluster_address + 1, half_bytes[2]) elif self.fat_cluster_address % 8 != 0: self._set_first_half_byte(self.real_cluster_address, half_bytes[0]) self.fatfs_state.binary_image[self.real_cluster_address + 1] = build_byte( half_bytes[2], half_bytes[1]) elif self.fatfs_state.fatfs_type == FAT16: self.fatfs_state.binary_image[self.real_cluster_address:self. real_cluster_address + 2] = Int16ul.build(value)
def _build_entry_long(names: List[bytes], checksum: int, order: int, is_last: bool, entity_type: int) -> bytes: """ Long entry starts with 1 bytes of the order, if the entry is the last in the chain it is or-masked with 0x40, otherwise is without change (or masked with 0x00). The following example shows 3 entries: first two (0x2000-0x2040) are long in the reverse order and the last one (0x2040-0x2060) is short. The entries define file name "thisisverylongfilenama.txt". 00002000: 42 67 00 66 00 69 00 6C 00 65 00 0F 00 43 6E 00 Bg.f.i.l.e...Cn. 00002010: 61 00 6D 00 61 00 2E 00 74 00 00 00 78 00 74 00 a.m.a...t...x.t. 00002020: 01 74 00 68 00 69 00 73 00 69 00 0F 00 43 73 00 .t.h.i.s.i...Cs. 00002030: 76 00 65 00 72 00 79 00 6C 00 00 00 6F 00 6E 00 v.e.r.y.l...o.n. 00002040: 54 48 49 53 49 53 7E 31 54 58 54 20 00 00 00 00 THISIS~1TXT..... 00002050: 21 00 00 00 00 00 00 00 21 00 02 00 15 00 00 00 !.......!....... """ order |= (0x40 if is_last else 0x00) long_entry: bytes = ( Int8ul.build(order) + # order of the long name entry (possibly masked with 0x40) names[0] + # first 5 characters (10 bytes) of the name part Int8ul.build(entity_type) + # one byte entity type ATTR_LONG_NAME Int8ul.build(0) + # one byte of zeros Int8ul.build(checksum) + # lfn_checksum defined in utils.py names[1] + # next 6 characters (12 bytes) of the name part Int16ul.build(0) + # 2 bytes of zeros names[2]) # last 2 characters (4 bytes) of the name part return long_entry
def parse_entry_long(entry_bytes_: bytes, my_check: int) -> dict: order_ = Int8ul.parse(entry_bytes_[0:1]) names0 = entry_bytes_[1:11] if Int8ul.parse(entry_bytes_[12:13]) != 0 or Int16ul.parse( entry_bytes_[26:28]) != 0 or Int8ul.parse( entry_bytes_[11:12]) != 15: return {} if Int8ul.parse(entry_bytes_[13:14]) != my_check: return {} names1 = entry_bytes_[14:26] names2 = entry_bytes_[28:32] return { 'order': order_, 'name1': names0, 'name2': names1, 'name3': names2, 'is_last': bool(order_ & 0x40 == 0x40) }
def _parse(self): try: header = PascalUtf16(Int16ul).parse(self._raw) except (ConstructError, UnicodeDecodeError) as e: raise InvalidIPLError('Invalid IPL structure: {0}\n{1}'.format( e, hexdump(self._raw[:0x200]))) try: # IPL's code section is usually contained is the first 9 sectors. The remaining sectors are filled with # padding but it appears that the last (15th) sector can sometimes hold data not related to the boot process # and we need to exclude that from hash calculation. invariantCode = self._raw[:14 * 512] except IndexError: raise InvalidIPLError( 'Invalid sample size for IPL: {0} (should be 15 * 512-bytes sectors)' .format(len(self._raw))) expectedLoader = None # Starting with NT 6.2, IPL has a localized string that must be excluded from hash computation. # The difference between these two kinds of IPL can be told from the instruction located at 0x56 : # a Jump Short (EB) in case of IPL<6.2 or a Jump Near (E9) otherwise if header == 'BOOTMGR' and self._raw[0x56].encode( 'hex').upper() == 'E9': # The offset of the localized string seems to be stored in a DWORD at 0x117 (just before the beginning # of the assembly code). But the value seems to be an offset relative to the start of the whole # boot record (including the VBR) and not just the IPL. # Therefore we need to substract 0x200 to get the offset inside the IPL. strOffset = Int16ul.parse(self._raw[0x117:]) - 0x200 # Exclude from hash calculation everything between the string offset and the beginning of code invariantCode = invariantCode[:strOffset] + invariantCode[0x119:] expectedLoader = 'NT6.2+ IPL' codeHash = hashlib.sha256(invariantCode) self._matchHash(codeHash, expectedLoader) # If no whitelisted signature matched, try some simple heuristics to flag this IPL as malicious # Note that the self._checkCode method is only given the "stripped" code section to help the disassembling. # This will obviously leads to broken offsets, but it doesn't matter since the heuristics don't use them. if len(self._signature) == 0: self._checkCode(invariantCode)
def _getInvariantCode(self, vbrType, vbrStruct): """ Helper method that finds all the sections of the boot code that can be hashed and compared to a whitelist. This means that localized strings and other variable parameters (BPB, etc...) are excluded. Currently, this method only supports NTFS and Bitlocker VBR. Args: vbrType: unicode string corresponding to the VBR type ('NTFS' or 'bitlocker') vbrStruct: construct.container of the VBR Returns: 2-tuple (unicode string of expected loader, concatenated strings of invariant sections of code) """ codeStart = 0 codeEnd = None invariantCode = str() expectedLoader = None if vbrType == 'NTFS': # The first three bytes are a jump over the NTFS BPB to where the code really starts (0x54) and a NOP invariantCode += vbrStruct.JumpOverBPB codeStart = 0x54 # NTFS VBR contains localized strings which must be excluded from the hash computation. # Before Windows 8, these strings are located at 4 different offsets which can be calculated by adding # 0x100 to the values respectively stored in bytes 0x1f8, 0x1f9, 0x1fa and 0x1fb. # Starting from Windows 8, these strings are located at 3 different offsets which are directly stored in # little endian words respectively at 0x1f6, 0x1f8 and 0x1fa # Since there is no easy way to tell which version of Windows we are dealing with beforehand, we first # assume it is a Windows < 8 by testing 0x1f8 against all the known first offset. If all tests fail, assume # it is Windows >= 8 and check 0x1f6 against the only known first offset (to date) firstStrOffset = Int8ub.parse(self._raw[0x1f8]) # Windows NT5 if firstStrOffset == 0x83: expectedLoader = 'NT5.1/NT5.2 VBR' codeEnd = 0x100 + firstStrOffset # Windows NT6.0 elif firstStrOffset == 0x80: expectedLoader = 'NT6.0 VBR' codeEnd = 0x100 + firstStrOffset # Windows NT6.1 elif firstStrOffset == 0x8c: expectedLoader = 'NT6.1 VBR' codeEnd = 0x100 + firstStrOffset # Windows NT6.2+ else: firstStrOffset = Int16ul.parse(self._raw[0x1f6:0x1f8]) if firstStrOffset == 0x18a: expectedLoader = 'NT6.2+ VBR' codeEnd = firstStrOffset if codeEnd is None: self._suspiciousBehaviour.append( 'Invalid string offset: {0:#x}'.format(firstStrOffset)) self._logger.debug( 'First localized string offset is wrong for a NTFS VBR: {0:#x}. ' 'It should be 0x83, 0x80, 0x8c or 0x18a.'.format( firstStrOffset)) codeEnd = 0 elif vbrType == 'bitlocker': expectedLoader = 'NT6.1+ Bitlocker VBR' # The first three bytes are a jump over the NTFS BPB to where the code really starts (0x5A) and a NOP invariantCode += vbrStruct.JumpOverBPB # First section of code (_BITLOCKER_VBR_STRUCT.Code1) invariantCode += vbrStruct.Code1 # In the second section of code, there are localized strings which must be excluded from hash computation. # Their offsets are stored in the last 3 bytes before the VBR signature (0x55aa). # For Windows 8, 8.1 and 10, the first string offset seems to always be 0x100 (ie. FirstStrOffset = 0x00) if vbrStruct.FirstStrOffset != 0: self._suspiciousBehaviour.append( 'Invalid string offset: {0:#x}'.format( vbrStruct.FirstStrOffset)) self._logger.debug( 'First localized string offset is wrong for a Bitlocker VBR. ' 'It should be 0x00) : {0:#x}'.format( vbrStruct.FirstStrOffset)) codeStart = 0xc8 # Offset of Code2 codeEnd = 0x100 + vbrStruct.FirstStrOffset else: raise NotImplementedError( 'VBR type "{0}" is not implemented yet'.format(vbrType)) self._logger.debug( 'Expecting {0}. Code starts at {1:#x} and ends at {2:#x}'.format( expectedLoader, codeStart, codeEnd)) invariantCode += self._raw[codeStart:codeEnd] return expectedLoader, invariantCode
def uint16(self): return Int16ul.parse(self.read(2))
def split_by_half_byte_12_bit_little_endian(value: int) -> DATETIME: value_as_bytes: bytes = Int16ul.build(value) return value_as_bytes[0] & 0x0f, value_as_bytes[0] >> 4, value_as_bytes[ 1] & 0x0f
def split_by_half_byte_12_bit_little_endian( value: int) -> Tuple[int, int, int]: value_as_bytes = Int16ul.build(value) return value_as_bytes[0] & 0x0f, value_as_bytes[0] >> 4, value_as_bytes[ 1] & 0x0f
try: from typing import Any, Optional, Tuple except ImportError: pass RISCV_GP_REGS_COUNT = 32 PRSTATUS_SIZE = 204 PRSTATUS_OFFSET_PR_CURSIG = 12 PRSTATUS_OFFSET_PR_PID = 24 PRSTATUS_OFFSET_PR_REG = 72 ELF_GREGSET_T_SIZE = 128 PrStruct = Struct( Padding(PRSTATUS_OFFSET_PR_CURSIG), 'pr_cursig' / Int16ul, Padding(PRSTATUS_OFFSET_PR_PID - PRSTATUS_OFFSET_PR_CURSIG - Int16ul.sizeof()), 'pr_pid' / Int32ul, Padding(PRSTATUS_OFFSET_PR_REG - PRSTATUS_OFFSET_PR_PID - Int32ul.sizeof()), 'regs' / Int32ul[RISCV_GP_REGS_COUNT], Padding(PRSTATUS_SIZE - PRSTATUS_OFFSET_PR_REG - ELF_GREGSET_T_SIZE)) class RiscvMethodsMixin(BaseArchMethodsMixin): @staticmethod def get_registers_from_stack(data, grows_down): # type: (bytes, bool) -> Tuple[list[int], Optional[dict[int, int]]] regs = Int32ul[RISCV_GP_REGS_COUNT].parse(data) if not grows_down: raise ESPCoreDumpLoaderError( 'Growing up stacks are not supported for now!') return regs, None
def checksum_16(data): c = 0 for i in range(len(data) // 2): c = c + (data[i * 2 + 1] << 8) + data[i * 2] return c & 0xFFFF WriteCommand = Struct("address" / Int16ul, "value" / Int16ul) WriteMessageRequest = Struct( "fields" / RawCopy( Struct( "length" / Rebuild( Int16ul, len_(this.items) * WriteCommand.sizeof() // Int16ul.sizeof() + 2, ), "command" / Const(vlxDevConstants.WS_WEB_UI_COMMAND_WRITE_DATA, Int16ul), "items" / WriteCommand[len_(this.items)], )), "checksum" / Checksum(Int16ul, lambda data: checksum_16(data), this.fields.data), ) ReadTableResponse = GreedyRange(Int16ub) ReadTableRequest = Struct( "fields" / RawCopy( Struct(