Compare commits

...

9 Commits

Author SHA1 Message Date
GaryOderNichts dfce0c4601 Update Dockerfile and workflow 2024-04-02 17:12:49 +02:00
GaryOderNichts 0d19b3dca7 Bump version to 2.0 2024-04-02 16:49:57 +02:00
GaryOderNichts 720ed1bb30 Add DRC/DRH information 2024-04-02 16:49:57 +02:00
David Korth 6d5b19a8a6 SubmitScreen.cpp: Zero an unused OTP area before hashing.
This should normally be zero anyway, but zero it again just in case.
2023-07-05 10:30:43 +02:00
David Korth a6efca8a74 SubmitScreen.cpp: Mention IDs; consolidate model and serial number to one line. 2023-07-05 10:30:33 +02:00
GaryOderNichts 0143b2e867 Update for latest libraries 2023-04-17 22:40:54 +02:00
GaryOderNichts df66e207da Version 1.1 2023-04-01 12:20:49 +02:00
David Korth b90158c146 SubmitScreen.cpp: Update for server-side changes. 2023-04-01 12:09:19 +02:00
GaryOderNichts 171c3fb9ce Stop already playing sounds 2023-04-01 00:05:46 +02:00
12 changed files with 291 additions and 77 deletions

View File

@ -4,13 +4,13 @@ on: [push, pull_request]
jobs:
build-binary:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Build binary
run: |
docker run --rm -v ${PWD}:/project garyodernichts/wiiuident_builder make
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: WiiUIdent
path: |

View File

@ -1,32 +1,17 @@
FROM ghcr.io/wiiu-env/devkitppc:20230326
FROM ghcr.io/wiiu-env/devkitppc:20231112
COPY --from=ghcr.io/wiiu-env/libmocha:20220903 /artifacts $DEVKITPRO
COPY --from=ghcr.io/wiiu-env/libmocha:20231127 /artifacts $DEVKITPRO
# build and install latest wut
WORKDIR /
# Build latest wut
RUN \
mkdir wut && \
cd wut && \
git init . && \
git remote add origin https://github.com/devkitPro/wut.git && \
git fetch --depth 1 origin 451a1828f7646053b59ebacd813135e0300c67e8 && \
git fetch --depth 1 origin 682f8eb48a79796e653eb711c001ba47355f351c && \
git checkout FETCH_HEAD
WORKDIR /wut
RUN make -j$(nproc)
RUN make install
# build and install latest sdl
WORKDIR /
RUN \
mkdir SDL && \
cd SDL && \
git init . && \
git remote add origin https://github.com/GaryOderNichts/SDL.git && \
git fetch --depth 1 origin 687746c8c9514b5d48d5f9665a1d5fa36c5e5547 && \
git checkout FETCH_HEAD
WORKDIR /SDL
RUN /opt/devkitpro/portlibs/wiiu/bin/powerpc-eabi-cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$DEVKITPRO/portlibs/wiiu
RUN cmake --build build
RUN cmake --install build
WORKDIR /project

View File

@ -16,7 +16,7 @@ TOPDIR ?= $(CURDIR)
APP_NAME := WiiUIdent
APP_SHORTNAME := WiiUIdent
APP_AUTHOR := GaryOderNichts
APP_VERSION := 1.0
APP_VERSION := 2.0
DATABASE_URL := wiiu.gerbilsoft.com
include $(DEVKITPRO)/wut/share/wut_rules
@ -56,7 +56,7 @@ CXXFLAGS := $(CFLAGS) -std=gnu++20
ASFLAGS := $(ARCH)
LDFLAGS = $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map)
LIBS := -lcurl -lmbedtls -lmbedcrypto -lmbedx509 -lSDL2 -lSDL2_ttf -lfreetype -lpng -lbz2 -lz -lmocha -lwut
LIBS := -lcurl -lmbedtls -lmbedcrypto -lmbedx509 -lSDL2 -lSDL2_ttf -lfreetype -lharfbuzz -lfreetype -lpng -lbz2 -lz -lmocha -lwut
#-------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level

