Module API
The Module global provides functions for inspecting loaded libraries and their symbols.
Module.find(lib_name)
Find the base address of a loaded library.
local base = Module.find("libc.so")
if base then
print(string.format("libc.so loaded at: 0x%x", base))
else
print("libc.so not loaded")
end
-- Also works with partial names
local flutter = Module.find("libflutter.so")
Parameters:
lib_name- Library name (can be partial, e.g., “libc.so” or “flutter”)
Returns: Base address as integer, or nil if not found
Module.list()
Get a string listing all loaded libraries.
local libs = Module.list()
print(libs)
Returns: Newline-separated string of all loaded .so files with their addresses
Module.exports(lib_name)
Get exported symbols from a library’s .dynsym section (dynamic symbol table).
local exports = Module.exports("libc.so")
if exports then
for _, sym in ipairs(exports) do
print(string.format("%s: 0x%x", sym.name, sym.offset))
end
end
-- Find specific export
for _, sym in ipairs(Module.exports("libc.so")) do
if sym.name == "malloc" then
print(string.format("malloc offset: 0x%x", sym.offset))
break
end
end
Parameters:
lib_name- Library name
Returns: Table of symbols, each with:
name- Symbol nameoffset- Offset from library base
Note: This only returns publicly exported symbols. For internal/static symbols, use Module.symbols().
Module.symbols(lib_name)
Get all symbols from a library’s .symtab section (full symbol table).
This includes internal/static symbols that are not exported via .dynsym. Useful for hooking internal functions in system binaries like linker64.
-- Get linker's internal symbols (includes do_dlopen, call_constructors, etc.)
local symbols = Module.symbols("linker64")
if symbols then
print(string.format("Found %d symbols", #symbols))
for _, sym in ipairs(symbols) do
if sym.name:find("do_dlopen") then
print(string.format("Found: %s at 0x%x", sym.name, sym.offset))
end
end
end
Parameters:
lib_name- Library name
Returns: Table of symbols, each with:
name- Symbol nameoffset- Offset from library base
Returns nil if:
- Library not found
- Binary is stripped (no
.symtabsection) - Not a valid ELF file
Difference: exports() vs symbols()
| Feature | Module.exports() | Module.symbols() |
|---|---|---|
| ELF Section | .dynsym | .symtab |
| Symbol Types | Public/exported only | All symbols (including static/internal) |
| Availability | Always available | Only if not stripped |
| Use Case | Hook public APIs | Hook internal functions |
| Performance | Fast (smaller table) | Slower (larger table) |
Example - Finding linker internals:
-- exports() won't find internal linker functions
local exports = Module.exports("linker64")
-- do_dlopen NOT in exports (it's internal)
-- symbols() includes everything
local symbols = Module.symbols("linker64")
for _, s in ipairs(symbols) do
if s.name:find("do_dlopen") then
-- Found! __dl__Z9do_dlopenPKciPK17android_dlextinfoPKv
print(s.name, string.format("0x%x", s.offset))
end
end
Examples
Hook library load
-- Wait for a library to be loaded by hooking linker
local linker_syms = Module.symbols("linker64")
for _, sym in ipairs(linker_syms) do
if sym.name:find("do_dlopen") then
hook("linker64", sym.offset, {
onEnter = function(args)
local path = Memory.readString(args[0])
if path then
print("[dlopen] " .. path)
end
end
})
break
end
end
Calculate absolute address
local base = Module.find("libtarget.so")
local exports = Module.exports("libtarget.so")
for _, sym in ipairs(exports) do
if sym.name == "target_function" then
local abs_addr = base + sym.offset
print(string.format("Absolute address: 0x%x", abs_addr))
break
end
end
Search for symbol by pattern
local symbols = Module.symbols("libflutter.so") or Module.exports("libflutter.so")
if symbols then
for _, sym in ipairs(symbols) do
if sym.name:find("ssl") or sym.name:find("verify") then
print(string.format("%s: 0x%x", sym.name, sym.offset))
end
end
end
Notes
-
Stripped binaries: Most release binaries are stripped and won’t have
.symtab. In this case,Module.symbols()returnsnil. Fall back toModule.exports()or pattern scanning. - Symbol names: C++ symbols are mangled. Use pattern matching:
if sym.name:find("do_dlopen") then -- matches __dl__Z9do_dlopenPKc... -
Linker symbols: Android’s
linker64is typically not stripped and contains useful internal symbols likedo_dlopen,call_constructors, etc. - Performance:
Module.symbols()can return thousands of symbols. Cache the result if calling multiple times.