Wednesday, 27 April 2016

Examining Windows Kernel-mode Stacks via Triage Dumps





It can occasionally be useful to be able to inspect the kernel-mode stack of a thread and Microsoft does indeed provide a tool (in the Debugging Tools for Windows kit) that enables this: KDbgCtrl. One of the tool’s command-line options is:

 
-td ProcessID File
Obtains a kernel triage dump file. Enter the process ID and a name for the dump file.
There is not much publically available information about “kernel triage dump” files; the best references that I know of are:
 
 
Motivational Example
 
Examining the user-mode stacks of an unresponsive IIS worker process showed that many threads were stuck in this state:
 
ntdll!NtQueryFullAttributesFile()+0x12
KERNELBASE!GetFileAttributesExW()+0x93
cachfile!CACHED_FILE_INFO::CheckIfFileHasChanged()+0xf9
iiscore!W3_SERVER::GetFileInfo()+0x1c3
defdoc!W3_DEFAULT_DOC_HANDLER::DoWork()+0x511
defdoc!RequestDoWork()+0x17f
defdoc!CIISHttpModule::OnExecuteRequestHandler()+0x19
iiscore!NOTIFICATION_CONTEXT::RequestDoWork()+0x128
iiscore!NOTIFICATION_CONTEXT::CallModulesInternal()+0x305
iiscore!NOTIFICATION_CONTEXT::CallModules()+0x28
iiscore!W3_CONTEXT::DoStateRequestExecuteHandler()+0x36
iiscore!W3_CONTEXT::DoWork()+0xd7
iiscore!W3_MAIN_CONTEXT::StartNotificationLoop()+0x49
iiscore!W3_CONTEXT::ExecuteRequest()+0x20c
isapi!ISAPI_REQUEST::ExecuteUrl()+0x3e6
isapi!SSFExecuteUrl()+0x7f4
isapi!ServerSupportFunction()+0x59b
webengine4!EcbExecuteUrlUnicode()+0x12e
 
Using kernel triage dumps, it was possible to see that the kernel stack of most of these threads looked like this:
 
