Esempio n. 1
0
    def _rebasePage(self, ctx: MachOContext, pageOffset: int,
                    delta: int) -> None:
        locOff = pageOffset

        while True:
            locOff += delta
            locInfo = dyld_cache_slide_pointer3(self.dyldCtx.file, locOff)

            # It appears the delta encoded in the pointers are 64bit jumps...
            delta = locInfo.plain.offsetToNextPointer * 8

            if locInfo.auth.authenticated:
                newValue = locInfo.auth.offsetFromSharedCacheBase
                newValue += self.slideInfo.auth_value_add
            else:
                value51 = locInfo.plain.pointerValue
                top8Bits = value51 & 0x0007F80000000000
                bottom43Bits = value51 & 0x000007FFFFFFFFFF
                newValue = (top8Bits << 13) | bottom43Bits

            ctx.writeBytes(locOff, struct.pack("<Q", newValue))

            if delta == 0:
                break
            pass
        pass
Esempio n. 2
0
    def _rebasePage(self, ctx: MachOContext, pageStart: int,
                    pageOffset: int) -> None:
        """Process the slide info for a page.

		Args:
			pageStart: the file offset to the page.
			pageOffset: the offset from the pageStart to the first rebase location.
		"""

        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
        while delta != 0:
            loc = pageStart + pageOffset

            rawValue = self.dyldCtx.readFormat("<Q", loc)[0]
            delta = (rawValue & deltaMask) >> deltaShift

            newValue = rawValue & valueMask
            if valueMask != 0:
                newValue += valueAdd

            ctx.writeBytes(loc, struct.pack("<Q", newValue))
            pageOffset += delta
        pass
Esempio n. 3
0
def optimizeOffsets(extractionCtx: ExtractionContext) -> None:
    """Adjusts file offsets.

		MachO files in the Dyld Shared Cache are split up, which causes
	decached files to have really weird offsets. This fixes that.

	Args:
		machoCtx: A writable MachOContext.

	Returns:
		The processed MachO file.
	"""

    extractionCtx.statusBar.update(unit="Optimize Offsets")

    # The data in a MachO are defined by the segment load commands,
    # This includes the LinkEdit and MachO header
    machoCtx = extractionCtx.machoCtx

    # first change all the offset fields and record the shifts
    shiftProcedures = []  # Essentally a tuple with args to mmap.move
    dataHead = 0

    for segname, segment in machoCtx.segments.items():
        shiftDelta = dataHead - segment.seg.fileoff

        procedure = (
            segment.seg.fileoff + shiftDelta,  # dest
            segment.seg.fileoff,  # src
            segment.seg.filesize  # count
        )
        shiftProcedures.append(procedure)

        if segname == b"__LINKEDIT":
            _updateLinkEdit(machoCtx, shiftDelta)

        # Change the offsets for the segment and section structures
        segment.seg.fileoff += shiftDelta
        for sect in segment.sects.values():
            sect.offset += shiftDelta

        # update the data head to the next page aligned offset
        dataHead += segment.seg.filesize
        dataHead += _PAGE_SIZE - (dataHead % _PAGE_SIZE)

    # Now we need to actually move the segments.
    # 	We are moving the segments now because then we
    # 	don't have to constantly "re-point" various structures.
    for procedure in shiftProcedures:
        extractionCtx.statusBar.update(status="Moving Segments")

        machoCtx.file.move(procedure[0], procedure[1], procedure[2])

    # re-create the MachOContext so it reflects the new offsets
    extractionCtx.machoCtx = MachOContext(machoCtx.file, 0)
