porkchop/
lib.rs

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
28/// Takes the input PE data, parses it as a 32-bit PE, and if successful returns
29/// the `RES_UPDATE_INFO` data.
30pub 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
39/// Decrypts the input `update_info` and returns its decrypted form.
40pub fn decrypt(update_info: &[u8]) -> Result<Vec<u8>, Error> {
41    // First 4 bytes of the update info are a timestamp
42    let mut decryptor = Decryptor::default();
43    decryptor.inflate_timestamp(u32::from_le_bytes(update_info[..4].try_into().unwrap()));
44
45    // There's a blob of size 0x5c at the end of the update info that describes
46    // the update
47    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    // No idea if this is actually a string count
54    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 the input key so we don't accidentally use it from here on out
89        drop(key);
90
91        // the program technically reads DWORDS from the KEY_CONFIG and increments
92        // their pointer by 1 DWORD per iteration... but we only need to read out
93        // the MSB and this simplifies things
94        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                // We do 6 bytes per round
140                let iidx = i * BYTES_PER_ROUND;
141
142                // We read 6 bytes at a time
143                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                    // these get subtracted by 4 since in the original code there
170                    // is a `this` pointer stored between data buffers. We don't
171                    // have that.
172                    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        // Convert the timestamp to a string
339        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        // There are 4 different rounds for setting up the timestamp key
344        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            //println!("{:#x}", remaining_data);
378            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                // we need to reassemble 8 bits
399                for shift in 0..8 {
400                    decrypted[output_idx] |= inflated[7 - shift] << shift;
401                }
402
403                // println!("{:#X}", deobfuscated[output_idx]);
404                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}