Add base domain patch

This commit is contained in:
Spotlight 2021-12-31 03:02:44 -06:00
parent e4c4758290
commit 59dbc66f1d
No known key found for this signature in database
GPG Key ID: 874AA355B3209BDC
4 changed files with 116 additions and 10 deletions

View File

@ -5,4 +5,5 @@ Contents:
- [`opcacrt6.yml`](opcacrt6.yml): A [Kaitai](https://kaitai.io) structure describing a very basic `opcacrt6.dat`. - [`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. 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_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. - [`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.

29
docs/patch_base_domain.md Normal file
View File

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

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"errors" "errors"
"fmt" "fmt"
"github.com/logrusorgru/aurora/v3" "github.com/logrusorgru/aurora/v3"
) )
@ -20,6 +21,9 @@ type Patch struct {
Name string Name string
// AtOffset is the offset within the file this patch should be applied at. // 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 AtOffset int
// Before is an array of the bytes to find for, i.e. present within the original file. // 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) { if len(patch.Before) != len(patch.After) {
return ErrInconsistentPatch return ErrInconsistentPatch
} }
if patch.AtOffset > len(mainDol) { if patch.AtOffset != 0 && patch.AtOffset > len(mainDol) {
return ErrPatchOutOfRange return ErrPatchOutOfRange
} }
// Either Before or After should return the same length. // Either Before or After should return the same length.
patchLen := len(patch.Before) patchLen := len(patch.Before)
// Ensure original bytes are present // Determine our patching behavior.
originalBytes := mainDol[patch.AtOffset : patch.AtOffset+patchLen] if patch.AtOffset != 0 {
if !bytes.Equal(originalBytes, patch.Before) { // Ensure original bytes are present
return ErrInvalidPatch originalBytes := mainDol[patch.AtOffset : patch.AtOffset+patchLen]
} if !bytes.Equal(originalBytes, patch.Before) {
return ErrInvalidPatch
}
// Apply patch // Apply patch at the specified offset
copy(mainDol[patch.AtOffset:], patch.After) 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 return nil
} }
@ -80,5 +91,6 @@ func emptyBytes(length int) []byte {
// applyDefaultPatches iterates through a list of default patches. // applyDefaultPatches iterates through a list of default patches.
func applyDefaultPatches() { func applyDefaultPatches() {
applyPatchSet("Overwrite IOS Syscall for ES", OverwriteIOSPatch) 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())
} }

64
patch_base_domain.go Normal file
View File

@ -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)...)
}