View File

@ -32,6 +32,12 @@ WiiUIdent currently displays:
- Production Date (MLC only)
- Size
- CID/CSD
- DRC/DRH information
- Running Version
- Active Version
- Board Version
- Region
- Ext IDs
## System Database
WiiUIdent comes with an option to optionally upload system information to a database. This allows collecting various statistics about Wii U consoles.
@ -51,7 +57,8 @@ The database is publicly accessible but personally identifying information will
For building you need:
- [wut](https://github.com/devkitPro/wut)
- [libmocha](https://github.com/wiiu-env/libmocha)
- [SDL2 v2.22](https://github.com/GaryOderNichts/SDL/tree/wiiu-sdl2-2.0.22)
- [wiiu-sdl2](https://github.com/GaryOderNichts/SDL/tree/wiiu-sdl2-2.26)
- wiiu-sdl2_ttf
- wiiu-curl
- wiiu-mbedtls

View File

@ -106,9 +106,6 @@ bool Init()
return false;
}
// FIXME Probably SDL bug? If we don't draw before FC_LoadFont_RW our viewport shrinks
Gfx::DrawRectFilled(0, 0, 0, 0, COLOR_BLACK);
if (!FC_LoadFont_RW(monospaceFont, renderer, SDL_RWFromMem((void*)ter_u32b_bdf, ter_u32b_bdf_size), 1, 32, Gfx::COLOR_BLACK, TTF_STYLE_NORMAL)) {
FC_FreeFont(monospaceFont);
return false;

View File

@ -7,6 +7,7 @@
#include <sysapp/launch.h>
#include <vpad/input.h>
#include <padscore/kpad.h>
#include <sndcore2/core.h>
namespace
{
@ -130,6 +131,9 @@ int main(int argc, char const* argv[])
{
WHBProcInit();
// call AXInit to stop already playing sounds
AXInit();
KPADInit();
WPADEnableURCC(TRUE);
@ -161,6 +165,8 @@ int main(int argc, char const* argv[])
Gfx::Shutdown();
AXQuit();
WHBProcShutdown();
return 0;
}

View File

@ -0,0 +1,170 @@
#include "DRXInfoScreen.hpp"
#include "Utils.hpp"
#include <span>
#include <nsysccr/cdc.h>
#include <nsysccr/cfg.h>
namespace {
bool GetEepromValue(uint32_t offset, std::span<uint8_t> value)
{
uint8_t data[value.size() + 2];
if (CCRCFGGetCachedEeprom(0, offset, data, value.size() + 2) != 0) {
return false;
}
uint16_t crc = (uint16_t) data[value.size() + 1] << 8 | data[value.size()];
if (CCRCDCCalcCRC16(data, value.size()) != crc) {
return false;
}
std::copy(data, data + value.size(), value.begin());
return true;
}
// from gamepad firmare @0x000b2990
const char* kBoardMainVersions[] = {
"DK1",
"DK1 / EP / DK2",
"DP1",
"DP2",
"DK3",
"DK4",
"PreDP3 / DP3",
"DK5",
"DP4",
"DKMP",
"DP5",
"MASS",
"DKMP2",
"DRC-I",
"DKTVMP",
};
// from gamepad firmare @0x000b29cc
const char* kBoardSubVersions[] = {
"DK1 / EP / DK2",
"DP1 / DK3",
"DK4",
"DP3",
"DK5",
"DP4",
"DKMP",
"DP5",
"MASS",
"DKMP2",
"DRC-I",
"DKTVMP"
};
// from gamepad firmare @0x000b29fc
const char* kRegionStrings[] = {
"JAPAN",
"AMERICA",
"EUROPE",
"CHINA",
"SOUTH KOREA",
"TAIWAN",
"AUSTRALIA",
};
}
DRXInfoScreen::DRXInfoScreen()
{
CCRCDCSoftwareVersion softwareVersion;
if (CCRCDCSoftwareGetVersion(CCR_CDC_DESTINATION_DRC0, &softwareVersion) == 0) {
uint32_t v = softwareVersion.runningVersion;
mDRCList.push_back({"Running Version:", Utils::sprintf("%d.%d.%d", v >> 24 & 0xff, v >> 16 & 0xff, v & 0xffff)});
mDRCList.push_back({"", {Utils::sprintf("(0x%08x)", v), true}});
v = softwareVersion.activeVersion;
mDRCList.push_back({"Active Version:", Utils::sprintf("%d.%d.%d", v >> 24 & 0xff, v >> 16 & 0xff, v & 0xffff)});
mDRCList.push_back({"", {Utils::sprintf("(0x%08x)", v), true}});
} else {
mDRCList.push_back({"CCRCDCSoftwareGetVersion failed", ""});
}
uint8_t boardInfo;
if (GetEepromValue(0x100, std::span(std::addressof(boardInfo), 1))) {
uint8_t mainVersion = boardInfo & 0xf;
uint8_t subVersion = boardInfo >> 4;
mDRCList.push_back({"Board Version:", Utils::sprintf("%d.%d (0x%02x)", mainVersion, subVersion, boardInfo)});
mDRCList.push_back({"", Utils::sprintf("(%s / %s)",
mainVersion < 0xf ? kBoardMainVersions[mainVersion] : "UNKNOWN",
subVersion < 0xc ? kBoardSubVersions[subVersion] : "UNKNOWN")});
} else {
mDRCList.push_back({"GetEepromValue failed", ""});
}
uint8_t region;
if (GetEepromValue(0x103, std::span(std::addressof(region), 1))) {
mDRCList.push_back({"Region:", Utils::sprintf("%s (0x%02x)",
region < 0x7 ? kRegionStrings[region] : "UNKNOWN", region)});
} else {
mDRCList.push_back({"GetRegion failed", ""});
}
if (CCRCDCSoftwareGetVersion(CCR_CDC_DESTINATION_DRH, &softwareVersion) == 0) {
uint32_t v = softwareVersion.runningVersion;
mDRHList.push_back({"Running Version:", Utils::sprintf("%d.%d.%d", v >> 24 & 0xff, v >> 16 & 0xff, v & 0xffff)});
mDRHList.push_back({"", {Utils::sprintf("(0x%08x)", v), true}});
v = softwareVersion.activeVersion;
mDRHList.push_back({"Active Version:", Utils::sprintf("%d.%d.%d", v >> 24 & 0xff, v >> 16 & 0xff, v & 0xffff)});
mDRHList.push_back({"", {Utils::sprintf("(0x%08x)", v), true}});
} else {
mDRHList.push_back({"CCRCDCSoftwareGetVersion failed", ""});
}
uint32_t extId;
if (CCRCDCSoftwareGetExtId(CCR_CDC_DESTINATION_DRC0, CCR_CDC_EXT_LANGUAGE, &extId) == 0) {
mExtIdList.push_back({"Language:", {Utils::sprintf("0x%08x", extId), true}});
mExtIdList.push_back({"", Utils::sprintf("(version: %04d bank: %02d)", extId >> 8 & 0xffff, extId >> 24)});
} else {
mExtIdList.push_back({"CCRCDCSoftwareGetExtId(CCR_CDC_EXT_LANGUAGE) failed", ""});
}
if (CCRCDCSoftwareGetExtId(CCR_CDC_DESTINATION_DRC0, CCR_CDC_EXT_RC_DATABASE, &extId) == 0) {
mExtIdList.push_back({"RC Database:", {Utils::sprintf("0x%08x", extId), true}});
} else {
mExtIdList.push_back({"CCRCDCSoftwareGetExtId(CCR_CDC_EXT_RC_DATABASE) failed", ""});
}
for (int i = CCR_CDC_EXT_UNK2; i <= CCR_CDC_EXT_UNK4; i++) {
if (CCRCDCSoftwareGetExtId(CCR_CDC_DESTINATION_DRC0, (CCRCDCExt) i, &extId) == 0) {
mExtIdList.push_back({Utils::sprintf("ID %d:", i), {Utils::sprintf("0x%08x", extId), true}});
} else {
mExtIdList.push_back({Utils::sprintf("CCRCDCSoftwareGetExtId(%d) failed", i), ""});
}
}
}
DRXInfoScreen::~DRXInfoScreen()
{
}
void DRXInfoScreen::Draw()
{
DrawTopBar("DRC/DRH Information");
int yOff = 128;
yOff = DrawHeader(32, yOff, 896, 0xf11b, "DRC Info");
yOff = DrawList(32, yOff, 896, mDRCList);
yOff = DrawHeader(32, yOff, 896, 0xf0cb, "DRC Ext IDs");
yOff = DrawList(32, yOff, 896, mExtIdList);
yOff = 128;
yOff = DrawHeader(992, yOff, 896, 0xf2db, "DRH Info");
yOff = DrawList(992, yOff, 896, mDRHList);
DrawBottomBar(nullptr, "\ue044 Exit", "\ue001 Back");
}
bool DRXInfoScreen::Update(VPADStatus& input)
{
if (input.trigger & VPAD_BUTTON_B) {
return false;
}
return true;
}

View File

@ -0,0 +1,20 @@
#pragma once
#include "Screen.hpp"
class DRXInfoScreen : public Screen
{
public:
DRXInfoScreen();
virtual ~DRXInfoScreen();
void Draw();
bool Update(VPADStatus& input);
private:
ScreenList mDRCList;
ScreenList mExtIdList;
ScreenList mDRHList;
};

View File

@ -8,20 +8,6 @@
#include <coreinit/mcp.h>
#include <coreinit/bsp.h>
extern "C"
{
typedef struct {
uint32_t major;
uint32_t minor;
uint32_t patch;
char region;
} MCPSystemVersion;
MCPError MCP_GetSystemVersion(int32_t handle, MCPSystemVersion* systemVersion);
}
namespace
{

View File

@ -1,6 +1,7 @@
#include "MenuScreen.hpp"
#include "Gfx.hpp"
#include "AboutScreen.hpp"
#include "DRXInfoScreen.hpp"
#include "GeneralScreen.hpp"
#include "StorageScreen.hpp"
#include "SubmitScreen.hpp"
@ -11,6 +12,7 @@ MenuScreen::MenuScreen()
: entries({
{ MENU_ID_GENERAL, { 0xf085, "General System Information" }},
{ MENU_ID_STORAGE, { 0xf7c2, "Storage Information" }},
{ MENU_ID_DRX, { 0xf11b, "DRC/DRH Information" }},
{ MENU_ID_SUBMIT, { 0xf0ee, "Submit System Information" }},
// { MENU_ID_TITLE, { 0xf022, "Title Information" }},
{ MENU_ID_ABOUT, { 0xf05a, "About WiiUIdent" }},
@ -76,6 +78,9 @@ bool MenuScreen::Update(VPADStatus& input)
case MENU_ID_STORAGE:
subscreen = std::make_unique<StorageScreen>();
break;
case MENU_ID_DRX:
subscreen = std::make_unique<DRXInfoScreen>();
break;
case MENU_ID_SUBMIT:
subscreen = std::make_unique<SubmitScreen>();
break;

View File

@ -20,6 +20,7 @@ private:
enum MenuID {
MENU_ID_GENERAL,
MENU_ID_STORAGE,
MENU_ID_DRX,
MENU_ID_SUBMIT,
MENU_ID_ABOUT,

View File

@ -11,64 +11,84 @@
#include <curl/curl.h>
#include <cacert_pem.h>
#include <mocha/mocha.h>
struct IOSCEccSignedCert {
uint32_t signature_type; // [0x000] 0x00010002 or 0x00010005
uint8_t signature[0x3C]; // [0x004] ECC signature
uint8_t reserved1[0x40]; // [0x040] All zeroes
char issuer[0x40]; // [0x080] Issuer
uint32_t key_type; // [0x0C0] Key type (2)
char console_id[0x40]; // [0x0C4] Console ID ("NGxxxxxxxx")
uint32_t ng_id; // [0x104] NG ID
uint8_t pub_key[0x3C]; // [0x108] ECC public key
uint8_t reserved2[0x3C]; // [0x10C] All zeroes
};
WUT_CHECK_SIZE(IOSCEccSignedCert, 0x180);
// POST data
struct post_data {
char system_model[16]; // [0x000] seeprom[0xB8]
char system_serial[16]; // [0x010] seeprom[0xAC] + seeprom[0xB0] - need to mask the last 3 digits
uint8_t mfg_date[6]; // [0x020] seeprom[0xC4]
uint8_t productArea; // [0x026]
uint8_t gameRegion; // [0x027]
uint32_t sec_level; // [0x028] otp[0x080]
uint16_t boardType; // [0x02C] seeprom[0x21]
uint16_t boardRevision; // [0x02E] seeprom[0x22]
uint16_t bootSource; // [0x030] seeprom[0x23]
uint16_t ddr3Size; // [0x032] seeprom[0x24]
uint16_t ddr3Speed; // [0x034] seeprom[0x25]
uint16_t sataDevice; // [0x036] seeprom[0x2C]
uint16_t consoleType; // [0x038] seeprom[0x2D]
uint16_t reserved1; // [0x03A]
uint32_t bsp_rev; // [0x03C] bspGetHardwareVersion();
uint16_t ddr3Vendor; // [0x040] seeprom[0x29]
uint8_t reserved2[62]; // [0x042]
char system_model[16]; // [0x000] seeprom[0xB8]
char system_serial[16]; // [0x010] seeprom[0xAC] + seeprom[0xB0] - need to mask the last 3 digits
uint8_t mfg_date[6]; // [0x020] seeprom[0xC4]
uint8_t productArea; // [0x026]
uint8_t gameRegion; // [0x027]
uint32_t sec_level; // [0x028] otp[0x080]
uint16_t boardType; // [0x02C] seeprom[0x21]
uint16_t boardRevision; // [0x02E] seeprom[0x22]
uint16_t bootSource; // [0x030] seeprom[0x23]
uint16_t ddr3Size; // [0x032] seeprom[0x24]
uint16_t ddr3Speed; // [0x034] seeprom[0x25]
uint16_t ddr3Vendor; // [0x036] seeprom[0x29]
uint16_t sataDevice; // [0x038] seeprom[0x2C]
uint16_t consoleType; // [0x03A] seeprom[0x2D]
uint32_t bsp_rev; // [0x03C] bspGetHardwareVersion();
uint32_t wiiu_root_ms_id; // [0x040] otp[0x280]
uint32_t wiiu_root_ca_id; // [0x044] otp[0x284]
uint32_t wiiu_ng_id; // [0x048] otp[0x21C]
uint32_t wiiu_ng_key_id; // [0x04C] otp[0x288]
uint8_t reserved2[48]; // [0x050]
// [0x080]
struct {
uint32_t cid[4]; // [0x080] CID
uint32_t mid_prv; // [0x090] Manufacturer and product revision
uint32_t blockSize; // [0x094] Block size
uint64_t numBlocks; // [0x098] Number of blocks
char name1[128]; // [0x0A0] Product name
uint32_t cid[4]; // [0x080] CID
uint32_t mid_prv; // [0x090] Manufacturer and product revision
uint32_t blockSize; // [0x094] Block size
uint64_t numBlocks; // [0x098] Number of blocks
} mlc;
// [0x120]
uint8_t otp_sha256[32]; // [0x120] OTP SHA-256 hash (to prevent duplicates)
}; // size == 0x140 (320)
WUT_CHECK_SIZE(post_data, 0x140);
// [0x0A0]
uint8_t otp_sha256[32]; // [0x0A0] OTP SHA-256 hash (to prevent duplicates)
// [0x0C0]
IOSCEccSignedCert device_cert; // [0x0C0] Device client certificate
}; // size == 0x240 (576)
WUT_CHECK_SIZE(post_data, 0x240);
struct post_data_hashed {
struct post_data data;
uint8_t post_sha256[32]; // [0x140] SHA-256 hash of post_data, with adjustments
}; // size == 0x160 (352)
WUT_CHECK_SIZE(post_data_hashed, 0x160);
}; // size == 0x260 (608)
WUT_CHECK_SIZE(post_data_hashed, 0x260);
namespace
{
const char* desc =
const char desc[] =
"This will submit statistical data to the developers of WiiUIdent,\n"
"which will help to determine various statistics about Wii U consoles,\n"
"e.g. eMMC manufacturers. The submitted data may be publicly accessible\n"
"but personally identifying information will be kept confidential.\n"
"\n"
"Information that will be submitted:\n"
"\uff65 System model\n"
"\uff65 System serial number (excluding the last 3 digits)\n"
"\uff65 System model and serial number (excluding the last 3 digits)\n"
"\uff65 Manufacturing date\n"
"\uff65 Region information\n"
"\uff65 Security level (keyset), sataDevice, consoleType, BSP revision\n"
"\uff65 boardType, boardRevision, bootSource, ddr3Size, ddr3Speed, ddr3Vendor\n"
"\uff65 MLC manufacturer, revision, name, size, and CID\n"
"\uff65 SHA-256 hash of OTP (to prevent duplicates)\n"
"\uff65 Device certificate and SHA-256 hash of OTP (to prevent duplicates)\n"
"\uff64 MS, CA, NG, and NG key IDs\n"
"\n"
"Do you want to submit your console's system data?\n";
}
@ -154,7 +174,7 @@ size_t SubmitScreen::CurlWriteCallback(void* contents, size_t size, size_t nmemb
void SubmitScreen::SubmitSystemData()
{
const auto& otp = OTP::Get();
auto otp = OTP::Get();
const auto& seeprom = SEEPROM::Get();
WUT_ALIGNAS(0x40) MCPSysProdSettings sysProd{};
int32_t mcpHandle = MCP_Open();
@ -183,9 +203,24 @@ void SubmitScreen::SubmitSystemData()
pd->ddr3Vendor = seeprom.bc.ddr3Vendor;
pd->sataDevice = seeprom.bc.sataDevice;
pd->consoleType = seeprom.bc.consoleType;
pd->wiiu_root_ms_id = otp.wiiUCertBank.rootCertMSId;
pd->wiiu_root_ca_id = otp.wiiUCertBank.rootCertCAId;
pd->wiiu_ng_id = otp.wiiUNGBank.ngId;
pd->wiiu_ng_key_id = otp.wiiUCertBank.rootCertNGKeyId;
if (bspGetHardwareVersion(&pd->bsp_rev) != 0) {
pd->bsp_rev = 0;
error = "Failed to get BSP revision";
return;
}
// Device certificate
// IOSC_GetDeviceCertificate() isn't directly accessible from PPC,
// so we'll cheat by reading a known buffer in the kernel. (2.13.01)
for (uint32_t i = 0; i < sizeof(pd->device_cert) / 4; i++) {
if (Mocha_IOSUKernelRead32(0x04024d40 + (i * 4), ((uint32_t*)&pd->device_cert) + i) != MOCHA_RESULT_SUCCESS) {
error = "Failed to read device certificate";
return;
}
}
// System serial number
@ -220,9 +255,11 @@ void SubmitScreen::SubmitSystemData()
pd->mlc.mid_prv = mlcDev->GetMID() << 16 | mlcDev->GetPRV();
pd->mlc.numBlocks = mlcDev->GetNumBlocks();
pd->mlc.blockSize = mlcDev->GetBlockSize();
strncpy(pd->mlc.name1, mlcDev->GetName().c_str(), sizeof(pd->mlc.name1) - 1);
// NOTE: Not copying pd->mlc.name1 because the eMMC device name
// is fully contained within the MLC CID.
}
memset(&otp.miscBank, 0, 0x40);
if (Utils::SHA256(&otp, sizeof(otp), pd->otp_sha256, sizeof(pd->otp_sha256)) != 0) {
error = "Failed to hash otp data";
return;