From 9774d68b3b8fd1b48525808beefb68a47903e11d Mon Sep 17 00:00:00 2001 From: chiteroman <98092901+chiteroman@users.noreply.github.com> Date: Thu, 23 Nov 2023 20:14:58 +0100 Subject: [PATCH] v13.5-beta --- .idea/vcs.xml | 1 + app/build.gradle.kts | 26 +- app/src/main/cpp/Android.mk | 33 + app/src/main/cpp/Application.mk | 6 + app/src/main/cpp/CMakeLists.txt | 15 - app/src/main/cpp/Dobby | 1 - app/src/main/cpp/libcxx | 1 + app/src/main/cpp/main.cpp | 168 +- app/src/main/cpp/shadowhook/arch/arm/sh_a32.c | 446 ++ app/src/main/cpp/shadowhook/arch/arm/sh_a32.h | 41 + .../main/cpp/shadowhook/arch/arm/sh_inst.c | 523 ++ .../main/cpp/shadowhook/arch/arm/sh_inst.h | 41 + app/src/main/cpp/shadowhook/arch/arm/sh_t16.c | 284 + app/src/main/cpp/shadowhook/arch/arm/sh_t16.h | 46 + app/src/main/cpp/shadowhook/arch/arm/sh_t32.c | 408 ++ app/src/main/cpp/shadowhook/arch/arm/sh_t32.h | 36 + app/src/main/cpp/shadowhook/arch/arm/sh_txx.c | 60 + app/src/main/cpp/shadowhook/arch/arm/sh_txx.h | 39 + .../main/cpp/shadowhook/arch/arm64/sh_a64.c | 310 ++ .../main/cpp/shadowhook/arch/arm64/sh_a64.h | 44 + .../main/cpp/shadowhook/arch/arm64/sh_inst.c | 203 + .../main/cpp/shadowhook/arch/arm64/sh_inst.h | 42 + app/src/main/cpp/shadowhook/common/bytesig.c | 291 + app/src/main/cpp/shadowhook/common/bytesig.h | 157 + .../main/cpp/shadowhook/common/sh_config.h | 55 + app/src/main/cpp/shadowhook/common/sh_errno.c | 107 + app/src/main/cpp/shadowhook/common/sh_errno.h | 40 + app/src/main/cpp/shadowhook/common/sh_log.c | 53 + app/src/main/cpp/shadowhook/common/sh_log.h | 70 + app/src/main/cpp/shadowhook/common/sh_sig.h | 49 + .../main/cpp/shadowhook/common/sh_trampo.c | 172 + .../main/cpp/shadowhook/common/sh_trampo.h | 52 + app/src/main/cpp/shadowhook/common/sh_util.c | 538 ++ app/src/main/cpp/shadowhook/common/sh_util.h | 96 + .../main/cpp/shadowhook/include/shadowhook.h | 191 + app/src/main/cpp/shadowhook/sh_enter.c | 47 + app/src/main/cpp/shadowhook/sh_enter.h | 30 + app/src/main/cpp/shadowhook/sh_exit.c | 420 ++ app/src/main/cpp/shadowhook/sh_exit.h | 34 + app/src/main/cpp/shadowhook/sh_hub.c | 538 ++ app/src/main/cpp/shadowhook/sh_hub.h | 45 + app/src/main/cpp/shadowhook/sh_jni.c | 139 + app/src/main/cpp/shadowhook/sh_linker.c | 409 ++ app/src/main/cpp/shadowhook/sh_linker.h | 41 + app/src/main/cpp/shadowhook/sh_recorder.c | 517 ++ app/src/main/cpp/shadowhook/sh_recorder.h | 37 + app/src/main/cpp/shadowhook/sh_safe.c | 133 + app/src/main/cpp/shadowhook/sh_safe.h | 37 + app/src/main/cpp/shadowhook/sh_switch.c | 343 ++ app/src/main/cpp/shadowhook/sh_switch.h | 34 + app/src/main/cpp/shadowhook/sh_task.c | 333 ++ app/src/main/cpp/shadowhook/sh_task.h | 40 + app/src/main/cpp/shadowhook/shadowhook.c | 328 ++ .../cpp/shadowhook/third_party/bsd/queue.h | 551 ++ .../cpp/shadowhook/third_party/bsd/tree.h | 759 +++ .../cpp/shadowhook/third_party/lss/LICENSE | 28 + .../third_party/lss/linux_syscall_support.h | 4867 +++++++++++++++++ .../cpp/shadowhook/third_party/xdl/LICENSE | 21 + .../main/cpp/shadowhook/third_party/xdl/xdl.c | 910 +++ .../main/cpp/shadowhook/third_party/xdl/xdl.h | 92 + .../shadowhook/third_party/xdl/xdl_iterate.c | 297 + .../shadowhook/third_party/xdl/xdl_iterate.h | 43 + .../shadowhook/third_party/xdl/xdl_linker.c | 234 + .../shadowhook/third_party/xdl/xdl_linker.h | 40 + .../cpp/shadowhook/third_party/xdl/xdl_lzma.c | 187 + .../cpp/shadowhook/third_party/xdl/xdl_lzma.h | 40 + .../cpp/shadowhook/third_party/xdl/xdl_util.c | 95 + .../cpp/shadowhook/third_party/xdl/xdl_util.h | 71 + .../playintegrityfix/EntryPoint.java | 14 +- 69 files changed, 16266 insertions(+), 133 deletions(-) create mode 100644 app/src/main/cpp/Android.mk create mode 100644 app/src/main/cpp/Application.mk delete mode 100644 app/src/main/cpp/CMakeLists.txt delete mode 160000 app/src/main/cpp/Dobby create mode 160000 app/src/main/cpp/libcxx create mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_a32.c create mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_a32.h create mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_inst.c create mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_inst.h create mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_t16.c create mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_t16.h create mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_t32.c create mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_t32.h create mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_txx.c create mode 100644 app/src/main/cpp/shadowhook/arch/arm/sh_txx.h create mode 100644 app/src/main/cpp/shadowhook/arch/arm64/sh_a64.c create mode 100644 app/src/main/cpp/shadowhook/arch/arm64/sh_a64.h create mode 100644 app/src/main/cpp/shadowhook/arch/arm64/sh_inst.c create mode 100644 app/src/main/cpp/shadowhook/arch/arm64/sh_inst.h create mode 100644 app/src/main/cpp/shadowhook/common/bytesig.c create mode 100644 app/src/main/cpp/shadowhook/common/bytesig.h create mode 100644 app/src/main/cpp/shadowhook/common/sh_config.h create mode 100644 app/src/main/cpp/shadowhook/common/sh_errno.c create mode 100644 app/src/main/cpp/shadowhook/common/sh_errno.h create mode 100644 app/src/main/cpp/shadowhook/common/sh_log.c create mode 100644 app/src/main/cpp/shadowhook/common/sh_log.h create mode 100644 app/src/main/cpp/shadowhook/common/sh_sig.h create mode 100644 app/src/main/cpp/shadowhook/common/sh_trampo.c create mode 100644 app/src/main/cpp/shadowhook/common/sh_trampo.h create mode 100644 app/src/main/cpp/shadowhook/common/sh_util.c create mode 100644 app/src/main/cpp/shadowhook/common/sh_util.h create mode 100644 app/src/main/cpp/shadowhook/include/shadowhook.h create mode 100644 app/src/main/cpp/shadowhook/sh_enter.c create mode 100644 app/src/main/cpp/shadowhook/sh_enter.h create mode 100644 app/src/main/cpp/shadowhook/sh_exit.c create mode 100644 app/src/main/cpp/shadowhook/sh_exit.h create mode 100644 app/src/main/cpp/shadowhook/sh_hub.c create mode 100644 app/src/main/cpp/shadowhook/sh_hub.h create mode 100644 app/src/main/cpp/shadowhook/sh_jni.c create mode 100644 app/src/main/cpp/shadowhook/sh_linker.c create mode 100644 app/src/main/cpp/shadowhook/sh_linker.h create mode 100644 app/src/main/cpp/shadowhook/sh_recorder.c create mode 100644 app/src/main/cpp/shadowhook/sh_recorder.h create mode 100644 app/src/main/cpp/shadowhook/sh_safe.c create mode 100644 app/src/main/cpp/shadowhook/sh_safe.h create mode 100644 app/src/main/cpp/shadowhook/sh_switch.c create mode 100644 app/src/main/cpp/shadowhook/sh_switch.h create mode 100644 app/src/main/cpp/shadowhook/sh_task.c create mode 100644 app/src/main/cpp/shadowhook/sh_task.h create mode 100644 app/src/main/cpp/shadowhook/shadowhook.c create mode 100644 app/src/main/cpp/shadowhook/third_party/bsd/queue.h create mode 100644 app/src/main/cpp/shadowhook/third_party/bsd/tree.h create mode 100644 app/src/main/cpp/shadowhook/third_party/lss/LICENSE create mode 100644 app/src/main/cpp/shadowhook/third_party/lss/linux_syscall_support.h create mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/LICENSE create mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl.c create mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl.h create mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl_iterate.c create mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl_iterate.h create mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.c create mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl_linker.h create mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.c create mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl_lzma.h create mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.c create mode 100644 app/src/main/cpp/shadowhook/third_party/xdl/xdl_util.h diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 35eb1dd..25dfc0f 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4bdc43e..c6a5fc3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -15,20 +15,11 @@ android { versionCode = 1 versionName = "1.0" - packaging { - jniLibs { - excludes += "**/libdobby.so" - } - } - - buildFeatures { - prefab = true - } - externalNativeBuild { - cmake { - arguments += setOf("-DANDROID_STL=none", "-DCMAKE_BUILD_TYPE=MinSizeRel") - cppFlags += setOf("-std=c++20", "-fno-exceptions", "-fno-rtti", "-fvisibility=hidden", "-fvisibility-inlines-hidden") + ndk { + jobs = Runtime.getRuntime().availableProcessors() + abiFilters += "armeabi-v7a" + abiFilters += "arm64-v8a" } } } @@ -47,13 +38,8 @@ android { } externalNativeBuild { - cmake { - path = file("src/main/cpp/CMakeLists.txt") - version = "3.22.1" + ndkBuild { + path = file("src/main/cpp/Android.mk") } } -} - -dependencies { - implementation("dev.rikka.ndk.thirdparty:cxx:1.2.0") } \ No newline at end of file diff --git a/app/src/main/cpp/Android.mk b/app/src/main/cpp/Android.mk new file mode 100644 index 0000000..9632627 --- /dev/null +++ b/app/src/main/cpp/Android.mk @@ -0,0 +1,33 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := zygisk +LOCAL_SRC_FILES := main.cpp +LOCAL_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/*.c) +LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/common/*.c) +LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/third_party/xdl/*.c) + +LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook +LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/common +LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/include +LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/bsd +LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/lss +LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/third_party/xdl + +ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) + LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/arch/arm/*.c) + LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/arch/arm +endif + +ifeq ($(TARGET_ARCH_ABI),arm64-v8a) + LOCAL_SRC_FILES += $(wildcard $(LOCAL_PATH)/shadowhook/arch/arm64/*.c) + LOCAL_C_INCLUDES += $(LOCAL_PATH)/shadowhook/arch/arm64 +endif + +LOCAL_STATIC_LIBRARIES := libcxx +LOCAL_LDLIBS := -llog +include $(BUILD_SHARED_LIBRARY) + +include $(LOCAL_PATH)/libcxx/Android.mk \ No newline at end of file diff --git a/app/src/main/cpp/Application.mk b/app/src/main/cpp/Application.mk new file mode 100644 index 0000000..c6cdbe7 --- /dev/null +++ b/app/src/main/cpp/Application.mk @@ -0,0 +1,6 @@ +FLAGS := -DNDEBUG -g0 -Oz -fno-exceptions -fno-rtti -fvisibility=hidden -fvisibility-inlines-hidden -ffunction-sections -fdata-sections -flto=full -Wl,--icf=all -Wl,--exclude-libs,ALL -Wl,--gc-sections + +APP_STL := none +APP_CPPFLAGS := -std=c++20 +APP_CFLAGS := $(FLAGS) +APP_LDFLAGS := $(FLAGS) \ No newline at end of file diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt deleted file mode 100644 index 2071d71..0000000 --- a/app/src/main/cpp/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -cmake_minimum_required(VERSION 3.22.1) - -project("playintegrityfix") - -find_package(cxx REQUIRED CONFIG) - -link_libraries(cxx::cxx) - -add_library(${CMAKE_PROJECT_NAME} SHARED main.cpp) - -add_subdirectory(Dobby) - -SET_OPTION(Plugin.Android.BionicLinkerUtil ON) - -target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE log dobby_static) \ No newline at end of file diff --git a/app/src/main/cpp/Dobby b/app/src/main/cpp/Dobby deleted file mode 160000 index b0176de..0000000 --- a/app/src/main/cpp/Dobby +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b0176de574104726bb68dff3b77ee666300fc338 diff --git a/app/src/main/cpp/libcxx b/app/src/main/cpp/libcxx new file mode 160000 index 0000000..12c8f4e --- /dev/null +++ b/app/src/main/cpp/libcxx @@ -0,0 +1 @@ +Subproject commit 12c8f4e93f196a700137e983dcceeac43cf807f2 diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp index c987095..b206c60 100644 --- a/app/src/main/cpp/main.cpp +++ b/app/src/main/cpp/main.cpp @@ -4,27 +4,27 @@ #include #include #include +#include +#include #include "zygisk.hpp" -#include "dobby.h" +#include "shadowhook.h" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "PIF/Native", __VA_ARGS__) -static std::string SECURITY_PATCH, FIRST_API_LEVEL; - #define DEX_FILE_PATH "/data/adb/modules/playintegrityfix/classes.dex" #define PROP_FILE_PATH "/data/adb/modules/playintegrityfix/pif.prop" +static std::string SECURITY_PATCH, FIRST_API_LEVEL; + typedef void (*T_Callback)(void *, const char *, const char *, uint32_t); -static std::map callbacks; +static T_Callback o_callback = nullptr; static void modify_callback(void *cookie, const char *name, const char *value, uint32_t serial) { - if (cookie == nullptr || name == nullptr || value == nullptr || - !callbacks.contains(cookie)) - return; + if (cookie == nullptr || name == nullptr || value == nullptr || o_callback == nullptr) return; std::string_view prop(name); @@ -44,30 +44,33 @@ static void modify_callback(void *cookie, const char *name, const char *value, u if (!prop.starts_with("cache") && !prop.starts_with("debug")) LOGD("[%s] -> %s", name, value); - return callbacks[cookie](cookie, name, value, serial); + return o_callback(cookie, name, value, serial); } static void (*o_system_property_read_callback)(const prop_info *, T_Callback, void *); static void my_system_property_read_callback(const prop_info *pi, T_Callback callback, void *cookie) { - if (pi == nullptr || callback == nullptr || cookie == nullptr) { return o_system_property_read_callback(pi, callback, cookie); } - callbacks[cookie] = callback; + o_callback = callback; return o_system_property_read_callback(pi, modify_callback, cookie); } static void doHook() { - void *handle = DobbySymbolResolver("libc.so", "__system_property_read_callback"); + shadowhook_init(SHADOWHOOK_MODE_UNIQUE, true); + void *handle = shadowhook_hook_sym_name( + "libc.so", + "__system_property_read_callback", + (void *) my_system_property_read_callback, + (void **) &o_system_property_read_callback + ); if (handle == nullptr) { LOGD("Couldn't find '__system_property_read_callback' handle. Report to @chiteroman"); return; } LOGD("Found '__system_property_read_callback' handle at %p", handle); - DobbyHook(handle, (void *) my_system_property_read_callback, - (void **) &o_system_property_read_callback); } class PlayIntegrityFix : public zygisk::ModuleBase { @@ -96,10 +99,30 @@ public: int fd = api->connectCompanion(); - int mapSize; + long size = 0; + read(fd, &size, sizeof(size)); + + if (size < 1) { + close(fd); + LOGD("Couldn't read classes.dex from socket! File exists?"); + return; + } + + moduleDex.resize(size); + read(fd, moduleDex.data(), size); + + int mapSize = 0; read(fd, &mapSize, sizeof(mapSize)); - for (int i = 0; i < mapSize; ++i) { + if (mapSize < 1) { + close(fd); + SECURITY_PATCH = "2017-08-05"; + FIRST_API_LEVEL = "23"; + LOGD("Couldn't read pif.prop from socket! File exists?"); + return; + } + + for (int i = 0; i < size; ++i) { int keyLenght, valueLenght; std::string key, value; @@ -112,19 +135,9 @@ public: read(fd, key.data(), keyLenght); read(fd, value.data(), valueLenght); - props.insert({key, value}); + props[key] = value; } - long size; - read(fd, &size, sizeof(size)); - - char buffer[size]; - read(fd, buffer, size); - - close(fd); - - moduleDex.insert(moduleDex.end(), buffer, buffer + size); - LOGD("Received from socket %d props!", static_cast(props.size())); for (const auto &item: props) { @@ -134,6 +147,8 @@ public: FIRST_API_LEVEL = item.second; } } + + close(fd); } void postAppSpecialize(const zygisk::AppSpecializeArgs *args) override { @@ -166,11 +181,6 @@ private: return; } - if (props.empty()) { - LOGD("No props loaded in memory"); - return; - } - LOGD("Preparing to inject %d bytes to the process", static_cast(moduleDex.size())); LOGD("get system classloader"); @@ -195,13 +205,15 @@ private: auto entryClass = (jclass) entryClassObj; - LOGD("call add prop"); - auto addProp = env->GetStaticMethodID(entryClass, "addProp", - "(Ljava/lang/String;Ljava/lang/String;)V"); - for (const auto &item: props) { - auto key = env->NewStringUTF(item.first.c_str()); - auto value = env->NewStringUTF(item.second.c_str()); - env->CallStaticVoidMethod(entryClass, addProp, key, value); + if (!props.empty()) { + LOGD("call add prop"); + auto addProp = env->GetStaticMethodID(entryClass, "addProp", + "(Ljava/lang/String;Ljava/lang/String;)V"); + for (const auto &item: props) { + auto key = env->NewStringUTF(item.first.c_str()); + auto value = env->NewStringUTF(item.second.c_str()); + env->CallStaticVoidMethod(entryClass, addProp, key, value); + } } LOGD("call init"); @@ -210,44 +222,42 @@ private: } }; -static void parsePropsFile(int fd) { - LOGD("Proceed to parse '%s' file", PROP_FILE_PATH); +static void companion(int fd) { + std::ifstream dex(DEX_FILE_PATH, std::ios::binary | std::ios::ate); + + long size = static_cast(dex.tellg()); + dex.seekg(std::ios::beg); + + char buffer[size]; + dex.read(buffer, size); + + dex.close(); + + write(fd, &size, sizeof(size)); + write(fd, buffer, size); + + std::ifstream prop(PROP_FILE_PATH); + + if (prop.bad()) { + prop.close(); + int i = 0; + write(fd, &i, sizeof(i)); + return; + } std::map props; - FILE *file = fopen(PROP_FILE_PATH, "r"); - - char line[256]; - - while (fgets(line, sizeof(line), file)) { - + std::string line; + while (std::getline(prop, line)) { + std::istringstream iss(line); std::string key, value; - char *data = strtok(line, "="); - - while (data) { - if (key.empty()) { - key = data; - } else { - value = data; - } - data = strtok(nullptr, "="); + if (std::getline(iss, key, '=') && std::getline(iss, value)) { + props[key] = value; } - - key.erase(std::remove_if(key.begin(), key.end(), - [](unsigned char x) { return std::isspace(x); }), key.end()); - value.erase(std::remove(value.begin(), value.end(), '\n'), value.cend()); - - props.insert({key, value}); - - key.clear(); - key.shrink_to_fit(); - - value.clear(); - value.shrink_to_fit(); } - fclose(file); + prop.close(); int mapSize = static_cast(props.size()); @@ -263,28 +273,6 @@ static void parsePropsFile(int fd) { write(fd, item.first.data(), keyLenght); write(fd, item.second.data(), valueLenght); } - - props.clear(); -} - -static void companion(int fd) { - parsePropsFile(fd); - - FILE *dex = fopen(DEX_FILE_PATH, "rb"); - - fseek(dex, 0, SEEK_END); - long size = ftell(dex); - fseek(dex, 0, SEEK_SET); - - char buffer[size]; - fread(buffer, 1, size, dex); - - fclose(dex); - - buffer[size] = '\0'; - - write(fd, &size, sizeof(size)); - write(fd, buffer, size); } REGISTER_ZYGISK_MODULE(PlayIntegrityFix) diff --git a/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c b/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c new file mode 100644 index 0000000..d71e4f8 --- /dev/null +++ b/app/src/main/cpp/shadowhook/arch/arm/sh_a32.c @@ -0,0 +1,446 @@ +// Copyright (c) 2021-2022 ByteDance Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +// Created by Kelun Cai (caikelun@bytedance.com) on 2021-04-11. + +#include "sh_a32.h" + +#include +#include +#include + +#include "sh_log.h" + +// https://developer.arm.com/documentation/ddi0406/latest +// https://developer.arm.com/documentation/ddi0597/latest + +typedef enum { + IGNORED = 0, + B_A1, + BX_A1, + BL_IMM_A1, + BLX_IMM_A2, + ADD_REG_A1, + ADD_REG_PC_A1, + SUB_REG_A1, + SUB_REG_PC_A1, + ADR_A1, + ADR_A2, + MOV_REG_A1, + MOV_REG_PC_A1, + LDR_LIT_A1, + LDR_LIT_PC_A1, + LDRB_LIT_A1, + LDRD_LIT_A1, + LDRH_LIT_A1, + LDRSB_LIT_A1, + LDRSH_LIT_A1, + LDR_REG_A1, + LDR_REG_PC_A1, + LDRB_REG_A1, + LDRD_REG_A1, + LDRH_REG_A1, + LDRSB_REG_A1, + LDRSH_REG_A1 +} sh_a32_type_t; + +static sh_a32_type_t sh_a32_get_type(uint32_t inst) { + if (((inst & 0x0F000000u) == 0x0A000000) && ((inst & 0xF0000000) != 0xF0000000)) + return B_A1; + else if (((inst & 0x0FFFFFFFu) == 0x012FFF1F) && ((inst & 0xF0000000) != 0xF0000000)) + return BX_A1; + else if (((inst & 0x0F000000u) == 0x0B000000) && ((inst & 0xF0000000) != 0xF0000000)) + return BL_IMM_A1; + else if ((inst & 0xFE000000) == 0xFA000000) + return BLX_IMM_A2; + else if (((inst & 0x0FE00010u) == 0x00800000) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x0010F000u) != 0x0010F000) && ((inst & 0x000F0000u) != 0x000D0000) && + (((inst & 0x000F0000u) == 0x000F0000) || ((inst & 0x0000000Fu) == 0x0000000F))) + return ((inst & 0x0000F000u) == 0x0000F000) ? ADD_REG_PC_A1 : ADD_REG_A1; + else if (((inst & 0x0FE00010u) == 0x00400000) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x0010F000u) != 0x0010F000) && ((inst & 0x000F0000u) != 0x000D0000) && + (((inst & 0x000F0000u) == 0x000F0000) || ((inst & 0x0000000Fu) == 0x0000000F))) + return ((inst & 0x0000F000u) == 0x0000F000) ? SUB_REG_PC_A1 : SUB_REG_A1; + else if (((inst & 0x0FFF0000u) == 0x028F0000) && ((inst & 0xF0000000) != 0xF0000000)) + return ADR_A1; + else if (((inst & 0x0FFF0000u) == 0x024F0000) && ((inst & 0xF0000000) != 0xF0000000)) + return ADR_A2; + else if (((inst & 0x0FEF001Fu) == 0x01A0000F) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x0010F000u) != 0x0010F000) && + (!(((inst & 0x0000F000u) == 0x0000F000) && ((inst & 0x00000FF0u) != 0x00000000)))) + return ((inst & 0x0000F000u) == 0x0000F000) ? MOV_REG_PC_A1 : MOV_REG_A1; + else if (((inst & 0x0F7F0000u) == 0x051F0000) && ((inst & 0xF0000000) != 0xF0000000)) + return ((inst & 0x0000F000u) == 0x0000F000) ? LDR_LIT_PC_A1 : LDR_LIT_A1; + else if (((inst & 0x0F7F0000u) == 0x055F0000) && ((inst & 0xF0000000) != 0xF0000000)) + return LDRB_LIT_A1; + else if (((inst & 0x0F7F00F0u) == 0x014F00D0) && ((inst & 0xF0000000) != 0xF0000000)) + return LDRD_LIT_A1; + else if (((inst & 0x0F7F00F0u) == 0x015F00B0) && ((inst & 0xF0000000) != 0xF0000000)) + return LDRH_LIT_A1; + else if (((inst & 0x0F7F00F0u) == 0x015F00D0) && ((inst & 0xF0000000) != 0xF0000000)) + return LDRSB_LIT_A1; + else if (((inst & 0x0F7F00F0u) == 0x015F00F0) && ((inst & 0xF0000000) != 0xF0000000)) + return LDRSH_LIT_A1; + else if (((inst & 0x0E5F0010u) == 0x061F0000) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x01200000u) != 0x00200000)) + return ((inst & 0x0000F000u) == 0x0000F000) ? LDR_REG_PC_A1 : LDR_REG_A1; + else if (((inst & 0x0E5F0010u) == 0x065F0000) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x01200000u) != 0x00200000)) + return LDRB_REG_A1; + else if (((inst & 0x0E5F0FF0u) == 0x000F00D0) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x01200000u) != 0x00200000)) + return LDRD_REG_A1; + else if (((inst & 0x0E5F0FF0u) == 0x001F00B0) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x01200000u) != 0x00200000)) + return LDRH_REG_A1; + else if (((inst & 0x0E5F0FF0u) == 0x001F00D0) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x01200000u) != 0x00200000)) + return LDRSB_REG_A1; + else if (((inst & 0x0E5F0FF0u) == 0x001F00F0) && ((inst & 0xF0000000) != 0xF0000000) && + ((inst & 0x01200000u) != 0x00200000)) + return LDRSH_REG_A1; + else + return IGNORED; +} + +size_t sh_a32_get_rewrite_inst_len(uint32_t inst) { + static uint8_t map[] = { + 4, // IGNORED + 12, // B_A1 + 12, // BX_A1 + 16, // BL_IMM_A1 + 16, // BLX_IMM_A2 + 32, // ADD_REG_A1 + 32, // ADD_REG_PC_A1 + 32, // SUB_REG_A1 + 32, // SUB_REG_PC_A1 + 12, // ADR_A1 + 12, // ADR_A2 + 32, // MOV_REG_A1 + 12, // MOV_REG_PC_A1 + 24, // LDR_LIT_A1 + 36, // LDR_LIT_PC_A1 + 24, // LDRB_LIT_A1 + 24, // LDRD_LIT_A1 + 24, // LDRH_LIT_A1 + 24, // LDRSB_LIT_A1 + 24, // LDRSH_LIT_A1 + 32, // LDR_REG_A1 + 36, // LDR_REG_PC_A1 + 32, // LDRB_REG_A1 + 32, // LDRD_REG_A1 + 32, // LDRH_REG_A1 + 32, // LDRSB_REG_A1 + 32 // LDRSH_REG_A1 + }; + + return (size_t)(map[sh_a32_get_type(inst)]); +} + +static bool sh_a32_is_addr_need_fix(uintptr_t addr, sh_a32_rewrite_info_t *rinfo) { + return (rinfo->overwrite_start_addr <= addr && addr < rinfo->overwrite_end_addr); +} + +static uintptr_t sh_a32_fix_addr(uintptr_t addr, sh_a32_rewrite_info_t *rinfo) { + if (rinfo->overwrite_start_addr <= addr && addr < rinfo->overwrite_end_addr) { + uintptr_t cursor_addr = rinfo->overwrite_start_addr; + size_t offset = 0; + for (size_t i = 0; i < rinfo->rewrite_inst_lens_cnt; i++) { + if (cursor_addr >= addr) break; + cursor_addr += 4; + offset += rinfo->rewrite_inst_lens[i]; + } + uintptr_t fixed_addr = (uintptr_t)rinfo->rewrite_buf + offset; + SH_LOG_INFO("a32 rewrite: fix addr %" PRIxPTR " -> %" PRIxPTR, addr, fixed_addr); + return fixed_addr; + } + + return addr; +} + +static size_t sh_a32_rewrite_b(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type, + sh_a32_rewrite_info_t *rinfo) { + uint32_t cond; + if (type == B_A1 || type == BL_IMM_A1 || type == BX_A1) + cond = SH_UTIL_GET_BITS_32(inst, 31, 28); + else + // type == BLX_IMM_A2 + cond = 0xE; // 1110 None (AL) + + uint32_t addr; + if (type == B_A1 || type == BL_IMM_A1) { + uint32_t imm24 = SH_UTIL_GET_BITS_32(inst, 23, 0); + uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32(imm24 << 2u, 26u); + addr = pc + imm32; // arm -> arm + } else if (type == BLX_IMM_A2) { + uint32_t h = SH_UTIL_GET_BIT_32(inst, 24); + uint32_t imm24 = SH_UTIL_GET_BITS_32(inst, 23, 0); + uint32_t imm32 = SH_UTIL_SIGN_EXTEND_32((imm24 << 2u) | (h << 1u), 26u); + addr = SH_UTIL_SET_BIT0(pc + imm32); // arm -> thumb + } else { + // type == BX_A1 + // BX PC + // PC must be even, and the "arm" instruction must be at a 4-byte aligned address, + // so the instruction set must keep "arm" unchanged. + addr = pc; // arm -> arm + } + addr = sh_a32_fix_addr(addr, rinfo); + + size_t idx = 0; + if (type == BL_IMM_A1 || type == BLX_IMM_A2) { + buf[idx++] = 0x028FE008u | (cond << 28u); // ADD LR, PC, #8 + } + buf[idx++] = 0x059FF000u | (cond << 28u); // LDR PC, [PC, #0] + buf[idx++] = 0xEA000000; // B #0 + buf[idx++] = addr; + return idx * 4; // 12 or 16 +} + +static size_t sh_a32_rewrite_add_or_sub(uint32_t *buf, uint32_t inst, uintptr_t pc) { + // ADD{S} , , PC{, } or ADD{S} , PC, {, } + // SUB{S} , , PC{, } or SUB{S} , PC, {, } + uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); + uint32_t rn = SH_UTIL_GET_BITS_32(inst, 19, 16); + uint32_t rm = SH_UTIL_GET_BITS_32(inst, 3, 0); + uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12); + + uint32_t rx; // r0 - r3 + for (rx = 3;; --rx) + if (rx != rn && rx != rm && rx != rd) break; + + if (rd == 0xF) // Rd == PC + { + uint32_t ry; // r0 - r4 + for (ry = 4;; --ry) + if (ry != rn && ry != rm && ry != rd && ry != rx) break; + + buf[0] = 0x0A000000u | (cond << 28u); // B #0 + buf[1] = 0xEA000005; // B #20 + buf[2] = 0xE92D8000 | (1u << rx) | (1u << ry); // PUSH {Rx, Ry, PC} + buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8] + if (rn == 0xF) + // Rn == PC + buf[4] = + (inst & 0x0FF00FFFu) | 0xE0000000 | (ry << 12u) | (rx << 16u); // ADD/SUB Ry, Rx, Rm{, } + else + // Rm == PC + buf[4] = (inst & 0x0FFF0FF0u) | 0xE0000000 | (ry << 12u) | rx; // ADD/SUB Ry, Rn, Rx{, } + buf[5] = 0xE58D0008 | (ry << 12u); // STR Ry, [SP, #8] + buf[6] = 0xE8BD8000 | (1u << rx) | (1u << ry); // POP {Rx, Ry, PC} + buf[7] = pc; + return 32; + } else { + buf[0] = 0x0A000000u | (cond << 28u); // B #0 + buf[1] = 0xEA000005; // B #20 + buf[2] = 0xE52D0004 | (rx << 12u); // PUSH {Rx} + buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8] + if (rn == 0xF) + // Rn == PC + buf[4] = (inst & 0x0FF0FFFFu) | 0xE0000000 | (rx << 16u); // ADD/SUB{S} Rd, Rx, Rm{, } + else + // Rm == PC + buf[4] = (inst & 0x0FFFFFF0u) | 0xE0000000 | rx; // ADD/SUB{S} Rd, Rn, Rx{, } + buf[5] = 0xE49D0004 | (rx << 12u); // POP {Rx} + buf[6] = 0xEA000000; // B #0 + buf[7] = pc; + return 32; + } +} + +static size_t sh_a32_rewrite_adr(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type, + sh_a32_rewrite_info_t *rinfo) { + uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); + uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12); // r0 - r15 + uint32_t imm12 = SH_UTIL_GET_BITS_32(inst, 11, 0); + uint32_t imm32 = sh_util_arm_expand_imm(imm12); + uint32_t addr = (type == ADR_A1 ? (SH_UTIL_ALIGN_4(pc) + imm32) : (SH_UTIL_ALIGN_4(pc) - imm32)); + if (sh_a32_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed + + buf[0] = 0x059F0000u | (cond << 28u) | (rd << 12u); // LDR Rd, [PC, #0] + buf[1] = 0xEA000000; // B #0 + buf[2] = addr; + return 12; +} + +static size_t sh_a32_rewrite_mov(uint32_t *buf, uint32_t inst, uintptr_t pc) { + // MOV{S} , PC + uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); + uint32_t rd = SH_UTIL_GET_BITS_32(inst, 15, 12); + uint32_t rx = (rd == 0) ? 1 : 0; + + if (rd == 0xF) // Rd == PC (MOV PC, PC) + { + buf[0] = 0x059FF000u | (cond << 28u); // LDR PC, [PC, #0] + buf[1] = 0xEA000000; // B #0 + buf[2] = pc; + return 12; + } else { + buf[0] = 0x0A000000u | (cond << 28u); // B #0 + buf[1] = 0xEA000005; // B #20 + buf[2] = 0xE52D0004 | (rx << 12u); // PUSH {Rx} + buf[3] = 0xE59F0008 | (rx << 12u); // LDR Rx, [PC, #8] + buf[4] = (inst & 0x0FFFFFF0u) | 0xE0000000 | rx; // MOV{S} Rd, Rx{, #/RRX} + buf[5] = 0xE49D0004 | (rx << 12u); // POP {Rx} + buf[6] = 0xEA000000; // B #0 + buf[7] = pc; + return 32; + } +} + +static size_t sh_a32_rewrite_ldr_lit(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type, + sh_a32_rewrite_info_t *rinfo) { + uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); + uint32_t u = SH_UTIL_GET_BIT_32(inst, 23); + uint32_t rt = SH_UTIL_GET_BITS_16(inst, 15, 12); + + uint32_t imm32; + if (type == LDR_LIT_A1 || type == LDR_LIT_PC_A1 || type == LDRB_LIT_A1) + imm32 = SH_UTIL_GET_BITS_32(inst, 11, 0); + else + imm32 = (SH_UTIL_GET_BITS_32(inst, 11, 8) << 4u) + SH_UTIL_GET_BITS_32(inst, 3, 0); + uint32_t addr = (u ? (SH_UTIL_ALIGN_4(pc) + imm32) : (SH_UTIL_ALIGN_4(pc) - imm32)); + if (sh_a32_is_addr_need_fix(addr, rinfo)) return 0; // rewrite failed + + if (type == LDR_LIT_PC_A1 && rt == 0xF) { + // Rt == PC + buf[0] = 0x0A000000u | (cond << 28u); // B #0 + buf[1] = 0xEA000006; // B #24 + buf[2] = 0xE92D0003; // PUSH {R0, R1} + buf[3] = 0xE59F0000; // LDR R0, [PC, #0] + buf[4] = 0xEA000000; // B #0 + buf[5] = addr; // + buf[6] = 0xE5900000; // LDR R0, [R0] + buf[7] = 0xE58D0004; // STR R0, [SP, #4] + buf[8] = 0xE8BD8001; // POP {R0, PC} + return 36; + } else { + buf[0] = 0x0A000000u | (cond << 28u); // B #0 + buf[1] = 0xEA000003; // B #12 + buf[2] = 0xE59F0000 | (rt << 12u); // LDR Rt, [PC, #0] + buf[3] = 0xEA000000; // B #0 + buf[4] = addr; // +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wswitch" + switch (type) { + case LDR_LIT_A1: + buf[5] = 0xE5900000 | (rt << 16u) | (rt << 12u); // LDR Rt, [Rt] + break; + case LDRB_LIT_A1: + buf[5] = 0xE5D00000 | (rt << 16u) | (rt << 12u); // LDRB Rt, [Rt] + break; + case LDRD_LIT_A1: + buf[5] = 0xE1C000D0 | (rt << 16u) | (rt << 12u); // LDRD Rt, [Rt] + break; + case LDRH_LIT_A1: + buf[5] = 0xE1D000B0 | (rt << 16u) | (rt << 12u); // LDRH Rt, [Rt] + break; + case LDRSB_LIT_A1: + buf[5] = 0xE1D000D0 | (rt << 16u) | (rt << 12u); // LDRSB Rt, [Rt] + break; + case LDRSH_LIT_A1: + buf[5] = 0xE1D000F0 | (rt << 16u) | (rt << 12u); // LDRSH Rt, [Rt] + break; + } +#pragma clang diagnostic pop + return 24; + } +} + +static size_t sh_a32_rewrite_ldr_reg(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_type_t type) { + // LDR , [PC,+/-{, }]{!} + // ...... + uint32_t cond = SH_UTIL_GET_BITS_32(inst, 31, 28); + uint32_t rt = SH_UTIL_GET_BITS_16(inst, 15, 12); + uint32_t rt2 = rt + 1; + uint32_t rm = SH_UTIL_GET_BITS_16(inst, 3, 0); + uint32_t rx; // r0 - r3 + for (rx = 3;; --rx) + if (rx != rt && rx != rt2 && rx != rm) break; + + if (type == LDR_REG_PC_A1 && rt == 0xF) { + // Rt == PC + uint32_t ry; // r0 - r4 + for (ry = 4;; --ry) + if (ry != rt && ry != rt2 && ry != rm && ry != rx) break; + + buf[0] = 0x0A000000u | (cond << 28u); // B #0 + buf[1] = 0xEA000006; // B #24 + buf[2] = 0xE92D8000 | (1u << rx) | (1u << ry); // PUSH {Rx, Ry, PC} + buf[3] = 0xE59F0000 | (rx << 12u); // LDR Rx, [PC, #8] + buf[4] = 0xEA000000; // B #0 + buf[5] = pc; + buf[6] = + (inst & 0x0FF00FFFu) | 0xE0000000 | (rx << 16u) | (ry << 12u); // LDRxx Ry, [Rx],+/-Rm{, } + buf[7] = 0xE58D0008 | (ry << 12u); // STR Ry, [SP, #8] + buf[8] = 0xE8BD8000 | (1u << rx) | (1u << ry); // POP {Rx, Ry, PC} + return 36; + } else { + buf[0] = 0x0A000000u | (cond << 28u); // B #0 + buf[1] = 0xEA000005; // B #20 + buf[2] = 0xE52D0004 | (rx << 12u); // PUSH {Rx} + buf[3] = 0xE59F0000 | (rx << 12u); // LDR Rx, [PC, #0] + buf[4] = 0xEA000000; // B #0 + buf[5] = pc; + buf[6] = (inst & 0x0FF0FFFFu) | 0xE0000000 | (rx << 16u); // LDRxx Rt, [Rx],+/-Rm{, } + buf[7] = 0xE49D0004 | (rx << 12u); // POP {Rx} + return 32; + } +} + +size_t sh_a32_rewrite(uint32_t *buf, uint32_t inst, uintptr_t pc, sh_a32_rewrite_info_t *rinfo) { + sh_a32_type_t type = sh_a32_get_type(inst); + SH_LOG_INFO("a32 rewrite: type %d, inst %" PRIx32, type, inst); + + // We will only overwrite 4 to 8 bytes on A32, so PC cannot be in the coverage. + // In this case, the add/sub/mov/ldr_reg instruction does not need to consider + // the problem of PC in the coverage area when rewriting. + + if (type == B_A1 || type == BX_A1 || type == BL_IMM_A1 || type == BLX_IMM_A2) + return sh_a32_rewrite_b(buf, inst, pc, type, rinfo); + else if (type == ADD_REG_A1 || type == ADD_REG_PC_A1 || type == SUB_REG_A1 || type == SUB_REG_PC_A1) + return sh_a32_rewrite_add_or_sub(buf, inst, pc); + else if (type == ADR_A1 || type == ADR_A2) + return sh_a32_rewrite_adr(buf, inst, pc, type, rinfo); + else if (type == MOV_REG_A1 || type == MOV_REG_PC_A1) + return sh_a32_rewrite_mov(buf, inst, pc); + else if (type == LDR_LIT_A1 || type == LDR_LIT_PC_A1 || type == LDRB_LIT_A1 || type == LDRD_LIT_A1 || + type == LDRH_LIT_A1 || type == LDRSB_LIT_A1 || type == LDRSH_LIT_A1) + return sh_a32_rewrite_ldr_lit(buf, inst, pc, type, rinfo); + else if (type == LDR_REG_A1 || type == LDR_REG_PC_A1 || type == LDRB_REG_A1 || type == LDRD_REG_A1 || + type == LDRH_REG_A1 || type == LDRSB_REG_A1 || type == LDRSH_REG_A1) + return sh_a32_rewrite_ldr_reg(buf, inst, pc, type); + else { + // IGNORED + buf[0] = inst; + return 4; + } +} + +size_t sh_a32_absolute_jump(uint32_t *buf, uintptr_t addr) { + buf[0] = 0xE51FF004; // LDR PC, [PC, #-4] + buf[1] = addr; + return 8; +} + +size_t sh_a32_relative_jump(uint32_t *buf, uintptr_t addr, uintptr_t pc) { + buf[0] = 0xEA000000 | (((addr - pc) & 0x03FFFFFFu) >> 2u); // B