From 59dbc66f1d99fec39888e73ee51658e809ce1ffe Mon Sep 17 00:00:00 2001 From: Spotlight Date: Fri, 31 Dec 2021 03:02:44 -0600 Subject: [PATCH] Add base domain patch --- docs/README.md | 3 +- docs/patch_base_domain.md | 29 ++++++++++++++++++ modify_dol.go | 30 ++++++++++++------ patch_base_domain.go | 64 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 10 deletions(-) create mode 100644 docs/patch_base_domain.md create mode 100644 patch_base_domain.go diff --git a/docs/README.md b/docs/README.md index 18f7e06..5dcee7e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -5,4 +5,5 @@ Contents: - [`opcacrt6.yml`](opcacrt6.yml): A [Kaitai](https://kaitai.io) structure describing a very basic `opcacrt6.dat`. It does not attempt to handle things such as client certificates or user passwords. - [`patch_overwrite_ios.md`](patch_overwrite_ios.md): An explanation over why and how IOS is patched for operation of the Wii Shop Channel. - - [`patch_custom_ca_ios.md`](patch_custom_ca_ios.md): The logistics of inserting our custom CA into IOS as well for EC usage. \ No newline at end of file + - [`patch_custom_ca_ios.md`](patch_custom_ca_ios.md): The logistics of inserting our custom CA into IOS as well for EC usage. + - [`patch_base_domain.md`](patch_base_domain.md): Information about what URLs are present within the main DOL and information about patching them. \ No newline at end of file diff --git a/docs/patch_base_domain.md b/docs/patch_base_domain.md new file mode 100644 index 0000000..a375d9e --- /dev/null +++ b/docs/patch_base_domain.md @@ -0,0 +1,29 @@ +# Patch: Change Base Domain + +## Motivation +Ranging from HTML/JS/etc loaded from the browser portion of the shop channel to ES underneath, traffic points to Nintendo's servers without other modifications. + +We need to have all traffic directed to our controlled servers. + +## Explanation +With Opera's `myfilter.ini`, we simply insert our custom URL to the allowed domains on the filter list. + +Unfortunately, we cannot simply `s/shop.wii.com/$USER_BASE_DOMAIN/` in the main DOL, no matter how appealing as that sounds. Most prominently, we need to handle padding for domain names shorter than `shop.wii.com`. + +Thankfully, for all types present, we can replace the domain name replaced and add padding to match if necessary. + +We can identify five types of URLs within the main DOL to patch: + - `https://oss-auth.shop.wii.com/startup?initpage=showManual&titleId=` + - For an unknown reason, this specific URL (alongside several others) is present 9 times within the main DOL. It is only referenced once. The other 8 occurrences appear to be directly after the data segment for other JS plugins. + - `https://oss-auth.shop.wii.com/oss/getLog` + - It is unclear on what this is for, as it is never requested whatsoever or accessed during normal runtime. Perhaps an earlier version of the Wii Shop Channel posted `ec.getLog()` to it. + - It appears directly after the data for `wiiShop`'s JS plugin, implying it goes unused. The similarly-named `getLogUrl` Setter within `wiiShop` appears to be stubbed out. + - `.shop.wii.com` + - This suffix is compared on all pages. If the loaded page's domain does not match, most EC functionality is disabled. + - `https://oss-auth.shop.wii.com` + - Similar to the first, this appears 9 times and is only referenced once. + - `https://ecs.a.taur.cloud/ecs/services/ECommerceSOAP` + - Appears to be present for `GetECConfig`, which is most likely not called within the Wii Shop channel. Instead, `ECommerceInterface#setWebSvcUrls` is preferred. + +## Execution +We simply iterate through these 5 types of URLs, replace the domain, and pad if appropiate. Doing so allows us to not fragment the rest of the URL with null bytes should padding be added. \ No newline at end of file diff --git a/modify_dol.go b/modify_dol.go index 74b0fd3..ca691dd 100644 --- a/modify_dol.go +++ b/modify_dol.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + "github.com/logrusorgru/aurora/v3" ) @@ -20,6 +21,9 @@ type Patch struct { Name string // AtOffset is the offset within the file this patch should be applied at. + // If not present, the patch will be recursively applied across the entire file. + // Relying on this behavior is highly discouraged, as it may damage other parts of the binary + // if gone unchecked. AtOffset int // Before is an array of the bytes to find for, i.e. present within the original file. @@ -43,21 +47,28 @@ func applyPatch(patch Patch) error { if len(patch.Before) != len(patch.After) { return ErrInconsistentPatch } - if patch.AtOffset > len(mainDol) { + if patch.AtOffset != 0 && patch.AtOffset > len(mainDol) { return ErrPatchOutOfRange } // Either Before or After should return the same length. patchLen := len(patch.Before) - // Ensure original bytes are present - originalBytes := mainDol[patch.AtOffset : patch.AtOffset+patchLen] - if !bytes.Equal(originalBytes, patch.Before) { - return ErrInvalidPatch - } + // Determine our patching behavior. + if patch.AtOffset != 0 { + // Ensure original bytes are present + originalBytes := mainDol[patch.AtOffset : patch.AtOffset+patchLen] + if !bytes.Equal(originalBytes, patch.Before) { + return ErrInvalidPatch + } - // Apply patch - copy(mainDol[patch.AtOffset:], patch.After) + // Apply patch at the specified offset + copy(mainDol[patch.AtOffset:], patch.After) + } else { + // Recursively apply this patch. + // We cannot verify if the original contents are present via this. + mainDol = bytes.ReplaceAll(mainDol, patch.Before, patch.After) + } return nil } @@ -80,5 +91,6 @@ func emptyBytes(length int) []byte { // applyDefaultPatches iterates through a list of default patches. func applyDefaultPatches() { applyPatchSet("Overwrite IOS Syscall for ES", OverwriteIOSPatch) - applyPatchSet("Load Custom CA within IOS", LoadCustomCA(rootCertificate)) + applyPatchSet("Load Custom CA within IOS", LoadCustomCA()) + applyPatchSet("Change Base Domain", PatchBaseDomain()) } diff --git a/patch_base_domain.go b/patch_base_domain.go new file mode 100644 index 0000000..b47f7e4 --- /dev/null +++ b/patch_base_domain.go @@ -0,0 +1,64 @@ +package main + +import "strings" + +const ( + NintendoBaseDomain = "shop.wii.com" + ShowManualURL = "https://oss-auth.shop.wii.com/startup?initpage=showManual&titleId=" + GetLogURL = "https://oss-auth.shop.wii.com/oss/getLog" + TrustedDomain = ".shop.wii.com" + ECommerceBaseURL = "https://ecs.shop.wii.com/ecs/services/ECommerceSOAP" +) + +// PatchBaseDomain replaces all Nintendo domains to be the user's +// specified base domain. +// See docs/patch_base_domain.md for more information. +func PatchBaseDomain() PatchSet { + return PatchSet{ + Patch{ + Name: "Modify /startup domain", + + Before: []byte(ShowManualURL), + After: padReplace(ShowManualURL), + }, + Patch{ + Name: "Modify oss-auth URL", + AtOffset: 3180692, + + Before: []byte(GetLogURL), + After: padReplace(GetLogURL), + }, + Patch{ + Name: "Modify trusted base domain prefix", + AtOffset: 3323432, + + Before: []byte(TrustedDomain), + After: padReplace(TrustedDomain), + }, + Patch{ + Name: "Modify ECS SOAP endpoint URL", + AtOffset: 3268896, + + Before: []byte(ECommerceBaseURL), + After: padReplace(ECommerceBaseURL), + }, + Patch{ + Name: "Wildcard replace other instances", + + Before: []byte(NintendoBaseDomain), + After: padReplace(baseDomain), + }, + } +} + +func padReplace(url string) []byte { + replaced := strings.ReplaceAll(url, NintendoBaseDomain, baseDomain) + + // See if we truly need to pad. + if len(url) == len(replaced) { + return []byte(replaced) + } + + padding := len(url) - len(replaced) + return append([]byte(replaced), emptyBytes(padding)...) +}