Hook API
Renef’s hook engine writes to memory via
/proc/self/mem(pwrite) instead ofmprotect. This bypasses SELinux/seccomp restrictions, maintains W^X compliance, and leaves no permission changes visible in/proc/self/maps. Falls back tomprotectautomatically if/proc/self/memis unavailable.
hook(library, offset, callbacks)
Hook a native function by library name and offset.
hook("libc.so", 0x12340, {
onEnter = function(args)
print("[+] malloc called")
print(string.format(" size: 0x%x", args[0]))
-- Modify argument
args[0] = 0x200
end,
onLeave = function(retval)
print(string.format("[-] malloc returning: 0x%x", retval))
-- Modify return value
return retval + 0x100
end
})
Parameters:
library- Library name (e.g., “libc.so”)offset- Offset from library base (hex number)callbacks- Table withonEnterand/oronLeavefunctions, and optionalcallerfield
Callbacks table fields:
| Field | Type | Description |
|---|---|---|
onEnter | function | Called before the original function |
onLeave | function | Called after the original function returns |
caller | string or table | Filter: only hook calls from this library (enables PLT/GOT hooking) |
-- Hook only calls to malloc from libnative.so (PLT/GOT mode)
hook("libc.so", malloc_offset, {
caller = "libnative.so",
onEnter = function(args)
print(string.format("malloc(0x%x) from libnative.so", args[0]))
end
})
-- Hook calls from multiple libraries
hook("libc.so", open_offset, {
caller = {"libnative.so", "libutils.so"},
onEnter = function(args) ... end
})
Without
caller, a trampoline hook is used (patches the target function directly). Withcaller, a PLT/GOT hook is used (patches the caller’s GOT entries only).
onEnter arguments:
args- Table with function arguments (args[0], args[1], … args[7] for x0-x7)- Arguments can be modified by assignment
onLeave arguments:
retval- Return value from function (x0 register)- Return a value to replace the original return value
Stack Trace in Hooks
Use Thread.backtrace() inside hook callbacks to see who called the hooked function:
hook("libc.so", fopen_offset, {
onEnter = function(args)
local path = Memory.readString(args[0])
print("fopen(" .. tostring(path) .. ")")
print(Thread.backtrace())
end
})
hook(class, method, signature, callbacks)
Hook a Java method via JNI.
Android 10+ Supported: Java hooks with
onEnterandonLeavecallbacks work on Android 10 (API 29) and later. Tested and verified on Android 10, 11, 12, 13, 14, 15, and 16.
Nested Hooks Supported (v0.2.2+): Java hooks can be nested recursively with proper call stack tracking. Hook callbacks can safely call other hooked methods.
hook("com/example/MainActivity", "getSecret", "(Ljava/lang/String;)Ljava/lang/String;", {
onEnter = function(args)
print("[+] MainActivity.getSecret() called")
print(string.format(" this: 0x%x", args[1]))
print(string.format(" param1: 0x%x", args[2]))
end,
onLeave = function(retval)
print(string.format("[*] Original return: 0x%x", retval.raw))
if retval.value then
print(" String value: " .. retval.value)
end
-- Create and return a new string
local newStr = Jni.newStringUTF("Modified!")
return newStr
end
})
Parameters:
class- Class name with/separators (e.g., “com/example/MainActivity”)method- Method namesignature- JNI signature (e.g., “(Ljava/lang/String;)Ljava/lang/String;”)callbacks- Table withonEnterand/oronLeavefunctions
Java hook onEnter — args table:
| Key | Type | Description |
|---|---|---|
args[0] | integer | ArtMethod pointer (internal, not useful to scripts) |
args[1] | integer | this pointer (instance methods) or first parameter (static methods) |
args[2..7] | integer | Method parameters as raw pointers |
args.class | string | Class name |
args.method | string | Method name |
args.signature | string | JNI signature |
args.isStatic | boolean | Whether method is static |
args.skip | boolean | Set to true to skip calling the original method entirely |
Arguments are modifiable: args[2] = newValue will change the parameter passed to the original method.
Java hook onLeave — retval table:
| Field | Type | Description |
|---|---|---|
retval.raw | integer | Raw return value (x0 register) |
retval.value | string or nil | Decoded string content (only for methods returning String) |
args.skip — Skip Original Method:
Setting args.skip = true in onEnter prevents the original Java method from being called. The hook returns immediately with a zero value, and onLeave still runs normally.
This is essential for void methods that signal failure by throwing exceptions (e.g., checkServerTrusted). Skipping the original avoids both the exception and potential ART stack walk crashes when multiple hooked methods are nested on the same call stack.
hook("javax/net/ssl/X509TrustManager", "checkServerTrusted",
"([Ljava/security/cert/X509Certificate;Ljava/lang/String;)V", {
onEnter = function(args)
args.skip = true -- Don't call original, method returns cleanly
print("[*] checkServerTrusted bypassed")
end
})
Modifying return values (onLeave):
| Return from onLeave | Effect |
|---|---|
nil (or no return) | Original return value unchanged |
| integer | Sets x0 register directly (e.g., return 1 for true, return 0 for false) |
{__jni_type="string", value="..."} | Creates a new Java String and returns it |
{__jni_type="int", value=N} | Sets x0 to N |
{__jni_type="boolean", value=true} | Sets x0 to 1 or 0 |
Jni.newStringUTF("...") | Returns raw pointer to new Java String |
-- Return a modified string
onLeave = function(retval)
return Jni.newStringUTF("Modified!")
end
-- Return a boolean (for verify() methods)
onLeave = function(retval)
return 1 -- true
end
Jni Namespace
The Jni global provides JNI helper functions for working with Java objects in hooks.
Jni.newStringUTF(str)
Create a new Java String from a Lua string. Returns a raw object pointer that can be used as a return value in Java hooks.
local jstr = Jni.newStringUTF("Hello from Lua!")
return jstr -- Use in onLeave to replace return value
Parameters:
str- Lua string to convert
Returns: Raw object pointer (integer) for the new Java String
Jni.getStringUTF(ref)
Get the content of a Java String as a Lua string.
local content = Jni.getStringUTF(args[1])
if content then
print("String content: " .. content)
end
Parameters:
ref- Raw object pointer or JNI reference to a String
Returns: Lua string with the content, or nil if invalid
Jni.getStringLength(ref)
Get the length of a Java String.
local len = Jni.getStringLength(args[1])
print("String length: " .. len)
Parameters:
ref- Raw object pointer or JNI reference to a String
Returns: Integer length of the string
Jni.deleteGlobalRef(ref)
Delete a global JNI reference to free memory.
Jni.deleteGlobalRef(globalRef)
Parameters:
ref- Global reference to delete
JNI Signature Reference
Common JNI type signatures:
| Java Type | JNI Signature |
|---|---|
void | V |
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
String | Ljava/lang/String; |
Object | Ljava/lang/Object; |
int[] | [I |
String[] | [Ljava/lang/String; |
Method signature format: (ParameterTypes)ReturnType
Examples:
-- void method()
"()V"
-- int method(int x, int y)
"(II)I"
-- String method(String s)
"(Ljava/lang/String;)Ljava/lang/String;"
-- void method(int[] arr, boolean flag)
"([IZ)V"
-- Object method(String name, int count)
"(Ljava/lang/String;I)Ljava/lang/Object;"