mirror of
https://github.com/shchmue/Lockpick.git
synced 2024-12-22 14:35:26 +00:00
Improve speed of finding FS keys
This commit is contained in:
parent
49c6fd8dc7
commit
352c39ae8a
|
@ -124,7 +124,7 @@ byte_vector Key::cmac(byte_vector data) {
|
|||
return dest;
|
||||
}
|
||||
|
||||
void Key::find_key(const byte_vector &buffer) {
|
||||
void Key::find_key(const byte_vector &buffer, size_t start) {
|
||||
if ((buffer.size() == 0) || (found()))
|
||||
return;
|
||||
|
||||
|
@ -136,10 +136,11 @@ void Key::find_key(const byte_vector &buffer) {
|
|||
return;
|
||||
std::copy(buffer.begin(), buffer.begin() + length, std::back_inserter(key));
|
||||
is_found = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// hash every length-sized byte chunk in buffer until it matches member hash
|
||||
for (size_t i = 0; i < buffer.size() - length; i++) {
|
||||
for (size_t i = start; i < buffer.size() - length; i++) {
|
||||
if (xx_hash == XXHash64::hash(buffer.data() + i, length, 0)) {
|
||||
// double-check sha256 since xxhash64 isn't as collision-safe
|
||||
Common::sha256(buffer.data() + i, temp_hash, length);
|
||||
|
|
|
@ -54,18 +54,17 @@ public:
|
|||
// return CMAC of data
|
||||
byte_vector cmac(byte_vector data);
|
||||
// find key in buffer by hash, optionally specify start offset
|
||||
void find_key(const byte_vector &buffer);
|
||||
void find_key(const byte_vector &buffer, size_t start = 0);
|
||||
// get key encryption key
|
||||
byte_vector generate_kek(Key &master_key, const Key &kek_seed, const Key &key_seed);
|
||||
|
||||
byte_vector key;
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
u64 xx_hash;
|
||||
byte_vector hash;
|
||||
u8 length;
|
||||
bool is_found = false;
|
||||
|
||||
private:
|
||||
static size_t saved_key_count;
|
||||
};
|
|
@ -22,9 +22,9 @@
|
|||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
@ -158,10 +158,10 @@ KeyCollection::KeyCollection() {
|
|||
sd_card_kek_source = {"sd_card_kek_source", 0xc408d710a3b821eb, {
|
||||
0x6B, 0x2E, 0xD8, 0x77, 0xC2, 0xC5, 0x23, 0x34, 0xAC, 0x51, 0xE5, 0x9A, 0xBF, 0xA7, 0xEC, 0x45,
|
||||
0x7F, 0x4A, 0x7D, 0x01, 0xE4, 0x62, 0x91, 0xE9, 0xF2, 0xEA, 0xA4, 0x5F, 0x01, 0x1D, 0x24, 0xB7}, 0x10};
|
||||
sd_card_nca_key_source = {"sd_card_nca_key_source", 0xbea347c9f8472947, {
|
||||
sd_card_nca_key_source = {"sd_card_nca_key_source", 0xb026106d9699fec0, { // xxhash of first 0x10 bytes
|
||||
0x2E, 0x75, 0x1C, 0xEC, 0xF7, 0xD9, 0x3A, 0x2B, 0x95, 0x7B, 0xD5, 0xFF, 0xCB, 0x08, 0x2F, 0xD0,
|
||||
0x38, 0xCC, 0x28, 0x53, 0x21, 0x9D, 0xD3, 0x09, 0x2C, 0x6D, 0xAB, 0x98, 0x38, 0xF5, 0xA7, 0xCC}, 0x20};
|
||||
sd_card_save_key_source = {"sd_card_save_key_source", 0xf87fe8c3688c3022, {
|
||||
sd_card_save_key_source = {"sd_card_save_key_source", 0x9697ba2fec3d3ed1, { // xxhash of first 0x10 bytes
|
||||
0xD4, 0x82, 0x74, 0x35, 0x63, 0xD3, 0xEA, 0x5D, 0xCD, 0xC3, 0xB7, 0x4E, 0x97, 0xC9, 0xAC, 0x8A,
|
||||
0x34, 0x21, 0x64, 0xFA, 0x04, 0x1A, 0x1D, 0xC8, 0x0F, 0x17, 0xF6, 0xD3, 0x1E, 0x4B, 0xC0, 0x1C}, 0x20};
|
||||
|
||||
|
@ -195,10 +195,6 @@ KeyCollection::KeyCollection() {
|
|||
};
|
||||
|
||||
fs_rodata_keys = {
|
||||
&bis_kek_source,
|
||||
&bis_key_source_00,
|
||||
&bis_key_source_01,
|
||||
&bis_key_source_02,
|
||||
&header_kek_source,
|
||||
&key_area_key_application_source,
|
||||
&key_area_key_ocean_source,
|
||||
|
@ -216,25 +212,10 @@ KeyCollection::KeyCollection() {
|
|||
});
|
||||
}
|
||||
|
||||
package1ldr_keys = {
|
||||
&keyblob_mac_key_source,
|
||||
&master_key_source,
|
||||
&per_console_key_source
|
||||
};
|
||||
|
||||
ssl_keys = {
|
||||
&ssl_rsa_kek_source_x,
|
||||
&ssl_rsa_kek_source_y
|
||||
};
|
||||
|
||||
tz_keys = {
|
||||
&aes_kek_generation_source,
|
||||
&package2_key_source,
|
||||
&titlekek_source,
|
||||
&retail_specific_aes_key_source,
|
||||
&aes_kek_seed_01,
|
||||
&aes_kek_seed_03
|
||||
};
|
||||
};
|
||||
|
||||
void KeyCollection::get_keys() {
|
||||
|
@ -361,15 +342,22 @@ void KeyCollection::get_memory_keys() {
|
|||
FSRodata.get_from_memory(FS_TID, SEG_RODATA);
|
||||
FSData.get_from_memory(FS_TID, SEG_DATA);
|
||||
|
||||
for (auto k : fs_rodata_keys)
|
||||
k->find_key(FSRodata.data);
|
||||
FSRodata.find_keys(fs_rodata_keys);
|
||||
|
||||
header_key_source.find_key(FSData.data);
|
||||
size_t i = 0;
|
||||
/*for ( ; i < FSData.data.size(); i++) {
|
||||
// speeds things up but i'm not 100% sure this is always here
|
||||
if (*reinterpret_cast<u128 *>(FSData.data.data() + i) == 0x10001)
|
||||
break;
|
||||
}*/
|
||||
header_key_source.find_key(FSData.data, i);
|
||||
|
||||
SSLRodata.get_from_memory(SSL_TID, SEG_RODATA);
|
||||
// using find_keys on these is actually slower
|
||||
for (auto k : ssl_keys)
|
||||
k->find_key(SSLRodata.data);
|
||||
|
||||
// firmware 1.0.0 doesn't have the ES keys
|
||||
if (!kernelAbove200())
|
||||
return;
|
||||
ESRodata.get_from_memory(ES_TID, SEG_RODATA);
|
||||
|
@ -558,7 +546,7 @@ void KeyCollection::get_titlekeys() {
|
|||
esListCommonTicket(&ids_written, common_rights_ids, sizeof(common_rights_ids));
|
||||
esListPersonalizedTicket(&ids_written, personalized_rights_ids, sizeof(personalized_rights_ids));
|
||||
esExit();
|
||||
if ((common_count == 0) && (personalized_count == 0))
|
||||
if (common_count + personalized_count == 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
|
@ -567,7 +555,7 @@ void KeyCollection::get_titlekeys() {
|
|||
this would be fine, except we have to match the exact list so we don't stop too early
|
||||
*/
|
||||
char titlekey_block[0x100], buffer[TITLEKEY_BUFFER_SIZE], rights_id_string[0x21], titlekey_string[0x21];
|
||||
std::set<std::string> rights_ids;
|
||||
std::unordered_set<std::string> rights_ids;
|
||||
for (size_t i = 0; i < common_count; i++) {
|
||||
for (size_t j = 0; j < 0x10; j++) {
|
||||
sprintf(&rights_id_string[j*2], "%02x", common_rights_ids[i].c[j]);
|
||||
|
@ -620,7 +608,7 @@ void KeyCollection::get_titlekeys() {
|
|||
for (size_t k = 0; k < 0x10; k++)
|
||||
sprintf(&rights_id_string[k*2], "%02x", buffer[j + 0x2a0 + k]);
|
||||
|
||||
// skip if rights id found but not reported by es
|
||||
// skip if rights id not reported by es
|
||||
if (rights_ids.find(rights_id_string) == rights_ids.end())
|
||||
continue;
|
||||
// skip if rights id already in map
|
||||
|
@ -648,8 +636,18 @@ void KeyCollection::get_titlekeys() {
|
|||
for (size_t i = 0; i < bytes_read; i += 0x4000) {
|
||||
for (size_t j = i; j < i + 0x4000; j += 0x400) {
|
||||
if (*reinterpret_cast<u32 *>(&buffer[j]) == 0x10004) {
|
||||
for (size_t k = 0; k < 0x10; k++)
|
||||
sprintf(&rights_id_string[k*2], "%02x", buffer[j + 0x2a0 + k]);
|
||||
|
||||
// skip if rights id not reported by es
|
||||
if (rights_ids.find(rights_id_string) == rights_ids.end())
|
||||
continue;
|
||||
// skip if rights id already in map
|
||||
if (titlekeys.find(rights_id_string) != titlekeys.end())
|
||||
continue;
|
||||
|
||||
std::copy(buffer + j + 0x180, buffer + j + 0x280, titlekey_block);
|
||||
|
||||
|
||||
splUserExpMod(titlekey_block, N, D, 0x100, M);
|
||||
|
||||
// decrypts the titlekey from personalized ticket
|
||||
|
@ -666,16 +664,6 @@ void KeyCollection::get_titlekeys() {
|
|||
if (!std::equal(db, db + 0x20, null_hash))
|
||||
continue;
|
||||
|
||||
for (size_t k = 0; k < 0x10; k++)
|
||||
sprintf(&rights_id_string[k*2], "%02x", buffer[j + 0x2a0 + k]);
|
||||
|
||||
// skip if rights id found but not reported by es
|
||||
if (rights_ids.find(rights_id_string) == rights_ids.end())
|
||||
continue;
|
||||
// skip if rights id already in map
|
||||
if (titlekeys.find(rights_id_string) != titlekeys.end())
|
||||
continue;
|
||||
|
||||
for (size_t k = 0; k < 0x10; k++)
|
||||
sprintf(&titlekey_string[k*2], "%02x", db[k + 0xcf]);
|
||||
titlekeys[rights_id_string] = titlekey_string;
|
||||
|
|
|
@ -113,7 +113,7 @@ private:
|
|||
titlekek;
|
||||
|
||||
std::vector<Key *>
|
||||
es_keys, fs_rodata_keys, package1ldr_keys, ssl_keys, tz_keys;
|
||||
es_keys, fs_rodata_keys, ssl_keys;
|
||||
|
||||
// hash of empty string used to verify titlekeys for personalized tickets
|
||||
static const u8 null_hash[0x20];
|
||||
|
|
|
@ -16,9 +16,15 @@
|
|||
|
||||
#include "KeyLocation.hpp"
|
||||
|
||||
#include "Common.hpp"
|
||||
#include "xxhash64.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <switch.h>
|
||||
|
||||
void KeyLocation::get_from_memory(u64 tid, u8 segMask) {
|
||||
void KeyLocation::get_from_memory(u64 tid, u8 seg_mask) {
|
||||
Handle debug_handle = INVALID_HANDLE;
|
||||
u64 d[8];
|
||||
|
||||
|
@ -62,10 +68,10 @@ void KeyLocation::get_from_memory(u64 tid, u8 segMask) {
|
|||
{
|
||||
svcQueryDebugProcessMemory(&mem_info, &page_info, debug_handle, addr);
|
||||
// weird code to allow for bitmasking segments
|
||||
if ((mem_info.perm & Perm_R) &&
|
||||
if ((mem_info.perm & Perm_R) &&
|
||||
((mem_info.type & 0xff) >= MemType_CodeStatic) &&
|
||||
((mem_info.type & 0xff) < MemType_Heap) &&
|
||||
((segment <<= 1) >> 1 & segMask) > 0)
|
||||
((segment <<= 1) >> 1 & seg_mask) > 0)
|
||||
{
|
||||
data.resize(data.size() + mem_info.size);
|
||||
if(R_FAILED(svcReadDebugProcessMemory(data.data() + data.size() - mem_info.size, debug_handle, mem_info.addr, mem_info.size))) {
|
||||
|
@ -86,4 +92,38 @@ void KeyLocation::get_keyblobs() {
|
|||
data.resize(0x200 * KNOWN_KEYBLOBS);
|
||||
fsStorageRead(&boot0, KEYBLOB_OFFSET, data.data(), data.size());
|
||||
fsStorageClose(&boot0);
|
||||
}
|
||||
|
||||
void KeyLocation::find_keys(std::vector<Key *> &keys) {
|
||||
if (data.size() == 0)
|
||||
return;
|
||||
|
||||
u8 temp_hash[0x20];
|
||||
size_t key_indices_left = keys.size();
|
||||
u64 hash = 0;
|
||||
std::unordered_map<u64, size_t> hash_index;
|
||||
for (size_t i = 0; i < keys.size(); i++)
|
||||
hash_index[keys[i]->xx_hash] = i;
|
||||
|
||||
// hash every length-sized byte chunk in data until it matches a key hash
|
||||
for (size_t i = 0; i < data.size() - 0x10; i++) {
|
||||
hash = XXHash64::hash(data.data() + i, 0x10, 0);
|
||||
auto search = hash_index.find(hash);
|
||||
if (search == hash_index.end()) {
|
||||
continue;
|
||||
}
|
||||
size_t key_index = hash_index[hash];
|
||||
u8 key_length = keys[key_index]->length;
|
||||
// double-check sha256 since xxhash64 isn't as collision-safe
|
||||
Common::sha256(data.data() + i, temp_hash, key_length);
|
||||
if (!std::equal(keys[key_index]->hash.begin(), keys[key_index]->hash.end(), temp_hash))
|
||||
continue;
|
||||
std::copy(data.begin() + i, data.begin() + i + key_length, std::back_inserter(keys[key_index]->key));
|
||||
keys[key_index]->is_found = true;
|
||||
key_indices_left--;
|
||||
if (key_indices_left == 0)
|
||||
return;
|
||||
hash_index.erase(hash);
|
||||
i += key_length - 1;
|
||||
}
|
||||
}
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Key.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <switch/types.h>
|
||||
|
@ -44,9 +46,11 @@ typedef std::vector<u8> byte_vector;
|
|||
class KeyLocation {
|
||||
public:
|
||||
// get memory in requested segments from running title
|
||||
void get_from_memory(u64 tid, u8 segMask);
|
||||
void get_from_memory(u64 tid, u8 seg_mask);
|
||||
// get keyblobs from BOOT0
|
||||
void get_keyblobs();
|
||||
// locate keys in data
|
||||
void find_keys(std::vector<Key *> &keys);
|
||||
|
||||
// data found by get functions
|
||||
byte_vector data;
|
||||
|
|
Loading…
Reference in a new issue