Esempio n. 4
0
def _extractImage(dyldFile: BufferedReader, dyldCtx: DyldContext,
                  image: dyld_cache_image_info, outputPath: str) -> None:
    """Extract an image and save it.

	The order of converters is essentally a reverse of Apple's SharedCacheBuilder
	"""

    logger = logging.getLogger()

    # get a a writable copy of the MachOContext
    machoFile = mmap.mmap(dyldFile.fileno(), 0, access=mmap.ACCESS_COPY)
    machoCtx = MachOContext(machoFile, dyldCtx.convertAddr(image.address))

    statusBar = progressbar.ProgressBar(
        prefix="{variables.unit} >> {variables.status} :: [",
        variables={
            "unit": "--",
            "status": "--"
        },
        widgets=[progressbar.widgets.AnimatedMarker(), "]"],
        redirect_stdout=True)

    extractionCtx = ExtractionContext(dyldCtx, machoCtx, statusBar, logger)

    slide_info.processSlideInfo(extractionCtx)
    linkedit_optimizer.optimizeLinkedit(extractionCtx)
    stub_fixer.fixStubs(extractionCtx)
    objc_fixer.fixObjC(extractionCtx)

    macho_offset.optimizeOffsets(extractionCtx)

    # Write the MachO file
    with open(outputPath, "wb") as outFile:
        statusBar.update(unit="Extractor", status="Writing file")

        newMachoCtx = extractionCtx.machoCtx

        # get the size of the file
        linkEditSeg = newMachoCtx.segments[b"__LINKEDIT"].seg
        fileSize = linkEditSeg.fileoff + linkEditSeg.filesize

        newMachoCtx.file.seek(0)
        outFile.write(newMachoCtx.file.read(fileSize))

    statusBar.update(unit="Extractor", status="Done")
def _imageRunner(dyldPath: str, imageIndex: int) -> None:
    level = logging.DEBUG
    loggingStream = io.StringIO()

    # setup logging
    logger = logging.getLogger(f"Worker: {imageIndex}")
    handler = logging.StreamHandler(loggingStream)
    formatter = logging.Formatter(
        fmt=
        "{asctime}:{msecs:03.0f} [{levelname:^9}] {filename}:{lineno:d} : {message}",  # noqa
        datefmt="%H:%M:%S",
        style="{",
    )

    handler.setFormatter(formatter)
    logger.addHandler(handler)
    logger.setLevel(level)

    # process the image
    with open(dyldPath, "rb") as f:
        dyldFile = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
        dyldCtx = DyldContext(dyldFile)
        imageOffset = dyldCtx.convertAddr(dyldCtx.images[imageIndex].address)

        machoFile = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_COPY)
        machoCtx = MachOContext(machoFile, imageOffset)

        extractionCtx = ExtractionContext(dyldCtx, machoCtx,
                                          _DummyProgressBar(), logger)

        try:
            linkedit_optimizer.optimizeLinkedit(extractionCtx)
            stub_fixer.fixStubs(extractionCtx)
            objc_fixer.fixObjC(extractionCtx)
        except Exception as e:
            logger.exception(e)
        pass

    # cleanup
    handler.close()
    return loggingStream.getvalue()
Esempio n. 6
0
def runForAllImages(dyldFile: BufferedReader,
                    dyldCtx: DyldContext,
                    statusBar: progressbar.ProgressBar,
                    logger: logging.Logger,
                    startIndex: int = 0,
                    stopIndex: int = -1) -> None:
    total = dyldCtx.header.imagesCount

    for index, imageData in enumerate(dyldCtx.images[startIndex:], startIndex):
        if index == stopIndex:
            break

        # TODO: Imp sub caches
        imageOffset = dyldCtx.convertAddr(imageData.address)
        imagePath = dyldCtx.readString(imageData.pathFileOffset)[0:-1]
        imagePath = imagePath.decode("utf-8")
        imageName = imagePath.split("/")[-1]

        # Make a writable copy of the dyld file
        machoFile = mmap.mmap(dyldFile.fileno(), 0, access=mmap.ACCESS_COPY)
        machoCtx = MachOContext(machoFile, imageOffset)

        extractionCtx = ExtractionContext(dyldCtx, machoCtx, statusBar, logger)

        # Test space start

        slide_info.processSlideInfo(extractionCtx)
        linkedit_optimizer.optimizeLinkedit(extractionCtx)
        stub_fixer.fixStubs(extractionCtx)
        objc_fixer.fixObjC(extractionCtx)
        macho_offset.optimizeOffsets(extractionCtx)

        # Test space end

        logger.info(f"processed: ({index + 1}/{total}): {imageName}")
        pass

    statusBar.update(unit="Extractor", status="Done")
    pass
