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
+