def readLazyBind(self, offset: int) -> typing.Tuple[int, int, bytes]: """ Reads the OPCODE_SET_SEGMENT_AND_OFFSET_ULEB and OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM from the lazy bind info at the given offset. """ dyldInfo = self.machoFile.getLoadCommand((MachO.LoadCommands.LC_DYLD_INFO, MachO.LoadCommands.LC_DYLD_INFO_ONLY)) lazyBindData = dyldInfo.lazy_bindData segIndex = None segOff = None flags = None # assume that OPCODE_SET_SEGMENT_AND_OFFSET_ULEB comes first # and then OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM while offset < dyldInfo.lazy_bind_size: imm = lazyBindData[offset] & MachO.Bind.BIND_IMMEDIATE_MASK opcode = lazyBindData[offset] & MachO.Bind.BIND_OPCODE_MASK offset += 1 if opcode == MachO.Bind.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: segIndex = imm segOff, ulebLen = Uleb128.readUleb128(lazyBindData, offset) offset += ulebLen elif opcode == MachO.Bind.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: flagsEnd = lazyBindData.index(b"\x00", offset) + 1 flags = lazyBindData[offset:flagsEnd] break if segIndex is None or segOff is None or flags is None: raise Exception("Incorrect parsing") return (segIndex, segOff, flags)
def processNode(self, exportOff: int, cumStrHead: int) -> None: """Recursively process a node. Parameters ---------- exportOff : int The offset in the export data to the node. cumStrHead : int The end of the string in the cumulative string, not including the null byte. Raises ------ Exception raises "Export off beyond export length" if the exportOff is beyond the length of the export data. """ if exportOff >= len(self.exports): raise Exception("Export off beyond export length") # read the entry entryReadHead = exportOff terminalSize, ulebSize = Uleb128.readUleb128(self.exports, entryReadHead) entryReadHead += ulebSize if terminalSize != 0: entry = TrieEntry() entry.nodeOffset = exportOff currentStrEnd = self.cumulativeStr.index(b"\x00") + 1 entry.name = bytes(self.cumulativeStr[0:currentStrEnd]) entry.flags, ulebSize = Uleb128.readUleb128(self.exports, entryReadHead) entryReadHead += ulebSize if entry.flags & Export.EXPORT_SYMBOL_FLAGS_REEXPORT: entry.address = 0 entry.other, ulebSize = Uleb128.readUleb128(self.exports, entryReadHead) entryReadHead += ulebSize importNameEnd = self.exports.index(b"\x00", entryReadHead) + 1 entry.importName = bytes(self.exports[entryReadHead:importNameEnd]) else: entry.address, ulebSize = Uleb128.readUleb128(self.exports, entryReadHead) entryReadHead += ulebSize entry.importName = None if entry.flags & Export.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER: entry.other, ulebSize = Uleb128.readUleb128(self.exports, entryReadHead) entryReadHead += ulebSize else: entry.other = 0 self.entries.append(entry) # process children childrenCount = int(self.exports[exportOff + terminalSize + 1]) childrenReadHead = exportOff + terminalSize + 2 for _ in range(0, childrenCount): # read the extra chars edgeStrEnd = self.exports.index(b"\x00", childrenReadHead) + 1 edgeStr = self.exports[childrenReadHead:edgeStrEnd] childrenReadHead += len(edgeStr) self.cumulativeStr[cumStrHead:] = edgeStr # process child childNodeOff, ulebSize = Uleb128.readUleb128(self.exports, childrenReadHead) childrenReadHead += ulebSize self.processNode(childNodeOff, cumStrHead + (len(edgeStr) - 1)) pass
def rebasePage(self, pageOffset: int, firstRebaseOffset: int, segment: MachO.segment_command_64) -> None: """ processes the rebase infomation in a page ### parameters pageOffset: int The file offset of the page. firstRebaseOffset: int The offset from the start of the page to the first rebase location. segment: segment_command_64 The segment to rebase. """ segmentIndex = self.machoFile.loadCommands.index(segment) deltaMask = self.slideInfo.delta_mask valueMask = ~deltaMask valueAdd = self.slideInfo.value_add # basically __builtin_ctzll(deltaMask) - 2; deltaShift = "{0:b}".format(deltaMask) deltaShift = len(deltaShift) - len(deltaShift.rstrip("0")) deltaShift = deltaShift - 2 delta = 1 rebaseOffset = firstRebaseOffset while delta != 0: realLoc = pageOffset + rebaseOffset self.dyldFile.file.seek(realLoc) rawValueBytes = self.dyldFile.file.read(8) rawValue = struct.unpack("<Q", rawValueBytes)[0] delta = (rawValue & deltaMask) >> deltaShift value = rawValue & valueMask if value: value += valueAdd # if the location is within the segment, adjust the data if realLoc >= segment.fileoff and realLoc < (segment.fileoff + segment.filesize): self.slideLocation(realLoc, value, segment) # add a rebase entry self.rebaseInfo.append( MachO.Rebase.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | segmentIndex) self.rebaseInfo += Uleb128.encodeUleb128(realLoc - segment.fileoff) self.rebaseInfo.append( MachO.Rebase.REBASE_OPCODE_DO_REBASE_IMM_TIMES | 0x1) rebaseOffset += delta