barebone: Read only the kernel module on the host

Walking the whole module list and every module's exports over the
wire cost the better part of a minute, and served nobody: the host
needs the allocator's two symbols, and the rest is for the agent,
which can read it in the guest for free. Pass the list head along
instead.
This commit is contained in:
Ole André Vadla Ravnås committed 2026-09-17 15:16:26 +02:00
1 parent 746a297542
commit d30e8fbbb2
4 files changed
+42 -35

No files matched your search

+6 -1
View File
@@ -30,6 +30,7 @@ namespace Frida.Barebone {
private Allocator allocator;
private Gee.List<ModuleInfo> kernel_modules;
private Gee.List<SymbolInfo> kernel_symbols;
private Gee.Map<string, uint64?> kernel_notes;
private KernelFlavor flavor;
private Allocation elf_allocation;
@@ -54,7 +55,8 @@ namespace Frida.Barebone {
public AgentConnection (BareboneInjectedAgentConfig agent_config, BareboneImageConfig? image_config,
BareboneKernelKind kernel_kind, KernelRelocation? relocation, uint64 kernel_base, Machine machine,
Allocator allocator, Gee.List<ModuleInfo> kernel_modules, Gee.List<SymbolInfo> kernel_symbols) {
Allocator allocator, Gee.List<ModuleInfo> kernel_modules, Gee.List<SymbolInfo> kernel_symbols,
Gee.Map<string, uint64?> kernel_notes) {
this.agent_config = agent_config;
this.image_config = image_config;
this.kernel_kind = kernel_kind;
@@ -64,6 +66,7 @@ namespace Frida.Barebone {
this.allocator = allocator;
this.kernel_modules = kernel_modules;
this.kernel_symbols = kernel_symbols;
this.kernel_notes = kernel_notes;
}
public async void open (Cancellable? cancellable) throws Error, IOError {
@@ -275,6 +278,8 @@ namespace Frida.Barebone {
// What a kernel does not say about itself and the agent cannot work out from where it
// runs: the host looks once and passes on what it found.
config_builder.open (new VariantType ("a(st)"));
foreach (var e in kernel_notes.entries)
config_builder.add ("(st)", e.key, e.value);
if (xnu_layout != null) {
config_builder.add ("(st)", "process.number", xnu_layout.number_offset);
config_builder.add ("(st)", "process.name", xnu_layout.name_offset);
+3 -1
View File
@@ -230,6 +230,7 @@ namespace Frida {
Gee.List<Barebone.ModuleInfo> kernel_modules = new Gee.ArrayList<Barebone.ModuleInfo> ();
Gee.List<Barebone.SymbolInfo> kernel_symbols = new Gee.ArrayList<Barebone.SymbolInfo> ();
var kernel_notes = new Gee.HashMap<string, uint64?> ();
if (config.kernel == WIN9X) {
var win9x_layout = yield Barebone.collect_win9x_layout (machine, cancellable);
kernel_modules = win9x_layout.modules;
@@ -238,6 +239,7 @@ namespace Frida {
var winnt_layout = yield Barebone.collect_winnt_layout (machine, cancellable);
kernel_modules = winnt_layout.modules;
kernel_symbols = winnt_layout.symbols;
kernel_notes[Barebone.MODULE_LIST_NOTE] = winnt_layout.module_list;
} else if (config.kernel == LINUX) {
var kernel = image as BareboneLinuxKernelConfig;
if (kernel == null)
@@ -291,7 +293,7 @@ namespace Frida {
var agent_config = config.agent as BareboneInjectedAgentConfig;
if (agent_config != null) {
agent_connection = new Barebone.AgentConnection (agent_config, config.image, config.kernel, relocation,
kernel_base, machine, allocator, kernel_modules, kernel_symbols);
kernel_base, machine, allocator, kernel_modules, kernel_symbols, kernel_notes);
agent_connection.progress.connect ((status, fraction) => connecting (status, 0.2 + 0.8 * fraction));
yield agent_connection.open (cancellable);
}
+30 -30
View File
@@ -9,15 +9,16 @@ namespace Frida.Barebone {
Anchors anchors = yield find_anchors (machine, shape, cancellable);
foreach (LoadedModule module in yield read_loaded_modules (machine, anchors.module_list, shape, cancellable)) {
LoadedModule? kernel = yield read_first_loaded_module (machine, anchors.module_list, shape, cancellable);
if (kernel != null) {
modules.add (new ModuleInfo () {
name = module.name,
name = kernel.name,
version = "",
offset = module.base_address,
size = module.size,
offset = kernel.base_address,
size = kernel.size,
});
yield add_export_symbols (machine, module, symbols, cancellable);
yield add_export_symbols (machine, kernel, symbols, cancellable);
}
if (anchors.process_list_head != 0) {
@@ -29,7 +30,7 @@ namespace Frida.Barebone {
});
}
return new WinNtLayout (modules, symbols);
return new WinNtLayout (modules, symbols, anchors.module_list);
}
public sealed class WinNtLayout : Object {
@@ -43,8 +44,13 @@ namespace Frida.Barebone {
construct;
}
public WinNtLayout (Gee.List<ModuleInfo> modules, Gee.List<SymbolInfo> symbols) {
Object (modules: modules, symbols: symbols);
public uint64 module_list {
get;
construct;
}
public WinNtLayout (Gee.List<ModuleInfo> modules, Gee.List<SymbolInfo> symbols, uint64 module_list) {
Object (modules: modules, symbols: symbols, module_list: module_list);
}
}
@@ -152,15 +158,14 @@ namespace Frida.Barebone {
}
private static string image_pointer_register (Debugger debugger) {
return (debugger.arch == Frida.TargetArch.ARM64) ? KERNEL_PCR_REGISTER : KERNEL_STACK_REGISTER;
return (debugger.arch == TargetArch.ARM64) ? KERNEL_PCR_REGISTER : KERNEL_STACK_REGISTER;
}
private static async uint64 find_image_below_pointers (Machine machine, uint64 seed, Shape shape,
Gee.Set<uint64?> visited, Cancellable? cancellable) throws Error, IOError {
Debugger debugger = machine.debugger;
uint64 seed_page = seed - (seed % PCR_SCAN_SIZE);
Buffer page = debugger.make_buffer (yield debugger.read_byte_array (seed_page, PCR_SCAN_SIZE,
cancellable));
Buffer page = debugger.make_buffer (yield debugger.read_byte_array (seed_page, PCR_SCAN_SIZE, cancellable));
for (size_t offset = 0; offset != PCR_SCAN_SIZE; offset += shape.pointer_size) {
uint64 candidate = read_pointer (page, offset, shape);
@@ -371,32 +376,26 @@ namespace Frida.Barebone {
return head;
}
private static async Gee.List<LoadedModule> read_loaded_modules (Machine machine, uint64 head, Shape shape,
private static async LoadedModule? read_first_loaded_module (Machine machine, uint64 head, Shape shape,
Cancellable? cancellable) throws Error, IOError {
var modules = new Gee.ArrayList<LoadedModule> ();
Debugger debugger = machine.debugger;
uint64 entry = read_pointer (debugger.make_buffer (yield debugger.read_byte_array (head, shape.pointer_size,
cancellable)), 0, shape);
var visited = new Gee.HashSet<uint64?> ((n) => (uint) (*(uint64 *) n), (a, b) => *(uint64 *) a == *(uint64 *) b);
while (entry != head && is_kernel_address (entry, shape) && !visited.contains (entry)) {
visited.add (entry);
if (entry == head || !is_kernel_address (entry, shape))
return null;
Buffer e = debugger.make_buffer (yield debugger.read_byte_array (entry, shape.table_entry_size, cancellable));
Buffer e = debugger.make_buffer (yield debugger.read_byte_array (entry, shape.table_entry_size, cancellable));
uint64 base_address = read_pointer (e, shape.dll_base, shape);
if (is_kernel_address (base_address, shape)) {
modules.add (new LoadedModule () {
base_address = base_address,
size = e.read_uint32 (shape.image_size),
name = yield read_unicode_string (machine, e, shape.base_name, shape, cancellable),
});
}
uint64 base_address = read_pointer (e, shape.dll_base, shape);
if (!is_kernel_address (base_address, shape))
return null;
entry = read_pointer (e, FORWARD_LINK_OFFSET, shape);
}
return modules;
return new LoadedModule () {
base_address = base_address,
size = e.read_uint32 (shape.image_size),
name = yield read_unicode_string (machine, e, shape.base_name, shape, cancellable),
};
}
private class LoadedModule {
@@ -536,6 +535,7 @@ namespace Frida.Barebone {
private const uint64 EXCEPTION_LEVEL_MASK = 3;
private const uint64 KERNEL_EXCEPTION_LEVEL = 1;
public const string MODULE_LIST_NOTE = "kernel.modules";
public const string PROCESS_LIST_HEAD = "PsActiveProcessHead";
private const size_t PROCESS_LIST_HEAD_OFFSET = 0x50;
+3 -3
View File
@@ -4412,7 +4412,7 @@ FAIL: %s
// All of these addresses are above the limit of a 32-bit kernel.
assert_true (kernel.offset > uint32.MAX);
assert_true (layout.modules.size > 20);
assert_true (layout.module_list > uint32.MAX);
bool named = false;
bool wide = true;
@@ -4459,7 +4459,7 @@ FAIL: %s
assert_nonnull (kernel);
assert_true (kernel.offset > uint32.MAX);
assert_true (layout.modules.size > 20);
assert_true (layout.module_list > uint32.MAX);
bool named = false;
bool process_list = false;
@@ -4506,7 +4506,7 @@ FAIL: %s
assert_nonnull (kernel);
assert_true (kernel.offset > uint32.MAX);
assert_true (layout.modules.size > 20);
assert_true (layout.module_list > uint32.MAX);
bool named = false;
bool process_list = false;