Esempio n. 7
0
def _imageRunner(dyldPath: str, imageIndex: int) -> None:
    level = logging.DEBUG
    loggingStream = io.StringIO()

    # setup logging
    logger = logging.getLogger(f"Worker: {imageIndex}")
    handler = logging.StreamHandler(loggingStream)
    formatter = logging.Formatter(
        fmt=
        "{asctime}:{msecs:03.0f} [{levelname:^9}] {filename}:{lineno:d} : {message}",  # noqa
        datefmt="%H:%M:%S",
        style="{",
    )

    handler.setFormatter(formatter)
    logger.addHandler(handler)
    logger.setLevel(level)

    # process the image
    with open(dyldPath, "rb") as f:
        dyldCtx = DyldContext(f)

        subCacheFiles: List[BinaryIO] = []
        try:
            # add sub caches if there are any
            if dyldCtx.hasSubCaches():
                subCacheFileCtxs, subCacheFiles = _openSubCaches(
                    dyldPath, dyldCtx.header.numSubCaches)
                dyldCtx.addSubCaches(subCacheFileCtxs)
                pass

            machoOffset, context = dyldCtx.convertAddr(
                dyldCtx.images[imageIndex].address)
            machoCtx = MachOContext(context.fileObject, machoOffset, True)

            # Add sub caches if necessary
            if dyldCtx.hasSubCaches():
                mappings = dyldCtx.mappings
                mainFileMap = next((mapping[0] for mapping in mappings
                                    if mapping[1] == context))
                machoCtx.addSubfiles(mainFileMap,
                                     ((m, ctx.makeCopy(copyMode=True))
                                      for m, ctx in mappings))
                pass

            extractionCtx = ExtractionContext(dyldCtx, machoCtx,
                                              _DummyProgressBar(), logger)

            # TODO: implement a way to select convertors
            slide_info.processSlideInfo(extractionCtx)
            linkedit_optimizer.optimizeLinkedit(extractionCtx)
            stub_fixer.fixStubs(extractionCtx)
            objc_fixer.fixObjC(extractionCtx)
            macho_offset.optimizeOffsets(extractionCtx)

        except Exception as e:
            logger.exception(e)
            pass

        finally:
            for file in subCacheFiles:
                file.close()
                pass
            pass
        pass

    # cleanup
    handler.close()
    return loggingStream.getvalue()
Esempio n. 8
0
	def run(self):
		# check if the optimization flag is set
		imageInfo = None
		imageInfoFile = None
		for seg in self._machoCtx.segmentsI:
			if b"__objc_imageinfo" in seg.sects:
				imageInfo = seg.sects[b"__objc_imageinfo"]
				imageInfoFile = self._machoCtx.ctxForAddr(seg.seg.vmaddr)
				break
			pass

		if not imageInfo:
			return

		flagsOff = self._dyldCtx.convertAddr(imageInfo.addr)[0]
		flags = imageInfoFile.readFormat(
			"<I",
			flagsOff + 4,
		)[0]
		if not flags & 0x8:
			self._logger.info("ObjC was not optimized by Dyld, not fixing ObjC.")
			return

		# Removed the optimized objc bit
		flags &= 0xfffffff7
		imageInfoFile.writeBytes(flagsOff, struct.pack("<I", flags))

		self._createExtraSegment()

		# Get the libobjc.A.dylib image
		for image in self._dyldCtx.images:
			path = self._dyldCtx.readString(image.pathFileOffset)
			if b"libobjc.A.dylib" in path:
				offset, ctx = self._dyldCtx.convertAddr(image.address)
				self._libobjcImage = MachOContext(ctx.fileObject, offset)
				break
			pass
		else:
			self._logger.error("Unable to find libobjc.A.dylib")
			return

		self._checkMethodNameStorage()

		# caches that map the original definition address
		# to its new processed address.
		self._categoryCache: Dict[int, int] = {}
		self._classCache: Dict[int, int] = {}
		self._classDataCache: Dict[int, int] = {}
		self._ivarListCache: Dict[int, int] = {}
		self._protocolListCache: Dict[int, int] = {}
		self._protocolCache: Dict[int, int] = {}
		self._propertyListCache: Dict[int, int] = {}
		self._methodListCache: Dict[int, int] = {}
		self._stringCache: Dict[int, int] = {}
		self._intCache: Dict[int, int] = {}
		self._methodNameCache: Dict[int, int] = {}

		# connects a selrefs old target to its pointer address
		self._selRefCache: Dict[int, int] = {}

		# A list of class pointers that are being processed.
		self._classesProcessing: List[int] = []

		# A list of pointers that need to be updated at the end
		# The first int is the address to the pointer that needs
		# to be changed. The second int is the address of the
		# target class
		self._futureClasses: List[Tuple[int, int]] = []

		self._processSections()
		self._finalizeFutureClasses()

		_ObjCSelectorFixer(self._extractionCtx, self).run()

		self._checkSpaceConstraints()
		self._addExtraDataSeg()
		pass