mirror of
https://github.com/frida/frida-core.git
synced 2026-11-09 01:59:34 -10:00
Compare commits
No files matched your search
@@ -451,7 +451,10 @@ jobs:
|
||||
cd build
|
||||
cp -a tests/frida-tests tests/labrats lib/agent/frida-agent.so /tmp/pkg/
|
||||
tar -C /tmp/pkg -cf /tmp/runner.tar .
|
||||
/opt/sabrelite/run.sh /tmp/runner.tar /opt/frida/frida-tests
|
||||
/opt/sabrelite/run.sh /tmp/runner.tar \
|
||||
"cp /opt/frida/frida-tests /opt/frida/frida-agent.so /tmp/ && \
|
||||
ln -sP /opt/frida/labrats /tmp/labrats && \
|
||||
/tmp/frida-tests"
|
||||
|
||||
# This job is used to check that the Linux helpers are built correctly for all
|
||||
# supported architectures. We do this because the binary artifacts are checked
|
||||
|
||||
@@ -43,7 +43,7 @@ platform-dependent (e.g. `linux-x86_64`).
|
||||
## Build Requirements
|
||||
|
||||
- **Vala compiler**: Must be the Frida-optimized fork from
|
||||
https://github.com/frida/vala (checks for `-frida` suffix
|
||||
https://github.com/frida-forks/vala (checks for `-frida` suffix
|
||||
in version)
|
||||
- **C/C++ standards**: gnu99/c99 for C, c++17 for C++
|
||||
- **Go >= 1.24**: Required for compiler backend
|
||||
|
||||
@@ -103,6 +103,9 @@ namespace Frida.Agent {
|
||||
private SpawnMonitor? spawn_monitor;
|
||||
private ThreadSuspendMonitor? thread_suspend_monitor;
|
||||
private UnwindSitter? unwind_sitter;
|
||||
#if ANDROID && (ARM || ARM64)
|
||||
private NativeCloakMirror? native_cloak_mirror;
|
||||
#endif
|
||||
|
||||
private delegate void CompletionNotify ();
|
||||
|
||||
@@ -350,6 +353,9 @@ namespace Frida.Agent {
|
||||
bool enable_unwind_broker = true;
|
||||
bool enable_exit_monitor = true;
|
||||
bool enable_thread_suspend_monitor = true;
|
||||
#if ANDROID && (ARM || ARM64)
|
||||
bool in_emulated_realm = false;
|
||||
#endif
|
||||
foreach (unowned string option in tokens[1:]) {
|
||||
if (option == "eternal")
|
||||
ensure_eternalized ();
|
||||
@@ -365,6 +371,10 @@ namespace Frida.Agent {
|
||||
enable_exit_monitor = false;
|
||||
else if (option == "thread-suspend-monitor:off")
|
||||
enable_thread_suspend_monitor = false;
|
||||
#if ANDROID && (ARM || ARM64)
|
||||
else if (option == "realm:emulated")
|
||||
in_emulated_realm = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (exceptor_mode != FULL)
|
||||
@@ -374,6 +384,10 @@ namespace Frida.Agent {
|
||||
|
||||
try {
|
||||
yield setup_connection_with_transport_uri (transport_uri);
|
||||
#if ANDROID && (ARM || ARM64)
|
||||
if (in_emulated_realm)
|
||||
yield connect_to_native_realm ();
|
||||
#endif
|
||||
} catch (Error e) {
|
||||
start_error = e;
|
||||
main_loop.quit ();
|
||||
@@ -406,6 +420,23 @@ namespace Frida.Agent {
|
||||
padder = null;
|
||||
}
|
||||
|
||||
#if ANDROID && (ARM || ARM64)
|
||||
private async void connect_to_native_realm () throws Error {
|
||||
try {
|
||||
NativeCloak cloak = yield connection.get_proxy (null, ObjectPath.NATIVE_CLOAK, DO_NOT_LOAD_PROPERTIES,
|
||||
null);
|
||||
native_cloak_mirror = new NativeCloakMirror (cloak);
|
||||
native_cloak_mirror.activate ();
|
||||
|
||||
NativeThreadRegistry registry = yield connection.get_proxy (null, ObjectPath.NATIVE_THREAD_REGISTRY,
|
||||
DO_NOT_LOAD_PROPERTIES, null);
|
||||
Gum.ThreadRegistry.set_provider (new NativeThreadProvider (registry));
|
||||
} catch (GLib.Error e) {
|
||||
throw new Error.TRANSPORT ("%s", e.message);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private static bool can_allocate_code () {
|
||||
size_t page_size = Gum.query_page_size ();
|
||||
|
||||
@@ -1194,6 +1225,8 @@ namespace Frida.Agent {
|
||||
unwind_sitter = null;
|
||||
|
||||
interceptor.end_transaction ();
|
||||
|
||||
Gum.shutdown ();
|
||||
}
|
||||
|
||||
private void ensure_eternalized () {
|
||||
@@ -1476,6 +1509,8 @@ namespace Frida.Agent {
|
||||
private Socket? emulated_socket;
|
||||
private BridgeState? emulated_bridge_state;
|
||||
private Thread<void>? emulated_worker;
|
||||
private NativeCloakService? native_cloak;
|
||||
private NativeThreadRegistryService? native_thread_registry;
|
||||
|
||||
private async AgentSessionProvider? try_get_emulated_provider (Cancellable? cancellable) throws IOError {
|
||||
if (get_emulated_request == null)
|
||||
@@ -1515,6 +1550,8 @@ namespace Frida.Agent {
|
||||
emulated_agent = nb_api.load_library_ext (emulated_agent_path, RTLD_LAZY, classloader_namespace);
|
||||
} else {
|
||||
emulated_agent = nb_api.load_library (emulated_agent_path, RTLD_LAZY);
|
||||
if (emulated_agent == null && nb_api.load_library_ext != null)
|
||||
emulated_agent = nb_api.load_library_ext (emulated_agent_path, RTLD_LAZY, null);
|
||||
}
|
||||
if (emulated_agent == null)
|
||||
throw new Error.NOT_SUPPORTED ("Process is not using emulation");
|
||||
@@ -1554,6 +1591,7 @@ namespace Frida.Agent {
|
||||
* code being discarded, which seems like an edge-case the translator doesn't handle.
|
||||
*/
|
||||
parameters.append ("|exceptor:off|exit-monitor:off");
|
||||
parameters.append ("|realm:emulated");
|
||||
|
||||
emulated_bridge_state = new BridgeState (parameters.str);
|
||||
|
||||
@@ -1562,6 +1600,13 @@ namespace Frida.Agent {
|
||||
var connection = yield new DBusConnection (stream, ServerGuid.HOST_SESSION_SERVICE,
|
||||
AUTHENTICATION_SERVER | AUTHENTICATION_ALLOW_ANONYMOUS, null, cancellable);
|
||||
|
||||
native_cloak = new NativeCloakService ();
|
||||
connection.register_object (ObjectPath.NATIVE_CLOAK, (NativeCloak) native_cloak);
|
||||
|
||||
native_thread_registry = new NativeThreadRegistryService ();
|
||||
connection.register_object (ObjectPath.NATIVE_THREAD_REGISTRY,
|
||||
(NativeThreadRegistry) native_thread_registry);
|
||||
|
||||
AgentSessionProvider provider = yield connection.get_proxy (null, ObjectPath.AGENT_SESSION_PROVIDER,
|
||||
DO_NOT_LOAD_PROPERTIES, cancellable);
|
||||
|
||||
@@ -1609,6 +1654,16 @@ namespace Frida.Agent {
|
||||
emulated_worker = null;
|
||||
}
|
||||
|
||||
if (native_thread_registry != null) {
|
||||
native_thread_registry.close ();
|
||||
native_thread_registry = null;
|
||||
}
|
||||
|
||||
if (native_cloak != null) {
|
||||
native_cloak.close ();
|
||||
native_cloak = null;
|
||||
}
|
||||
|
||||
emulated_socket = null;
|
||||
|
||||
if (emulated_agent != null) {
|
||||
@@ -1769,6 +1824,201 @@ namespace Frida.Agent {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ANDROID && (X86 || X86_64)
|
||||
private sealed class NativeCloakService : Object, NativeCloak, Gum.Cloak.Observer {
|
||||
private MainContext main_context = MainContext.ref_thread_default ();
|
||||
private bool observing = false;
|
||||
|
||||
public uint[] activate (Cancellable? cancellable) {
|
||||
Gum.Cloak.add_observer (this);
|
||||
observing = true;
|
||||
|
||||
uint[] threads = {};
|
||||
Gum.Cloak.enumerate_threads (id => {
|
||||
threads += (uint) id;
|
||||
return true;
|
||||
});
|
||||
return threads;
|
||||
}
|
||||
|
||||
public void close () {
|
||||
if (!observing)
|
||||
return;
|
||||
|
||||
Gum.Cloak.remove_observer (this);
|
||||
observing = false;
|
||||
}
|
||||
|
||||
public void thread_added (Gum.ThreadId id) {
|
||||
schedule (() => {
|
||||
thread_cloaked ((uint) id);
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
public void thread_removed (Gum.ThreadId id) {
|
||||
schedule (() => {
|
||||
thread_uncloaked ((uint) id);
|
||||
return Source.REMOVE;
|
||||
});
|
||||
}
|
||||
|
||||
private void schedule (owned SourceFunc function) {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback ((owned) function);
|
||||
source.attach (main_context);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class NativeThreadRegistryService : Object, NativeThreadRegistry {
|
||||
private unowned Gum.ThreadRegistry? registry;
|
||||
private ulong added_handler = 0;
|
||||
private ulong removed_handler = 0;
|
||||
private ulong renamed_handler = 0;
|
||||
|
||||
public NativeThreadInfo[] activate (Cancellable? cancellable) {
|
||||
registry = Gum.ThreadRegistry.obtain ();
|
||||
|
||||
NativeThreadInfo[] threads = {};
|
||||
|
||||
registry.lock ();
|
||||
|
||||
added_handler = registry.thread_added.connect (on_thread_added);
|
||||
removed_handler = registry.thread_removed.connect (on_thread_removed);
|
||||
renamed_handler = registry.thread_renamed.connect (on_thread_renamed);
|
||||
|
||||
registry.enumerate_threads (thread => {
|
||||
threads += NativeThreadInfo ((uint) thread.id, thread.name ?? "");
|
||||
return true;
|
||||
});
|
||||
|
||||
registry.unlock ();
|
||||
|
||||
return threads;
|
||||
}
|
||||
|
||||
public void deactivate (Cancellable? cancellable) {
|
||||
close ();
|
||||
}
|
||||
|
||||
public void close () {
|
||||
if (registry == null)
|
||||
return;
|
||||
|
||||
registry.disconnect (added_handler);
|
||||
registry.disconnect (removed_handler);
|
||||
registry.disconnect (renamed_handler);
|
||||
registry = null;
|
||||
}
|
||||
|
||||
private void on_thread_added (Gum.ThreadDetails thread) {
|
||||
thread_added (NativeThreadInfo ((uint) thread.id, thread.name ?? ""));
|
||||
}
|
||||
|
||||
private void on_thread_removed (Gum.ThreadDetails thread) {
|
||||
thread_removed ((uint) thread.id);
|
||||
}
|
||||
|
||||
private void on_thread_renamed (Gum.ThreadDetails thread, string? previous_name) {
|
||||
thread_renamed ((uint) thread.id, thread.name ?? "");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ANDROID && (ARM || ARM64)
|
||||
private sealed class NativeCloakMirror : Object {
|
||||
public NativeCloak native_cloak {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
public NativeCloakMirror (NativeCloak native_cloak) {
|
||||
Object (native_cloak: native_cloak);
|
||||
}
|
||||
|
||||
public void activate () throws GLib.Error {
|
||||
native_cloak.thread_cloaked.connect (on_thread_cloaked);
|
||||
native_cloak.thread_uncloaked.connect (on_thread_uncloaked);
|
||||
|
||||
foreach (uint id in native_cloak.activate (null))
|
||||
on_thread_cloaked (id);
|
||||
}
|
||||
|
||||
private void on_thread_cloaked (uint id) {
|
||||
if (!Gum.Cloak.has_thread (id))
|
||||
Gum.Cloak.add_thread (id);
|
||||
}
|
||||
|
||||
private void on_thread_uncloaked (uint id) {
|
||||
Gum.Cloak.remove_thread (id);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class NativeThreadProvider : Object, Gum.ThreadProvider {
|
||||
public NativeThreadRegistry native_registry {
|
||||
get;
|
||||
construct;
|
||||
}
|
||||
|
||||
private unowned Gum.ThreadRegistry registry;
|
||||
private ulong added_handler = 0;
|
||||
private ulong removed_handler = 0;
|
||||
private ulong renamed_handler = 0;
|
||||
|
||||
public NativeThreadProvider (NativeThreadRegistry native_registry) {
|
||||
Object (native_registry: native_registry);
|
||||
}
|
||||
|
||||
public void activate (Gum.ThreadRegistry registry) {
|
||||
this.registry = registry;
|
||||
|
||||
added_handler = native_registry.thread_added.connect (add_thread);
|
||||
removed_handler = native_registry.thread_removed.connect (on_thread_removed);
|
||||
renamed_handler = native_registry.thread_renamed.connect (on_thread_renamed);
|
||||
|
||||
NativeThreadInfo[] threads;
|
||||
try {
|
||||
threads = native_registry.activate (null);
|
||||
} catch (GLib.Error e) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var thread in threads)
|
||||
add_thread (thread);
|
||||
}
|
||||
|
||||
public void deactivate () {
|
||||
native_registry.disconnect (added_handler);
|
||||
native_registry.disconnect (removed_handler);
|
||||
native_registry.disconnect (renamed_handler);
|
||||
|
||||
try {
|
||||
native_registry.deactivate (null);
|
||||
} catch (GLib.Error e) {
|
||||
}
|
||||
}
|
||||
|
||||
private void add_thread (NativeThreadInfo thread) {
|
||||
var details = Gum.ThreadDetails ();
|
||||
details.id = thread.id;
|
||||
if (thread.name != "") {
|
||||
details.name = thread.name;
|
||||
details.flags = NAME;
|
||||
}
|
||||
|
||||
registry.register (details);
|
||||
}
|
||||
|
||||
private void on_thread_removed (uint id) {
|
||||
registry.unregister (id);
|
||||
}
|
||||
|
||||
private void on_thread_renamed (uint id, string name) {
|
||||
registry.rename (id, (name != "") ? name : null);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private sealed class LiveAgentSession : BaseAgentSession {
|
||||
public uint registration_id {
|
||||
get;
|
||||
|
||||
+12
-2
@@ -6,6 +6,8 @@ namespace Frida {
|
||||
|
||||
private Gee.Map<ChannelId?, weak IOStream> channels =
|
||||
new Gee.HashMap<ChannelId?, weak IOStream> (ChannelId.hash, ChannelId.equal);
|
||||
private Gee.Map<ChannelId?, IOStream> unlinked_channels =
|
||||
new Gee.HashMap<ChannelId?, IOStream> (ChannelId.hash, ChannelId.equal);
|
||||
|
||||
~ChannelRegistry () {
|
||||
clear ();
|
||||
@@ -18,25 +20,33 @@ namespace Frida {
|
||||
channel_stream.abandon ();
|
||||
}
|
||||
channels.clear ();
|
||||
unlinked_channels.clear ();
|
||||
}
|
||||
|
||||
public void register (ChannelId id, IOStream stream) {
|
||||
stream.set_data ("channel-registry-entry", new Entry (this, id));
|
||||
lock (channels)
|
||||
lock (channels) {
|
||||
channels[id] = stream;
|
||||
unlinked_channels[id] = stream;
|
||||
}
|
||||
}
|
||||
|
||||
public IOStream link (ChannelId id) throws Error {
|
||||
IOStream? stream;
|
||||
lock (channels)
|
||||
IOStream? unlinked_stream = null;
|
||||
lock (channels) {
|
||||
stream = channels[id];
|
||||
unlinked_channels.unset (id, out unlinked_stream);
|
||||
}
|
||||
if (stream == null)
|
||||
throw new Error.INVALID_ARGUMENT ("Invalid channel ID");
|
||||
return stream;
|
||||
}
|
||||
|
||||
public void unlink (ChannelId id) {
|
||||
IOStream? unlinked_stream = null;
|
||||
lock (channels) {
|
||||
unlinked_channels.unset (id, out unlinked_stream);
|
||||
if (!channels.unset (id))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -133,6 +133,36 @@ namespace Frida {
|
||||
Cancellable? cancellable) throws GLib.Error;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
[DBus (name = "re.frida.NativeThreadRegistry17")]
|
||||
public interface NativeThreadRegistry : Object {
|
||||
public abstract NativeThreadInfo[] activate (Cancellable? cancellable) throws GLib.Error;
|
||||
public abstract void deactivate (Cancellable? cancellable) throws GLib.Error;
|
||||
|
||||
public signal void thread_added (NativeThreadInfo thread);
|
||||
public signal void thread_removed (uint id);
|
||||
public signal void thread_renamed (uint id, string name);
|
||||
}
|
||||
|
||||
[DBus (name = "re.frida.NativeCloak17")]
|
||||
public interface NativeCloak : Object {
|
||||
public abstract uint[] activate (Cancellable? cancellable) throws GLib.Error;
|
||||
|
||||
public signal void thread_cloaked (uint id);
|
||||
public signal void thread_uncloaked (uint id);
|
||||
}
|
||||
|
||||
public struct NativeThreadInfo {
|
||||
public uint id;
|
||||
public string name;
|
||||
|
||||
public NativeThreadInfo (uint id, string name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
[DBus (name = "re.frida.AgentMessageSink17")]
|
||||
public interface AgentMessageSink : Object {
|
||||
public abstract async void post_messages (AgentMessage[] messages, uint batch_id,
|
||||
@@ -2802,6 +2832,8 @@ namespace Frida {
|
||||
public const string AGENT_SESSION_PROVIDER = "/re/frida/AgentSessionProvider";
|
||||
public const string AGENT_SESSION = "/re/frida/AgentSession";
|
||||
public const string AGENT_CONTROLLER = "/re/frida/AgentController";
|
||||
public const string NATIVE_THREAD_REGISTRY = "/re/frida/NativeThreadRegistry";
|
||||
public const string NATIVE_CLOAK = "/re/frida/NativeCloak";
|
||||
public const string AGENT_MESSAGE_SINK = "/re/frida/AgentMessageSink";
|
||||
public const string GADGET_SESSION = "/re/frida/GadgetSession";
|
||||
public const string CHANNEL = "/re/frida/Channel";
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ if not meson.get_compiler('vala').version().endswith('-frida')
|
||||
Frida currently relies on features not yet upstream. Grab the Frida-optimized
|
||||
Vala sources from:
|
||||
|
||||
https://github.com/frida/vala
|
||||
https://github.com/frida-forks/vala
|
||||
|
||||
and compile them with Meson.''')
|
||||
endif
|
||||
|
||||
+1
-1
Submodule releng updated: a3f1a54307...164a6a4117.
Generated
+4
-4
@@ -8,7 +8,7 @@
|
||||
"name": "frida-barebone-script-runtime",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"@types/frida-gum": "^19.12.0",
|
||||
"@types/frida-gum": "^19.13.0",
|
||||
"esbuild": "^0.25.5",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
@@ -439,9 +439,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/frida-gum": {
|
||||
"version": "19.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-19.12.0.tgz",
|
||||
"integrity": "sha512-fqXtwFA8AwE89tSdNHlaY+hNWI5j62bcO1xk5w0NSuHBIH6KRxYzIK6rp/JP7TXtRKU2ewBwx81FrHTfGF0q7w==",
|
||||
"version": "19.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-19.13.0.tgz",
|
||||
"integrity": "sha512-tbmEZj9JUt3S6FqFE4XJPi/WhQuUU0/sM1IWS1KDKqvy6077hwNjG1v4MC3wpk+FO/WzfZ8G1gAKqAKJosfj1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"watch": "tsc -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/frida-gum": "^19.12.0",
|
||||
"@types/frida-gum": "^19.13.0",
|
||||
"esbuild": "^0.25.5",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
|
||||
Generated
+4
-4
@@ -36,7 +36,7 @@
|
||||
"@frida/util": "^1.0.3",
|
||||
"@frida/vm": "^2.0.0",
|
||||
"@frida/web-streams": "^1.0.0",
|
||||
"@types/frida-gum": "^19.12.0",
|
||||
"@types/frida-gum": "^19.13.0",
|
||||
"@types/node": "18.19.103",
|
||||
"frida-fs": "7.1.1"
|
||||
}
|
||||
@@ -270,9 +270,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/frida-gum": {
|
||||
"version": "19.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-19.12.0.tgz",
|
||||
"integrity": "sha512-fqXtwFA8AwE89tSdNHlaY+hNWI5j62bcO1xk5w0NSuHBIH6KRxYzIK6rp/JP7TXtRKU2ewBwx81FrHTfGF0q7w==",
|
||||
"version": "19.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-19.13.0.tgz",
|
||||
"integrity": "sha512-tbmEZj9JUt3S6FqFE4XJPi/WhQuUU0/sM1IWS1KDKqvy6077hwNjG1v4MC3wpk+FO/WzfZ8G1gAKqAKJosfj1Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"@frida/util": "^1.0.3",
|
||||
"@frida/vm": "^2.0.0",
|
||||
"@frida/web-streams": "^1.0.0",
|
||||
"@types/frida-gum": "^19.12.0",
|
||||
"@types/frida-gum": "^19.13.0",
|
||||
"@types/node": "18.19.103",
|
||||
"frida-fs": "7.1.1"
|
||||
}
|
||||
|
||||
@@ -1074,7 +1074,9 @@ namespace Frida {
|
||||
lldb_session = new LLDBSession (lldb, process, null, ExceptionHandlingStatus.FULL, device);
|
||||
yield lldb_session.kill (cancellable);
|
||||
yield lldb_session.close (cancellable);
|
||||
} catch (Error e) {
|
||||
} catch (GLib.Error e) {
|
||||
if (e is IOError.CANCELLED)
|
||||
throw (IOError) e;
|
||||
var process_control = yield Idun.ProcessControlService.open (device, cancellable);
|
||||
yield process_control.kill (pid, cancellable);
|
||||
}
|
||||
|
||||
@@ -2433,7 +2433,7 @@ namespace Frida {
|
||||
|
||||
Gum.Linux.enumerate_ranges ((Posix.pid_t) session.pid, READ | EXECUTE, d => {
|
||||
unowned Gum.FileMapping? file = d.file;
|
||||
if (file != null && file.path.has_prefix ("memfd:"))
|
||||
if (file != null && file.path.has_prefix ("/memfd:"))
|
||||
return true;
|
||||
if (d.range.size >= code.length) {
|
||||
code_start = d.range.base_address + d.range.size - round_size_to_page_size (code.length);
|
||||
|
||||
@@ -1791,7 +1791,11 @@ namespace Frida {
|
||||
}
|
||||
|
||||
MatchInfo info;
|
||||
if (!/^([0-9a-f]+)-([0-9a-f]+) rw-p 00000000 00:00 0 *$/m.match (raw_maps, 0, out info)) {
|
||||
try {
|
||||
var anon_rw_pattern = new Regex ("^([0-9a-f]+)-([0-9a-f]+) rw-p 00000000 00:00 0 *$", MULTILINE | RAW);
|
||||
if (!anon_rw_pattern.match (raw_maps, 0, out info))
|
||||
assert_not_reached ();
|
||||
} catch (RegexError e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
assert (info != null);
|
||||
@@ -2478,7 +2482,7 @@ namespace Frida {
|
||||
if (stdout_pipe == null) {
|
||||
try {
|
||||
var process = new Subprocess.newv ({ "logcat", "-b", "crash", "-B" },
|
||||
STDIN_INHERIT | STDOUT_PIPE | STDERR_SILENCE);
|
||||
STDOUT_PIPE | STDERR_SILENCE);
|
||||
|
||||
logcat = process;
|
||||
stdout_pipe = process.get_stdout_pipe ();
|
||||
@@ -2894,7 +2898,7 @@ namespace Frida {
|
||||
},
|
||||
module_list => {
|
||||
foreach (var path in module_list)
|
||||
modules.add_value (new Variant.variant (new Variant.tuple ({ path })));
|
||||
modules.add_value (new Variant.variant (new Variant.tuple ({ path.make_valid () })));
|
||||
});
|
||||
|
||||
vardict_add (reply, "modules", modules.end ());
|
||||
@@ -3111,7 +3115,7 @@ namespace Frida {
|
||||
|
||||
switch (attachment_type) {
|
||||
case SyscallTracer.AttachmentType.STRING:
|
||||
return (string) data;
|
||||
return ((string) data).make_valid ();
|
||||
case SyscallTracer.AttachmentType.BYTES: {
|
||||
if (arg_type != null) {
|
||||
Variant? v = try_decode_timespec (arg_type, data, len);
|
||||
@@ -3208,7 +3212,7 @@ namespace Frida {
|
||||
if (name_len != 0)
|
||||
Memory.copy (str, path + 1, name_len);
|
||||
|
||||
return new Variant.tuple ({ "unix-abstract", (string) str });
|
||||
return new Variant.tuple ({ "unix-abstract", ((string) str).make_valid () });
|
||||
}
|
||||
|
||||
size_t n = 1;
|
||||
@@ -3218,7 +3222,7 @@ namespace Frida {
|
||||
var str = new uint8[n + 1];
|
||||
Memory.copy (str, path, n);
|
||||
|
||||
return new Variant.tuple ({ "unix", (string) str });
|
||||
return new Variant.tuple ({ "unix", ((string) str).make_valid () });
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
|
||||
+27
-72
@@ -21,7 +21,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public Bytes make_request () {
|
||||
Gee.List<string> missing = new TargetLibraries (pid).find_missing (module);
|
||||
Gee.List<string> missing = new TargetLibraries (pid, module).find_missing ();
|
||||
|
||||
var header = HelperMappedAgentRequest ();
|
||||
header.size = capacity;
|
||||
@@ -40,10 +40,10 @@ namespace Frida {
|
||||
|
||||
public Bytes map (uint64 base_address, string entrypoint) throws Error {
|
||||
try {
|
||||
var libraries = new TargetLibraries (pid);
|
||||
var libraries = new TargetLibraries (pid, module);
|
||||
var memory = new MappedAgentMemory (pid, blob, module, base_address, capacity);
|
||||
|
||||
var mapper = Gum.Mapper.new_take_blob ("frida-agent.so", blob, libraries.make_resolver (module));
|
||||
var mapper = Gum.Mapper.new_take_blob ("frida-agent.so", blob, libraries.make_resolver ());
|
||||
mapper.set_target (memory);
|
||||
|
||||
Gum.Address extra_address;
|
||||
@@ -61,16 +61,21 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private sealed class TargetLibraries {
|
||||
private uint pid;
|
||||
private Gum.ElfModule agent;
|
||||
private ProcMapsSnapshot maps;
|
||||
private Gee.Map<string, Gum.ElfModule> modules = new Gee.HashMap<string, Gum.ElfModule> ();
|
||||
private Gee.List<ProcMapsSnapshot.Mapping> library_starts = new Gee.ArrayList<ProcMapsSnapshot.Mapping> ();
|
||||
|
||||
public TargetLibraries (uint pid) {
|
||||
this.pid = pid;
|
||||
public TargetLibraries (uint pid, Gum.ElfModule agent) {
|
||||
this.agent = agent;
|
||||
this.maps = ProcMapsSnapshot.from_pid (pid);
|
||||
|
||||
foreach (ProcMapsSnapshot.Mapping m in maps) {
|
||||
if (is_library_start (m))
|
||||
library_starts.add (m);
|
||||
}
|
||||
}
|
||||
|
||||
public Gee.List<string> find_missing (Gum.ElfModule agent) {
|
||||
public Gee.List<string> find_missing () {
|
||||
var missing = new Gee.ArrayList<string> ();
|
||||
agent.enumerate_dependencies (d => {
|
||||
if (find (d.name) == null)
|
||||
@@ -80,7 +85,7 @@ namespace Frida {
|
||||
return missing;
|
||||
}
|
||||
|
||||
public Gum.ElfModuleResolver make_resolver (Gum.ElfModule agent) throws Error {
|
||||
public Gum.ElfModuleResolver make_resolver () throws Error {
|
||||
var resolver = new Gum.ElfModuleResolver ();
|
||||
|
||||
var pending = new Gee.ArrayQueue<string> ();
|
||||
@@ -98,7 +103,7 @@ namespace Frida {
|
||||
if (!seen.add (mapping.path))
|
||||
continue;
|
||||
|
||||
Gum.ElfModule library = load (mapping);
|
||||
Gum.ElfModule library = maps.load_image (mapping);
|
||||
resolver.add_module (library, mapping.start);
|
||||
|
||||
library.enumerate_dependencies (d => {
|
||||
@@ -111,16 +116,14 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private ProcMapsSnapshot.Mapping? find (string name) {
|
||||
foreach (ProcMapsSnapshot.Mapping m in maps) {
|
||||
if (is_library_start (m) && library_name_matches (name, Path.get_basename (m.path)))
|
||||
foreach (ProcMapsSnapshot.Mapping m in library_starts) {
|
||||
if (library_name_matches (name, Path.get_basename (m.path)) && is_compatible_image (m))
|
||||
return m;
|
||||
}
|
||||
|
||||
foreach (ProcMapsSnapshot.Mapping m in maps) {
|
||||
if (!is_library_start (m))
|
||||
continue;
|
||||
foreach (ProcMapsSnapshot.Mapping m in library_starts) {
|
||||
try {
|
||||
if (load (m).soname == name)
|
||||
if (maps.load_image (m).soname == name && is_compatible_image (m))
|
||||
return m;
|
||||
} catch (Error e) {
|
||||
}
|
||||
@@ -144,65 +147,17 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private Gum.ElfModule load (ProcMapsSnapshot.Mapping mapping) throws Error {
|
||||
Gum.ElfModule? module = modules[mapping.path];
|
||||
if (module == null) {
|
||||
module = open_mapped_file (mapping);
|
||||
if (module == null)
|
||||
module = copy_from_target (mapping);
|
||||
modules[mapping.path] = module;
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
private Gum.ElfModule? open_mapped_file (ProcMapsSnapshot.Mapping mapping) {
|
||||
private bool is_compatible_image (ProcMapsSnapshot.Mapping start) {
|
||||
Gum.ElfModule library;
|
||||
try {
|
||||
return new Gum.ElfModule.from_file (("/proc/%u/map_files/%" + uint64.FORMAT_MODIFIER + "x-%" +
|
||||
uint64.FORMAT_MODIFIER + "x").printf (pid, mapping.start, mapping.end));
|
||||
} catch (Gum.Error e) {
|
||||
library = maps.load_image (start);
|
||||
} catch (Error e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string path = "/proc/%u/root%s".printf (pid, mapping.path);
|
||||
Posix.Stat st;
|
||||
if (Posix.stat (path, out st) != 0 || st.st_ino != mapping.inode)
|
||||
return null;
|
||||
|
||||
try {
|
||||
return new Gum.ElfModule.from_file (path);
|
||||
} catch (Gum.Error e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Gum.ElfModule copy_from_target (ProcMapsSnapshot.Mapping first) throws Error {
|
||||
var segments = new Gee.ArrayList<ProcMapsSnapshot.Mapping> ();
|
||||
uint64 end = first.end;
|
||||
foreach (ProcMapsSnapshot.Mapping m in maps) {
|
||||
if (m.path == first.path && m.inode == first.inode && m.readable) {
|
||||
segments.add (m);
|
||||
end = uint64.max (end, m.end);
|
||||
}
|
||||
}
|
||||
|
||||
var image = new uint8[end - first.start];
|
||||
var mem = new FileDescriptor (Posix.open ("/proc/%u/mem".printf (pid), Posix.O_RDONLY));
|
||||
if (mem.handle == -1)
|
||||
throw new Error.PERMISSION_DENIED ("Unable to read memory of process %u", pid);
|
||||
foreach (ProcMapsSnapshot.Mapping m in segments)
|
||||
mem.pread (image[m.start - first.start:m.end - first.start], m.start);
|
||||
|
||||
var blob = new Bytes.take ((owned) image);
|
||||
var module = (Gum.ElfModule) Object.new (typeof (Gum.ElfModule),
|
||||
"base-address", (Gum.Address) first.start,
|
||||
"source-path", first.path,
|
||||
"source-blob", blob,
|
||||
"source-mode", Gum.ElfSourceMode.ONLINE);
|
||||
try {
|
||||
module.load ();
|
||||
} catch (Gum.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("Unable to parse %s: %s", first.path, e.message);
|
||||
}
|
||||
return module;
|
||||
return library.machine == agent.machine &&
|
||||
library.pointer_size == agent.pointer_size &&
|
||||
maps.is_image_base (start);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+101
-64
@@ -6,7 +6,9 @@ namespace Frida {
|
||||
default = 1;
|
||||
}
|
||||
|
||||
private uint32 pid;
|
||||
private Gee.List<Mapping> maps = new Gee.ArrayList<Mapping> ();
|
||||
private Gee.Map<string, Gum.ElfModule> images = new Gee.HashMap<string, Gum.ElfModule> ();
|
||||
|
||||
private const uint64 VM_READ = 1;
|
||||
private const uint64 VM_WRITE = 2;
|
||||
@@ -44,6 +46,7 @@ namespace Frida {
|
||||
}
|
||||
|
||||
private void build_from_pid (uint32 pid) {
|
||||
this.pid = pid;
|
||||
maps.clear ();
|
||||
|
||||
var it = ProcMapsIter.for_pid (pid);
|
||||
@@ -268,76 +271,105 @@ namespace Frida {
|
||||
}
|
||||
|
||||
public Mapping? find_module_by_path_at_gen (string path, uint32 at_gen) {
|
||||
Mapping? best_base = null;
|
||||
int best_score = int.MIN;
|
||||
|
||||
Mapping? cur_base = null;
|
||||
uint cur_total = 0;
|
||||
uint cur_exec = 0;
|
||||
|
||||
#if ANDROID
|
||||
unowned string libc_path = Gum.Process.get_libc_module ().path;
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < maps.size; i++) {
|
||||
var m = maps[i];
|
||||
if (!mapping_alive_at (m, at_gen))
|
||||
continue;
|
||||
|
||||
unowned string mp = m.path;
|
||||
|
||||
if (mp == "[page size compat]")
|
||||
continue;
|
||||
|
||||
if (mp != path) {
|
||||
consider_candidate (ref best_base, ref best_score, cur_base, cur_total, cur_exec);
|
||||
cur_base = null;
|
||||
cur_total = 0;
|
||||
cur_exec = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
#if ANDROID
|
||||
if (mp == libc_path && m.shared)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
if (m.file_offset == 0) {
|
||||
consider_candidate (ref best_base, ref best_score, cur_base, cur_total, cur_exec);
|
||||
cur_base = m;
|
||||
cur_total = 0;
|
||||
cur_exec = 0;
|
||||
}
|
||||
|
||||
if (cur_base != null) {
|
||||
cur_total++;
|
||||
if (m.executable)
|
||||
cur_exec++;
|
||||
}
|
||||
foreach (Mapping m in maps) {
|
||||
if (m.path == path && m.file_offset == 0 && mapping_alive_at (m, at_gen) && is_image_base_at_gen (m, at_gen))
|
||||
return m;
|
||||
}
|
||||
|
||||
consider_candidate (ref best_base, ref best_score, cur_base, cur_total, cur_exec);
|
||||
|
||||
return best_base;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void consider_candidate (ref Mapping? best_base, ref int best_score, Mapping? candidate_base, uint total,
|
||||
uint exec) {
|
||||
if (candidate_base == null)
|
||||
return;
|
||||
public bool is_image_base (Mapping start) {
|
||||
return is_image_base_at_gen (start, gen);
|
||||
}
|
||||
|
||||
int score = score_candidate (total, exec);
|
||||
if (score > best_score) {
|
||||
best_score = score;
|
||||
best_base = candidate_base;
|
||||
private bool is_image_base_at_gen (Mapping start, uint32 at_gen) {
|
||||
Gum.ElfModule image;
|
||||
try {
|
||||
image = load_image (start);
|
||||
} catch (Error e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64 page_mask = ~((uint64) Gum.query_page_size () - 1);
|
||||
bool mapped = true;
|
||||
image.enumerate_segments (s => {
|
||||
if (s.file_size == 0 || (s.protection & Gum.PageProtection.EXECUTE) == 0)
|
||||
return true;
|
||||
|
||||
uint64 address = start.start + ((s.vm_address - image.preferred_address) & page_mask);
|
||||
Mapping? m = find_mapping_at_gen (address, at_gen);
|
||||
mapped = m != null &&
|
||||
m.executable &&
|
||||
!m.shared &&
|
||||
m.inode == start.inode &&
|
||||
m.path == start.path &&
|
||||
m.file_offset + (address - m.start) == (s.file_offset & page_mask);
|
||||
return mapped;
|
||||
});
|
||||
return mapped;
|
||||
}
|
||||
|
||||
public Gum.ElfModule load_image (Mapping start) throws Error {
|
||||
Gum.ElfModule? image = images[start.path];
|
||||
if (image == null) {
|
||||
image = open_mapped_file (start);
|
||||
if (image == null)
|
||||
image = copy_from_process (start);
|
||||
images[start.path] = image;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
private Gum.ElfModule? open_mapped_file (Mapping mapping) {
|
||||
try {
|
||||
return new Gum.ElfModule.from_file (("/proc/%u/map_files/%" + uint64.FORMAT_MODIFIER + "x-%" +
|
||||
uint64.FORMAT_MODIFIER + "x").printf (pid, mapping.start, mapping.end));
|
||||
} catch (Gum.Error e) {
|
||||
}
|
||||
|
||||
string path = "/proc/%u/root%s".printf (pid, mapping.path);
|
||||
Posix.Stat st;
|
||||
if (Posix.stat (path, out st) != 0 || st.st_ino != mapping.inode)
|
||||
return null;
|
||||
|
||||
try {
|
||||
return new Gum.ElfModule.from_file (path);
|
||||
} catch (Gum.Error e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static int score_candidate (uint total, uint exec) {
|
||||
int score = (int) total;
|
||||
if (exec == 0)
|
||||
score = -score;
|
||||
return score;
|
||||
private Gum.ElfModule copy_from_process (Mapping first) throws Error {
|
||||
var segments = new Gee.ArrayList<Mapping> ();
|
||||
uint64 end = first.end;
|
||||
foreach (Mapping m in maps) {
|
||||
if (m.path == first.path && m.inode == first.inode && m.readable && m.start >= first.start &&
|
||||
mapping_alive_at (m, gen)) {
|
||||
segments.add (m);
|
||||
end = uint64.max (end, m.end);
|
||||
}
|
||||
}
|
||||
|
||||
var image = new uint8[end - first.start];
|
||||
var mem = new FileDescriptor (Posix.open ("/proc/%u/mem".printf (pid), Posix.O_RDONLY));
|
||||
if (mem.handle == -1)
|
||||
throw new Error.PERMISSION_DENIED ("Unable to read memory of process %u", pid);
|
||||
foreach (Mapping m in segments)
|
||||
mem.pread (image[m.start - first.start:m.end - first.start], m.start);
|
||||
|
||||
var blob = new Bytes.take ((owned) image);
|
||||
var module = (Gum.ElfModule) Object.new (typeof (Gum.ElfModule),
|
||||
"base-address", (Gum.Address) first.start,
|
||||
"source-path", first.path,
|
||||
"source-blob", blob,
|
||||
"source-mode", Gum.ElfSourceMode.ONLINE);
|
||||
try {
|
||||
module.load ();
|
||||
} catch (Gum.Error e) {
|
||||
throw new Error.NOT_SUPPORTED ("Unable to parse %s: %s", first.path, e.message);
|
||||
}
|
||||
return module;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,8 +449,13 @@ namespace Frida {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/^([0-9a-f]+)-([0-9a-f]+) (\S{4}) ([0-9a-f]+) ([0-9a-f]{2,}:[0-9a-f]{2,}) (\d+) *([^\n]*)$/m.match (
|
||||
contents, 0, out info)) {
|
||||
try {
|
||||
var line_pattern = new Regex (
|
||||
"""^([0-9a-f]+)-([0-9a-f]+) (\S{4}) ([0-9a-f]+) ([0-9a-f]{2,}:[0-9a-f]{2,}) (\d+) *([^\n]*)$""",
|
||||
MULTILINE | RAW);
|
||||
if (!line_pattern.match (contents, 0, out info))
|
||||
assert_not_reached ();
|
||||
} catch (RegexError e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <windows.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <strsafe.h>
|
||||
#include <winternl.h>
|
||||
|
||||
#define CHECK_OS_RESULT(n1, cmp, n2, op) \
|
||||
if (!((n1) cmp (n2))) \
|
||||
@@ -100,6 +101,7 @@ typedef NTSTATUS (WINAPI * RtlCreateUserThreadFunc) (HANDLE process, SECURITY_DE
|
||||
|
||||
static void frida_propagate_open_process_error (guint32 pid, DWORD os_error, GError ** error);
|
||||
static gboolean frida_enable_debug_privilege (void);
|
||||
static gboolean frida_process_is_exiting (HANDLE process);
|
||||
|
||||
static gboolean frida_remote_worker_context_init (FridaRemoteWorkerContext * rwc, FridaInjectionDetails * details, GError ** error);
|
||||
static gsize frida_remote_worker_context_emit_payload (FridaRemoteWorkerContext * rwc, gpointer code);
|
||||
@@ -265,7 +267,7 @@ _frida_windows_helper_backend_free_inject_instance (void * inject_instance, gboo
|
||||
|
||||
if (instance->mappers != NULL)
|
||||
{
|
||||
if (!*is_resident)
|
||||
if (!*is_resident && !frida_process_is_exiting (instance->process_handle))
|
||||
g_ptr_array_foreach (instance->mappers, (GFunc) gum_mapper_unmap, NULL);
|
||||
g_ptr_array_unref (instance->mappers);
|
||||
}
|
||||
@@ -713,6 +715,14 @@ beach:
|
||||
return success;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
frida_process_is_exiting (HANDLE process)
|
||||
{
|
||||
DWORD exit_code;
|
||||
|
||||
return GetExitCodeProcess (process, &exit_code) && exit_code != STILL_ACTIVE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
frida_remote_worker_context_init (FridaRemoteWorkerContext * rwc, FridaInjectionDetails * details, GError ** error)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/brotli.git
|
||||
url = https://github.com/frida-forks/brotli.git
|
||||
revision = dd581fd4fea06096e3949ad6cb16775538d0ef47
|
||||
depth = 1
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/efni.git
|
||||
revision = 54525e06e27659afb6c664c18f9a10bbaac2dca9
|
||||
revision = 72f90afcc1f7c663db4de934e9f7ab9d3f67d4b1
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/frida-gum.git
|
||||
revision = 17.25.0
|
||||
revision = 17.26.2
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/glib.git
|
||||
revision = c366177b9292432a26f6903fa82a71d83e4927eb
|
||||
url = https://github.com/frida-forks/glib.git
|
||||
revision = f11db6352345ad21deb5f5783bb988bfca2a3d85
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/idun.git
|
||||
revision = eaf98270beab0ecf174e611d99036db6843a1865
|
||||
revision = 59f682f4aba229ca73d175f66f3fa6d9e2f4b7a0
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/libbpf.git
|
||||
url = https://github.com/frida-forks/libbpf.git
|
||||
revision = 9c4e176810717e27b6990a8722b252fc1967667f
|
||||
depth = 1
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/libgee.git
|
||||
url = https://github.com/frida-forks/libgee.git
|
||||
revision = 69621d2c665b75c1e064abdecd540ef1749615ee
|
||||
depth = 1
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/libnice.git
|
||||
url = https://github.com/frida-forks/libnice.git
|
||||
revision = 91e2d02edb2999786d7ac854f99bdfd7832df64f
|
||||
depth = 1
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/libusb.git
|
||||
url = https://github.com/frida-forks/libusb.git
|
||||
revision = 23d5a2cdc55aa4f0895960d08528e5aa13e7f7e2
|
||||
depth = 1
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/lwip.git
|
||||
revision = f8424fd177ae3654fa040b8b6bc0c32de7fd6ad9
|
||||
url = https://github.com/frida-forks/lwip.git
|
||||
revision = 6ab74462bd4a5afb13394942ab031d7bee005b4c
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/lzfse.git
|
||||
url = https://github.com/frida-forks/lzfse.git
|
||||
revision = 5cfb7c86919d3c2c636d0d7552b51855a611ba1c
|
||||
depth = 1
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/nghttp2.git
|
||||
url = https://github.com/frida-forks/nghttp2.git
|
||||
revision = 3e516202a0df939d6ab6bab47780fa35c5c4662b
|
||||
depth = 1
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/ngtcp2.git
|
||||
url = https://github.com/frida-forks/ngtcp2.git
|
||||
revision = 0009518d5ac5e4ce80343fb5f3c719991ffd47af
|
||||
depth = 1
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/openssl.git
|
||||
url = https://github.com/frida-forks/openssl.git
|
||||
revision = a94348d1cd4fb009e7c590fe4e193c2ea89f5ab8
|
||||
depth = 1
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/selinux.git
|
||||
url = https://github.com/frida-forks/selinux.git
|
||||
revision = 507985d931eb1f0227218d5d845634e602426c20
|
||||
depth = 1
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/termux-elf-cleaner.git
|
||||
url = https://github.com/frida-forks/termux-elf-cleaner.git
|
||||
revision = 85ed0baf1ce6abc1bf2cde92c808cc853d0bf791
|
||||
depth = 1
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/usrsctp.git
|
||||
url = https://github.com/frida-forks/usrsctp.git
|
||||
revision = bfd359be44adc76c55efacce9a2f71ff0a7f8cd4
|
||||
depth = 1
|
||||
|
||||
|
||||
@@ -12,6 +12,9 @@ test_sources = [
|
||||
if host_os_family == 'darwin'
|
||||
test_sources += 'test-host-session-darwin-glue.m'
|
||||
endif
|
||||
if host_os_family == 'linux'
|
||||
test_sources += 'test-host-session-linux-glue.c'
|
||||
endif
|
||||
|
||||
test_vala_args = []
|
||||
test_depends = []
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#include <glib.h>
|
||||
|
||||
#ifdef HAVE_LINUX
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
static void frida_host_session_test_linux_on_sigchld (int sig);
|
||||
|
||||
static struct sigaction frida_host_session_test_linux_saved_sigchld_action;
|
||||
|
||||
void
|
||||
frida_host_session_test_linux_install_sigchld_handler (void)
|
||||
{
|
||||
struct sigaction action = { 0, };
|
||||
|
||||
action.sa_handler = frida_host_session_test_linux_on_sigchld;
|
||||
sigemptyset (&action.sa_mask);
|
||||
action.sa_flags = SA_RESTART;
|
||||
sigaction (SIGCHLD, &action, &frida_host_session_test_linux_saved_sigchld_action);
|
||||
}
|
||||
|
||||
gboolean
|
||||
frida_host_session_test_linux_sigchld_handler_is_installed (void)
|
||||
{
|
||||
struct sigaction action;
|
||||
|
||||
sigaction (SIGCHLD, NULL, &action);
|
||||
|
||||
return action.sa_handler == frida_host_session_test_linux_on_sigchld;
|
||||
}
|
||||
|
||||
void
|
||||
frida_host_session_test_linux_reset_sigchld_handler (void)
|
||||
{
|
||||
struct sigaction action = { 0, };
|
||||
|
||||
action.sa_handler = SIG_DFL;
|
||||
sigemptyset (&action.sa_mask);
|
||||
sigaction (SIGCHLD, &action, &frida_host_session_test_linux_saved_sigchld_action);
|
||||
}
|
||||
|
||||
void
|
||||
frida_host_session_test_linux_restore_sigchld_handler (void)
|
||||
{
|
||||
sigaction (SIGCHLD, &frida_host_session_test_linux_saved_sigchld_action, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
frida_host_session_test_linux_on_sigchld (int sig)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -113,6 +113,16 @@ namespace Frida.HostSessionTest {
|
||||
h.run ();
|
||||
});
|
||||
|
||||
GLib.Test.add_func ("/HostSession/Linux/spawn-should-not-replace-sigchld-handler", () => {
|
||||
var h = new Harness ((h) => Linux.spawn_should_not_replace_sigchld_handler.begin (h as Harness));
|
||||
h.run ();
|
||||
});
|
||||
|
||||
GLib.Test.add_func ("/HostSession/Linux/spawned-process-should-be-reaped-after-sigchld-reset", () => {
|
||||
var h = new Harness ((h) => Linux.spawned_process_should_be_reaped_after_sigchld_reset.begin (h as Harness));
|
||||
h.run ();
|
||||
});
|
||||
|
||||
GLib.Test.add_func ("/HostSession/Linux/ChildGating/fork", () => {
|
||||
var h = new Harness ((h) => Linux.fork.begin (h as Harness));
|
||||
h.run ();
|
||||
@@ -1742,6 +1752,81 @@ namespace Frida.HostSessionTest {
|
||||
h.done ();
|
||||
}
|
||||
|
||||
private async void spawn_should_not_replace_sigchld_handler (Harness h) {
|
||||
if (!can_run_sigchld_scenario ()) {
|
||||
h.done ();
|
||||
return;
|
||||
}
|
||||
|
||||
var backend = new LinuxHostSessionBackend ();
|
||||
|
||||
var prov = yield h.setup_local_backend (backend);
|
||||
|
||||
install_sigchld_handler ();
|
||||
|
||||
try {
|
||||
Cancellable? cancellable = null;
|
||||
|
||||
var host_session = yield prov.create (new NullHostSessionHub (), null, (status, progress) => {}, cancellable);
|
||||
|
||||
var pid = yield host_session.spawn (Frida.Test.Labrats.path_to_executable ("sleeper"), HostSpawnOptions (),
|
||||
cancellable);
|
||||
yield host_session.resume (pid, cancellable);
|
||||
assert_true (sigchld_handler_is_installed ());
|
||||
|
||||
yield host_session.kill (pid, cancellable);
|
||||
var reaped = yield wait_until_reaped (pid);
|
||||
assert_true (reaped);
|
||||
assert_true (sigchld_handler_is_installed ());
|
||||
} catch (GLib.Error e) {
|
||||
printerr ("Unexpected error: %s\n", e.message);
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
restore_sigchld_handler ();
|
||||
|
||||
yield h.teardown_backend (backend);
|
||||
|
||||
h.done ();
|
||||
}
|
||||
|
||||
private async void spawned_process_should_be_reaped_after_sigchld_reset (Harness h) {
|
||||
if (!can_run_sigchld_scenario ()) {
|
||||
h.done ();
|
||||
return;
|
||||
}
|
||||
|
||||
var backend = new LinuxHostSessionBackend ();
|
||||
|
||||
var prov = yield h.setup_local_backend (backend);
|
||||
|
||||
try {
|
||||
Cancellable? cancellable = null;
|
||||
|
||||
var host_session = yield prov.create (new NullHostSessionHub (), null, (status, progress) => {}, cancellable);
|
||||
|
||||
var pid = yield host_session.spawn (Frida.Test.Labrats.path_to_executable ("sleeper"), HostSpawnOptions (),
|
||||
cancellable);
|
||||
yield host_session.resume (pid, cancellable);
|
||||
|
||||
/* As libuv does once its last child has exited. */
|
||||
reset_sigchld_handler ();
|
||||
|
||||
yield host_session.kill (pid, cancellable);
|
||||
var reaped = yield wait_until_reaped (pid);
|
||||
assert_true (reaped);
|
||||
} catch (GLib.Error e) {
|
||||
printerr ("Unexpected error: %s\n", e.message);
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
restore_sigchld_handler ();
|
||||
|
||||
yield h.teardown_backend (backend);
|
||||
|
||||
h.done ();
|
||||
}
|
||||
|
||||
private async void fork (Harness h) {
|
||||
yield Unix.run_fork_scenario (h, Frida.Test.Labrats.path_to_executable ("forker"));
|
||||
}
|
||||
@@ -1758,6 +1843,40 @@ namespace Frida.HostSessionTest {
|
||||
yield Unix.run_exec_scenario (h, Frida.Test.Labrats.path_to_executable ("spawner"), "spawn-bad-then-good-path", "execv");
|
||||
}
|
||||
|
||||
private bool can_run_sigchld_scenario () {
|
||||
if (!GLib.Test.slow () && (
|
||||
Frida.Test.os () == Frida.Test.OS.ANDROID ||
|
||||
Frida.Test.os_arch_suffix () == "-linux-armbe8" ||
|
||||
Frida.Test.os_arch_suffix () == "-linux-arm64be"
|
||||
)) {
|
||||
stdout.printf ("<skipping, run in slow mode> ");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!PidFileDescriptor.is_supported ()) {
|
||||
stdout.printf ("<skipping, kernel lacks pidfd support> ");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async bool wait_until_reaped (uint pid) {
|
||||
var timer = new Timer ();
|
||||
while (FileUtils.test ("/proc/%u".printf (pid), EXISTS)) {
|
||||
if (timer.elapsed () >= 5.0)
|
||||
return false;
|
||||
Timeout.add (50, wait_until_reaped.callback);
|
||||
yield;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private extern void install_sigchld_handler ();
|
||||
private extern bool sigchld_handler_is_installed ();
|
||||
private extern void reset_sigchld_handler ();
|
||||
private extern void restore_sigchld_handler ();
|
||||
|
||||
namespace Manual {
|
||||
|
||||
private async void spawn_android_app (Harness h) {
|
||||
|
||||
@@ -22,6 +22,10 @@ namespace Frida.InjectorTest {
|
||||
|
||||
GLib.Test.add_func ("/Injector/resource-leaks", test_resource_leaks);
|
||||
|
||||
#if LINUX
|
||||
GLib.Test.add_func ("/Injector/Linux/inject-with-non-utf8-mapping", test_injection_with_non_utf8_mapping);
|
||||
#endif
|
||||
|
||||
#if DARWIN
|
||||
GLib.Test.add_func ("/Injector/suspended-injection-current-arch", () => {
|
||||
test_suspended_injection (Frida.Test.Arch.CURRENT);
|
||||
@@ -149,6 +153,45 @@ namespace Frida.InjectorTest {
|
||||
rat.close ();
|
||||
}
|
||||
|
||||
#if LINUX
|
||||
private void test_injection_with_non_utf8_mapping () {
|
||||
var logfile = File.new_for_path (Frida.Test.path_to_temporary_file ("non-utf8-mapping.log"));
|
||||
try {
|
||||
logfile.delete ();
|
||||
} catch (GLib.Error delete_error) {
|
||||
}
|
||||
var envp = new string[] {
|
||||
"FRIDA_LABRAT_LOGFILE=" + logfile.get_path ()
|
||||
};
|
||||
|
||||
var sleeper = File.new_for_path (Frida.Test.Labrats.path_to_executable ("sleeper"));
|
||||
var sleeper_copy = File.new_for_path (Frida.Test.path_to_temporary_file ("sleeper-\xff"));
|
||||
try {
|
||||
sleeper.copy (sleeper_copy, OVERWRITE);
|
||||
} catch (GLib.Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
FileUtils.chmod (sleeper_copy.get_path (), 0700);
|
||||
|
||||
var rat = new Labrat.from_path (sleeper_copy.get_path (), envp);
|
||||
|
||||
rat.inject ("simple-agent", "");
|
||||
rat.wait_for_uninject ();
|
||||
assert_true (content_of (logfile) == ">m<");
|
||||
|
||||
try {
|
||||
rat.process.kill ();
|
||||
|
||||
logfile.delete ();
|
||||
sleeper_copy.delete ();
|
||||
} catch (GLib.Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
rat.close ();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DARWIN
|
||||
private void test_suspended_injection (Frida.Test.Arch arch) {
|
||||
var logfile = File.new_for_path (Frida.Test.path_to_temporary_file ("suspended-injection.log"));
|
||||
@@ -193,8 +236,12 @@ namespace Frida.InjectorTest {
|
||||
private PendingUninject? pending_uninject;
|
||||
|
||||
public Labrat (string name, string[] envp, Frida.Test.Arch arch = Frida.Test.Arch.CURRENT) {
|
||||
this.from_path (Frida.Test.Labrats.path_to_executable (name), envp, arch);
|
||||
}
|
||||
|
||||
public Labrat.from_path (string path, string[] envp, Frida.Test.Arch arch = Frida.Test.Arch.CURRENT) {
|
||||
try {
|
||||
process = Frida.Test.Process.start (Frida.Test.Labrats.path_to_executable (name), null, envp, arch);
|
||||
process = Frida.Test.Process.start (path, null, envp, arch);
|
||||
} catch (Error e) {
|
||||
printerr ("\nFAIL: %s\n\n", e.message);
|
||||
assert_not_reached ();
|
||||
|
||||
Reference in new issue
Block a user