4 Commits

13 changed files with 180 additions and 80 deletions

View File

@ -32,7 +32,7 @@ include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
APP_TITLE := Lockpick
APP_AUTHOR := shchmue
APP_VERSION := 1.1.1
APP_VERSION := 1.2.1
TARGET := $(subst $e ,_,$(notdir $(APP_TITLE)))
BUILD := build

View File

@ -2,13 +2,15 @@ Lockpick
=
Lockpick is a ground-up C++17 rewrite of homebrew key derivation software, namely [kezplez-nx](https://github.com/tesnos/kezplez-nx). It also dumps titlekeys. This will dump all keys through `*_key_05` on firmwares below `6.2.0` and through `*_key_06` on `6.2.0`.
Due to key generation changes introduced in `7.0.0`, Lockpick is not able to dump keys ending in 07 at all. Furthermore, unfortunately the public method to dump `tsec_root_key` is only available on firmware `6.2.0` so `7.x` consoles can only dump through keys ending in 05.
What this software does differently
=
* Dumps `titlekeys` and SD seed
* Dumps all keys through `6.2.0`
* Uses the superfast `xxHash` instead of `sha256` when searching exefs for keys for a ~5x speed improvement
* Gets all possible keys from running process memory - this means no need to decrypt `Package2` at all, let alone decompress `KIP`s
* Gets `header_key` without `tsec`, `sbk`, `master_key_00` or `aes` sources - which may or may not be the same way `ChoiDujourNX` does it :eyes: (and I'm gonna issue a challenge to homebrew title installers to implement similar code so you don't need your users to use separate software like this :stuck_out_tongue_winking_eye: it's up to you to figure out if the same can be done for `key_area_keys` if needed)
* Gets bis keys and `header_key` without `tsec`, `sbk`, `master_key_00` or `aes` sources. Shoutout to exelix11 for using this method in [SwitchThemeInjector](https://github.com/exelix11/SwitchThemeInjector)! Homebrew devs should be doing this instead of requiring users to provide key files!
Usage
=
@ -34,7 +36,7 @@ Notes
Building
=
Release built with `libnx v1.6.0`.
Release built with `libnx v2.0.0` but still builds and runs with `v1.6.0`.
Uses `freetype` which comes with `switch-portlibs` via `devkitPro pacman`:
```

View File

@ -1,4 +1,21 @@
# Changelog
## Version 1.2.1
* Generate bis keys without master keys
* Update file size check to support Hekate v4.8 TSEC dump
* Fixed prod.keys alphabetization error
* Fixed build warning for ff.c
* Added in-app disclaimer about which keys can be dumped
## Version 1.2
* Update for libnx v2.0.0 compatibility and still runs when built with v1.6.0
* The binary got even smaller!
* Accelerate finding FS keys
* No longer find BIS sources as they're hardcoded (whoops)
* Find all keys on first pass hashing FS instead of hashing the whole thing from the beginning repeatedly (__*whoops*__)
## Version 1.1.1
* No longer try to dump SD seed and ES keys on 1.0.0 as they're not available until 2.0.0
## Version 1.1
* Changed titlekey dump methodology
* No longer crashes sysmodule, reboot no longer needed

View File

@ -34,8 +34,16 @@
#include "sha256.h"
#ifdef RGBX8
#define LIBNX_200
#endif
namespace Common {
static u32 framebuf_width = 0;
#ifdef LIBNX_200
static Framebuffer fb;
static u32 stride;
#endif
static u32 *framebuf;
// FreeType vars
static FT_Library library;
@ -111,7 +119,9 @@ namespace Common {
PlFontData font;
#ifndef LIBNX_200
consoleInit(NULL);
#endif
plGetSharedFontByType(&font, PlSharedFontType_Standard);
@ -119,11 +129,21 @@ namespace Common {
FT_New_Memory_Face(library, static_cast<FT_Byte *>(font.address), font.size, 0, &face);
FT_Set_Char_Size(face, 0, 6*64, 300, 300);
gfxSetMode(GfxMode_LinearDouble); // todo: update for nwindow/framebuffer
#ifdef LIBNX_200
framebufferCreate(&fb, nwindowGetDefault(), FB_WIDTH, FB_HEIGHT, PIXEL_FORMAT_RGBA_8888, 2);
framebufferMakeLinear(&fb);
framebuf = (u32 *)framebufferBegin(&fb, &stride);
framebuf_width = stride / sizeof(u32);
memset(framebuf, 0, stride*FB_HEIGHT);
framebufferEnd(&fb);
#else
gfxSetMode(GfxMode_LinearDouble);
framebuf = (u32 *)gfxGetFramebuffer(&framebuf_width, NULL);
memset(framebuf, 0, gfxGetFramebufferSize());
draw_text(0x10, 0x020, YELLOW, "Lockpick! by shchmue");
#endif
draw_text(0x010, 0x020, YELLOW, "Lockpick! by shchmue");
draw_text(0x190, 0x020, YELLOW, "Note: Only dumps keys 00-06 on 6.2.0");
draw_text(0x190, 0x040, YELLOW, " and keys 00-05 on all other firmwares including 7.0.0+");
draw_set_rect(814, 452 + 42 * 0, 450, 42, FLAG_RED);
draw_set_rect(814, 452 + 42 * 1, 450, 42, FLAG_ORANGE);
@ -148,7 +168,7 @@ namespace Common {
draw_text(0x10, 0x0e0, CYAN, "Saving keys to keyfile...");
draw_text(0x10, 0x110, CYAN, "Total time elapsed:");
consoleUpdate(NULL);
update_display();
}
void get_tegra_keys(Key &sbk, Key &tsec, Key &tsec_root) {
@ -167,7 +187,7 @@ namespace Common {
sbk = Key("secure_boot_key", 0x10, temp_key);
fclose(fuse_file);
}
else if (!tsec.found() && (p.file_size() == 0x30) &&
else if (!tsec.found() && (p.file_size() == 0x20 || p.file_size() == 0x30) &&
(std::string("tsec").compare(std::string(p.path().filename()).substr(0, 4)) == 0))
{
FILE *tsec_file = fopen(p.path().c_str(), "rb");
@ -206,17 +226,29 @@ namespace Common {
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
if (kDown & KEY_PLUS) break;
consoleUpdate(NULL);
update_display();
}
#ifdef LIBNX_200
framebufferClose(&fb);
#else
consoleExit(NULL);
#endif
FT_Done_Face(face);
FT_Done_FreeType(library);
consoleExit(NULL);
appletUnlockExit();
}
void update_display() {
#ifdef LIBNX_200
framebufferBegin(&fb, &stride);
framebufferEnd(&fb);
#else
consoleUpdate(NULL);
#endif
}
void sha256(const u8 *data, u8 *hash, size_t length) {
struct sha256_state ctx;
sha256_init(&ctx);

View File

@ -24,6 +24,9 @@
#include <switch/types.h>
#define FB_WIDTH 1280
#define FB_HEIGHT 720
#define GREEN RGBA8_MAXALPHA(0, 0xff, 0)
#define RED RGBA8_MAXALPHA(0xff, 0, 0)
#define CYAN RGBA8_MAXALPHA(0, 0xff, 0xff)
@ -61,6 +64,9 @@ namespace Common {
// print exit
void wait_to_exit();
// refresh display
void update_display();
void sha256(const u8 *data, u8 *hash, size_t length);
// reads "<keyname> = <hexkey>" and returns byte vector
byte_vector key_string_to_byte_vector(std::string key_string);

View File

@ -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);

View File

@ -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;
};

View File

@ -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() {
@ -270,16 +251,16 @@ void KeyCollection::get_keys() {
char keys_str[32];
sprintf(keys_str, "Total keys found: %lu", Key::get_saved_key_count());
Common::draw_text(0x2a0, 0x110, CYAN, keys_str);
Common::draw_text(0x80, 0x140, GREEN, "Keys saved to \"/switch/prod.keys\"!");
Common::draw_text(0x80, 0x140, YELLOW, "Keys saved to \"/switch/prod.keys\"!");
Common::draw_text(0x10, 0x170, CYAN, "Dumping titlekeys...");
consoleUpdate(NULL);
Common::update_display();
profiler_time = profile(&KeyCollection::get_titlekeys, *this);
Common::draw_text_with_time(0x10, 0x170, GREEN, "Dumping titlekeys...", profiler_time);
sprintf(keys_str, "Titlekeys found: %lu", titlekeys_dumped);
Common::draw_text(0x2a0, 0x170, CYAN, keys_str);
if (titlekeys_dumped > 0)
Common::draw_text(0x80, 0x1a0, GREEN, "Titlekeys saved to \"/switch/title.keys\"!");
Common::draw_text(0x80, 0x1a0, YELLOW, "Titlekeys saved to \"/switch/title.keys\"!");
else
Common::draw_text(0x80, 0x1a0, GREEN, "No titlekeys found. Either you've never played or installed a game or dump failed.");
}
@ -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);
@ -381,10 +369,32 @@ void KeyCollection::derive_keys() {
header_key = {"header_key", 0x20, {}};
if (header_kek_source.found() && header_key_source.found()) {
u8 tempheaderkek[0x10], tempheaderkey[0x20];
splCryptoInitialize();
splCryptoGenerateAesKek(header_kek_source.key.data(), 0, 0, tempheaderkek);
splCryptoGenerateAesKey(tempheaderkek, header_key_source.key.data(), tempheaderkey);
splCryptoGenerateAesKey(tempheaderkek, header_key_source.key.data() + 0x00, tempheaderkey + 0x00);
splCryptoGenerateAesKey(tempheaderkek, header_key_source.key.data() + 0x10, tempheaderkey + 0x10);
header_key = {"header_key", 0x20, byte_vector(&tempheaderkey[0], &tempheaderkey[0x20])};
header_key = {"header_key", 0x20, byte_vector(tempheaderkey, tempheaderkey + 0x20)};
splCryptoExit();
}
if (bis_key_source_00.found() && bis_key_source_01.found() && bis_key_source_02.found()) {
u8 tempbiskek[0x10], tempbiskey[0x20];
splFsInitialize();
splFsGenerateSpecificAesKey(bis_key_source_00.key.data() + 0x00, 0, 0, tempbiskey + 0x00);
splFsGenerateSpecificAesKey(bis_key_source_00.key.data() + 0x10, 0, 0, tempbiskey + 0x10);
bis_key.push_back(Key {"bis_key_00", 0x20, byte_vector(tempbiskey, tempbiskey + 0x20)});
splFsExit();
splCryptoInitialize();
splCryptoGenerateAesKek(bis_kek_source.key.data(), 0, 1, tempbiskek);
splCryptoGenerateAesKey(tempbiskek, bis_key_source_01.key.data() + 0x00, tempbiskey + 0x00);
splCryptoGenerateAesKey(tempbiskek, bis_key_source_01.key.data() + 0x10, tempbiskey + 0x10);
bis_key.push_back(Key {"bis_key_01", 0x20, byte_vector(tempbiskey, tempbiskey + 0x20)});
splCryptoGenerateAesKey(tempbiskek, bis_key_source_02.key.data() + 0x00, tempbiskey + 0x00);
splCryptoGenerateAesKey(tempbiskek, bis_key_source_02.key.data() + 0x10, tempbiskey + 0x10);
bis_key.push_back(Key {"bis_key_02", 0x20, byte_vector(tempbiskey, tempbiskey + 0x20)});
bis_key.push_back(Key {"bis_key_03", 0x20, bis_key[2].key});
splCryptoExit();
}
for (u8 i = 0; i < aes_kek_generation_source.key.size(); i++) {
@ -402,15 +412,6 @@ void KeyCollection::derive_keys() {
save_mac_key = Key {"save_mac_key", 0x10, kek.aes_decrypt_ecb(save_mac_key_source.key)};
}
if (device_key.found()) {
Key kek = {device_key.aes_decrypt_ecb(retail_specific_aes_key_source.key), 0x10};
bis_key.push_back(Key {"bis_key_00", 0x20, kek.aes_decrypt_ecb(bis_key_source_00.key)});
kek = Key {bis_kek_source.generate_kek(device_key, aes_kek_generation_source, aes_key_generation_source), 0x10};
bis_key.push_back(Key {"bis_key_01", 0x20, kek.aes_decrypt_ecb(bis_key_source_01.key)});
bis_key.push_back(Key {"bis_key_02", 0x20, kek.aes_decrypt_ecb(bis_key_source_02.key)});
bis_key.push_back(Key {"bis_key_03", 0x20, bis_key[2].key});\
}
char keynum[] = "00";
for (u8 i = 0; i < master_key.size(); i++) {
if (!master_key[i].found())
@ -479,11 +480,11 @@ void KeyCollection::save_keys() {
aes_kek_generation_source.save_key(key_file);
aes_key_generation_source.save_key(key_file);
bis_kek_source.save_key(key_file);
for (auto k : bis_key)
k.save_key(key_file);
bis_key_source_00.save_key(key_file);
bis_key_source_01.save_key(key_file);
bis_key_source_02.save_key(key_file);
for (auto k : bis_key)
k.save_key(key_file);
device_key.save_key(key_file);
eticket_rsa_kek.save_key(key_file);
for (auto k : es_keys)
@ -528,11 +529,11 @@ void KeyCollection::save_keys() {
save_mac_kek_source.save_key(key_file);
save_mac_key.save_key(key_file);
save_mac_key_source.save_key(key_file);
sbk.save_key(key_file);
sd_card_kek_source.save_key(key_file);
sd_card_nca_key_source.save_key(key_file);
sd_card_save_key_source.save_key(key_file);
sd_seed.save_key(key_file);
sbk.save_key(key_file);
ssl_rsa_kek.save_key(key_file);
for (auto k : ssl_keys)
k->save_key(key_file);
@ -558,7 +559,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 +568,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 +621,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 +649,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 +677,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;

View File

@ -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];

View File

@ -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;
}
}

View File

@ -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;

View File

@ -15,7 +15,7 @@
/ and optional writing functions as well. */
#define FF_FS_MINIMIZE 2
#define FF_FS_MINIMIZE 1
/* This option defines minimization level to remove some basic API functions.
/
/ 0: Basic functions are fully enabled.

View File

@ -23,7 +23,6 @@ extern "C" void userAppInit()
{
plInitialize();
pmdmntInitialize();
splCryptoInitialize();
splInitialize();
}
@ -31,7 +30,6 @@ extern "C" void userAppExit()
{
plExit();
pmdmntExit();
splCryptoExit();
splExit();
}