Skip to content
This repository has been archived by the owner on Feb 13, 2020. It is now read-only.

isign failing on newer IPA's - LC_CODE_SIGNATURE #120

Open
gregkuhnert opened this issue Jan 14, 2019 · 6 comments
Open

isign failing on newer IPA's - LC_CODE_SIGNATURE #120

gregkuhnert opened this issue Jan 14, 2019 · 6 comments

Comments

@gregkuhnert
Copy link

gregkuhnert commented Jan 14, 2019

I have played with iSign for a while now. Newer IPA's since about September last year are failing. The problem appears to be a failure parsing the LC_CODE_SIGNATURE LoadCommand in a decrypted IPA. There are no available forks with any fixes to this problem. But, older IPA's work just fine.

A traceback is available below showing 78 of 79 LoadCommand entries parsed.

Traceback (most recent call last):
File "/bin/isign", line 5, in
pkg_resources.run_script("isign==1.6.15.1547449924.dev43-root", "isign")
File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 540, in run_script
self.require(requires)[0].run_script(script_name, ns)
File "/usr/lib/python2.7/site-packages/pkg_resources.py", line 1455, in run_script
execfile(script_filename, namespace, namespace)
File "/usr/lib/python2.7/site-packages/isign-1.6.15.1547449924.dev43_root-py2.7.egg/EGG-INFO/scripts/isign", line 252, in
isign.resign(app_path, **kwargs)
File "/usr/lib/python2.7/site-packages/isign-1.6.15.1547449924.dev43_root-py2.7.egg/isign/isign.py", line 85, in resign
signer_helper_path)
File "/usr/lib/python2.7/site-packages/isign-1.6.15.1547449924.dev43_root-py2.7.egg/isign/archive.py", line 397, in resign
ua.bundle.resign(deep, signer, provisioning_profile, alternate_entitlements_path)
File "/usr/lib/python2.7/site-packages/isign-1.6.15.1547449924.dev43_root-py2.7.egg/isign/bundle.py", line 268, in resign
super(App, self).resign(deep, signer)
File "/usr/lib/python2.7/site-packages/isign-1.6.15.1547449924.dev43_root-py2.7.egg/isign/bundle.py", line 183, in resign
self.sign(deep, signer)
File "/usr/lib/python2.7/site-packages/isign-1.6.15.1547449924.dev43_root-py2.7.egg/isign/bundle.py", line 177, in sign
signer)
File "/usr/lib/python2.7/site-packages/isign-1.6.15.1547449924.dev43_root-py2.7.egg/isign/signable.py", line 44, in init
self.m = macho.MachoFile.parse_stream(self.f)
File "/usr/lib/python2.7/site-packages/construct-2.5.2-py2.7.egg/construct/core.py", line 198, in parse_stream
return self._parse(stream, Container())
File "/usr/lib/python2.7/site-packages/construct-2.5.2-py2.7.egg/construct/core.py", line 670, in _parse
subobj = sc._parse(stream, context)
File "/usr/lib/python2.7/site-packages/construct-2.5.2-py2.7.egg/construct/core.py", line 851, in _parse
obj = self.cases.get(key, self.default)._parse(stream, context)
File "/usr/lib/python2.7/site-packages/construct-2.5.2-py2.7.egg/construct/core.py", line 670, in _parse
subobj = sc._parse(stream, context)
File "/usr/lib/python2.7/site-packages/construct-2.5.2-py2.7.egg/construct/core.py", line 271, in _parse
return self.subcon._parse(stream, context)
File "/usr/lib/python2.7/site-packages/construct-2.5.2-py2.7.egg/construct/core.py", line 445, in _parse
raise ArrayError("expected %d, found %d" % (count, c), sys.exc_info()[1])
construct.core.ArrayError: ("expected 79, found 78", ArrayError("expected 6, found 3", SwitchError("no default case defined",)))

@gregkuhnert gregkuhnert changed the title isign failing on newer IPA's isign failing on newer IPA's - LC_CODE_SIGNATURE Jan 16, 2019
@gregkuhnert
Copy link
Author

gregkuhnert commented Jan 16, 2019

