2017-10-16 11:55:29 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <ogcsys.h>
|
2024-04-27 19:17:23 +02:00
|
|
|
#include <ogc/es.h>
|
2017-10-16 11:55:29 +02:00
|
|
|
|
|
|
|
#include "sha1.h"
|
2024-04-27 19:17:23 +02:00
|
|
|
#include "aes.h"
|
2017-10-16 11:55:29 +02:00
|
|
|
#include "utils.h"
|
2024-04-27 19:17:23 +02:00
|
|
|
#include "otp.h"
|
2024-02-13 03:19:57 +01:00
|
|
|
#include "malloc.h"
|
2017-10-16 11:55:29 +02:00
|
|
|
|
|
|
|
s32 Title_ZeroSignature(signed_blob *p_sig)
|
|
|
|
{
|
|
|
|
u8 *ptr = (u8 *)p_sig;
|
|
|
|
|
|
|
|
/* Fill signature with zeroes */
|
|
|
|
memset(ptr + 4, 0, SIGNATURE_SIZE(p_sig) - 4);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 Title_FakesignTik(signed_blob *p_tik)
|
|
|
|
{
|
|
|
|
tik *tik_data = NULL;
|
|
|
|
u16 fill;
|
|
|
|
|
|
|
|
/* Zero signature */
|
|
|
|
Title_ZeroSignature(p_tik);
|
|
|
|
|
|
|
|
/* Ticket data */
|
|
|
|
tik_data = (tik *)SIGNATURE_PAYLOAD(p_tik);
|
|
|
|
|
|
|
|
for (fill = 0; fill < USHRT_MAX; fill++) {
|
|
|
|
sha1 hash;
|
|
|
|
|
|
|
|
/* Modify ticket padding field */
|
|
|
|
tik_data->padding = fill;
|
|
|
|
|
|
|
|
/* Calculate hash */
|
|
|
|
SHA1((u8 *)tik_data, sizeof(tik), hash);
|
|
|
|
|
|
|
|
/* Found valid hash */
|
|
|
|
if (!hash[0])
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 Title_FakesignTMD(signed_blob *p_tmd)
|
|
|
|
{
|
|
|
|
tmd *tmd_data = NULL;
|
|
|
|
u16 fill;
|
|
|
|
|
|
|
|
/* Zero signature */
|
|
|
|
Title_ZeroSignature(p_tmd);
|
|
|
|
|
|
|
|
/* TMD data */
|
|
|
|
tmd_data = (tmd *)SIGNATURE_PAYLOAD(p_tmd);
|
|
|
|
|
|
|
|
for (fill = 0; fill < USHRT_MAX; fill++) {
|
|
|
|
sha1 hash;
|
|
|
|
|
|
|
|
/* Modify TMD fill field */
|
2022-04-19 12:39:21 +02:00
|
|
|
tmd_data->fill2 = fill;
|
2017-10-16 11:55:29 +02:00
|
|
|
|
|
|
|
/* Calculate hash */
|
|
|
|
SHA1((u8 *)tmd_data, TMD_SIZE(tmd_data), hash);
|
|
|
|
|
|
|
|
/* Found valid hash */
|
|
|
|
if (!hash[0])
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 Title_GetList(u64 **outbuf, u32 *outlen)
|
|
|
|
{
|
|
|
|
u64 *titles = NULL;
|
|
|
|
|
2024-02-13 03:19:57 +01:00
|
|
|
u32 nb_titles;
|
2017-10-16 11:55:29 +02:00
|
|
|
s32 ret;
|
|
|
|
|
|
|
|
/* Get number of titles */
|
|
|
|
ret = ES_GetNumTitles(&nb_titles);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
|
|
/* Allocate memory */
|
2024-02-13 03:19:57 +01:00
|
|
|
titles = memalign32(nb_titles * sizeof(u64));
|
2017-10-16 11:55:29 +02:00
|
|
|
if (!titles)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Get titles */
|
|
|
|
ret = ES_GetTitles(titles, nb_titles);
|
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
/* Set values */
|
|
|
|
*outbuf = titles;
|
|
|
|
*outlen = nb_titles;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
/* Free memory */
|
2022-04-19 13:09:14 +02:00
|
|
|
free(titles);
|
2017-10-16 11:55:29 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 Title_GetTicketViews(u64 tid, tikview **outbuf, u32 *outlen)
|
|
|
|
{
|
|
|
|
tikview *views = NULL;
|
|
|
|
|
|
|
|
u32 nb_views;
|
|
|
|
s32 ret;
|
|
|
|
|
|
|
|
/* Get number of ticket views */
|
|
|
|
ret = ES_GetNumTicketViews(tid, &nb_views);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Allocate memory */
|
2024-02-13 03:19:57 +01:00
|
|
|
views = memalign32(sizeof(tikview) * nb_views);
|
2017-10-16 11:55:29 +02:00
|
|
|
if (!views)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Get ticket views */
|
|
|
|
ret = ES_GetTicketViews(tid, views, nb_views);
|
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
/* Set values */
|
|
|
|
*outbuf = views;
|
|
|
|
*outlen = nb_views;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
/* Free memory */
|
2022-04-19 13:09:14 +02:00
|
|
|
free(views);
|
2017-10-16 11:55:29 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 Title_GetTMD(u64 tid, signed_blob **outbuf, u32 *outlen)
|
|
|
|
{
|
|
|
|
void *p_tmd = NULL;
|
|
|
|
|
|
|
|
u32 len;
|
|
|
|
s32 ret;
|
|
|
|
|
|
|
|
/* Get TMD size */
|
|
|
|
ret = ES_GetStoredTMDSize(tid, &len);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Allocate memory */
|
2024-02-13 03:19:57 +01:00
|
|
|
p_tmd = memalign32(len);
|
2017-10-16 11:55:29 +02:00
|
|
|
if (!p_tmd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Read TMD */
|
|
|
|
ret = ES_GetStoredTMD(tid, p_tmd, len);
|
|
|
|
if (ret < 0)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
/* Set values */
|
|
|
|
*outbuf = p_tmd;
|
|
|
|
*outlen = len;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
/* Free memory */
|
2022-04-19 13:09:14 +02:00
|
|
|
free(p_tmd);
|
2017-10-16 11:55:29 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 Title_GetVersion(u64 tid, u16 *outbuf)
|
|
|
|
{
|
|
|
|
signed_blob *p_tmd = NULL;
|
|
|
|
tmd *tmd_data = NULL;
|
|
|
|
|
|
|
|
u32 len;
|
|
|
|
s32 ret;
|
|
|
|
|
|
|
|
/* Get title TMD */
|
|
|
|
ret = Title_GetTMD(tid, &p_tmd, &len);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Retrieve TMD info */
|
|
|
|
tmd_data = (tmd *)SIGNATURE_PAYLOAD(p_tmd);
|
|
|
|
|
|
|
|
/* Set values */
|
|
|
|
*outbuf = tmd_data->title_version;
|
|
|
|
|
|
|
|
/* Free memory */
|
|
|
|
free(p_tmd);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 Title_GetSysVersion(u64 tid, u64 *outbuf)
|
|
|
|
{
|
|
|
|
signed_blob *p_tmd = NULL;
|
|
|
|
tmd *tmd_data = NULL;
|
|
|
|
|
|
|
|
u32 len;
|
|
|
|
s32 ret;
|
|
|
|
|
|
|
|
/* Get title TMD */
|
|
|
|
ret = Title_GetTMD(tid, &p_tmd, &len);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Retrieve TMD info */
|
|
|
|
tmd_data = (tmd *)SIGNATURE_PAYLOAD(p_tmd);
|
|
|
|
|
|
|
|
/* Set values */
|
|
|
|
*outbuf = tmd_data->sys_version;
|
|
|
|
|
|
|
|
/* Free memory */
|
|
|
|
free(p_tmd);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 Title_GetSize(u64 tid, u32 *outbuf)
|
|
|
|
{
|
|
|
|
signed_blob *p_tmd = NULL;
|
|
|
|
tmd *tmd_data = NULL;
|
|
|
|
|
|
|
|
u32 cnt, len, size = 0;
|
|
|
|
s32 ret;
|
|
|
|
|
|
|
|
/* Get title TMD */
|
|
|
|
ret = Title_GetTMD(tid, &p_tmd, &len);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Retrieve TMD info */
|
|
|
|
tmd_data = (tmd *)SIGNATURE_PAYLOAD(p_tmd);
|
|
|
|
|
|
|
|
/* Calculate title size */
|
|
|
|
for (cnt = 0; cnt < tmd_data->num_contents; cnt++) {
|
|
|
|
tmd_content *content = &tmd_data->contents[cnt];
|
|
|
|
|
|
|
|
/* Add content size */
|
|
|
|
size += content->size;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set values */
|
|
|
|
*outbuf = size;
|
|
|
|
|
|
|
|
/* Free memory */
|
|
|
|
free(p_tmd);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 Title_GetIOSVersions(u8 **outbuf, u32 *outlen)
|
|
|
|
{
|
|
|
|
u8 *buffer = NULL;
|
|
|
|
u64 *list = NULL;
|
|
|
|
|
|
|
|
u32 count, cnt, idx;
|
|
|
|
s32 ret;
|
|
|
|
|
|
|
|
/* Get title list */
|
|
|
|
ret = Title_GetList(&list, &count);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Count IOS */
|
|
|
|
for (cnt = idx = 0; idx < count; idx++) {
|
|
|
|
u32 tidh = (list[idx] >> 32);
|
|
|
|
u32 tidl = (list[idx] & 0xFFFFFFFF);
|
|
|
|
|
|
|
|
/* Title is IOS */
|
|
|
|
if ((tidh == 0x1) && (tidl >= 3) && (tidl <= 255))
|
|
|
|
cnt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate memory */
|
2024-02-13 03:19:57 +01:00
|
|
|
buffer = memalign32(cnt);
|
2017-10-16 11:55:29 +02:00
|
|
|
if (!buffer) {
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy IOS */
|
|
|
|
for (cnt = idx = 0; idx < count; idx++) {
|
|
|
|
u32 tidh = (list[idx] >> 32);
|
|
|
|
u32 tidl = (list[idx] & 0xFFFFFFFF);
|
|
|
|
|
|
|
|
/* Title is IOS */
|
|
|
|
if ((tidh == 0x1) && (tidl >= 3) && (tidl <= 255))
|
|
|
|
buffer[cnt++] = (u8)(tidl & 0xFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set values */
|
|
|
|
*outbuf = buffer;
|
|
|
|
*outlen = cnt;
|
|
|
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
out:
|
|
|
|
/* Free memory */
|
2022-04-19 13:09:14 +02:00
|
|
|
free(list);
|
2017-10-16 11:55:29 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2024-04-27 19:17:23 +02:00
|
|
|
|
|
|
|
__attribute__((aligned(0x10)))
|
|
|
|
aeskey WiiCommonKey, vWiiCommonKey;
|
|
|
|
|
|
|
|
void Title_SetupCommonKeys(void)
|
|
|
|
{
|
|
|
|
static bool keys_ok = false;
|
|
|
|
if (keys_ok)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Grab the Wii common key...
|
|
|
|
otp_read(WiiCommonKey, offsetof(otp_t, common_key), sizeof(aeskey));
|
|
|
|
|
|
|
|
// ...and decrypt the vWii common key with it.
|
|
|
|
static const unsigned char vwii_key_enc_bin[0x10] = { 0x6e, 0x18, 0xdb, 0x23, 0x84, 0x7c, 0xba, 0x6c, 0x19, 0x31, 0xa4, 0x17, 0x9b, 0xaf, 0x8e, 0x09 };
|
|
|
|
unsigned char iv[0x10] = {};
|
|
|
|
|
|
|
|
memcpy(vWiiCommonKey, vwii_key_enc_bin, sizeof(vwii_key_enc_bin));
|
|
|
|
AES_Decrypt(WiiCommonKey, sizeof(aeskey), iv, sizeof(iv), vWiiCommonKey, vWiiCommonKey, sizeof(aeskey));
|
|
|
|
|
|
|
|
keys_ok = true;
|
|
|
|
return;
|
|
|
|
};
|