diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt new file mode 100644 index 0000000..933c53e --- /dev/null +++ b/app/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.10.2) + + +include_directories( + src/main/jni/dobby +) +enable_language(C ASM) + + +add_library( + pmm + SHARED + src/main/jni/main.cpp +) + +include_directories(dobby) +add_library(local_dobby STATIC IMPORTED) +set_target_properties(local_dobby PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/libs/${ANDROID_ABI}/libdobby.a) + + +find_library( + log-lib + log +) + +find_library(android-lib android) +target_link_libraries( + pmm + local_dobby + ${log-lib} + ${android-lib} +) diff --git a/app/build.gradle b/app/build.gradle index b28183d..9cae024 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,6 +27,15 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + externalNativeBuild { + defaultConfig.externalNativeBuild.cmake { + abiFilters 'arm64-v8a' + } + cmake { + path file('CMakeLists.txt') + version '3.22.1' + } + } kotlinOptions { jvmTarget = '1.8' } diff --git a/app/libs/arm64-v8a/libdobby.a b/app/libs/arm64-v8a/libdobby.a new file mode 100644 index 0000000..f3e1356 Binary files /dev/null and b/app/libs/arm64-v8a/libdobby.a differ diff --git a/app/libs/arm64-v8a/libdobby.so b/app/libs/arm64-v8a/libdobby.so new file mode 100644 index 0000000..a7e5fb5 Binary files /dev/null and b/app/libs/arm64-v8a/libdobby.so differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8062d6e..4f15509 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ + android:value="Emulate Every AIC Card" /> diff --git a/app/src/main/java/moe/tqlwsl/aicemu/xp.java b/app/src/main/java/moe/tqlwsl/aicemu/xp.java index 10ab1dd..855dbc8 100644 --- a/app/src/main/java/moe/tqlwsl/aicemu/xp.java +++ b/app/src/main/java/moe/tqlwsl/aicemu/xp.java @@ -1,7 +1,13 @@ package moe.tqlwsl.aicemu; +import android.app.Application; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.util.Log; +import java.util.List; + import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XC_MethodReplacement; @@ -10,41 +16,45 @@ import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_LoadPackage; public class xp implements IXposedHookLoadPackage { - private final String TAG = "AICEmu"; + private final String TAG = "AICEmu-Xposed"; + ClassLoader mclassloader = null; + Context mcontext = null; // from https://github.com/OLIET2357/HCEFUnlocker @Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable { + XposedBridge.log("In " + lpparam.packageName); - if (lpparam.packageName.equals("com.android.nfc")) { - XposedHelpers.findAndHookMethod("android.nfc.cardemulation.NfcFCardEmulation", lpparam.classLoader, - "isValidSystemCode", String.class, new XC_MethodReplacement() { - @Override - protected Object replaceHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable { - String systemCode = (String) param.args[0]; - if (systemCode == null) { - return false; - } + if (lpparam.packageName.equals("com.android.nfc")) { - if (systemCode.length() != 4) { - Log.e(TAG, "System Code " + systemCode + " is not a valid System Code."); - return false; - } - // check if the value is between "4000" and "4FFF" (excluding "4*FF") - if (!systemCode.startsWith("4") || systemCode.toUpperCase().endsWith("FF")) { - // Log.e(TAG, "System Code " + systemCode + " is not a valid System Code."); - // return false; - } - try { - Integer.parseInt(systemCode, 16); - } catch (NumberFormatException e) { - Log.e(TAG, "System Code " + systemCode + " is not a valid System Code."); - return false; - } - return true; - } - }); +// XposedHelpers.findAndHookMethod("android.nfc.cardemulation.NfcFCardEmulation", lpparam.classLoader, +// "isValidSystemCode", String.class, new XC_MethodReplacement() { +// @Override +// protected Object replaceHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable { +// String systemCode = (String) param.args[0]; +// if (systemCode == null) { +// return false; +// } +// +// if (systemCode.length() != 4) { +// Log.e(TAG, "System Code " + systemCode + " is not a valid System Code."); +// return false; +// } +// // check if the value is between "4000" and "4FFF" (excluding "4*FF") +// if (!systemCode.startsWith("4") || systemCode.toUpperCase().endsWith("FF")) { +// // Log.e(TAG, "System Code " + systemCode + " is not a valid System Code."); +// // return false; +// } +// try { +// Integer.parseInt(systemCode, 16); +// } catch (NumberFormatException e) { +// Log.e(TAG, "System Code " + systemCode + " is not a valid System Code."); +// return false; +// } +// return true; +// } +// }); XposedHelpers.findAndHookMethod("android.nfc.cardemulation.NfcFCardEmulation", lpparam.classLoader, "isValidNfcid2", String.class, new XC_MethodReplacement() { @@ -73,7 +83,70 @@ public class xp implements IXposedHookLoadPackage { } }); + XposedHelpers.findAndHookMethod("com.android.nfc.NfcApplication", + lpparam.classLoader, "onCreate", new XC_MethodHook() { + @Override + protected void beforeHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable { + XposedBridge.log("Inside com.android.nfc.NfcApplication#onCreate"); + super.beforeHookedMethod(param); + Application application = (Application) param.thisObject; + mcontext = application.getApplicationContext(); + XposedBridge.log("Got context"); + } + }); + + + XposedHelpers.findAndHookMethod("android.nfc.cardemulation.NfcFCardEmulation", + lpparam.classLoader, "isValidSystemCode", String.class, new XC_MethodHook() { + @Override + protected void afterHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable { + super.afterHookedMethod(param); + XposedBridge.log("Inside android.nfc.cardemulation.NfcFCardEmulation#isValidSystemCode"); + XposedBridge.log("Got classloader"); + String path = getSoPath(); + XposedBridge.log("So path = " + path); + int version = android.os.Build.VERSION.SDK_INT; + try { + if (!path.equals("")) { + XposedBridge.log("Start injecting libpmm.so"); + if (version >= 28) { + XposedHelpers.callMethod(Runtime.getRuntime(), "nativeLoad", path, mclassloader); + } else { + XposedHelpers.callMethod(Runtime.getRuntime(), "doLoad", path, mclassloader); + } + XposedBridge.log("Injected libpmm.so"); + } + } catch (Exception e) { + XposedBridge.log(e); + e.printStackTrace(); + } + + // Unlocker + param.setResult(true); + } + }); + XposedBridge.log("Hook succeeded!!!"); } } + + private String getSoPath() { + try { + String text = ""; + PackageManager pm = mcontext.getPackageManager(); + List pkgList = pm.getInstalledPackages(0); + if (pkgList.size() > 0) { + for (PackageInfo pi: pkgList) { + if (pi.applicationInfo.publicSourceDir.contains("moe.tqlwsl.aicemu")) { + text = pi.applicationInfo.publicSourceDir.replace("base.apk", "lib/arm64/libpmm.so"); + return text; + } + } + } + } catch (Exception e) { + XposedBridge.log(e); + e.printStackTrace(); + } + return ""; + } } diff --git a/app/src/main/jni/dobby/dobby.h b/app/src/main/jni/dobby/dobby.h new file mode 100644 index 0000000..745b798 --- /dev/null +++ b/app/src/main/jni/dobby/dobby.h @@ -0,0 +1,152 @@ +#ifndef dobby_h +#define dobby_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef uintptr_t addr_t; +typedef uint32_t addr32_t; +typedef uint64_t addr64_t; + +typedef void *dobby_dummy_func_t; +typedef void *asm_func_t; + +#if defined(__arm__) +typedef struct { + uint32_t dummy_0; + uint32_t dummy_1; + + uint32_t dummy_2; + uint32_t sp; + + union { + uint32_t r[13]; + struct { + uint32_t r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12; + } regs; + } general; + + uint32_t lr; +} DobbyRegisterContext; +#elif defined(__arm64__) || defined(__aarch64__) +#define ARM64_TMP_REG_NDX_0 17 + +typedef union _FPReg { + __int128_t q; + struct { + double d1; + double d2; + } d; + struct { + float f1; + float f2; + float f3; + float f4; + } f; +} FPReg; + +// register context +typedef struct { + uint64_t dmmpy_0; // dummy placeholder + uint64_t sp; + + uint64_t dmmpy_1; // dummy placeholder + union { + uint64_t x[29]; + struct { + uint64_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, + x23, x24, x25, x26, x27, x28; + } regs; + } general; + + uint64_t fp; + uint64_t lr; + + union { + FPReg q[32]; + struct { + FPReg q0, q1, q2, q3, q4, q5, q6, q7; + // [!!! READ ME !!!] + // for Arm64, can't access q8 - q31, unless you enable full floating-point register pack + FPReg q8, q9, q10, q11, q12, q13, q14, q15, q16, q17, q18, q19, q20, q21, q22, q23, q24, q25, q26, q27, q28, q29, + q30, q31; + } regs; + } floating; +} DobbyRegisterContext; +#elif defined(_M_IX86) || defined(__i386__) +typedef struct _RegisterContext { + uint32_t dummy_0; + uint32_t esp; + + uint32_t dummy_1; + uint32_t flags; + + union { + struct { + uint32_t eax, ebx, ecx, edx, ebp, esp, edi, esi; + } regs; + } general; + +} DobbyRegisterContext; +#elif defined(_M_X64) || defined(__x86_64__) +typedef struct { + uint64_t dummy_0; + uint64_t rsp; + + union { + struct { + uint64_t rax, rbx, rcx, rdx, rbp, rsp, rdi, rsi, r8, r9, r10, r11, r12, r13, r14, r15; + } regs; + } general; + + uint64_t dummy_1; + uint64_t flags; +} DobbyRegisterContext; +#endif + +#define install_hook_name(name, fn_ret_t, fn_args_t...) \ + static fn_ret_t fake_##name(fn_args_t); \ + static fn_ret_t (*orig_##name)(fn_args_t); \ + /* __attribute__((constructor)) */ static void install_hook_##name(void *sym_addr) { \ + DobbyHook(sym_addr, (dobby_dummy_func_t)fake_##name, (dobby_dummy_func_t *)&orig_##name); \ + return; \ + } \ + fn_ret_t fake_##name(fn_args_t) + +// memory code patch +int DobbyCodePatch(void *address, uint8_t *buffer, uint32_t buffer_size); + +// function inline hook +int DobbyHook(void *address, dobby_dummy_func_t replace_func, dobby_dummy_func_t *origin_func); + +// dynamic binary instruction instrument +// for Arm64, can't access q8 - q31, unless enable full floating-point register pack +typedef void (*dobby_instrument_callback_t)(void *address, DobbyRegisterContext *ctx); +int DobbyInstrument(void *address, dobby_instrument_callback_t pre_handler); + +// destroy and restore code patch +int DobbyDestroy(void *address); + +const char *DobbyGetVersion(); + +// symbol resolver +void *DobbySymbolResolver(const char *image_name, const char *symbol_name); + +// import table replace +int DobbyImportTableReplace(char *image_name, char *symbol_name, dobby_dummy_func_t fake_func, + dobby_dummy_func_t *orig_func); + +// for arm, Arm64, try use b xxx instead of ldr absolute indirect branch +// for x86, x64, always use absolute indirect jump +void dobby_enable_near_branch_trampoline(); +void dobby_disable_near_branch_trampoline(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/app/src/main/jni/main.cpp b/app/src/main/jni/main.cpp new file mode 100644 index 0000000..182a1e7 --- /dev/null +++ b/app/src/main/jni/main.cpp @@ -0,0 +1,83 @@ + +#include "main.h" +#include + +char buff[30]; +char pmm_str[30]; +char target_pmm[8] = {0x00, 0xf1, 0x00, 0x00, 0x00, 0x01, 0x43, 0x00}; + +void *new_func(u_int8_t a1, u_int8_t *a2, int a3) { + + __android_log_print(6, "AICEmu-pmmtool", "hook _Z23nfa_dm_check_set_confighPhb arg0->%x arg1->%x", a1, a2); + + // if (a1 == 0x1d) { // hardcoded arg pattern + // 40 0a [syscode] [IDm] 53 02 01 00 55 01 01 51 08 [PMm] + // if (a1 == 0x1b) { // another type hardcoded arg pattern + // 40 12 [syscode] [IDm] [PMm] 53 02 01 00 55 01 01 + + // handmade hexdump + for (int i = 0x0; i < 0x10; ++i) + sprintf(buff + i * 3, "%02x ", *(char *)(a2 + i)); + __android_log_print(6, "AICEmu-pmmtool", "[%x]: %s", a2, buff); + for (int i = 0x0; i < 0x10; ++i) + sprintf(buff + i * 3, "%02x ", *(char *)(a2 + 0x10 + i)); + __android_log_print(6, "AICEmu-pmmtool", "[%x]: %s", a2 + 0x10, buff); + + // look for 51 08 (set pmm command) for type 0x1d + for (int i = 0x0; i < 0x20; ++i) { + if (*(char *)(a2 + i) == 0x51 && *(char *)(a2 + i + 1) == 0x08) { + + for (int j = 0; j < 8; ++j) + sprintf(pmm_str + j * 3, "%02x ", *(char *)(a2 + i + 2 + j)); + __android_log_print(6, "AICEmu-pmmtool", "[1] old PMm: %s", pmm_str); + + // set + for (int j = 0; j < 8; ++j) + *(char *)(a2 + i + 2 + j) = target_pmm[j]; + + for (int j = 0; j < 8; ++j) + sprintf(pmm_str + j * 3, "%02x ", *(char *)(a2 + i + 2 + j)); + __android_log_print(6, "AICEmu-pmmtool", "[1] new PMm: %s", pmm_str); + } + } + + // look for FF FF FF FF FF FF FF FF (pmm itself) + for (int i = 0x0; i < 0x20; ++i) { + if (*(char *)(a2 + i) == 0xff && *(char *)(a2 + i + 1) == 0xff + && *(char *)(a2 + i + 2) == 0xff && *(char *)(a2 + i + 3) == 0xff + && *(char *)(a2 + i + 4) == 0xff && *(char *)(a2 + i + 5) == 0xff + && *(char *)(a2 + i + 6) == 0xff && *(char *)(a2 + i + 7) == 0xff) { + + for (int j = 0; j < 8; ++j) + sprintf(pmm_str + j * 3, "%02x ", *(char *)(a2 + i + j)); + __android_log_print(6, "AICEmu-pmmtool", "[2] old PMm: %s", pmm_str); + + // set + for (int j = 0; j < 8; ++j) + *(char *)(a2 + i + j) = target_pmm[j]; + + for (int j = 0; j < 8; ++j) + sprintf(pmm_str + j * 3, "%02x ", *(char *)(a2 + i + j)); + __android_log_print(6, "AICEmu-pmmtool", "[2] new PMm: %s", pmm_str); + } + } + //} + //__android_log_print(6, "pmmtool", "load old func"); + void *result = old_func(a1, a2, a3); + //__android_log_print(6, "pmmtool", "hook result -> %x", result); + return result; +} + +jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { + __android_log_print(6, "AICEmu-pmmtool", "Inside JNI_OnLoad"); + JNIEnv *env = nullptr; + if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) == JNI_OK) { + //void *func_addr = DobbySymbolResolver("libnfc-nci.so", "_Z23nfa_dm_check_set_confighPhb"); + void *func_addr = DobbySymbolResolver(NULL, "_Z23nfa_dm_check_set_confighPhb"); + __android_log_print(6, "AICEmu-pmmtool", "_Z23nfa_dm_check_set_confighPhb addr->%x", func_addr); + DobbyHook(func_addr, (void *) new_func, (void **) &old_func); + __android_log_print(6, "AICEmu-pmmtool", "Dobby hooked"); + return JNI_VERSION_1_6; + } + return 0; +} diff --git a/app/src/main/jni/main.h b/app/src/main/jni/main.h new file mode 100644 index 0000000..631b8cc --- /dev/null +++ b/app/src/main/jni/main.h @@ -0,0 +1,18 @@ +#ifndef MAIN_H +#define MAIN_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void *(*old_func)(u_int8_t, u_int8_t *, int) = nullptr; +#endif //MAIN_H +