@gregkuhnert
Copy link
Author

I've done more research, and I think I have found the root cause. Looking at macho_cs.py, we see that the construct Switch on line 173 has no default action. However, there is a default in the enum above.

One of the magic numbers is CSMAGIC_ENTITLEMENT=0xfade7171 which relates to an entitlements plist. By examining new IPA files, we see a new magic number 0xfade7172. By manually examining the data in this part of the signing object, we see what appears to be a binary plist that contains the same data that is in the xml plist stored in CSMAGIC_ENTITLEMENT.

Next action: We need to create something in macho_cs.py that will parse and emit this object, and update other parts of isign where/as required to manipulate the data.

@gregkuhnert
Copy link
Author

By adding the two lines shown below to macho_cs.py, isign now completely parses these new IPA's without error. However, there are errors when "emitting". This proves my theory that the parsing is getting an exception due to a missing magic number for this new data type.

However, my python skills are fairly limited. I could use some help from someone with some python skills that are interested in solving this issue.

Blob_ = Struct("Blob",
               Enum(UBInt32("magic"),
                    CSMAGIC_REQUIREMENT=0xfade0c00,
                    CSMAGIC_REQUIREMENTS=0xfade0c01,
                    CSMAGIC_CODEDIRECTORY=0xfade0c02,
                    CSMAGIC_ENTITLEMENT=0xfade7171,  # actually, this is kSecCodeMagicEntitlement, and not defined in the C version
		    CSMAGIC_UNKNOWN=0xfade7172,  ####### NEW LINE INSERTED ######
                    CSMAGIC_BLOBWRAPPER=0xfade0b01,  # and this isn't even defined in libsecurity_codesigning; it's in _utilities
                    CSMAGIC_EMBEDDED_SIGNATURE=0xfade0cc0,
                    CSMAGIC_DETACHED_SIGNATURE=0xfade0cc1,
                    CSMAGIC_CODE_SIGN_DRS=0xfade0c05,
                    _default_=Pass,
                    ),
               UBInt32("length"),
               Peek(Switch("data", lambda ctx: ctx['magic'],
                           {'CSMAGIC_REQUIREMENT': Requirement,
                            'CSMAGIC_REQUIREMENTS': Entitlements,
                            'CSMAGIC_CODEDIRECTORY': CodeDirectory,
                            'CSMAGIC_ENTITLEMENT': Entitlement,
                            'CSMAGIC_BLOBWRAPPER': BlobWrapper,
                            'CSMAGIC_EMBEDDED_SIGNATURE': SuperBlob,
                            'CSMAGIC_DETACHED_SIGNATURE': SuperBlob,
                            'CSMAGIC_CODE_SIGN_DRS': SuperBlob,
			    'CSMAGIC_UNKNOWN': Data,  ####### NEW LINE INSERTED ######
                            })),
               OnDemand(Bytes('bytes', lambda ctx: ctx['length'] - 8)),
               )

@MAVProxyUser
Copy link

What happens if you change the name to
CSMAGIC_ENTITLEMENT from 0xfade7171 to 0xfade7172, and simply remove your _UNKNOWN example. This may break old instances, but fix this one?

@gregkuhnert
Copy link
Author

I wish it were that simple. The newer IPA's have both the old 7171 magic number as well as the 7172 magic number. Also, the 7171 data is an xml plist of entitlements. The new 7172 data appears to be a binary encoded entitlement plist. If we just swap, it will throw exceptions because of the missing 7171 magic in the inbound stream.

@sgleadow
Copy link

Did anyone find a solution to this? Saucelabs must have a solution if they're still using this tool for re-codesigning in their device cloud.

I'm reading the source code for Apple's codesign to see what's going on.

In terms of the difference between 0xfade7171 and 0xfade7172, I can see:

	kSecCodeMagicEntitlement = 0xfade7171,		/* entitlement blob */
	kSecCodeMagicEntitlementDER = 0xfade7172,	/* entitlement DER blob */

and a comment:

	@constant kSecCodeMagicEntitlement Magic number for a standard entitlement blob.
 	@constant kSecCodeMagicEntitlementDER Magic number for a DER entitlement blob.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants