summaryrefslogtreecommitdiffstats
path: root/src/ace3.c
blob: b96e6b4fb15a9506039fdfe6bb0f770e434a5815 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/*
 * ace3.c
 * Functions to handle Ace3/uarp firmware format
 *
 * Copyright (c) 2024 Nikias Bassen, All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <libimobiledevice-glue/nskeyedarchive.h>

#include "common.h"
#include "ace3.h"
#include "endianness.h"

static uint32_t crc_buffer(const unsigned char* buffer, unsigned int bufsize, unsigned int salt)
{
	uint32_t result;
	unsigned int i;
	unsigned int j;

	if ( !buffer )
		return 0xFFFFFFFF;
	result = salt;
	for (i = 0; i < bufsize; ++i) {
		for (j = 0; j != 8; ++j) {
			unsigned int tmp0 = 2 * result;
			unsigned int tmp1 = *(unsigned char*)(buffer + i);
			unsigned int tmp2 = ((unsigned int)result >> 31) ^ ((tmp1 >> j) & 1);
			result = (tmp2 + 2 * result) ^ 0x4C11DB6;
			if (!tmp2)
				result = tmp0;
		}
	}
	return result;
}

int ace3_create_binary(const unsigned char* uarp_fw, size_t uarp_size, uint64_t bdid, unsigned int prev, plist_t tss, unsigned char** bin_out, size_t* bin_size)
{
	struct ace3bin_header {
		uint32_t magic;        // 0xACE00003
		uint32_t unk4;         // 0x00203400
		uint32_t unk8;         // 0x00002800
		uint32_t header_size;  // 0x00000040
		uint32_t data1_size;
		uint32_t data2_size;
		uint32_t im4m_offset;
		uint32_t im4m_dl_size;
		uint32_t content_size;
		uint32_t crc;
		uint64_t fill1;        // 0xFFFFFFFFFFFFFFFF
		uint64_t fill2;        // 0xFFFFFFFFFFFFFFFF
		uint64_t fill3;        // 0xFFFFFFFFFFFFFFFF
	};

	struct uarp_header {
		uint32_t unk_00;        // BE 0x00000002
		uint32_t header_size;   // BE usually 0x0000002C
		uint32_t plist_offset;  // BE
		uint32_t unk_0c;        // 0
		uint32_t unk_10;        // 0
		uint32_t unk_14;        // 0
		uint32_t unk_18;        // 0
		uint32_t c_offset;      // BE
		uint32_t unk_20;        // 0
		uint32_t toc_offset;    // BE usually 0x0000002c
		uint32_t toc_size;      // BE
	};
	struct uarp_toc_entry {
		uint32_t this_size;    // BE usually 0x28
		uint32_t fourcc;        // 'PT01' or similar
		uint32_t index;         // BE starting with 0, increment+1 for each entry
		uint32_t unk_0c;        // BE usually not zero
		uint32_t unk_10;        // BE usually 0
		uint32_t unk_14;        // BE usually 0
		uint32_t unk_18;        // BE other offset, not sure
		uint32_t unk_1c;        // BE usually 0
		uint32_t offset;        // BE
		uint32_t size;          //
	};

	plist_t p_im4m = plist_dict_get_item(tss, "USBPortController1,Ticket");
	uint64_t im4m_size = 0;
	const char* im4m = plist_get_data_ptr(p_im4m, &im4m_size);

	struct uarp_header* uarp_hdr = (struct uarp_header*)uarp_fw;
	uint32_t uarp_hdr_size = be32toh(uarp_hdr->header_size);
	uint32_t plist_offset = be32toh(uarp_hdr->plist_offset);
	uint32_t plist_size = uarp_size - plist_offset;
	nskeyedarchive_t ka = nskeyedarchive_new_from_data(uarp_fw + plist_offset, plist_size);
	if (!ka) {
		return -1;
	}
	plist_t uarp_dict = nskeyedarchive_to_plist(ka);
	nskeyedarchive_free(ka);

	// find the corresponding entries for given BoardID+PREV

	char* payload_4cc = NULL;
	char* data_payload_4ccs = NULL;

	plist_t sb_payloads = plist_dict_get_item(uarp_dict, "SuperBinary Payloads");
	if (PLIST_IS_ARRAY(sb_payloads)) {
		plist_array_iter iter = NULL;
		plist_array_new_iter(sb_payloads, &iter);
		plist_t payload = NULL;
		do {
			plist_array_next_item(sb_payloads, iter, &payload);
			if (!payload) {
				break;
			}
			plist_t meta = plist_dict_get_item(payload, "Payload MetaData");
			if (!PLIST_IS_DICT(meta)) {
				continue;
			}
			plist_t prefix = plist_dict_get_item(meta, "Personalization Manifest Prefix");
			if (!PLIST_IS_STRING(prefix)) {	
				continue;
			}
			if (strcmp(plist_get_string_ptr(prefix, NULL), "USBPortController") != 0) {
				continue;
			}
			plist_t p_boardid = plist_dict_get_item(meta, "Personalization Board ID (64 bits)");
			if (!PLIST_IS_INT(p_boardid)) {
				continue;
			}
			uint64_t boardid = 0;
			plist_get_uint_val(p_boardid, &boardid);
			if (boardid == bdid) {
				plist_t p4cc = plist_dict_get_item(payload, "Payload 4CC");
				plist_get_string_val(p4cc, &payload_4cc);
				plist_t matching = plist_dict_get_item(meta, "Personalization Matching Data");
				if (PLIST_IS_ARRAY(matching)) {
					plist_array_iter iter2 = NULL;
					plist_array_new_iter(matching, &iter2);
					plist_t match = NULL;
					do {
						plist_array_next_item(matching, iter2, &match);
						if (!PLIST_IS_DICT(match)) {
							break;
						}
						uint64_t minrev = 0;
						plist_t p_min = plist_dict_get_item(match, "Personalization Matching Data Product Revision Minimum");
						plist_get_uint_val(p_min, &minrev);
						uint64_t maxrev = 0;
						plist_t p_max = plist_dict_get_item(match, "Personalization Matching Data Product Revision Maximum");
						plist_get_uint_val(p_max, &maxrev);
						if (prev >= minrev && prev <= maxrev) {
							plist_t tags = plist_dict_get_item(match, "Personalization Matching Data Payload Tags");
							plist_get_string_val(tags, &data_payload_4ccs);
							break;
						}
					} while (match);
					plist_mem_free(iter2);
				}
				break;
			}
		} while (payload);
		plist_mem_free(iter);
	}
	if (!payload_4cc) {
		printf("Failed to get payload 4cc\n");
		return -1;
	}
	if (!data_payload_4ccs) {
		printf("Failed to get data payload 4ccs\n");
		return -1;
	}

	// now find the blobs in UARP data
	uint32_t dl_offset = 0;
	uint32_t dl_size = 0;
	uint32_t data1_offset = 0;
	uint32_t data1_size = 0;
	uint32_t data2_offset = 0;
	uint32_t data2_size = 0;
	uint32_t toc_offset = be32toh(uarp_hdr->toc_offset);
	uint32_t toc_size = be32toh(uarp_hdr->toc_size);
	const unsigned char* p = uarp_fw + uarp_hdr_size; 
	while (p < uarp_fw + toc_size) {
		struct uarp_toc_entry* entry = (struct uarp_toc_entry*)p;
		uint32_t te_size = be32toh(entry->this_size);
		if (strncmp((char*)&(entry->fourcc), payload_4cc, 4) == 0) {
			dl_offset = be32toh(entry->offset);
			dl_size = be32toh(entry->size);
		} else if (strncmp((char*)&(entry->fourcc), data_payload_4ccs, 4) == 0) {
			data1_offset = be32toh(entry->offset);
			data1_size = be32toh(entry->size);
		} else if (strncmp((char*)&(entry->fourcc), data_payload_4ccs+5, 4) == 0) {
			data2_offset = be32toh(entry->offset);
			data2_size = be32toh(entry->size);
		}
		p += te_size;
	}

	uint32_t content_size = data1_size + data2_size + im4m_size + dl_size;

	*bin_out = (unsigned char*)malloc(0x40 + content_size);
	struct ace3bin_header* hdr = (struct ace3bin_header*)(*bin_out);
	hdr->magic = htole32(0xACE00003);
	hdr->unk4 = htole32(0x00203400);
	hdr->unk8 = htole32(0x00002800);
	hdr->header_size = htole32(0x40);
	hdr->data1_size = htole32(data1_size);
	hdr->data2_size = htole32(data2_size);;
	hdr->im4m_offset = htole32(0x40 + data1_size + data2_size);
	hdr->im4m_dl_size = htole32(im4m_size + dl_size);
	hdr->content_size = htole32(content_size);
	hdr->crc = 0;
	hdr->fill1 = 0xFFFFFFFFFFFFFFFFLL;
	hdr->fill2 = 0xFFFFFFFFFFFFFFFFLL;
	hdr->fill3 = 0xFFFFFFFFFFFFFFFFLL;

	// write data1 payload
	memcpy(*bin_out + 0x40, uarp_fw + data1_offset, data1_size);
	// write data2 payload
	memcpy(*bin_out + 0x40 + data1_size, uarp_fw + data2_offset, data2_size);
	// write IM4M
	memcpy(*bin_out + 0x40 + data1_size + data2_size, im4m, im4m_size);
	// write dl payload
	memcpy(*bin_out + 0x40 + data1_size + data2_size + im4m_size, uarp_fw + dl_offset, dl_size);

	// calculate CRC and update header
	hdr->crc = htole32(crc_buffer(*bin_out + 0x40, content_size, 0xFFFFFFFF));

	*bin_size = 0x40 + content_size;

	return 0;
}