The nmmvm is the core dex virtual machine implementation. The entry point for execution is the vmInterpret function.
To implement custom hardening or testing, you interact with two primary structures:
vmCode: Provides the execution context, including instructions (insns), instruction size (insnsSize), registers (regs), register type flags (reg_flags), and exception handlers (triesHandlers).vmResolver: A collection of function pointers used to resolve symbols at runtime (fields, methods, types, classes, and constant strings).
By providing custom implementations for these two structures, you can control how the VM interprets bytecode and resolves symbols.
jvalue vmInterpret(
JNIEnv *env,
const vmCode *code,
const vmResolver *dvmResolver
);
typedef struct {
const u2 *insns; // instructions
const u4 insnsSize; // instruction size
regptr_t *regs; // registers
u1 *reg_flags; // register data type flags (e.g., object marker)
const u1 *triesHandlers; // exception table
} vmCode;
typedef struct {
const vmField *(*dvmResolveField)(JNIEnv *env, u4 idx, bool isStatic);
const vmMethod *(*dvmResolveMethod)(JNIEnv *env, u4 idx, bool isStatic);
const char *(*dvmResolveTypeUtf)(JNIEnv *env, u4 idx);
jclass (*dvmResolveClass)(JNIEnv *env, u4 idx);
jclass (*dvmFindClass)(JNIEnv *env, const char *type);
jstring (*dvmConstantString)(JNIEnv *env, u4 idx);
} vmResolver;