Improve speed of finding FS keys
This commit is contained in:
		| @@ -124,7 +124,7 @@ byte_vector Key::cmac(byte_vector data) { | |||||||
|     return dest; |     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())) |     if ((buffer.size() == 0) || (found())) | ||||||
|         return; |         return; | ||||||
|  |  | ||||||
| @@ -136,10 +136,11 @@ void Key::find_key(const byte_vector &buffer) { | |||||||
|             return; |             return; | ||||||
|         std::copy(buffer.begin(), buffer.begin() + length, std::back_inserter(key)); |         std::copy(buffer.begin(), buffer.begin() + length, std::back_inserter(key)); | ||||||
|         is_found = true; |         is_found = true; | ||||||
|  |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // hash every length-sized byte chunk in buffer until it matches member hash |     // 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)) { |         if (xx_hash == XXHash64::hash(buffer.data() + i, length, 0)) { | ||||||
|             // double-check sha256 since xxhash64 isn't as collision-safe |             // double-check sha256 since xxhash64 isn't as collision-safe | ||||||
|             Common::sha256(buffer.data() + i, temp_hash, length); |             Common::sha256(buffer.data() + i, temp_hash, length); | ||||||
|   | |||||||
| @@ -54,18 +54,17 @@ public: | |||||||
|     // return CMAC of data |     // return CMAC of data | ||||||
|     byte_vector cmac(byte_vector data); |     byte_vector cmac(byte_vector data); | ||||||
|     // find key in buffer by hash, optionally specify start offset |     // 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 |     // get key encryption key | ||||||
|     byte_vector generate_kek(Key &master_key, const Key &kek_seed, const Key &key_seed); |     byte_vector generate_kek(Key &master_key, const Key &kek_seed, const Key &key_seed); | ||||||
|  |  | ||||||
|     byte_vector key; |     byte_vector key; | ||||||
|  |  | ||||||
| private: |  | ||||||
|     std::string name; |     std::string name; | ||||||
|     u64 xx_hash; |     u64 xx_hash; | ||||||
|     byte_vector hash; |     byte_vector hash; | ||||||
|     u8 length; |     u8 length; | ||||||
|     bool is_found = false; |     bool is_found = false; | ||||||
|  |  | ||||||
|  | private: | ||||||
|     static size_t saved_key_count; |     static size_t saved_key_count; | ||||||
| }; | }; | ||||||
| @@ -22,9 +22,9 @@ | |||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <chrono> | #include <chrono> | ||||||
| #include <functional> | #include <functional> | ||||||
| #include <set> |  | ||||||
| #include <string> | #include <string> | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
|  | #include <unordered_set> | ||||||
|  |  | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
|  |  | ||||||
| @@ -158,10 +158,10 @@ KeyCollection::KeyCollection() { | |||||||
|     sd_card_kek_source = {"sd_card_kek_source", 0xc408d710a3b821eb, { |     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, |         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}; |         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, |         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}; |         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, |         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}; |         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 = { |     fs_rodata_keys = { | ||||||
|         &bis_kek_source, |  | ||||||
|         &bis_key_source_00, |  | ||||||
|         &bis_key_source_01, |  | ||||||
|         &bis_key_source_02, |  | ||||||
|         &header_kek_source, |         &header_kek_source, | ||||||
|         &key_area_key_application_source, |         &key_area_key_application_source, | ||||||
|         &key_area_key_ocean_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_keys = { | ||||||
|         &ssl_rsa_kek_source_x, |         &ssl_rsa_kek_source_x, | ||||||
|         &ssl_rsa_kek_source_y |         &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() { | void KeyCollection::get_keys() { | ||||||
| @@ -361,15 +342,22 @@ void KeyCollection::get_memory_keys() { | |||||||
|     FSRodata.get_from_memory(FS_TID, SEG_RODATA); |     FSRodata.get_from_memory(FS_TID, SEG_RODATA); | ||||||
|     FSData.get_from_memory(FS_TID, SEG_DATA); |     FSData.get_from_memory(FS_TID, SEG_DATA); | ||||||
|  |  | ||||||
|     for (auto k : fs_rodata_keys) |     FSRodata.find_keys(fs_rodata_keys); | ||||||
|         k->find_key(FSRodata.data); |  | ||||||
|  |  | ||||||
|     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); |     SSLRodata.get_from_memory(SSL_TID, SEG_RODATA); | ||||||
|  |     // using find_keys on these is actually slower | ||||||
|     for (auto k : ssl_keys) |     for (auto k : ssl_keys) | ||||||
|         k->find_key(SSLRodata.data); |         k->find_key(SSLRodata.data); | ||||||
|  |  | ||||||
|  |     // firmware 1.0.0 doesn't have the ES keys | ||||||
|     if (!kernelAbove200()) |     if (!kernelAbove200()) | ||||||
|         return; |         return; | ||||||
|     ESRodata.get_from_memory(ES_TID, SEG_RODATA); |     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)); |     esListCommonTicket(&ids_written, common_rights_ids, sizeof(common_rights_ids)); | ||||||
|     esListPersonalizedTicket(&ids_written, personalized_rights_ids, sizeof(personalized_rights_ids)); |     esListPersonalizedTicket(&ids_written, personalized_rights_ids, sizeof(personalized_rights_ids)); | ||||||
|     esExit(); |     esExit(); | ||||||
|     if ((common_count == 0) && (personalized_count == 0)) |     if (common_count + personalized_count == 0) | ||||||
|         return; |         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 |         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]; |     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 i = 0; i < common_count; i++) { | ||||||
|         for (size_t j = 0; j < 0x10; j++) { |         for (size_t j = 0; j < 0x10; j++) { | ||||||
|             sprintf(&rights_id_string[j*2], "%02x", common_rights_ids[i].c[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++) |                     for (size_t k = 0; k < 0x10; k++) | ||||||
|                         sprintf(&rights_id_string[k*2], "%02x", buffer[j + 0x2a0 + 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()) |                     if (rights_ids.find(rights_id_string) == rights_ids.end()) | ||||||
|                         continue; |                         continue; | ||||||
|                     // skip if rights id already in map |                     // 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 i = 0; i < bytes_read; i += 0x4000) { | ||||||
|             for (size_t j = i; j < i + 0x4000; j += 0x400) { |             for (size_t j = i; j < i + 0x4000; j += 0x400) { | ||||||
|                 if (*reinterpret_cast<u32 *>(&buffer[j]) == 0x10004) { |                 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); |                     std::copy(buffer + j + 0x180, buffer + j + 0x280, titlekey_block); | ||||||
|                      |  | ||||||
|                     splUserExpMod(titlekey_block, N, D, 0x100, M); |                     splUserExpMod(titlekey_block, N, D, 0x100, M); | ||||||
|  |  | ||||||
|                     // decrypts the titlekey from personalized ticket |                     // decrypts the titlekey from personalized ticket | ||||||
| @@ -666,16 +664,6 @@ void KeyCollection::get_titlekeys() { | |||||||
|                     if (!std::equal(db, db + 0x20, null_hash)) |                     if (!std::equal(db, db + 0x20, null_hash)) | ||||||
|                         continue; |                         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++) |                     for (size_t k = 0; k < 0x10; k++) | ||||||
|                         sprintf(&titlekey_string[k*2], "%02x", db[k + 0xcf]); |                         sprintf(&titlekey_string[k*2], "%02x", db[k + 0xcf]); | ||||||
|                     titlekeys[rights_id_string] = titlekey_string; |                     titlekeys[rights_id_string] = titlekey_string; | ||||||
|   | |||||||
| @@ -113,7 +113,7 @@ private: | |||||||
|         titlekek; |         titlekek; | ||||||
|  |  | ||||||
|     std::vector<Key *> |     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 |     // hash of empty string used to verify titlekeys for personalized tickets | ||||||
|     static const u8 null_hash[0x20]; |     static const u8 null_hash[0x20]; | ||||||
|   | |||||||
| @@ -16,9 +16,15 @@ | |||||||
|  |  | ||||||
| #include "KeyLocation.hpp" | #include "KeyLocation.hpp" | ||||||
|  |  | ||||||
|  | #include "Common.hpp" | ||||||
|  | #include "xxhash64.h" | ||||||
|  |  | ||||||
|  | #include <algorithm> | ||||||
|  | #include <unordered_map> | ||||||
|  |  | ||||||
| #include <switch.h> | #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; |     Handle debug_handle = INVALID_HANDLE; | ||||||
|     u64 d[8]; |     u64 d[8]; | ||||||
|  |  | ||||||
| @@ -62,10 +68,10 @@ void KeyLocation::get_from_memory(u64 tid, u8 segMask) { | |||||||
|     { |     { | ||||||
|         svcQueryDebugProcessMemory(&mem_info, &page_info, debug_handle, addr); |         svcQueryDebugProcessMemory(&mem_info, &page_info, debug_handle, addr); | ||||||
|         // weird code to allow for bitmasking segments |         // 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_CodeStatic) && | ||||||
|             ((mem_info.type & 0xff) < MemType_Heap) && |             ((mem_info.type & 0xff) < MemType_Heap) && | ||||||
|             ((segment <<= 1) >> 1 & segMask) > 0) |             ((segment <<= 1) >> 1 & seg_mask) > 0) | ||||||
|         { |         { | ||||||
|             data.resize(data.size() + mem_info.size); |             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))) { |             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); |     data.resize(0x200 * KNOWN_KEYBLOBS); | ||||||
|     fsStorageRead(&boot0, KEYBLOB_OFFSET, data.data(), data.size()); |     fsStorageRead(&boot0, KEYBLOB_OFFSET, data.data(), data.size()); | ||||||
|     fsStorageClose(&boot0); |     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 | #pragma once | ||||||
|  |  | ||||||
|  | #include "Key.hpp" | ||||||
|  |  | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| #include <switch/types.h> | #include <switch/types.h> | ||||||
| @@ -44,9 +46,11 @@ typedef std::vector<u8> byte_vector; | |||||||
| class KeyLocation { | class KeyLocation { | ||||||
| public: | public: | ||||||
|     // get memory in requested segments from running title |     // 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 |     // get keyblobs from BOOT0 | ||||||
|     void get_keyblobs(); |     void get_keyblobs(); | ||||||
|  |     // locate keys in data | ||||||
|  |     void find_keys(std::vector<Key *> &keys); | ||||||
|  |  | ||||||
|     // data found by get functions |     // data found by get functions | ||||||
|     byte_vector data; |     byte_vector data; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user