mirror of
https://github.com/AlexAltea/orbital.git
synced 2025-04-02 10:32:05 -04:00
610 lines
20 KiB
C
610 lines
20 KiB
C
/**
|
|
* (c) 2017-2018 Alexandro Sanchez Bach.
|
|
* Released under MIT license. Read LICENSE for more details.
|
|
*
|
|
* Based in previous tools and research by: fail0verflow, flatz.
|
|
*/
|
|
|
|
#include "self_decrypter.h"
|
|
|
|
#include "ksdk.h"
|
|
#include "blob.h"
|
|
#include "debug.h"
|
|
|
|
/* debugging */
|
|
#define DEBUG_SELF 0
|
|
|
|
#define assert(cond) if (!(cond)) { \
|
|
dprintf("%s:%d: failed.\n", __FUNCTION__, __LINE__); \
|
|
goto error; \
|
|
}
|
|
#define kassert(cond) if (!(cond)) { \
|
|
kdprintf("%s:%d: failed.\n", __FUNCTION__, __LINE__); \
|
|
goto error; \
|
|
}
|
|
|
|
/* constants */
|
|
#define SELF_MAGIC 0x1D3D154F
|
|
#define SELF_VERSION 0x0
|
|
#define SELF_MODE 0x1
|
|
#define SELF_ENDIANNESS 0x1
|
|
|
|
#define SELF_AUTH_INFO_SIZE 0x88
|
|
#define SELF_KEY_SIZE 0x10
|
|
#define SELF_DIGEST_SIZE 0x20
|
|
#define SELF_SEGMENT_BLOCK_ALIGNMENT 0x10
|
|
|
|
/* fields */
|
|
#define SELF_PROPS_ORDERED(B) B( 0, 0)
|
|
#define SELF_PROPS_ENCRYPTED(B) B( 1, 1)
|
|
#define SELF_PROPS_SIGNED(B) B( 2, 2)
|
|
#define SELF_PROPS_COMPRESSED(B) B( 3, 3)
|
|
#define SELF_PROPS_WINDOW(B) B(10, 8)
|
|
#define SELF_PROPS_BLOCKED(B) B(11, 11)
|
|
#define SELF_PROPS_BLOCK_SIZE(B) B(15, 12)
|
|
#define SELF_PROPS_HAS_DIGESTS(B) B(16, 16)
|
|
#define SELF_PROPS_HAS_EXTENTS(B) B(17, 17)
|
|
#define SELF_PROPS_SEGMENT_INDEX(B) B(31, 20)
|
|
|
|
typedef struct self_block_extent_t {
|
|
uint32_t offset;
|
|
uint32_t size;
|
|
} self_block_extent_t;
|
|
|
|
typedef struct self_block_info_t {
|
|
uint32_t size;
|
|
uint16_t index;
|
|
struct self_block_extent_t extent;
|
|
uint8_t digest[SELF_DIGEST_SIZE];
|
|
} self_block_info_t;
|
|
|
|
/* debug */
|
|
void trace_self(self_t *self)
|
|
{
|
|
#define BOOL(x) ((x) ? "true" : "false")
|
|
int i;
|
|
self_entry_t *entry;
|
|
|
|
dprintf("self:\n");
|
|
dprintf(" file-path: %s\n", self->file_path);
|
|
dprintf(" file-size: 0x%llX bytes\n", self->file_size);
|
|
dprintf(" header:\n");
|
|
dprintf(" magic: 0x%08X\n", self->header.magic);
|
|
dprintf(" version: 0x%02X\n", self->header.version);
|
|
dprintf(" mode: 0x%02X\n", self->header.mode);
|
|
dprintf(" endian: %s (0x%02X)\n",
|
|
self->header.endian == 1 ? "little-endian" : (
|
|
self->header.endian == 2 ? "big-endian" : "???"),
|
|
self->header.endian);
|
|
dprintf(" attr: 0x%02X\n", self->header.attr);
|
|
dprintf(" header-size: 0x%llX bytes\n", self->header.header_size);
|
|
dprintf(" meta-size: 0x%llX bytes\n", self->header.meta_size);
|
|
dprintf(" file-size: 0x%llX bytes\n", self->header.file_size);
|
|
dprintf(" num-entries: 0x%X\n", self->header.num_entries);
|
|
|
|
dprintf(" entries:\n");
|
|
for (i = 0; i < self->header.num_entries; i++) {
|
|
entry = &self->entries[i];
|
|
dprintf(" [%d]\n", i);
|
|
dprintf(" props: 0x%08X\n", entry->props);
|
|
dprintf(" ordered: %s\n", BOOL(EXTRACT(entry->props, SELF_PROPS_ORDERED)));
|
|
dprintf(" encrypted: %s\n", BOOL(EXTRACT(entry->props, SELF_PROPS_ENCRYPTED)));
|
|
dprintf(" signed: %s\n", BOOL(EXTRACT(entry->props, SELF_PROPS_SIGNED)));
|
|
dprintf(" compressed: %s\n", BOOL(EXTRACT(entry->props, SELF_PROPS_COMPRESSED)));
|
|
if (EXTRACT(entry->props, SELF_PROPS_COMPRESSED))
|
|
dprintf(" window: 0x%llX\n",
|
|
(1 << EXTRACT(entry->props, SELF_PROPS_WINDOW)) - 1);
|
|
dprintf(" has-blocks: %s\n", BOOL(EXTRACT(entry->props, SELF_PROPS_BLOCKED)));
|
|
if (EXTRACT(entry->props, SELF_PROPS_BLOCKED))
|
|
dprintf(" block-size: 0x%llX\n",
|
|
(1 << (EXTRACT(entry->props, SELF_PROPS_BLOCK_SIZE) + 12)));
|
|
dprintf(" has-digests: %s\n", BOOL(EXTRACT(entry->props, SELF_PROPS_HAS_DIGESTS)));
|
|
dprintf(" has-extents: %s\n", BOOL(EXTRACT(entry->props, SELF_PROPS_HAS_EXTENTS)));
|
|
dprintf(" segment-index: %d\n", EXTRACT(entry->props, SELF_PROPS_SEGMENT_INDEX));
|
|
dprintf(" offset: 0x%llX\n", entry->offset);
|
|
dprintf(" filesz: 0x%llX bytes\n", entry->filesz);
|
|
dprintf(" memsz: 0x%llX bytes\n", entry->memsz);
|
|
}
|
|
#undef BOOL
|
|
}
|
|
|
|
/* kernel */
|
|
#define SELF_MAX_CONTEXTS 4
|
|
|
|
typedef struct self_kdecrypt_segment_args_t {
|
|
unsigned int segment_idx;
|
|
unsigned int is_block_table;
|
|
uint8_t *segment_data_user;
|
|
size_t segment_data_user_size;
|
|
} self_kdecrypt_segment_args_t;
|
|
|
|
typedef struct self_kdecrypt_block_args_t {
|
|
struct self_block_info_t *block;
|
|
unsigned int segment_idx;
|
|
uint8_t *blob_data;
|
|
size_t blob_size;
|
|
} self_kdecrypt_block_args_t;
|
|
|
|
typedef struct self_kmethod_uap_t {
|
|
void *kmethod;
|
|
self_t *self;
|
|
void *args;
|
|
} self_kmethod_uap_t;
|
|
|
|
int self_kacquire_context(
|
|
struct thread *td, struct self_kmethod_uap_t *uap)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int self_krelease_context(
|
|
struct thread *td, struct self_kmethod_uap_t *uap)
|
|
{
|
|
int ctx_id;
|
|
self_t *self = uap->self;
|
|
|
|
ctx_id = self->ctx_id;
|
|
if (0 <= ctx_id && ctx_id <= 3) {
|
|
self_ctx_status[ctx_id] = 3;
|
|
self->ctx_id = -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int self_kverify_header(
|
|
struct thread *td, struct self_kmethod_uap_t *uap)
|
|
{
|
|
int ret, ctx_id;
|
|
char payload[0x80];
|
|
void* header_data = NULL;
|
|
uint64_t header_data_size;
|
|
uint64_t header_data_mapped = NULL;
|
|
uint64_t header_data_mapdesc = NULL;
|
|
void* auth_info = NULL;
|
|
uint64_t auth_info_size;
|
|
uint64_t auth_info_mapped = NULL;
|
|
uint64_t auth_info_mapdesc = NULL;
|
|
self_t *self = uap->self;
|
|
|
|
/* acquire context */
|
|
kdprintf("Waiting for free context...\n");
|
|
if (self->ctx_id == -1) {
|
|
ctx_id = 0;
|
|
while (self_ctx_status[ctx_id] != 3) {
|
|
ctx_id = (ctx_id + 1) % SELF_MAX_CONTEXTS;
|
|
}
|
|
kdprintf("Available context ID: %d\n", ctx_id);
|
|
self_ctx_status[ctx_id] = 1;
|
|
self->ctx_id = ctx_id;
|
|
}
|
|
if (self->svc_id == -1) {
|
|
self->svc_id = *sceSblAuthMgrModuleId;
|
|
}
|
|
kassert(self->ctx_id >= 0);
|
|
kassert(self->ctx_id <= 3);
|
|
self->ctx = &self_contexts[self->ctx_id];
|
|
_sceSblAuthMgrSmFinalize(self->ctx);
|
|
|
|
/* allocate memory for command */
|
|
header_data_size = ALIGN_PAGE(self->header.header_size + self->header.meta_size);
|
|
header_data = kmalloc(header_data_size, M_AUTHMGR, 0x102);
|
|
kassert(header_data);
|
|
memset(header_data, 0, header_data_size);
|
|
memcpy(header_data, self->data, MIN(self->file_size, header_data_size));
|
|
kassert(!sceSblDriverMapPages(&header_data_mapped, header_data, 1, 0x61, NULL, &header_data_mapdesc));
|
|
|
|
auth_info_size = ALIGN_PAGE(SELF_AUTH_INFO_SIZE);
|
|
auth_info = kmalloc(auth_info_size, M_AUTHMGR, 0x102);
|
|
kassert(auth_info);
|
|
memset(auth_info, 0, auth_info_size);
|
|
kassert(!sceSblDriverMapPages(&auth_info_mapped, auth_info, 1, 0x61, NULL, &auth_info_mapdesc));
|
|
|
|
/* send command */
|
|
sbl_authmgr_verify_header_t *args = (void*)&payload[0];
|
|
memset(payload, 0, sizeof(payload));
|
|
args->function = AUTHMGR_CMD_VERIFY_HEADER;
|
|
args->status = 0;
|
|
args->header_addr = header_data_mapped;
|
|
args->header_size = self->header.header_size + self->header.meta_size;
|
|
args->context_id = self->ctx_id;
|
|
args->auth_info_addr = auth_info_mapped;
|
|
args->key_id = 0;
|
|
memset(&args->key, 0, SELF_KEY_SIZE);
|
|
|
|
kdprintf("Sending AUTHMGR_CMD_VERIFY_HEADER...\n");
|
|
sceSblServiceMailbox_locked(ret, self->svc_id, &payload, &payload);
|
|
kassert(!ret);
|
|
kassert(!args->status);
|
|
kassert(args->function == AUTHMGR_CMD_VERIFY_HEADER);
|
|
|
|
kdprintf("Confirmed context ID: %d\n", self->ctx_id);
|
|
self->auth_ctx_id = args->context_id;
|
|
memcpy(&self->auth_info, auth_info, sizeof(self->auth_info));
|
|
self->verified = 1;
|
|
|
|
if (auth_info_mapped)
|
|
kassert(!sceSblDriverUnmapPages(auth_info_mapdesc));
|
|
if (header_data_mapped)
|
|
kassert(!sceSblDriverUnmapPages(header_data_mapdesc));
|
|
return 0;
|
|
|
|
error:
|
|
self_krelease_context(td, uap);
|
|
if (auth_info_mapped)
|
|
kassert(!sceSblDriverUnmapPages(auth_info_mapdesc));
|
|
if (header_data_mapped)
|
|
kassert(!sceSblDriverUnmapPages(header_data_mapdesc));
|
|
return 1;
|
|
}
|
|
|
|
int self_kdecrypt_segment(
|
|
struct thread *td, struct self_kmethod_uap_t *uap)
|
|
{
|
|
int ret;
|
|
char payload[0x80];
|
|
size_t segment_data_size;
|
|
uint64_t segment_data_gpu_paddr = NULL;
|
|
uint64_t segment_data_gpu_desc = NULL;
|
|
uint8_t *segment_data = NULL;
|
|
uint64_t chunk_table_gpu_paddr = NULL;
|
|
uint64_t chunk_table_gpu_desc = NULL;
|
|
uint8_t *chunk_table = NULL;
|
|
self_kdecrypt_segment_args_t *args = uap->args;
|
|
self_t *self = uap->self;
|
|
ret = 1;
|
|
|
|
/* copy segment data */
|
|
segment_data_size = ALIGN_PAGE(args->segment_data_user_size);
|
|
segment_data = kmalloc(segment_data_size, M_AUTHMGR, 0x102);
|
|
kassert(segment_data);
|
|
memset(segment_data, 0, segment_data_size);
|
|
memcpy(segment_data, args->segment_data_user, args->segment_data_user_size);
|
|
|
|
/* create chunk table */
|
|
chunk_table = kmalloc(0x4000, M_AUTHMGR, 0x102);
|
|
kassert(chunk_table);
|
|
make_chunk_table(
|
|
&segment_data_gpu_paddr,
|
|
&segment_data_gpu_desc,
|
|
segment_data,
|
|
segment_data_size,
|
|
chunk_table,
|
|
0x4000, 1);
|
|
kassert(segment_data_gpu_paddr);
|
|
kassert(segment_data_gpu_desc);
|
|
map_chunk_table(
|
|
&chunk_table_gpu_paddr,
|
|
&chunk_table_gpu_desc,
|
|
chunk_table);
|
|
kassert(chunk_table_gpu_paddr);
|
|
kassert(chunk_table_gpu_desc);
|
|
|
|
/* decrypt segment */
|
|
sbl_authmgr_load_self_segment_t *cmd = (void*)&payload[0];
|
|
memset(payload, 0, sizeof(payload));
|
|
cmd->function = AUTHMGR_CMD_LOAD_SELF_SEGMENT;
|
|
cmd->status = 0;
|
|
cmd->chunk_table_addr = chunk_table_gpu_paddr;
|
|
cmd->segment_index = args->segment_idx;
|
|
cmd->is_block_table = args->is_block_table;
|
|
cmd->context_id = self->auth_ctx_id;
|
|
|
|
kdprintf("Sending AUTHMGR_CMD_LOAD_SELF_SEGMENT...\n");
|
|
sceSblServiceMailbox_locked(ret, self->svc_id, &payload, &payload);
|
|
kassert(!ret);
|
|
kassert(!cmd->status);
|
|
kassert(cmd->function == AUTHMGR_CMD_LOAD_SELF_SEGMENT);
|
|
memcpy(args->segment_data_user, segment_data, args->segment_data_user_size);
|
|
ret = 0;
|
|
|
|
error:
|
|
if (chunk_table_gpu_paddr)
|
|
kassert(!sceSblDriverUnmapPages(chunk_table_gpu_desc));
|
|
if (segment_data_gpu_paddr)
|
|
kassert(!sceSblDriverUnmapPages(segment_data_gpu_desc));
|
|
if (chunk_table)
|
|
kfree(chunk_table, M_AUTHMGR);
|
|
if (segment_data)
|
|
kfree(segment_data, M_AUTHMGR);
|
|
return ret;
|
|
}
|
|
|
|
int self_kdecrypt_block(
|
|
struct thread *td, struct self_kmethod_uap_t *uap)
|
|
{
|
|
int ret;
|
|
char payload[0x80];
|
|
void* input_data = NULL;
|
|
uint64_t input_size;
|
|
uint64_t input_mapped = NULL;
|
|
uint64_t input_mapdesc = NULL;
|
|
void* output_data = NULL;
|
|
uint64_t output_size;
|
|
uint64_t output_mapped = NULL;
|
|
uint64_t output_mapdesc = NULL;
|
|
self_kdecrypt_block_args_t *args = uap->args;
|
|
self_block_info_t *block = args->block;
|
|
self_t *self = uap->self;
|
|
ret = 1;
|
|
|
|
/* allocate memory for command */
|
|
input_size = ALIGN_PAGE(args->blob_size);
|
|
input_data = kmalloc(input_size, M_AUTHMGR, 0x102);
|
|
kassert(input_data);
|
|
memset(input_data, 0, input_size);
|
|
memcpy(input_data, args->blob_data, args->blob_size);
|
|
kassert(!sceSblDriverMapPages(&input_mapped, input_data, 1, 0x61, NULL, &input_mapdesc));
|
|
|
|
output_size = ALIGN_PAGE(args->blob_size);
|
|
output_data = kmalloc(output_size, M_AUTHMGR, 0x102);
|
|
kassert(output_data);
|
|
memset(output_data, 0, output_size);
|
|
kassert(!sceSblDriverMapPages(&output_mapped, output_data, 1, 0x61, NULL, &output_mapdesc));
|
|
|
|
/* decrypt block */
|
|
sbl_authmgr_load_self_block_t *cmd = (void*)&payload[0];
|
|
memset(payload, 0, sizeof(payload));
|
|
memcpy(&cmd->digest, &block->digest, sizeof(block->digest));
|
|
memcpy(&cmd->extent, &block->extent, sizeof(block->extent));
|
|
cmd->function = AUTHMGR_CMD_LOAD_SELF_BLOCK;
|
|
cmd->status = 0;
|
|
cmd->pages_addr = output_mapped;
|
|
cmd->segment_index = args->segment_idx;
|
|
cmd->context_id = self->auth_ctx_id;
|
|
cmd->block_index = block->index;
|
|
cmd->data_offset = 0;
|
|
cmd->data_size = args->blob_size;
|
|
cmd->data_start_addr = input_mapped;
|
|
cmd->data_end_addr = 0;
|
|
|
|
kdprintf("Sending AUTHMGR_CMD_LOAD_SELF_BLOCK...\n");
|
|
sceSblServiceMailbox_locked(ret, self->svc_id, &payload, &payload);
|
|
kassert(!ret);
|
|
kassert(!cmd->status);
|
|
kassert(cmd->function == AUTHMGR_CMD_LOAD_SELF_BLOCK);
|
|
memcpy(args->blob_data, output_data, args->blob_size);
|
|
ret = 0;
|
|
|
|
error:
|
|
if (input_mapped)
|
|
kassert(!sceSblDriverUnmapPages(input_mapdesc));
|
|
if (output_mapped)
|
|
kassert(!sceSblDriverUnmapPages(output_mapdesc));
|
|
if (input_data)
|
|
kfree(input_data, M_AUTHMGR);
|
|
if (output_data)
|
|
kfree(output_data, M_AUTHMGR);
|
|
return ret;
|
|
}
|
|
|
|
/* functions */
|
|
static void self_get_block_info(self_t *self, unsigned int target_entry_idx, self_block_info_t *info)
|
|
{
|
|
struct self_entry_t* table_segment;
|
|
struct self_entry_t* target_segment;
|
|
struct self_block_extent_t* extents;
|
|
const uint8_t* segment_data;
|
|
unsigned int target_num_blocks;
|
|
unsigned int i;
|
|
|
|
memset(&info->digest, 0, sizeof(info->digest));
|
|
memset(&info->extent, 0, sizeof(info->extent));
|
|
|
|
for (i = 0; i < self->header.num_entries; ++i) {
|
|
table_segment = &self->entries[i];
|
|
if (!EXTRACT(table_segment->props, SELF_PROPS_HAS_DIGESTS) &&
|
|
!EXTRACT(table_segment->props, SELF_PROPS_HAS_EXTENTS))
|
|
continue;
|
|
if (EXTRACT(table_segment->props, SELF_PROPS_SEGMENT_INDEX) != target_entry_idx)
|
|
continue;
|
|
|
|
target_segment = &self->entries[target_entry_idx];
|
|
target_num_blocks = (target_segment->memsz + (info->size - 1)) / info->size;
|
|
segment_data = &self->data[table_segment->offset];
|
|
if (EXTRACT(table_segment->props, SELF_PROPS_HAS_DIGESTS)) {
|
|
memcpy(&info->digest, &segment_data[info->index * sizeof(info->digest)], sizeof(info->digest));
|
|
}
|
|
if (EXTRACT(table_segment->props, SELF_PROPS_HAS_EXTENTS)) {
|
|
if (EXTRACT(table_segment->props, SELF_PROPS_HAS_DIGESTS))
|
|
extents = (void*)(&segment_data[target_num_blocks * sizeof(info->digest)]);
|
|
else
|
|
extents = (void*)(&segment_data[0]);
|
|
memcpy(&info->extent, &extents[info->index], sizeof(info->extent));
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
self_t* self_open(const char* file)
|
|
{
|
|
self_t* self;
|
|
ssize_t size;
|
|
off_t off;
|
|
int fd;
|
|
|
|
/* allocate object */
|
|
self = malloc(sizeof(self_t));
|
|
assert(self);
|
|
memset(self, 0, sizeof(self_t));
|
|
|
|
/* open file */
|
|
fd = open(file, O_RDONLY, 0);
|
|
assert(fd >= 0);
|
|
self->fd = fd;
|
|
|
|
/* get self size */
|
|
off = lseek(fd, 0, SEEK_END);
|
|
assert(off >= 0);
|
|
self->file_size = off;
|
|
off = lseek(fd, 0, SEEK_SET);
|
|
assert(off >= 0);
|
|
|
|
/* get self header */
|
|
size = read(fd, &self->header, sizeof(self_header_t));
|
|
assert(size == sizeof(self_header_t));
|
|
assert(self->header.magic == SELF_MAGIC);
|
|
assert(self->header.version == SELF_VERSION);
|
|
assert(self->header.mode == SELF_MODE);
|
|
assert(self->header.endian == SELF_ENDIANNESS);
|
|
assert(self->header.file_size == self->file_size);
|
|
|
|
/* get self entries */
|
|
self->entries_size = self->header.num_entries * sizeof(self_entry_t);
|
|
self->entries = malloc(self->entries_size);
|
|
assert(self->entries);
|
|
assert(self->header.header_size >= sizeof(self_header_t) + self->entries_size);
|
|
assert(self->header.header_size + self->header.meta_size <= 0x4000);
|
|
memset(self->entries, 0, self->entries_size);
|
|
size = read(fd, self->entries, self->entries_size);
|
|
assert(size == self->entries_size);
|
|
|
|
/* copy file contents */
|
|
self->data = malloc(self->file_size);
|
|
off = lseek(fd, 0, SEEK_SET);
|
|
assert(off >= 0);
|
|
size = read(fd, self->data, self->file_size);
|
|
assert(size == self->file_size);
|
|
|
|
self->fd = fd;
|
|
self->file_path = strdup(file);
|
|
self->ctx_id = -1;
|
|
if (DEBUG_SELF) {
|
|
trace_self(self);
|
|
}
|
|
return self;
|
|
|
|
error:
|
|
self_close(self);
|
|
return NULL;
|
|
}
|
|
|
|
int self_verify_header(self_t *self)
|
|
{
|
|
syscall(11, self_kverify_header, self);
|
|
return 0;
|
|
}
|
|
|
|
int self_load_segments(self_t *self)
|
|
{
|
|
struct self_kdecrypt_segment_args_t args_ds;
|
|
struct self_kdecrypt_block_args_t args_db;
|
|
struct self_block_info_t block;
|
|
struct self_entry_t* segment;
|
|
struct blob_t *blob;
|
|
unsigned int this_segment_idx;
|
|
unsigned int that_segment_idx;
|
|
unsigned int num_blocks;
|
|
unsigned int block_idx_offset;
|
|
unsigned int block_offset;
|
|
unsigned int block_size;
|
|
unsigned int i, j;
|
|
|
|
if (!self->verified)
|
|
goto error;
|
|
|
|
/* prepare linked list of blobs */
|
|
blob = malloc(sizeof(blob_t));
|
|
memset(blob, 0, sizeof(blob_t));
|
|
self->blobs = blob;
|
|
|
|
/* load block table segments */
|
|
for (i = 0; i < self->header.num_entries; i++) {
|
|
segment = &self->entries[i];
|
|
if (!EXTRACT(segment->props, SELF_PROPS_HAS_DIGESTS) &&
|
|
!EXTRACT(segment->props, SELF_PROPS_HAS_EXTENTS))
|
|
continue;
|
|
|
|
dprintf("Processing block table segment @ entry #%u...\n", i);
|
|
that_segment_idx = EXTRACT(segment->props, SELF_PROPS_SEGMENT_INDEX);
|
|
this_segment_idx = EXTRACT(self->entries[that_segment_idx].props, SELF_PROPS_SEGMENT_INDEX);
|
|
dprintf(" that-segment-idx: %u\n", that_segment_idx);
|
|
dprintf(" this-segment-idx: %u\n", this_segment_idx);
|
|
|
|
blob->size = segment->filesz;
|
|
blob->data = malloc(blob->size);
|
|
assert(blob->data);
|
|
assert(segment->offset + segment->filesz <= self->file_size);
|
|
memcpy(blob->data, &self->data[segment->offset], blob->size);
|
|
blob_set_path_hash(blob, blob->data, blob->size);
|
|
|
|
args_ds.segment_idx = this_segment_idx;
|
|
args_ds.is_block_table = 1;
|
|
args_ds.segment_data_user = blob->data;
|
|
args_ds.segment_data_user_size = blob->size;
|
|
syscall(11, self_kdecrypt_segment, self, &args_ds);
|
|
memcpy(&self->data[segment->offset], blob->data, blob->size);
|
|
blob = blob_add(blob);
|
|
}
|
|
|
|
/* load blocked segments */
|
|
for (i = 0; i < self->header.num_entries; i++) {
|
|
segment = &self->entries[i];
|
|
if (!EXTRACT(segment->props, SELF_PROPS_BLOCKED))
|
|
continue;
|
|
|
|
dprintf("Processing blocked segment @ entry #%u...\n", i);
|
|
memset(&block, 0, sizeof(block));
|
|
block.size = (1 << (EXTRACT(segment->props, SELF_PROPS_BLOCK_SIZE) + 12));
|
|
this_segment_idx = EXTRACT(segment->props, SELF_PROPS_SEGMENT_INDEX);
|
|
num_blocks = (segment->memsz + block.size - 1) / block.size;
|
|
|
|
for (j = 0; j < num_blocks; j++) {
|
|
block.index = j;
|
|
self_get_block_info(self, i, &block);
|
|
block_idx_offset = block.extent.offset & ~(block.size - 1);
|
|
block_offset = block.extent.offset & (block.size - 1);
|
|
block_size = block.extent.size & ~(SELF_SEGMENT_BLOCK_ALIGNMENT - 1);
|
|
if (block_size == 0) {
|
|
block_idx_offset = block.index * block.size;
|
|
block_size = (block_idx_offset + block.size <= segment->memsz) ?
|
|
(block.size) :
|
|
(segment->memsz - block_idx_offset);
|
|
} else if (block.index * block.size + block.extent.size == segment->memsz) {
|
|
block_size = block.extent.size;
|
|
}
|
|
|
|
blob->size = block_size;
|
|
blob->data = malloc(blob->size);
|
|
assert(blob->data);
|
|
memcpy(blob->data, &self->data[segment->offset + block_idx_offset + block_offset], blob->size);
|
|
blob_set_path_hash(blob, blob->data, blob->size);
|
|
|
|
args_db.block = █
|
|
args_db.segment_idx = this_segment_idx;
|
|
args_db.blob_data = blob->data;
|
|
args_db.blob_size = blob->size;
|
|
syscall(11, self_kdecrypt_block, self, &args_db);
|
|
blob = blob_add(blob);
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
error:
|
|
return 1;
|
|
}
|
|
|
|
void self_close(self_t *self)
|
|
{
|
|
struct blob_t *blob;
|
|
struct blob_t *next;
|
|
if (!self) {
|
|
return;
|
|
}
|
|
|
|
/* remove blobs */
|
|
blob = self->blobs;
|
|
while (blob) {
|
|
next = blob->next;
|
|
free(blob->data);
|
|
free(blob);
|
|
blob = next;
|
|
}
|
|
/* close file */
|
|
if (self->fd) {
|
|
close(self->fd);
|
|
}
|
|
syscall(11, self_krelease_context, self);
|
|
free(self->file_path);
|
|
free(self->entries);
|
|
free(self);
|
|
}
|