Version 2.0: Patch the useragent

Fixes #3
This commit is contained in:
GaryOderNichts 2022-11-01 00:35:11 +01:00
parent 961c94b512
commit 1037e1b119
10 changed files with 266 additions and 31 deletions

6
Dockerfile Normal file
View file

@ -0,0 +1,6 @@
FROM wiiuenv/devkitppc:20220806
COPY --from=wiiuenv/libkernel:20220724 /artifacts $DEVKITPRO
COPY --from=wiiuenv/wiiupluginsystem:20220826 /artifacts $DEVKITPRO
WORKDIR /project

View file

@ -11,6 +11,7 @@ TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/wups/share/wups_rules
WUT_ROOT := $(DEVKITPRO)/wut
WUMS_ROOT := $(DEVKITPRO)/wums
#-------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
@ -20,9 +21,9 @@ WUT_ROOT := $(DEVKITPRO)/wut
#-------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := .
SOURCES := source source/patcher
DATA := data
INCLUDES :=
INCLUDES := source
#-------------------------------------------------------------------------------
# options for code generation
@ -32,18 +33,18 @@ CFLAGS := -Wall -O2 -ffunction-sections \
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -D__WUPS__
CXXFLAGS := $(CFLAGS)
CXXFLAGS := $(CFLAGS) -std=gnu++20
ASFLAGS := $(ARCH)
LDFLAGS = $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) $(WUPSSPECS)
LDFLAGS = $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map) -T$(WUMS_ROOT)/share/libkernel.ld $(WUPSSPECS)
LIBS := -lwups -lwut
LIBS := -lkernel -lwups -lwut
#-------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level
# containing include and lib
#-------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(WUPS_ROOT) $(WUT_ROOT)
LIBDIRS := $(PORTLIBS) $(WUMS_ROOT) $(WUPS_ROOT) $(WUT_ROOT)
#-------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional

View file

