Example #1
0
extern bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key)
{
	if (!self)
		return false;

	self.seek(0);

	if (self.size() >= 4 && self.read<u32>() == "SCE\0"_u32)
	{
		// Check the ELF file class (32 or 64 bit).
		bool isElf32 = IsSelfElf32(self);

		// Start the decrypter on this SELF file.
		SELFDecrypter self_dec(self);

		// Load the SELF file headers.
		if (!self_dec.LoadHeaders(isElf32))
		{
			LOG_ERROR(LOADER, "SELF: Failed to load SELF file headers!");
			return false;
		}

		// Load and decrypt the SELF file metadata.
		if (!self_dec.LoadMetadata(klic_key))
		{
			LOG_ERROR(LOADER, "SELF: Failed to load SELF file metadata!");
			return false;
		}
	}
	return true;
}
Example #2
0
extern fs::file decrypt_self(fs::file elf_or_self, u8* klic_key)
{
	if (!elf_or_self)
	{
		return fs::file{};
	}

	elf_or_self.seek(0);

	// Check SELF header first. Check for a debug SELF.
	if (elf_or_self.size() >= 4 && elf_or_self.read<u32>() == "SCE\0"_u32 && !CheckDebugSelf(elf_or_self))
	{
		// Check the ELF file class (32 or 64 bit).
		bool isElf32 = IsSelfElf32(elf_or_self);

		// Start the decrypter on this SELF file.
		SELFDecrypter self_dec(elf_or_self);

		// Load the SELF file headers.
		if (!self_dec.LoadHeaders(isElf32))
		{
			LOG_ERROR(LOADER, "SELF: Failed to load SELF file headers!");
			return fs::file{};
		}

		// Load and decrypt the SELF file metadata.
		if (!self_dec.LoadMetadata(klic_key))
		{
			LOG_ERROR(LOADER, "SELF: Failed to load SELF file metadata!");
			return fs::file{};
		}

		// Decrypt the SELF file data.
		if (!self_dec.DecryptData())
		{
			LOG_ERROR(LOADER, "SELF: Failed to decrypt SELF file data!");
			return fs::file{};
		}

		// Make a new ELF file from this SELF.
		return self_dec.MakeElf(isElf32);
	}

	return elf_or_self;
}
Example #3
0
static bool CheckDebugSelf(fs::file& s)
{
	if (s.size() < 0x18)
	{
		return false;
	}

	// Get the key version.
	s.seek(0x08);

	const u16 key_version = s.read<le_t<u16>>();

	// Check for DEBUG version.
	if (key_version == 0x80 || key_version == 0xc0)
	{
		LOG_WARNING(LOADER, "Debug SELF detected! Removing fake header...");

		// Get the real elf offset.
		s.seek(0x10);

		// Start at the real elf offset.
		s.seek(key_version == 0x80 ? +s.read<be_t<u64>>() : +s.read<le_t<u64>>());

		// Write the real ELF file back.
		fs::file e = fs::make_stream<std::vector<u8>>();

		// Copy the data.
		char buf[2048];
		while (u64 size = s.read(buf, 2048))
		{
			e.write(buf, size);
		}

		s = std::move(e);
		return true;
	}

	// Leave the file untouched.
	return false;
}
Example #4
0
bool pkg_install(const fs::file& pkg_f, const std::string& dir, atomic_t<double>& sync, const std::string& pkg_filepath)
{
	const std::size_t BUF_SIZE = 8192 * 1024; // 8 MB
	std::vector<fs::file> filelist;
	u32 cur_file=0;

	fs::file original_file(pkg_filepath);
	original_file.seek(pkg_f.pos());

	filelist.push_back(std::move(original_file));

	// Save current file offset (probably zero)
	const u64 start_offset = pkg_f.pos();
	u64 cur_offset = start_offset;
	u64 cur_file_offset = start_offset;

	// Get basic PKG information
	PKGHeader header;

	auto archive_seek = [&](const s64 new_offset, const fs::seek_mode damode = fs::seek_set)
	{
		if(damode == fs::seek_set) cur_offset = new_offset;
		else if (damode == fs::seek_cur) cur_offset += new_offset;

		u64 _offset = 0;
		for (u32 i = 0; i < filelist.size(); i++)
		{
			if (cur_offset < (_offset + filelist[i].size()))
			{
				cur_file = i;
				cur_file_offset = cur_offset - _offset;
				filelist[i].seek(cur_file_offset);
				break;
			}
			_offset += filelist[i].size();
		}
	};

	auto archive_read = [&](const void *data_ptr, const u64 num_bytes)
	{
		u64 num_bytes_left = filelist[cur_file].size() - cur_file_offset;
		//check if it continues in another file
		if (num_bytes > num_bytes_left)
		{
			filelist[cur_file].read((u8 *)data_ptr, num_bytes_left);
			if ((cur_file + 1) < filelist.size()) cur_file++;
			else
			{
				cur_offset += num_bytes_left;
				cur_file_offset = filelist[cur_file].size();
				return num_bytes_left;
			}
			u64 num_read = filelist[cur_file].read((u8 *)data_ptr + num_bytes_left, num_bytes - num_bytes_left);
			cur_offset += (num_read + num_bytes_left);
			cur_file_offset = num_read;
			return (num_read+num_bytes_left);
		}

		u64 num_read = filelist[cur_file].read((u8 *)data_ptr, num_bytes);

		cur_offset += num_read;
		cur_file_offset += num_read;

		return num_read;
	};


	if (archive_read(&header, sizeof(header)) != sizeof(header))
	{
		LOG_ERROR(LOADER, "PKG file is too short!");
		return false;
	}

	if (header.pkg_magic != "\x7FPKG"_u32)
	{
		LOG_ERROR(LOADER, "Not a PKG file!");
		return false;
	}

	switch (const u16 type = header.pkg_type)
	{
	case PKG_RELEASE_TYPE_DEBUG:   break;
	case PKG_RELEASE_TYPE_RELEASE: break;
	default:
	{
		LOG_ERROR(LOADER, "Unknown PKG type (0x%x)", type);
		return false;
	}
	}

	switch (const u16 platform = header.pkg_platform)
	{
	case PKG_PLATFORM_TYPE_PS3: break;
	case PKG_PLATFORM_TYPE_PSP: break;
	default:
	{
		LOG_ERROR(LOADER, "Unknown PKG platform (0x%x)", platform);
		return false;
	}
	}

	if (header.pkg_size > pkg_f.size())
	{
		//Check if multi-files pkg
		if (pkg_filepath.length() < 7 || pkg_filepath.substr(pkg_filepath.length() - 7).compare("_00.pkg") != 0)
		{
			LOG_ERROR(LOADER, "PKG file size mismatch (pkg_size=0x%llx)", header.pkg_size);
			return false;
		}

		std::string name_wo_number = pkg_filepath.substr(0, pkg_filepath.length() - 7);
		u64 cursize = pkg_f.size();
		while (cursize != header.pkg_size)
		{
			std::string archive_filename = fmt::format("%s_%2d.pkg", name_wo_number, filelist.size());

			fs::file archive_file(archive_filename);
			if (!archive_file)
			{
				LOG_ERROR(LOADER, "Missing part of the multi-files pkg: %s", archive_filename);
				return false;
			}

			cursize += archive_file.size();
			filelist.push_back(std::move(archive_file));
		}
	}

	if (header.data_size + header.data_offset > header.pkg_size)
	{
		LOG_ERROR(LOADER, "PKG data size mismatch (data_size=0x%llx, data_offset=0x%llx, file_size=0x%llx)", header.data_size, header.data_offset, header.pkg_size);
		return false;
	}

	be_t<u32> drm_type{0};
	be_t<u32> content_type{0};

	archive_seek(header.pkg_info_off);

	for (u32 i = 0; i < header.pkg_info_num; i++)
	{
		struct packet_T
		{
			be_t<u32> id;
			be_t<u32> size;
		} packet;
		
		archive_read(&packet, sizeof(packet));

		// TODO
		switch (+packet.id)
		{
		case 0x1:
		{
			if (packet.size == sizeof(drm_type))
			{
				archive_read(&drm_type, sizeof(drm_type));
				continue;
			}

			break;
		}
		case 0x2:
		{
			if (packet.size == sizeof(content_type))
			{
				archive_read(&content_type, sizeof(content_type));
				continue;
			}

			break;
		}
		}

		archive_seek(packet.size, fs::seek_cur);
	}

	// Allocate buffer with BUF_SIZE size or more if required
	const std::unique_ptr<u128[]> buf(new u128[std::max<u64>(BUF_SIZE, sizeof(PKGEntry) * header.file_count) / sizeof(u128)]);

	// Define decryption subfunction (`psp` arg selects the key for specific block)
	auto decrypt = [&](u64 offset, u64 size, const uchar* key) -> u64
	{
		archive_seek(start_offset + header.data_offset + offset);

		// Read the data and set available size
		const u64 read = archive_read(buf.get(), size);

		// Get block count
		const u64 blocks = (read + 15) / 16;

		if (header.pkg_type == PKG_RELEASE_TYPE_DEBUG)
		{
			// Debug key
			be_t<u64> input[8] =
			{
				header.qa_digest[0],
				header.qa_digest[0],
				header.qa_digest[1],
				header.qa_digest[1],
			};

			for (u64 i = 0; i < blocks; i++)
			{
				// Initialize stream cipher for current position
				input[7] = offset / 16 + i;

				union sha1_hash
				{
					u8 data[20];
					u128 _v128;
				} hash;
				
				sha1(reinterpret_cast<const u8*>(input), sizeof(input), hash.data);

				buf[i] ^= hash._v128;
			}
		}

		if (header.pkg_type == PKG_RELEASE_TYPE_RELEASE)
		{
			aes_context ctx;

			// Set encryption key for stream cipher
			aes_setkey_enc(&ctx, key, 128);

			// Initialize stream cipher for start position
			be_t<u128> input = header.klicensee.value() + offset / 16;

			// Increment stream position for every block
			for (u64 i = 0; i < blocks; i++, input++)
			{
				u128 key;

				aes_crypt_ecb(&ctx, AES_ENCRYPT, reinterpret_cast<const u8*>(&input), reinterpret_cast<u8*>(&key));

				buf[i] ^= key;
			}
		}

		// Return the amount of data written in buf
		return read;
	};

	std::array<uchar, 16> dec_key;

	if (header.pkg_platform == PKG_PLATFORM_TYPE_PSP && content_type >= 0x15 && content_type <= 0x17)
	{
		const uchar psp2t1[] = {0xE3, 0x1A, 0x70, 0xC9, 0xCE, 0x1D, 0xD7, 0x2B, 0xF3, 0xC0, 0x62, 0x29, 0x63, 0xF2, 0xEC, 0xCB};
		const uchar psp2t2[] = {0x42, 0x3A, 0xCA, 0x3A, 0x2B, 0xD5, 0x64, 0x9F, 0x96, 0x86, 0xAB, 0xAD, 0x6F, 0xD8, 0x80, 0x1F};
		const uchar psp2t3[] = {0xAF, 0x07, 0xFD, 0x59, 0x65, 0x25, 0x27, 0xBA, 0xF1, 0x33, 0x89, 0x66, 0x8B, 0x17, 0xD9, 0xEA};

		aes_context ctx;
		aes_setkey_enc(&ctx, content_type == 0x15 ? psp2t1 : content_type == 0x16 ? psp2t2 : psp2t3, 128);
		aes_crypt_ecb(&ctx, AES_ENCRYPT, reinterpret_cast<const uchar*>(&header.klicensee), dec_key.data());
		decrypt(0, header.file_count * sizeof(PKGEntry), dec_key.data());
	}
	else
	{
		std::memcpy(dec_key.data(), PKG_AES_KEY, dec_key.size());
		decrypt(0, header.file_count * sizeof(PKGEntry), header.pkg_platform == PKG_PLATFORM_TYPE_PSP ? PKG_AES_KEY2 : dec_key.data());
	}

	std::vector<PKGEntry> entries(header.file_count);

	std::memcpy(entries.data(), buf.get(), entries.size() * sizeof(PKGEntry));

	for (const auto& entry : entries)
	{
		const bool is_psp = (entry.type & PKG_FILE_ENTRY_PSP) != 0;

		if (entry.name_size > 256)
		{
			LOG_ERROR(LOADER, "PKG name size is too big (0x%x)", entry.name_size);
			continue;
		}

		decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : dec_key.data());

		const std::string name(reinterpret_cast<char*>(buf.get()), entry.name_size);

		LOG_NOTICE(LOADER, "Entry 0x%08x: %s", entry.type, name);

		switch (entry.type & 0xff)
		{
		case PKG_FILE_ENTRY_NPDRM:
		case PKG_FILE_ENTRY_NPDRMEDAT:
		case PKG_FILE_ENTRY_SDAT:
		case PKG_FILE_ENTRY_REGULAR:
		case PKG_FILE_ENTRY_UNK1:
		case 0xe:
		case 0x10:
		case 0x11:
		case 0x13:
		case 0x15:
		case 0x16:
		{
			const std::string path = dir + name;

			const bool did_overwrite = fs::is_file(path);

			if (did_overwrite && (entry.type&PKG_FILE_ENTRY_OVERWRITE) == 0)
			{
				LOG_NOTICE(LOADER, "Didn't overwrite %s", name);
				break;
			}

			if (fs::file out{ path, fs::rewrite })
			{
				for (u64 pos = 0; pos < entry.file_size; pos += BUF_SIZE)
				{
					const u64 block_size = std::min<u64>(BUF_SIZE, entry.file_size - pos);

					if (decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : dec_key.data()) != block_size)
					{
						LOG_ERROR(LOADER, "Failed to extract file %s", path);
						break;
					}

					if (out.write(buf.get(), block_size) != block_size)
					{
						LOG_ERROR(LOADER, "Failed to write file %s", path);
						break;
					}

					if (sync.fetch_add((block_size + 0.0) / header.data_size) < 0.)
					{
						LOG_ERROR(LOADER, "Package installation cancelled: %s", dir);
						return false;
					}
				}

				if (did_overwrite)
				{
					LOG_WARNING(LOADER, "Overwritten file %s", name);
				}
				else
				{
					LOG_NOTICE(LOADER, "Created file %s", name);
				}
			}
			else
			{
				LOG_ERROR(LOADER, "Failed to create file %s", path);
			}

			break;
		}

		case PKG_FILE_ENTRY_FOLDER:
		case 0x12:
		{
			const std::string path = dir + name;

			if (fs::create_dir(path))
			{
				LOG_NOTICE(LOADER, "Created directory %s", name);
			}
			else if (fs::is_dir(path))
			{
				LOG_WARNING(LOADER, "Reused existing directory %s", name);
			}
			else
			{
				LOG_ERROR(LOADER, "Failed to create directory %s", path);
			}

			break;
		}

		default:
		{
			LOG_ERROR(LOADER, "Unknown PKG entry type (0x%x) %s", entry.type, name);
		}
		}
	}

	LOG_SUCCESS(LOADER, "Package successfully installed to %s", dir);
	return true;
}
Example #5
0
File: PSF.cpp Project: AniLeo/rpcs3
	registry load_object(const fs::file& stream)
	{
		registry result;

		// Hack for empty input (TODO)
		if (!stream)
		{
			return result;
		}

		// Get header
		header_t header;
		verify(HERE), stream.read(header);

		// Check magic and version
		verify(HERE),
			header.magic == "\0PSF"_u32,
			header.version == 0x101,
			sizeof(header_t) + header.entries_num * sizeof(def_table_t) <= header.off_key_table,
			header.off_key_table <= header.off_data_table,
			header.off_data_table <= stream.size();

		// Get indices
		std::vector<def_table_t> indices;
		verify(HERE), stream.read(indices, header.entries_num);

		// Get keys
		std::string keys;
		verify(HERE), stream.seek(header.off_key_table) == header.off_key_table;
		verify(HERE), stream.read(keys, header.off_data_table - header.off_key_table);

		// Load entries
		for (u32 i = 0; i < header.entries_num; ++i)
		{
			verify(HERE), indices[i].key_off < header.off_data_table - header.off_key_table;

			// Get key name (null-terminated string)
			std::string key(keys.data() + indices[i].key_off);

			// Check entry
			verify(HERE),
				result.count(key) == 0,
				indices[i].param_len <= indices[i].param_max,
				indices[i].data_off < stream.size() - header.off_data_table,
				indices[i].param_max < stream.size() - indices[i].data_off;

			// Seek data pointer
			stream.seek(header.off_data_table + indices[i].data_off);

			if (indices[i].param_fmt == format::integer && indices[i].param_max == sizeof(u32) && indices[i].param_len == sizeof(u32))
			{
				// Integer data
				le_t<u32> value;
				verify(HERE), stream.read(value);

				result.emplace(std::piecewise_construct,
					std::forward_as_tuple(std::move(key)),
					std::forward_as_tuple(value));
			}
			else if (indices[i].param_fmt == format::string || indices[i].param_fmt == format::array)
			{
				// String/array data
				std::string value;
				verify(HERE), stream.read(value, indices[i].param_len);

				if (indices[i].param_fmt == format::string)
				{
					// Find null terminator
					value.resize(std::strlen(value.c_str()));
				}

				result.emplace(std::piecewise_construct,
					std::forward_as_tuple(std::move(key)),
					std::forward_as_tuple(indices[i].param_fmt, indices[i].param_max, std::move(value)));
			}
			else
			{
				// Possibly unsupported format, entry ignored
				LOG_ERROR(LOADER, "Unknown entry format (key='%s', fmt=0x%x, len=0x%x, max=0x%x)", key, indices[i].param_fmt, indices[i].param_len, indices[i].param_max);
			}
		}

		return result;
	}