Skip to content

Appendix B: Smali Quick Reference

Usage: Keep this open alongside your editor when writing or reviewing smali hooks. Every instruction, type descriptor, and pattern template you need is on this page.


Every type in smali uses a single-character code (primitives) or L-prefixed descriptor (objects). Arrays prepend [.

Java TypeSmaliWidthNotes
voidVReturn type only
booleanZ1 register0x0 = false, 0x1 = true
byteB1 register
shortS1 register
charC1 register
intI1 register
longJ2 registersRegister pair: vN and vN+1
floatF1 registerIEEE 754 hex literal
doubleD2 registersRegister pair: vN and vN+1
Java TypeSmali Notation
StringLjava/lang/String;
ObjectLjava/lang/Object;
BundleLandroid/os/Bundle;
BitmapLandroid/graphics/Bitmap;
LocationLandroid/location/Location;
ImageProxyLandroidx/camera/core/ImageProxy;
SensorEventLandroid/hardware/SensorEvent;
ContextLandroid/content/Context;
IntentLandroid/content/Intent;
Java TypeSmali Notation
int[][I
byte[][B
float[][F
String[][Ljava/lang/String;
Object[][Ljava/lang/Object;
int[][][[I
Lcom/example/Foo;->process(Ljava/lang/String;IZ)Landroid/graphics/Bitmap;

Reads as: com.example.Foo.process(String, int, boolean) returning Bitmap.


These are the instructions you will actually use when writing hooks. The full Dalvik instruction set has 200+ opcodes; you need roughly 15.

InstructionWhat It DoesExample
invoke-virtualCall an instance method through virtual dispatch (normal method call)invoke-virtual {p0, v0}, Lcom/example/Foo;->bar(I)V
invoke-staticCall a static method (no receiver object)invoke-static {v1}, Lcom/hook/Intercept;->transform(Landroid/graphics/Bitmap;)Landroid/graphics/Bitmap;
invoke-interfaceCall a method on an interface referenceinvoke-interface {v0}, Landroidx/camera/core/ImageProxy;->close()V
invoke-directCall a constructor (<init>) or private methodinvoke-direct {p0}, Ljava/lang/Object;-><init>()V

Arguments go in the braces. For instance methods, the first argument is the receiver (this). For static methods, all arguments are explicit parameters.

InstructionWhat It DoesExample
move-result-object vNCapture an object returned by the preceding invoke-*move-result-object v4
move-result vNCapture a primitive (int, boolean, float) returned by the preceding invoke-*move-result v0

Must appear immediately after the invoke-* instruction. Omitting this when the return value is needed causes a VerifyError.

InstructionWhat It DoesExample
const-string vN, "text"Load a string literal into a registerconst-string v0, "HookDebug"
const/4 vN, 0xHLoad a 4-bit signed int (-8 to 7)const/4 v0, 0x1 (boolean true)
const/16 vN, 0xHHHHLoad a 16-bit signed intconst/16 v0, 0x00ff
const/high16 vN, 0xHHHH0000Load a 32-bit value with only high 16 bits setconst/high16 v0, 0x7f0b0000 (resource ID)
InstructionWhat It DoesExample
return-voidReturn from a void methodreturn-void
return vNReturn a primitive valuereturn v0
return-object vNReturn an object referencereturn-object v1
InstructionWhat It DoesExample
if-eqz vN, :labelBranch to :label if vN == 0 (or null)if-eqz v0, :skip_hook
if-nez vN, :labelBranch to :label if vN != 0 (or non-null)if-nez v0, :has_value
goto :labelUnconditional jumpgoto :end
InstructionWhat It DoesExample
nopDo nothing (1 code unit). Used to neutralize instructions without shifting offsetsReplace if-nez v0, :fail with nop

DirectiveDeclaresParameter Registers
.registers NTotal register count (locals + params)Included in N
.locals NLocal register count onlyAdded automatically on top of N

Always use .locals in code you write. Bumping .registers shifts parameter register assignments and silently breaks existing code. Bumping .locals adds local slots without affecting p0, p1, etc.

In an instance method with .locals 3 and one parameter:

v0, v1, v2 = local variables (yours to use freely)
p0 = this (the receiver object)
p1 = first method parameter

In a static method (no this):

v0, v1, v2 = local variables
p0 = first method parameter
p1 = second method parameter

Under .registers N, parameter registers are the last slots. For an instance method with signature (Landroid/os/Bundle;)V and .registers 4:

v0 = local 0
v1 = local 1
v2 = p0 = this
v3 = p1 = Bundle parameter

Under .locals N, you do not need to calculate. v0 through vN-1 are locals, and p0 through pM are params. The runtime handles the mapping.

Bump .locals by 1 when your injected code needs a scratch register and all existing locals are occupied. Example:

# BEFORE: .locals 2 -- v0 and v1 are in use
.method public doWork()V
.locals 2
# AFTER: .locals 3 -- v2 is now available as scratch
.method public doWork()V
.locals 3
const-string v2, "HookDebug"

Wide types (long, double) consume two consecutive registers. If you need a wide scratch, bump .locals by 2.


Copy-paste these templates. Replace the placeholder comments with your actual class names, method signatures, and register numbers.

Intercept a parameter before the method body runs. Used for: analyze(ImageProxy), onLocationResult(LocationResult), onSensorChanged(SensorEvent).

.method public {METHOD_NAME}({PARAM_TYPE}){RETURN_TYPE}
.locals {N}
# --- HOOK START ---
invoke-static {p1}, L{HOOK_CLASS};->{HOOK_METHOD}({PARAM_TYPE}){PARAM_TYPE}
move-result-object p1
# --- HOOK END ---
# ... original method body (now operates on replaced p1) ...
.end method

When the hook method returns void instead of replacing the parameter, omit move-result-object:

invoke-static {p1}, L{HOOK_CLASS};->{HOOK_METHOD}({PARAM_TYPE})V

Modify the return value of a method call inside a method body. Used for: getLastKnownLocation(), isFromMockProvider(), getString().

# Original call (do not modify these two lines)
invoke-virtual {vA}, L{TARGET_CLASS};->{TARGET_METHOD}({ARGS}){ORIG_RETURN}
move-result-object vB
# --- HOOK START ---
invoke-static {vB}, L{HOOK_CLASS};->{HOOK_METHOD}({ORIG_RETURN}){ORIG_RETURN}
move-result-object vB
# --- HOOK END ---

For primitive returns, use move-result instead of move-result-object.

Transform the value a method is about to return. Used for: toBitmap(), processed images, computed scores.

# ... method body computes result into vN ...
# --- HOOK START ---
invoke-static {vN}, L{HOOK_CLASS};->{HOOK_METHOD}({RETURN_TYPE}){RETURN_TYPE}
move-result-object vN
# --- HOOK END ---
return-object vN
.end method

Replace an entire method body to always return a fixed value. Used for: isRooted(), isValid(), isMockProvider().

.method public {METHOD_NAME}()Z
.locals 1
const/4 v0, 0x1 # 0x1 = true, 0x0 = false
return v0
.end method

ErrorCauseOne-Line Fix
VerifyErrorRegister out of bounds, type mismatch, or missing move-result-object after an invoke-* that returns a valueCheck register count in .locals; ensure every invoke with a non-void return is followed by the correct move-result variant
ClassNotFoundExceptionHook calls a class not present in the APK (runtime classes not injected)Confirm the hook-core runtime exists in one of the smali_classesN/ directories; re-run the patch-tool if missing
AbstractMethodErrorMethod signature in invoke-* does not match the actual method (wrong parameter types or return type)Compare your invoke signature against the real method in the decompiled smali character by character
NoSuchMethodErrorMethod name is misspelled or does not exist on the target classGrep the decoded APK for the exact method name and verify the declaring class
NullPointerException in hookThe register you passed to invoke-static was null (e.g., the app had no data at that point)Add a null guard: if-eqz vN, :skip before the hook call

Insert a Log.d() call to confirm your hook is reached:

const-string v0, "HookDebug"
const-string v1, "Hook fired"
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

Then monitor: adb logcat -s HookDebug