@ -13,10 +13,21 @@ This endpoint now responds with:
The app then checks if acquiring this token has been sucessful and if not it will display the error. The acquired token itself is not used.
This is a Wii U Plugin System plugin for Aroma which just makes this request always return sucess and thus allowing the YouTube app to continue functioning.
## Info about version 2.0
On November 1st, 2022 YouTube started returning response 404 for the `WiiU` useragent platform.
Since v2.0 this plugin patches the platform in the useragent for the YouTube app to `NoU` instead of `WiiU`, which bypasses this block.
## Special Thanks to
[@ashquarky](https://github.com/ashquarky) for the awesome patcher framework which was taken from [Nimble](https://github.com/PretendoNetwork/Nimble).
## Building
```bash
# Build docker image (only needed once)
docker build . -t givemiiyoutube_builder
For building you need:
- [wups](https://github.com/wiiu-env/WiiUPluginSystem)
- [wut](https://github.com/decaf-emu/wut)
# make
docker run -it --rm -v ${PWD}:/project givemiiyoutube_builder make
Install them (in this order) according to their README's. Don't forget the dependencies of the libs itself.
# make clean
docker run -it --rm -v ${PWD}:/project givemiiyoutube_builder make clean
```

View file

@ -1,21 +0,0 @@
#include <wups.h>
#include <string.h>
WUPS_PLUGIN_NAME("GiveMiiYouTube");
WUPS_PLUGIN_DESCRIPTION("Allows the YouTube app to continue functioning after its discontinuation");
WUPS_PLUGIN_VERSION("v1.0");
WUPS_PLUGIN_AUTHOR("GaryOderNichts");
WUPS_PLUGIN_LICENSE("MIT");
#define CLIENT_ID_YT "e921a604fce89365498613fdf001b492"
DECL_FUNCTION(int, AcquireIndependentServiceToken__Q2_2nn3actFPcPCc, uint8_t* token, const char* client_id)
{
if (strcmp(client_id, CLIENT_ID_YT) == 0) {
return 0;
}
return real_AcquireIndependentServiceToken__Q2_2nn3actFPcPCc(token, client_id);
}
WUPS_MUST_REPLACE(AcquireIndependentServiceToken__Q2_2nn3actFPcPCc, WUPS_LOADER_LIBRARY_NN_ACT, AcquireIndependentServiceToken__Q2_2nn3actFPcPCc);

81
source/main.cpp Normal file
View file

@ -0,0 +1,81 @@
#include <wups.h>
#include <whb/log.h>
#include <whb/log_module.h>
#include <whb/log_cafe.h>
#include <whb/log_udp.h>
#include <string.h>
#include <coreinit/title.h>
#include <patcher/rplinfo.h>
#include <patcher/patcher.h>
WUPS_PLUGIN_NAME("GiveMiiYouTube");
WUPS_PLUGIN_DESCRIPTION("Allows the YouTube app to continue functioning after its discontinuation");
WUPS_PLUGIN_VERSION("v2.0");
WUPS_PLUGIN_AUTHOR("GaryOderNichts");
WUPS_PLUGIN_LICENSE("MIT");
#define YOUTUBE_CLIENT_ID "e921a604fce89365498613fdf001b492"
#define YOUTUBE_TITLE_ID 0x0005000010105700llu
#define YOUTUBE_USERAGENT_PLATFORM "WiiU; "
#define YOUTUBE_USERAGENT_PLATFORM_REPLACEMENT "NoU; "
ON_APPLICATION_START()
{
// If this is not the YouTube app no need to do anything
if (OSGetTitleID() != YOUTUBE_TITLE_ID) {
return;
}
// Init logging
if (!WHBLogModuleInit()) {
WHBLogCafeInit();
WHBLogUdpInit();
}
WHBLogPrintf("GiveMiiYouTube: applying patches...");
// Patch the dynload functions so GetRPLInfo works
if (!PatchDynLoadFunctions()) {
WHBLogPrintf("GiveMiiYouTube: Failed to patch dynload functions");
return;
}
// Get the RPLInfo
auto rpl_info = TryGetRPLInfo();
if (!rpl_info) {
WHBLogPrintf("GiveMiiYouTube: Failed to get RPL info");
return;
}
// Find the rpx
rplinfo rpls = *rpl_info;
auto lb_shell_rpx = FindRPL(rpls, "lb_shell.rpx");
if (!lb_shell_rpx) {
WHBLogPrintf("GiveMiiYouTube: Failed to find lb_shell.rpx");
return;
}
// Patch the useragent platform
OSDynLoad_NotifyData rpx_data = *lb_shell_rpx;
if (!replace_string(rpx_data.dataAddr, rpx_data.dataSize,
YOUTUBE_USERAGENT_PLATFORM, sizeof(YOUTUBE_USERAGENT_PLATFORM),
YOUTUBE_USERAGENT_PLATFORM_REPLACEMENT, sizeof(YOUTUBE_USERAGENT_PLATFORM_REPLACEMENT))) {
WHBLogPrintf("GiveMiiYouTube: Failed to replace useragent platform");
return;
}
}
DECL_FUNCTION(int, AcquireIndependentServiceToken__Q2_2nn3actFPcPCc, uint8_t* token, const char* client_id)
{
// If this is the YouTube client, return sucess
if (client_id && strcmp(client_id, YOUTUBE_CLIENT_ID) == 0) {
WHBLogPrintf("GiveMiiYouTube: Faking service sucess for '%s'", client_id);
return 0;
}
return real_AcquireIndependentServiceToken__Q2_2nn3actFPcPCc(token, client_id);
}
WUPS_MUST_REPLACE(AcquireIndependentServiceToken__Q2_2nn3actFPcPCc, WUPS_LOADER_LIBRARY_NN_ACT, AcquireIndependentServiceToken__Q2_2nn3actFPcPCc);

View file

@ -0,0 +1,20 @@
#include "patcher.h"
#include "utils/logger.h"
#include <kernel/kernel.h>
#include <coreinit/memorymap.h>
bool replace_string(uint32_t start, uint32_t size, const char* original_val, size_t original_val_sz, const char* new_val, size_t new_val_sz) {
for (uint32_t addr = start; addr < start + size - original_val_sz; addr++) {
int ret = memcmp(original_val, (void*)addr, original_val_sz);
if (ret == 0) {
DEBUG_FUNCTION_LINE("found str @%08x: %s", addr, (const char*)addr);
KernelCopyData(OSEffectiveToPhysical(addr), OSEffectiveToPhysical((uint32_t)new_val), new_val_sz);
DEBUG_FUNCTION_LINE("new str @%08x: %s", addr, (const char*)addr);
return true;
}
}
return false;
}

6
source/patcher/patcher.h Normal file
View file

@ -0,0 +1,6 @@
#pragma once
#include <cstdint>
#include <cstdlib>
bool replace_string(uint32_t start, uint32_t size, const char* original_val, size_t original_val_sz, const char* new_val, size_t new_val_sz);

View file

@ -0,0 +1,70 @@
/* Copyright 2022 Pretendo Network contributors <pretendo.network>
Copyright 2022 Ash Logan <ash@heyquark.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "rplinfo.h"
#include "utils/logger.h"
#include <kernel/kernel.h>
#include <coreinit/cache.h>
#include <coreinit/memorymap.h>
std::optional<std::vector<OSDynLoad_NotifyData>> TryGetRPLInfo() {
int num_rpls = OSDynLoad_GetNumberOfRPLs();
if (num_rpls == 0) {
return std::nullopt;
}
DEBUG_FUNCTION_LINE("num_rpls: %d", num_rpls);
std::vector<OSDynLoad_NotifyData> rpls;
rpls.resize(num_rpls);
bool ret = OSDynLoad_GetRPLInfo(0, num_rpls, rpls.data());
if (!ret) {
return std::nullopt;
}
return rpls;
}
bool PatchInstruction(void* instr, uint32_t original, uint32_t replacement) {
uint32_t current = *(uint32_t*)instr;
DEBUG_FUNCTION_LINE("current instr %08x", current);
if (current != original) return current == replacement;
KernelCopyData(OSEffectiveToPhysical((uint32_t)instr), OSEffectiveToPhysical((uint32_t)&replacement), sizeof(replacement));
//Only works on AROMA! WUPS 0.1's KernelCopyData is uncached, needs DCInvalidate here instead
DCFlushRange(instr, 4);
ICInvalidateRange(instr, 4);
current = *(uint32_t*)instr;
DEBUG_FUNCTION_LINE("patched instr %08x", current);
return true;
}
bool PatchDynLoadFunctions() {
uint32_t *patch1 = ((uint32_t *) &OSDynLoad_GetNumberOfRPLs) + 6;
uint32_t *patch2 = ((uint32_t *) &OSDynLoad_GetRPLInfo) + 22;
if (!PatchInstruction(patch1, 0x41820038 /* beq +38 */, 0x60000000 /*nop*/)) {
return false;
}
if (!PatchInstruction(patch2, 0x41820100 /* beq +100 */, 0x60000000 /*nop*/)) {
return false;
}
return true;
}

