1use std::io::Cursor;
2
3use byteorder::{LittleEndian, ReadBytesExt};
4use chrono::{TimeZone, Utc};
5use log::*;
6use pelite::{
7 pe32::{Pe, PeFile},
8 resources::Name,
9};
10use pretty_hex::*;
11
12use thiserror::Error;
13
14mod constants;
15
16#[derive(Debug, Error)]
17pub enum Error {
18 #[error("input file is not a Windows PE file")]
19 InvalidFile,
20 #[error("could not read metadata: {0}")]
21 InvalidMetadata(&'static str),
22 #[error("error occurred while trying to parse input file")]
23 Parsing(#[from] pelite::Error),
24 #[error("error occurred while trying to find `RES_UPDATE_INFO` resource")]
25 NoResource(#[from] pelite::resources::FindError),
26}
27
28pub fn update_info_from_pe(pe_data: &[u8]) -> Result<&[u8], Error> {
31 let pe_file = PeFile::from_bytes(pe_data)?;
32 let resources = pe_file.resources()?;
33
34 resources
35 .find_resource(&[Name::Id(23), Name::Str("RES_UPDATE_INFO")])
36 .map_err(Error::from)
37}
38
39pub fn decrypt(update_info: &[u8]) -> Result<Vec<u8>, Error> {
41 let mut decryptor = Decryptor::default();
43 decryptor.inflate_timestamp(u32::from_le_bytes(update_info[..4].try_into().unwrap()));
44
45 let mut reader = Cursor::new(&update_info[update_info.len() - 0x5c..]);
48 let firmware_size = reader
49 .read_u32::<LittleEndian>()
50 .map_err(|_| Error::InvalidMetadata("firmware_size"))? as usize;
51 eprintln!("Firmware size: {:#x}", firmware_size);
52
53 let maybe_string_count = reader
55 .read_u32::<LittleEndian>()
56 .map_err(|_| Error::InvalidMetadata("string_count"))?;
57
58 let mut str_buffer = Vec::with_capacity(0x100);
59 for _ in 0..maybe_string_count as usize {
60 str_buffer.clear();
61 loop {
62 let c = reader
63 .read_u8()
64 .map_err(|_| Error::InvalidMetadata("string_table"))?;
65 if c == 0x0 {
66 break;
67 }
68
69 str_buffer.push(c);
70 }
71 eprintln!(
72 "{}",
73 std::str::from_utf8(str_buffer.as_slice())
74 .map_err(|_| Error::InvalidMetadata("string_in_string_table"))?
75 );
76 }
77
78 Ok(decryptor.decrypt_data(&update_info[4..firmware_size + 4]))
79}
80
81struct Decryptor {
82 key: [u8; 0x300],
83}
84
85impl Decryptor {
86 fn set_key(&mut self, key: &[u8; 56]) {
87 let mut scrambled_table = key.clone();
88 drop(key);
90
91 for i in 0..16 {
95 let rounds = if constants::KEY_CONFIG[i * 4] == 2 {
96 2
97 } else {
98 1
99 };
100
101 for _ in 0..rounds {
102 let mut temp_byte = scrambled_table[0];
103 scrambled_table.copy_within(1..=27, 0);
104 scrambled_table[27] = temp_byte;
105
106 temp_byte = scrambled_table[28];
107 scrambled_table.copy_within(29.., 28);
108 scrambled_table[55] = temp_byte;
109 }
110
111 for key_config_idx in 0..48 {
112 let scrambled_idx = (constants::KEY_CONFIG2[key_config_idx] as usize) - 1;
113
114 let swaptable_idx = key_config_idx + (i * 48);
115 self.key[swaptable_idx] = scrambled_table[scrambled_idx];
116 }
117 }
118 }
119
120 fn decrypt(&mut self, outbuf: &mut [u8], is_encrypt: bool) {
121 let mut multiplier = 0x0;
122 let mut remaining_data = 15;
123 let mut scratch_buffer = [0u8; 48];
124 let mut done = false;
125 if !is_encrypt {
126 multiplier = 0xF;
127 }
128
129 while !done {
130 for i in 0..(48 / 6) {
131 trace!("");
132 trace!("round {}", i);
133 trace!("\n{} start temp:\n{:?}", i, (&scratch_buffer).hex_dump());
134 trace!("\ninflated data: {:?}\n", outbuf.hex_dump());
135
136 trace!("multiplier: 0x{:X}", multiplier);
137
138 const BYTES_PER_ROUND: usize = 6;
139 let iidx = i * BYTES_PER_ROUND;
141
142 for j in 0..BYTES_PER_ROUND {
144 debug!("{}, {}", iidx, j);
145 debug!("config: {:#x}", constants::ENCRYPTION_CONFIG[iidx + j]);
146 let rhs_idx = (constants::ENCRYPTION_CONFIG[iidx + j] as usize) + 31;
147 debug!("inflated_data idx: {:#x}", rhs_idx);
148 let rhs = outbuf[rhs_idx];
149 debug!("inflated_data : {:#x}", rhs);
150 let lhs_idx = (48 * multiplier) + iidx + j;
151 debug!("key idx: {:#x}", lhs_idx);
152 let lhs = self.key[lhs_idx];
153 debug!("key : {:#x}", lhs);
154 let result = lhs ^ rhs;
155 debug!("result: {:#x}", result);
156 let result_idx = iidx + j;
157
158 debug!("result idx: {:#x}", result_idx);
159 scratch_buffer[result_idx] = result;
160
161 debug!("");
162 }
163 debug!("\n{} temp:\n{:?}", i, (&scratch_buffer).hex_dump());
164 }
165 debug!("temp:\n{:?}", (&scratch_buffer).hex_dump());
166
167 macro_rules! combine {
168 ($offset:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr) => {{
169 let a = $a - 4;
173 let b = $b - 4;
174 let c = $c - 4;
175 let d = $d - 4;
176 let e = $e - 4;
177 let f = $f - 4;
178
179 debug!(
180 "a={}, val={:#x}, shifted={:#x}",
181 $a,
182 scratch_buffer[a] as u32,
183 (scratch_buffer[a] as u32 * 32) + 2
184 );
185 debug!(
186 "b={}, val={:#x}, shifted={:#x}",
187 $b,
188 scratch_buffer[b] as u32,
189 (scratch_buffer[b] as u32 * 16) + 2
190 );
191 debug!(
192 "c={}, val={:#x}, shifted={:#x}",
193 $c,
194 scratch_buffer[c] as u32,
195 (scratch_buffer[c] as u32 * 8 + 2)
196 );
197 debug!(
198 "d={}, val={:#x}, shifted={:#x}",
199 $d,
200 scratch_buffer[d] as u32,
201 (scratch_buffer[d] as u32 * 4 + 2)
202 );
203 debug!(
204 "e={}, val={:#x}, shifted={:#x}",
205 $e,
206 scratch_buffer[e] as u32,
207 (scratch_buffer[e] as u32 * 2 + 2)
208 );
209 debug!(
210 "f={}, val={:#x}, shifted={:#x}",
211 $f,
212 scratch_buffer[f] as u32,
213 (scratch_buffer[f] as u32 + 2)
214 );
215 let mystery_idx = ((scratch_buffer[a] as u32 * 32 + 2)
216 | (scratch_buffer[b] as u32 * 16 + 2)
217 | (scratch_buffer[c] as u32 * 8 + 2)
218 | (scratch_buffer[d] as u32 * 4 + 2)
219 | (scratch_buffer[e] as u32 * 2 + 2)
220 | (scratch_buffer[f] as u32 + 2)) as usize;
221 debug!("offset: {:}", $offset);
222 debug!("full idx: {:#x}", mystery_idx + $offset);
223
224 debug!("{:#X?}", &constants::ENCRYPTION_CONFIG2[$offset + mystery_idx..][..4]);
225
226 let val = u32::from_le_bytes(constants::ENCRYPTION_CONFIG2[$offset + mystery_idx..][..4].try_into().unwrap());
227 debug!("{:#x}", val);
228 val
229 }};
230 }
231
232 let temp1 = combine!(0, 4, 9, 5, 6, 7, 8);
233 let temp2 = combine!(0x100, 10, 15, 11, 12, 13, 14);
234 let temp3 = combine!(0x200, 16, 21, 17, 18, 19, 20);
235 let temp4 = combine!(0x300, 22, 27, 23, 24, 25, 26);
236 let temp5 = combine!(0x400, 28, 33, 29, 30, 31, 32);
237 let temp6 = combine!(0x500, 34, 39, 35, 36, 37, 38);
238 let temp7 = combine!(0x600, 40, 45, 41, 42, 43, 44);
239 let temp8 = combine!(0x700, 46, 51, 47, 48, 49, 50);
240 let mut temp_key_material: Vec<u8> = [
241 temp1.to_le_bytes(),
242 temp2.to_le_bytes(),
243 temp3.to_le_bytes(),
244 temp4.to_le_bytes(),
245 temp5.to_le_bytes(),
246 temp6.to_le_bytes(),
247 temp7.to_le_bytes(),
248 temp8.to_le_bytes(),
249 ]
250 .iter()
251 .flatten()
252 .cloned()
253 .collect();
254
255 debug!(
256 "temp_key_material before append: {:?}",
257 temp_key_material.hex_dump()
258 );
259 temp_key_material.extend_from_slice(&scratch_buffer);
260 debug!("temp_key_material: {:?}", temp_key_material.hex_dump());
261
262 debug!("\n\noutput:{:?}\n\n", outbuf.hex_dump());
263 let mut output_buffer_offset = 0;
264 if remaining_data == 0 {
265 for _i in 0..8 {
266 debug!("\n\noutput BEFORE:{:?}\n\n", outbuf.hex_dump());
267
268 outbuf[output_buffer_offset + 0] ^= temp_key_material
269 [constants::ENCRYPTION_CONFIG3[output_buffer_offset] as usize - 1];
270 outbuf[output_buffer_offset + 1] ^= temp_key_material
271 [constants::ENCRYPTION_CONFIG3[output_buffer_offset + 1] as usize - 1];
272 outbuf[output_buffer_offset + 2] ^= temp_key_material
273 [constants::ENCRYPTION_CONFIG3[output_buffer_offset + 2] as usize - 1];
274 outbuf[output_buffer_offset + 3] ^= temp_key_material
275 [constants::ENCRYPTION_CONFIG3[output_buffer_offset + 3] as usize - 1];
276 output_buffer_offset += 4;
277
278 debug!("\n\noutput AFTER:{:?}\n\n", outbuf.hex_dump());
279 }
280 } else {
281 for i in 0..8 {
282 debug!("");
283 debug!("round: {}", i);
284 for (first, second) in (0x1c..=0x1f).zip(0..4) {
285 debug!("\n\noutput BEFORE:{:?}\n\n", outbuf.hex_dump());
286 debug!("swapping {:#x} with {:#x}", first, second);
287
288 let original_byte_idx = first + output_buffer_offset + 4;
289
290 debug!("original byte index: {:#x}", original_byte_idx);
291 let original_byte = outbuf[original_byte_idx];
292 debug!("original byte: {:#x}", original_byte);
293
294 let constant =
295 constants::ENCRYPTION_CONFIG3[output_buffer_offset + second] as usize;
296
297 debug!("curr byte: {:#x}", outbuf[output_buffer_offset + second]);
298 debug!("constant: {:#x}", constant);
299 debug!("xor rhs: {:#x}", temp_key_material[constant - 1]);
300 debug!(
301 "{:#x} ^ {:#x}",
302 outbuf[output_buffer_offset + second],
303 temp_key_material[constant - 1]
304 );
305
306 let new_byte =
307 outbuf[output_buffer_offset + second] ^ temp_key_material[constant - 1];
308
309 debug!("new byte: {:#x}", new_byte);
310
311 let new_idx = original_byte_idx;
312 debug!("new byte goes to {:#X}", new_idx);
313 debug!("old byte goes to {:#X}", output_buffer_offset + second);
314 outbuf[new_idx] = new_byte;
315 outbuf[output_buffer_offset + second] = original_byte;
316
317 debug!("\n\noutput AFTER:{:?}\n\n", outbuf.hex_dump());
318 debug!("");
319 }
320
321 output_buffer_offset += 4;
322
323 debug!("");
324 }
325 }
326
327 done = remaining_data == 0;
328 remaining_data -= 1;
329 if is_encrypt {
330 multiplier += 1;
331 } else {
332 multiplier = multiplier.saturating_sub(1);
333 }
334 }
335 }
336
337 pub fn inflate_timestamp(&mut self, timestamp: u32) {
338 let date = Utc.timestamp(timestamp as i64, 0);
340 let formatted_date = date.format("%Y%m%d%H%M%S").to_string();
341 let mut outbuf = [0u8; 64];
342
343 let mut date_bytes = formatted_date.as_bytes().iter().cloned();
345 let mut curr_byte = date_bytes.next().unwrap();
346 for swap_table in 0..4 {
347 self.set_key(&constants::TIMESTAMP_TABLES[swap_table]);
348 for byte_idx in 0..8 {
349 for bit_idx in 0..8 {
350 let bit_value = (curr_byte >> (7 - bit_idx)) & 1;
351 let outbuf_idx = (byte_idx * 8) + bit_idx;
352 outbuf[outbuf_idx] = bit_value ^ outbuf[outbuf_idx];
353 }
354
355 if let Some(next) = date_bytes.next() {
356 curr_byte = next;
357 } else {
358 curr_byte = 0x0;
359 }
360 }
361
362 self.decrypt(&mut outbuf, true);
363 }
364
365 self.set_key(&outbuf[..56].try_into().unwrap());
366 }
367
368 pub fn decrypt_data(&mut self, encrypted_data: &[u8]) -> Vec<u8> {
369 let mut decrypted = vec![0u8; encrypted_data.len()];
370 let mut inflated: [u8; 64] = [0u8; 64];
371
372 let mut output_idx = 0;
373 let mut remaining_data = encrypted_data.len();
374 loop {
375 let mut block_size = std::cmp::min(8, remaining_data);
376 remaining_data = remaining_data.saturating_sub(block_size);
377 for i in 0..block_size {
379 let encrypted_bytes = encrypted_data[i + output_idx];
380 let bit_idx = i * 8;
381 inflated[bit_idx] = encrypted_bytes >> 7;
382 inflated[bit_idx + 1] = (encrypted_bytes >> 6) & 1;
383 inflated[bit_idx + 2] = (encrypted_bytes >> 5) & 1;
384 inflated[bit_idx + 3] = (encrypted_bytes >> 4) & 1;
385 inflated[bit_idx + 4] = (encrypted_bytes >> 3) & 1;
386 inflated[bit_idx + 5] = (encrypted_bytes >> 2) & 1;
387 inflated[bit_idx + 6] = (encrypted_bytes >> 1) & 1;
388 inflated[bit_idx + 7] = encrypted_bytes & 0x1;
389 }
390
391 self.decrypt(&mut inflated, false);
392
393 let mut curr_inflated_idx = 0;
394 while block_size > 0 {
395 block_size -= 1;
396
397 let inflated = &mut inflated[curr_inflated_idx * 8..];
398 for shift in 0..8 {
400 decrypted[output_idx] |= inflated[7 - shift] << shift;
401 }
402
403 output_idx += 1;
405 curr_inflated_idx += 1;
406 }
407
408 if remaining_data == 0 {
409 return decrypted;
410 }
411 }
412 }
413}
414
415impl Default for Decryptor {
416 fn default() -> Decryptor {
417 Self { key: [0u8; 0x300] }
418 }
419}