nt!DbgkpLkmdSnapThreadInContext+0x79
nt!DbgkpLkmdSnapThreadApc+0x3b
nt!KiDeliverApc+0x1e3
nt!KiCommitThreadWait+0x3dd
nt!KeWaitForSingleObject+0x19f
nt!ExpWaitForResource+0xae
nt!ExAcquireResourceExclusiveLite+0x14f
rdbss!_RxAcquireFcb+0x254
rdbss!RxFindOrCreateFcb+0x3d8
rdbss!RxCreateFromNetRoot+0x53a
rdbss!RxCommonCreate+0x34f
rdbss!RxFsdCommonDispatch+0x870
rdbss!RxFsdDispatch+0x224
mrxsmb!MRxSmbFsdDispatch+0xc0
mup!MupiCallUncProvider+0x169
mup!MupStateMachine+0x165
mup!MupCreate+0x31d
fltmgr!FltpLegacyProcessingAfterPreCallbacksCompleted+0x24f
fltmgr!FltpCreate+0x2a9
nt!IopParseDevice+0x14e2
nt!ObpLookupObjectName+0x784
nt!ObOpenObjectByName+0x306
nt!NtQueryFullAttributesFile+0x14f
nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff880`1adb9c20)
 
The kernel-mode stack of another thread in the process partially explained what was happening:
 
nt!DbgkpLkmdSnapThreadInContext+0x79
nt!DbgkpLkmdSnapThreadApc+0x3b
nt!KiDeliverApc+0x1e3
nt!KiCommitThreadWait+0x3dd
nt!KeWaitForMultipleObjects+0x272
nt!FsRtlCancellableWaitForMultipleObjects+0xac
mrxsmb!SmbCeWaitForCompletionAndFinalizeExchangeEx+0x70
mrxsmb20!MRxSmb2Create+0x658
mrxsmb!SmbpShellCreateWithNewStack+0x1b
nt!KySwitchKernelStackCallout+0x27 (TrapFrame @ fffff880`12d6fe20)
nt!KiSwitchKernelStackContinue
nt!KeExpandKernelStackAndCalloutEx+0x2a2
nt!KeExpandKernelStackAndCallout+0x12
mrxsmb!SmbShellCreate+0x5d
rdbss!RxCollapseOrCreateSrvOpen+0x4de
rdbss!RxCreateFromNetRoot+0x608
rdbss!RxCommonCreate+0x34f
rdbss!RxFsdCommonDispatch+0x870
rdbss!RxFsdDispatch+0x224
mrxsmb!MRxSmbFsdDispatch+0xc0
mup!MupiCallUncProvider+0x169
mup!MupStateMachine+0x165
mup!MupCreate+0x31d
fltmgr!FltpLegacyProcessingAfterPreCallbacksCompleted+0x24f
fltmgr!FltpCreate+0x2a9
nt!IopParseDevice+0x14e2
nt!ObpLookupObjectName+0x784
nt!ObOpenObjectByName+0x306
nt!IopCreateFile+0x2bc
nt!NtCreateFile+0x78
nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff880`0c81bc20)
 
In summary, one thread held a lock on the file control block of a particular file and was waiting for an “oplock” blocked SMB request to complete and the other threads were just waiting to obtain a lock on the file control block.
 
Limitations of Triage Dumps
 
Triage dumps have two major limitations:
 
  • If a thread is badly “wedged” then the kernel APC which gathers the data won’t be able to run.
  • The total size of a triage dump cannot exceed one megabyte; in my experience, this is enough space to hold information for 16 to 35 threads (the space required varies according to how much indirect data is included for a thread).
 
In IIS worker processes, it is not uncommon to have several hundred threads, so the simple functionality offered by KDbgCtrl (just specify a process ID and retrieve as many threads as will fit in one megabyte) is normally inadequate to capture the kernel context of the worker threads. The only option is to develop a new tool to perform the capture, using the low-level (and undocumented) API.
 
The Triage Dump API
 
The triage dump functionality is exposed via the NtSystemDebugControl/ZwSystemDebugControl system function. One way of declaring this function in C# is:
 
[DllImport("ntdll.dll")]
static extern uint ZwSystemDebugControl(SYSDBG_COMMAND cmd, [MarshalAs(UnmanagedType.AsAny)] object xin, int nin, [Out] byte[] xout, int nout, out int n);

public enum SYSDBG_COMMAND
{
    SysDbgGetTriageDump = 29,
    SysDbgWriteLiveKernelDump = 37
}

[StructLayout(LayoutKind.Sequential)]
public class SYSDBG_TRIAGE_DUMP
{
    public uint Flags;
    public uint BugCheckCode;
    public IntPtr BugCheckParam1;
    public IntPtr BugCheckParam2;
    public IntPtr BugCheckParam3;
    public IntPtr BugCheckParam4;
    public uint ProcessHandles;
    public uint ThreadHandles;
    public IntPtr Handles; // points to IntPtr[]

    public SYSDBG_TRIAGE_DUMP(IntPtr[] h)
    {
      BugCheckCode = 0x69696969;
      ThreadHandles = (uint)h.Length;
      Handles = Marshal.AllocHGlobal(IntPtr.Size * h.Length);
      Marshal.Copy(h, 0, Handles, h.Length);
    }
}
 
For a triage dump, the “cmd” is SYSDBG_COMMAND.SysDbgGetTriageDump, “Flags” is best set to zero, the “BugCheckCode” is conventionally set to 0x69696969, “ProcessHandles” must be zero and up to 16 (perhaps 40 at most) thread handles (to threads in a single process) with GENERIC_ALL access can be usefully included in the array of Handles (more can be added, but there probably won’t be space in the dump buffer to hold their data; when creating unattended triage dumps triggered by rare events, I err on the side of caution and specify at most 10 thread handles per dump). SeDebugPrivilege must be enabled as a prerequisite for creating a triage dump.
 
An invocation of the function might look like this:
 
IntPtr[] threads = […];
SYSDBG_TRIAGE_DUMP triage = new SYSDBG_TRIAGE_DUMP(threads);
byte[] dump = new byte[1024 * 1024];
int n;

Process.EnterDebugMode();

ZwSystemDebugControl(SYSDBG_COMMAND.SysDbgGetTriageDump, triage, Marshal.SizeOf(triage), dump, dump.Length, out n);

Process.LeaveDebugMode();
 
The resulting array of bytes (“dump”), trimmed to length “n”, should then be written to file for analysis with the standard Microsoft tools (cdb, kd, windbg, etc.).
 
The IDs of threads within a process can be obtained by a variety of mechanisms (including the Debugging API (WaitForDebugEvent et al) and the Tool Help Library) and thread handles can be obtained via the Win32 OpenThread API.
 
Difficulties Analysing the Triage Dump
 
If the KDbgCtrl command was used to create the dump, or the Tool Help Library used to identify the “first” n threads in the process in a “home-brew” dump, then there is a good chance that most of the useful content in the dump can be viewed with a command like “!process -1 1f”.
 
The key here is that the threads in the dump should be the first threads in the linked-list of threads in the kernel process object. The “!process -1 1f” command will read the kernel process object (which is included in the dump file) and follow the linked-list of thread objects, dumping each thread in turn. Under stable conditions, the order of threads returned by the Tool Help Library (and similar mechanisms) will correspond to their actual order in the kernel linked-list – however, this is an implementation artefact rather than a guarantee.
 
One way of dumping all of the kernel thread contexts in, say, an IIS worker process, is to march through all of the threads, creating a separate triage dump for each batch of, say, 16 threads. The first such triage dump can be viewed with “!process -1 1f”, but a different command will be required for subsequent dump files. Assuming that the threads have been dumped in the same order as the linked-list of threads, the following commands can be used: “!thread” to see the first thread in the triage dump (the format of the dump file and the mechanisms used by the debugger extensions ensure that this will work) and “!list -t nt!_KTHREAD.ThreadListEntry.Flink -x "!thread" -a "1f" $thread” to view all of the threads.
 
If, however, the threads to be dumped have been selected according to some criteria (e.g. thread state or user mode stack frame content), it is unlikely that they will be chained together and other techniques must be used. One such technique is to search the virtual address space captured in the dump file for plausible thread objects. The pool header (and pool tag) of the thread object is not included in the triage dump, so searching for ‘Thre’, say, would be unproductive. One command that normally works is:
 
.foreach (t { s -[1]q ($thread & fffffe0000000000) L? 1ffffffffff $proc }) { !thread t - 220 1f }
 
The address range is arrived at by trial and error, balancing time for the search against the size of the search area. There is not enough information in the triage dump to deduce “likely” ranges (e.g. non-paged pool boundaries); the address of the process object and the first thread object give an example of the addresses that need to be covered by the search range. The value (0x)220 is obtained from this command: “dt nt!_KTHREAD Process”. What the command does is to search for pointers to the process object, assume that the location containing the pointer is the Process field of a KTHREAD structure and then attempt to interpret the data at the purported thread object address as a thread.
 
An Alternative Approach
 
One of the many ways of obtaining a list of threads in a process is to use the Native API function NtQuerySystemInformation/ZwQuerySystemInformation (this is, in fact, the basis for most of the other methods too). This API offers no major advantages when just obtaining the list of thread IDs (with the SystemProcessInformation or SystemExtendedProcessInformation information classes) but, once using the Native API, it can be useful to use other information classes too – especially SystemHandleInformation or SystemExtendedHandleInformation. The information returned for these classes (for each open handle) is structured like this:
 
[StructLayout(LayoutKind.Sequential)]
struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX
{
    public IntPtr Object;
    public IntPtr UniqueProcessId;
    public IntPtr HandleValue;
    public uint GrantedAccess;
    public ushort CreatorBackTraceIndex;
    public ushort ObjectTypeIndex;
    public uint HandleAttributes;
    public uint Reserved;
}

Of particular interest is the “Object” field – this is the virtual address of the kernel object corresponding to the handle. If one has a handle to a thread object, say, one can search the information returned by the API call, using the current process ID and thread handle value, for the kernel address of the thread object. When saving a triage dump, one can then “augment” the dump with the known addresses of the thread objects contained within it. A simple way of “augmenting” the dump file is to include an encoding of the thread addresses in the name of the dump file (but watch out for exceeding the maximum file name length); another option might be to store the information in an alternative data stream of the dump file or even in a separate file. Up to 4 thread addresses could be saved in the dump file itself by using the BugCheckParamN fields of the SYSDBG_TRIAGE_DUMP structure.
 
Kernel Live Memory Dump
 
Newer versions of Windows include a new SYSDBG_COMMAND value (SysDbgWriteLiveKernelDump) and an additional structure definition that allow the kernel address space of a live system to be written to disk (without crashing the system).
 
[StructLayout(LayoutKind.Sequential)]
class SYSDBG_LIVEDUMP_CONTROL
{
    public uint Version;
    public uint BugCheckCode;
    public IntPtr BugCheckParam1;
    public IntPtr BugCheckParam2;
    public IntPtr BugCheckParam3;
    public IntPtr BugCheckParam4;
    public SafeFileHandle DumpFileHandle;
    public SafeWaitHandle CancelEventHandle;
    public SYSDBG_LIVEDUMP_CONTROL_FLAGS Flags;
    public SYSDBG_LIVEDUMP_CONTROL_ADDPAGES AddPagesControl;

    public SYSDBG_LIVEDUMP_CONTROL(FileStream fs)
    {
      BugCheckCode = 0x69696969;
      DumpFileHandle = fs.SafeFileHandle;
      CancelEventHandle = new SafeWaitHandle(IntPtr.Zero, false);
    }
}

[Flags]
enum SYSDBG_LIVEDUMP_CONTROL_FLAGS
{
    IncludeUserSpaceMemoryPages = 4
}

[Flags]
enum SYSDBG_LIVEDUMP_CONTROL_ADDPAGES
{
    HypervisorPages = 1
}

Unless live kernel debugging has been enabled, only kernel mode virtual addresses are dumped. If kernel live debugging has been enabled then a control flag is provided to specify whether user space memory pages should be included in the dump.
 
As with a triage dump, SeDebugPrivilege must be enabled to use this feature, the bug check code is conventionally set to 0x69696969 and the bug check params are included in the dump as given. The “Version” field is not currently inspected (but one won’t go far wrong if one sets it to 1) and the file handle must be opened for synchronous I/O. A kernel live dump can take several tens of seconds to complete, so a mechanism to abort/cancel the dump is provided (the CancelEventHandle).
 
An invocation of the function might look like this:
 
Process.EnterDebugMode();

using (FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite))
{
    int n;

    SYSDBG_LIVEDUMP_CONTROL live = new SYSDBG_LIVEDUMP_CONTROL(fs);

    ZwSystemDebugControl(SYSDBG_COMMAND.SysDbgWriteLiveKernelDump, live, Marshal.SizeOf(live), null, 0, out n);
}

Process.LeaveDebugMode();
 
The resulting dump is a full-value kernel memory dump and all of the normal kernel memory dump analysis/inspection techniques can be used.
 
ETW Tracing of Kernel Live Memory Dump
 
The Kernel Live Memory Dump functionality has been instrumented with manifest-based ETW (the provider “Microsoft-Windows-Kernel-LiveDump”). The following is a textual interpretation of the trace output, preceded on each line by the time (in seconds) since the start of the trace:
 
06.8533715 Live Dump Capture Dump Data API started.
06.8535649 Sizing Workflow: RemovePages Callbacks started.
06.8535652 Sizing Workflow: RemovePages Callbacks ended.
06.8535979 Sizing Workflow: Mirroring started.
06.8586117 Sizing Workflow: Mirroring Phase 0 ended.
06.8586136 Sizing Workflow: System Quiesce started.
07.0432997 Sizing Workflow: Mirroring Phase 1 ended.
07.0556710 Sizing Workflow: System Quiesce ended.
07.0560841 Sizing Workflow: Estimation. NT: 1675096064 bytes (Minimum 37429248 bytes). Hypervisor: Primary 0 bytes. Secondary 0 bytes.
07.2512462 Sizing Workflow: Allocation. NT: 1675100160 bytes. Hypervisor: Primary 0 bytes. Secondary 0 bytes.
07.2512920 Capture Pages Workflow: Mirroring started.
07.2568896 Capture Pages Workflow: Mirroring Phase 0 ended.
07.2609501 Capture Pages Workflow: System Quiesce started.
07.4204586 Capture Pages Workflow: Mirroring Phase 1 ended.
07.4346195 Capture Pages Workflow: Copy memory pages started.
07.7354418 Capture Pages Workflow: Copy memory pages ended.
07.7354793 Capture Pages Workflow: System Quiesce ended.
07.7356464 Writing dump file started.
41.8362706 Writing dump file ended. NT Status: 0x0. Total 1571217408 bytes (Header|Primary|Secondary: 286720|1570930688|0 bytes).
41.8362725 Live Dump Capture Dump Data API ended. NT Status: 0x0.
 
In this example, a little under one second was needed to prepare the dump and about 34 seconds was required to write the 1.5GB dump to


Thursday, 31 March 2016

Re-signing Apple apps under Windows – increasing use of SHA-256


Until recently, most code signature “superblobs” typically contained four “blobs” (CODEDIRECTORY, REQUIREMENTS, ENTITLEMENTS and SIGNATURE), but now code signatures with five or more blobs (including two CODEDIRECTORY blobs) are sometimes seen.
 
The two CODEDIRECTORY blobs differ in the following respects: 
  • The superblob index type for one CODEDIRECTORY blob is cdCodeDirectorySlot (0) and the index type for the other blob is 4096 (no symbolic name known).
  • The hashType of the “legacy” CODEDIRECTORY is cdHashTypeSHA1 (1) and the hashType of the “new” blob is cdHashTypeSHA256 (2).
  • The hash related information in each CODEDIRECTORY blob corresponds to the hashType (e.g. the hashSize information and the hashes in the tables of code and special hashes). 
The integrity of the “legacy” CODEDIRECTORY is assured by the SIGNATURE blob which contains a PKCS #7 signed data “detached signature”, signing the contents of the CODEDIRECTORY blob. The integrity of the “new” CODEDIRECTORY is assured by a different mechanism – an additional signed attribute in the PKCS #7 SignerInfos with OID 1.2.840.113635.100.9.1 and plist content similar to the following:
 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
     <key>cdhashes</key>
     <array>
           <data>
           JgHMg3GqvtZUvY3pnROMbKJ2kgo=
           </data>
           <data>
           rSlwo1k7/H14Mv/Q/sYwjFz0It4=
           </data>
     </array>
</dict>
</plist>
 
The first base64 encoded hash in the array of “cdhashes” is an SHA-1 hash of the legacy CODEDIRECTORY; the second base64 encoded hash in the array is the first 20 bytes of the SHA-256 hash of the new CODEDIRECTORY.
 
The following OID definitions are known but, at the time of writing, no names for “{ appleDataSecurity 9 }” and its descendants can be found.
 
apple OBJECT IDENTIFIER ::= { iso(1) member-body(2) US(840) 113635 }
appleDataSecurity OBJECT IDENTIFIER ::= { apple 100 }
 

Corresponding extensions to CodeResources

 

The mechanisms above protect the core data structures with SHA-256 strength hashes, but additions to the format of the CodeResources file are needed to protect other content in the application bundle. Previously, entries in the “files2” section of CodeResources were either “simple” (base64 encoded SHA-1 hash) values or “dictionaries” with a value named “hash” (an SHA-1 hash) and other named values:
 
                             <key>icon.png</key>
           <data>
           OjXVVA9dws7I7sB3ivQa9oVq1eU=
           </data>
           <key>icon2.png</key>
           <dict>
                <key>hash</key>
                <data>
                Z9iDVp4SnvkVuXHCtUMcYOfhReI=
                </data>
                <key>optional</key>
                <true/>
           </dict>
 
Now, entries like the following can be seen:
 
                             <key>icon.png</key>
           <dict>
                <key>hash</key>
                <data>
                OjXVVA9dws7I7sB3ivQa9oVq1eU=
                </data>
                <key>hash2</key>
                <data>
                x3Wj/BjxGPLpf6OAh/qnZCbfYgbmIuaxEHyoyoP5r04=
                </data>
           </dict>
 
The “dictionary” format is always used with at least two named values: “hash” (containing the SHA-1 hash of the file) and “hash2” (containing the SHA-256 hash of the file).

Monday, 9 March 2015

Re-signing Apple apps under Windows

Companies often need to re-sign custom apps which they have commissioned for deployment in an enterprise app store. Typically this is done with tools available on an Apple Mac (specifically, a tool called “codesign” that can either be invoked directly or via “Xcode” – an integrated development environment). However, Apple Mac devices are not commonly available in many companies and the question is sometimes asked whether it is practical to sign such apps using tools that can run under Microsoft Windows.

An Apple app is actually a ZIP archive with a naming convention for certain core files. The top-level folder is always called “Payload” and this contains a single folder named after the application (e.g. Example.app). The first core file is called Info.plist and would be found at this path in the archive:

                Payload/Example.app/Info.plist

The extension “.plist” indicates that this is a “property list” file and some of the properties in this file are essential to signing and executing the app. For example, the property “CFBundleExecutable” identifies the file that is the main “executable” file for the app and “CFBundleIdentifier” specifies the globally unique identifier for the app. As the Wikipedia article mentions, there are a variety of representations of property list files, one of which is an XML format; this XML format is the most easy to manipulate under Windows. If re-signing the app will require changes to Info.plist (more on this later), then Info.plist can be converted into the XML format (if it is not already in this format), modified and saved in XML format (there is no need to convert it back into its original format).

Signing a Mach-O binary app


If the main executable is a Mach-O binary file then (normally) the bulk of the code signing information is embedded in this file. A Mach-O file can be “fat” (contains a collections of machine architecture specific slices) or “thin” (contains just the code for one machine architecture). Each machine architecture specific slice is signed individually, so signing a “fat” Mach-O file is equivalent to individually signing a collection of “thin” Mach-O files.

The embedded code signing information obviously consumes space in the Mach-O file and this space is not available/reserved in a newly compiled and linked Mach-O file – space has to be added. There is a program for the Apple Mac called “codesign_allocate” that extends Mach-O files to make space for a code signature. Typically, extending a Mach-O file to make space for a signature involves locating the segment called “__LINKEDIT” (other common segments are called “__TEXT” and “__DATA”) and extending it to make space for (an estimate of the size of) the code signature. A Mach-O “load command” called “LC_CODE_SIGNATURE” is also appended to the other load commands to record the size and location of the code signature. If the Mach-O file is “fat” then the individual “thin” slices must be separately extended and moved within the “fat” file, and the corresponding sizes and offsets to the “thin” slice updated.

The space reserved by “codesign_allocate” is typically large enough to contain a replacement code signature, which means that it is not normally necessary to repeat the operation when re-signing an app.

A “C” include file containing definitions of the data structures in a “thin” Mach-O file (“loader.h”) can be found here, and definitions of the data structures in a “fat” file (“fat.h”) can be found here. The third (and final) include file (“codesign.h”), containing the code signing data structures themselves, needed to understand the code signing process can be found here.

The code signature is actually a “superblob” (using a name derived from codesign.h), which typically contains four “blobs”:  CODEDIRECTORY, REQUIREMENTS, ENTITLEMENTS and SIGNATURE.

Let’s start with the CODEDIRECTORY blob. At the time of writing, three versions of this data structure are often encountered (2.00, 2.01 and 2.02). Amongst other things, this blob contains the globally unique identifier of the app, the hash algorithm (normally SHA-1), hashes of the pages of the main executable (up to, but not including, the code signature) and hashes of other important data structures (e.g. REQUIREMENTS and ENTITLEMENTS blobs) and files (e.g. Info.plist and _CodeSignature/CodeResources).

Even if the app is just being resigned with a “newer” certificate issued to the same subject as the original signer, it will still be necessary to modify this blob. The reason is rather convoluted. The signing certificate (in its entirety, base64 encoded) is included in the “provisioning profile” which is a PKCS #7 signed data blob, signed by Apple and downloaded from the iOS Developer Enterprise Program web site; the provisioning profile is included in the app in a file with the name “embedded.mobileprovision” (e.g. “Payload/Example.app/embedded.mobileprovision”). A hash (or two) of “embedded.mobileprovision” is included in the file _CodeSignature/CodeResources (e.g. “Payload/Example.app/_CodeSignature/CodeResources”) and a hash of _CodeSignature/CodeResources is included in the CODEDIRECTORY blob.

If the app was originally signed by a software developer and we now wish to re-sign the app for enterprise deployment, then even more elements of the blob will change (e.g. the globally unique identifier, the hash of the ENTITLEMENTS blob, the hash of Info.plist, amongst others). The globally unique identifier in the CODEDIRECTORY blob must match the value of the CFBundleIdentifier property in Info.plist; the CFBundleIdentifier property in Info.plist must be consistent with the application-identifier in the provisioning profile; the entitlements in the ENTITLEMENTS blob must be consistent with the entitlements in the provisioning profile’s “Entitlements” property, etc..

The REQUIREMENTS blob expresses additional requirements that the application must fulfil in order to be allowed to run. The requirements are expressed in a language documented in the “Code Signing Guide” from Apple; the Apple codesign tool (whose source code is available here) uses ANTLR to recognize the requirements.

A typical requirement might be:

designated => identifier "com.acme.myapp" and anchor apple generic and certificate leaf[subject.CN] = "iPhone Distribution: Acme Corporation" and certificate 1[field.1.2.840.113635.100.6.2.1] exists

These requirements  are not linked to the provisioning profile, so one has some flexibility about what requirements to include. The requirements are checked when the app is loaded, so whatever the requirements express needs to be true for the signed app. The requirement shown above is of the form of the default requirement set by the “codesign” tool. “1.2.840.113635.100.6.2.1” is a “marker” certificate extension OID included in the “Apple Worldwide Developer Relations Certification Authority” certificate.

Apple apps run in a sandbox environment and the ENTITLEMENTS blob specifies which additional rights/entitlements the app should be granted. As mentioned earlier, the contents of the ENTITLEMENTS blob must be “consistent” with the entitlements in the provisioning profile’s “Entitlements” property. “Consistent” in this context means that the claimed entitlements should be a subset of the entitlements granted by the provisioning profile; wildcard values in the provisioning profile should be replaced by fully qualified values in the ENTITLEMENTS blob.

The SIGNATURE blob contains a PKCS #7 signed data “detached signature”, signing the contents of the CODEDIRECTORY blob. The “signer infos” element typically includes the full trust chain of the signing certificate (including the trusted root certification authority certificate) and sometimes includes a PKCS #9 “signing time” attribute in the set of authenticated attributes; the signature is not countersigned by a time stamping service. The signing certificate contains an “Authority Information Access” extension that contains the URL of an Apple “On-Line Certificate Status Protocol” (OCSP) server.

Signing a script-based app


If the app does not have a Mach-O binary file as its “bundle executable” then the various blobs described above are stored as separate files in the _CodeSignature folder; the folder structure will look like this:
  • Payload/Example.app/_CodeSignature/CodeDirectory
  • Payload/Example.app/_CodeSignature/CodeRequirements
  • Payload/Example.app/_CodeSignature/CodeResources
  • Payload/Example.app/_CodeSignature/CodeSignature

Additional steps for “first time” signature

The additional steps involved in signing an app for the first time are the reservation of space in Mach-O executables and computing the list of files to be hashed and their hashes (i.e. generating the file _CodeSignature/CodeResources).

There are built-in (into the codesign tool) “rules” for deciding which files in an app bundle should have their hashes included in CodeResources, but this can be influenced by creating a property list file containing other inclusion rules (often a file called “ResourceRules.plist” is used for this purpose). To be consistent with the newest code signing expectations, all “possible” files should have their hashes included in CodeResources; some files cannot have their hashes included in CodeResources (e.g. CodeResources itself and other code signing files in the _CodeSignature folder and the bundle executable (if it is in Mach-O format)) because they include a hash of  CodeResources and are consequently modified after CodeResources has been created.

Magic is important


The information in Mach-O files can be either big or little endian and Mach-O slices can be 32 or 64 bit. The values of the magic numbers at the start of many of the Mach-O data structures allows both of these characteristics to be determined. The signature blobs contain magic numbers too, so even if the signature information is stored in separate files in the _CodeSignature folder, the byte ordering needed for the file can be determined.

Run-time validation of an app


The text above describes what “raw” information is available when making a decision about whether an app should be allowed to run. The tests that are actually performed depend upon the platform and the version of the operating system installed on the device.

A key feature is the inclusion of information signed by Apple (the file “embedded.mobileprovision” containing the provisioning profile) and the link between this information and the “distribution” certificate used to sign the app bundle.

As an example, in the case of apps signed for distribution in an enterprise app store, the App ID and/or Team ID prefixes provide the raw information needed to check (against a database of installed apps) whether apps from other “enterprises” are installed on the device. This could be (is?) used to prevent apps from being sold outside the Apple App Store.

Developing a tool under Windows


The .NET framework contains classes that make implementing most of the necessary functionality straightforward. For example, there are classes that allow files in a ZIP archive to be updated or replaced in-situ and classes that implement all of the necessary cryptographic operations. The only “unexpected” difficulty is the manipulation or conversion of binary property list files: an authorative specification of this format is difficult to find.