31
source/patcher/rplinfo.h Normal file
View file

@ -0,0 +1,31 @@
/* Copyright 2022 Pretendo Network contributors <pretendo.network>
Copyright 2022 Ash Logan <ash@heyquark.com>
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <optional>
#include <vector>
#include <string>
#include <algorithm>
#include <coreinit/dynload.h>
using rplinfo = std::vector<OSDynLoad_NotifyData>;
std::optional<rplinfo> TryGetRPLInfo();
bool PatchDynLoadFunctions();
constexpr inline std::optional<OSDynLoad_NotifyData> FindRPL(const rplinfo& rpls, const std::string& name) {
auto res = std::find_if(rpls.cbegin(), rpls.cend(), [&](const OSDynLoad_NotifyData& data){ return std::string(data.name).ends_with(name); });
if (res == rpls.cend()) return std::nullopt;
return *res;
}

30
source/utils/logger.h Normal file
View file

@ -0,0 +1,30 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <string.h>
#include <whb/log.h>
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
#define OSFATAL_FUNCTION_LINE(FMT, ARGS...) \
do { \
OSFatal_printf("[%s]%s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
} while (0)
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) \
do { \
WHBLogPrintf("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
} while (0);
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) \
do { \
WHBLogWritef("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
} while (0);
#ifdef __cplusplus
}
#endif