frida 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CODE_OF_CONDUCT.md +84 -0
  3. data/Gemfile +12 -0
  4. data/Gemfile.lock +25 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +64 -0
  7. data/Rakefile +20 -0
  8. data/exe/frida +3 -0
  9. data/ext/c_frida/Application.c +79 -0
  10. data/ext/c_frida/Bus.c +91 -0
  11. data/ext/c_frida/Child.c +134 -0
  12. data/ext/c_frida/Compiler.c +208 -0
  13. data/ext/c_frida/Crash.c +92 -0
  14. data/ext/c_frida/Device.c +955 -0
  15. data/ext/c_frida/DeviceManager.c +260 -0
  16. data/ext/c_frida/EndpointParameters.c +189 -0
  17. data/ext/c_frida/FileMonitor.c +67 -0
  18. data/ext/c_frida/GObject.c +138 -0
  19. data/ext/c_frida/IOStream.c +228 -0
  20. data/ext/c_frida/PortalMembership.c +43 -0
  21. data/ext/c_frida/PortalService.c +413 -0
  22. data/ext/c_frida/Process.c +67 -0
  23. data/ext/c_frida/Relay.c +121 -0
  24. data/ext/c_frida/Script.c +221 -0
  25. data/ext/c_frida/Session.c +626 -0
  26. data/ext/c_frida/Spawn.c +53 -0
  27. data/ext/c_frida/c_frida.c +68 -0
  28. data/ext/c_frida/extconf.rb +26 -25
  29. data/ext/c_frida/gutils.c +498 -0
  30. data/ext/c_frida/gvl_bridge.c +131 -0
  31. data/ext/c_frida/inc/Application.h +9 -0
  32. data/ext/c_frida/inc/Bus.h +15 -0
  33. data/ext/c_frida/inc/Child.h +9 -0
  34. data/ext/c_frida/inc/Compiler.h +21 -0
  35. data/ext/c_frida/inc/Crash.h +9 -0
  36. data/ext/c_frida/inc/Device.h +71 -0
  37. data/ext/c_frida/inc/DeviceManager.h +20 -0
  38. data/ext/c_frida/inc/EndpointParameters.h +15 -0
  39. data/ext/c_frida/inc/FileMonitor.h +9 -0
  40. data/ext/c_frida/inc/GObject.h +10 -0
  41. data/ext/c_frida/inc/IOStream.h +29 -0
  42. data/ext/c_frida/inc/PortalMembership.h +9 -0
  43. data/ext/c_frida/inc/PortalService.h +47 -0
  44. data/ext/c_frida/inc/Process.h +9 -0
  45. data/ext/c_frida/inc/Relay.h +9 -0
  46. data/ext/c_frida/inc/Script.h +21 -0
  47. data/ext/c_frida/inc/Session.h +40 -0
  48. data/ext/c_frida/inc/Spawn.h +9 -0
  49. data/ext/c_frida/inc/c_frida.h +129 -0
  50. data/ext/c_frida/inc/gutils.h +21 -0
  51. data/ext/c_frida/inc/gvl_bridge.h +42 -0
  52. data/frida.gemspec +39 -0
  53. data/lib/frida/version.rb +5 -0
  54. data/lib/frida.rb +8 -0
  55. metadata +55 -3
@@ -0,0 +1,260 @@
1
+ #include "DeviceManager.h"
2
+
3
+ VALUE DeviceManager_from_FridaDeviceManager(FridaDeviceManager *handle)
4
+ {
5
+ VALUE self;
6
+
7
+ if (!handle)
8
+ return (Qnil);
9
+ self = rb_class_new_instance(0, NULL, cDeviceManager);
10
+ GET_GOBJECT_DATA();
11
+ d->destroy(d->handle);
12
+ d->handle = handle;
13
+ return (self);
14
+ }
15
+
16
+ GVL_FREE_PROXY_FUNC(close_device_manager_err, FridaDeviceManager *device_manager)
17
+ {
18
+ GError *gerr = NULL;
19
+
20
+ frida_device_manager_close_sync(device_manager, NULL, &gerr);
21
+ RETURN_GVL_FREE_RESULT(NULL);
22
+ }
23
+
24
+ GVL_FREE_PROXY_FUNC(close_device_manager, FridaDeviceManager *device_manager)
25
+ {
26
+ frida_device_manager_close_sync(device_manager, NULL, NULL);
27
+ return (NULL);
28
+ }
29
+
30
+ static void DeviceManager_destroy(void *handle)
31
+ {
32
+ CALL_GVL_FREE(close_device_manager, handle);
33
+ frida_unref(handle);
34
+ }
35
+
36
+ /*
37
+ call-seq:
38
+ #close() -> nil
39
+ */
40
+ static VALUE DeviceManager_close(VALUE self)
41
+ {
42
+ GET_GOBJECT_DATA();
43
+ REQUIRE_GOBJECT_HANDLE();
44
+ CALL_GVL_FREE_WITH_RET(void *dummy, close_device_manager_err, d->handle);
45
+ return (Qnil);
46
+
47
+ GERROR_BLOCK
48
+ }
49
+
50
+ GVL_FREE_PROXY_FUNC(enumerate_devices_sync, FridaDeviceManager *device_manager)
51
+ {
52
+ GError *gerr = NULL;
53
+ FridaDeviceList * devices;
54
+
55
+ devices = frida_device_manager_enumerate_devices_sync(device_manager, NULL, &gerr);
56
+ RETURN_GVL_FREE_RESULT(devices);
57
+ }
58
+
59
+ /*
60
+ call-seq:
61
+ #enumerate_device() -> Array[Device]
62
+ */
63
+ static VALUE DeviceManager_enumerate_devices(VALUE self)
64
+ {
65
+ GET_GOBJECT_DATA();
66
+ REQUIRE_GOBJECT_HANDLE();
67
+ FridaDeviceList * devices;
68
+ int devices_count;
69
+ VALUE Devices_arr;
70
+
71
+ CALL_GVL_FREE_WITH_RET(devices, enumerate_devices_sync, d->handle);
72
+ devices_count = frida_device_list_size(devices);
73
+ Devices_arr = rb_ary_new_capa(devices_count);
74
+ for (int i = 0; i < devices_count; i++)
75
+ rb_ary_store(Devices_arr, i, Device_from_FridaDevice(frida_device_list_get(devices, i)));
76
+ frida_unref(devices);
77
+ return (Devices_arr);
78
+
79
+ GERROR_BLOCK
80
+ }
81
+
82
+ /*
83
+ call-seq:
84
+ #get_device_matching() {} -> Device
85
+ */
86
+ static VALUE DeviceManager_get_device_matching(VALUE self)
87
+ {
88
+ GET_GOBJECT_DATA();
89
+ REQUIRE_GOBJECT_HANDLE();
90
+ VALUE block;
91
+ VALUE devices, current_device, block_ret;
92
+ int devices_count;
93
+
94
+ if (!rb_block_given_p()) {
95
+ raise_argerror("a block argument is required");
96
+ return (Qnil);
97
+ }
98
+ block = rb_block_proc();
99
+ // NOTE: Ruby does not support (currently) setting up a non Ruby created thread to be able
100
+ // to call its C APIs, frida_device_manager_get_device_sync() would call our filtering predicate from a frida thread
101
+ // which is not created by Ruby, and GET_EC() call in the Ruby's C APIs would return NULL.
102
+ // so the filtering is done in this thread that currently holds the GVL, mimicking frida_device_manager_get_device_sync().
103
+ devices = rb_funcall(self, rb_intern("enumerate_devices"), 0, NULL);
104
+ devices_count = RARRAY_LEN(devices);
105
+ for (int i = 0; i < devices_count; i++) {
106
+ current_device = rb_ary_entry(devices, i);
107
+ block_ret = rb_funcall(block, rb_intern("call"), 1, current_device);
108
+ if (block_ret == Qtrue) return (current_device);
109
+ }
110
+ return (Qnil);
111
+ }
112
+
113
+ static FridaRemoteDeviceOptions *parse_add_rdevice_kws_to_options(VALUE kws)
114
+ {
115
+ FridaRemoteDeviceOptions *options;
116
+ VALUE cert, origin, token, keepalive_interval;
117
+
118
+ options = frida_remote_device_options_new();
119
+ cert = rb_hash_aref(kws, ID2SYM(rb_intern("certificate")));
120
+ if (!NIL_P(cert)) {
121
+ if (!RB_TYPE_P(cert, T_STRING)) {
122
+ raise_argerror("certificate should be a string.");
123
+ goto error;
124
+ }
125
+ GTlsCertificate * certificate;
126
+ if (!rbGObject_unmarshal_certificate(StringValueCStr(cert), &certificate))
127
+ goto error;
128
+ frida_remote_device_options_set_certificate(options, certificate);
129
+ g_object_unref(certificate);
130
+ }
131
+ origin = rb_hash_aref(kws, ID2SYM(rb_intern("origin")));
132
+ if (!NIL_P(origin)) {
133
+ if (!RB_TYPE_P(origin, T_STRING)) {
134
+ raise_argerror("origin should be a string.");
135
+ goto error;
136
+ }
137
+ frida_remote_device_options_set_origin(options, StringValueCStr(origin));
138
+ }
139
+ token = rb_hash_aref(kws, ID2SYM(rb_intern("token")));
140
+ if (!NIL_P(token)) {
141
+ if (!RB_TYPE_P(token, T_STRING)) {
142
+ raise_argerror("token should be a string.");
143
+ goto error;
144
+ }
145
+ frida_remote_device_options_set_token(options, StringValueCStr(token));
146
+ }
147
+ keepalive_interval = rb_hash_aref(kws, ID2SYM(rb_intern("keepalive_interval")));
148
+ if (!NIL_P(keepalive_interval)) {
149
+ if (!RB_TYPE_P(keepalive_interval, T_FIXNUM)) {
150
+ raise_argerror("keepalive_interval should be a number.");
151
+ goto error;
152
+ }
153
+ frida_remote_device_options_set_keepalive_interval(options, NUM2INT(keepalive_interval));
154
+ }
155
+ return (options);
156
+
157
+ error:
158
+ g_object_unref(options);
159
+ return (NULL);
160
+ }
161
+
162
+ GVL_FREE_PROXY_FUNC(add_remote_device_sync, add_remote_device_proxy_args *args)
163
+ {
164
+ GError *gerr = NULL;
165
+ FridaDevice *device;
166
+
167
+ device = frida_device_manager_add_remote_device_sync(args->device_manager, args->address, args->options, NULL, &gerr);
168
+ RETURN_GVL_FREE_RESULT(device);
169
+ }
170
+
171
+ /*
172
+ call-seq:
173
+ #add_remote_device(address, certificate:, origin:, token:, keepalive_interval:) -> Device
174
+ */
175
+ static VALUE DeviceManager_add_remote_device(int argc, VALUE *argv, VALUE self)
176
+ {
177
+ GET_GOBJECT_DATA();
178
+ REQUIRE_GOBJECT_HANDLE();
179
+ VALUE address, kws;
180
+ FridaRemoteDeviceOptions *options;
181
+ FridaDevice *device;
182
+
183
+ rb_scan_args(argc, argv, "1:", &address, &kws);
184
+ if (!RB_TYPE_P(address, T_STRING))
185
+ return (raise_argerror("address must be a string."), Qnil);
186
+ if (NIL_P(kws)) kws = rb_hash_new();
187
+ options = parse_add_rdevice_kws_to_options(kws);
188
+ if (!options)
189
+ return (Qnil);
190
+ add_remote_device_proxy_args args = {
191
+ .device_manager = d->handle,
192
+ .options = options,
193
+ .address = StringValueCStr(address)
194
+ };
195
+ CALL_GVL_FREE_WITH_RET(device, add_remote_device_sync, &args);
196
+ g_object_unref(options);
197
+ return (Device_from_FridaDevice(device));
198
+
199
+ gerror:
200
+ g_object_unref(options);
201
+ raise_rerror(NULL, _gerr);
202
+ return (Qnil);
203
+ }
204
+
205
+ GVL_FREE_PROXY_FUNC(remove_remote_device_sync, remove_remote_devices_proxy_args *args)
206
+ {
207
+ GError *gerr = NULL;
208
+
209
+ frida_device_manager_remove_remote_device_sync(args->device_manager, args->address, NULL, &gerr);
210
+ RETURN_GVL_FREE_RESULT(NULL);
211
+ }
212
+
213
+ /*
214
+ call-seq:
215
+ #remove_remote_device(address) -> nil
216
+ */
217
+ static VALUE DeviceManager_remove_remote_device(VALUE self, VALUE address)
218
+ {
219
+ GET_GOBJECT_DATA();
220
+ REQUIRE_GOBJECT_HANDLE();
221
+
222
+ if (!RB_TYPE_P(address, T_STRING)) {
223
+ raise_argerror("address should be a string.");
224
+ return (Qnil);
225
+ }
226
+ remove_remote_devices_proxy_args args = {
227
+ .device_manager = d->handle,
228
+ .address = StringValueCStr(address)
229
+ };
230
+ CALL_GVL_FREE_WITH_RET(void *dummy, remove_remote_device_sync, &args);
231
+ return (Qnil);
232
+
233
+ GERROR_BLOCK
234
+ }
235
+
236
+ /*
237
+ call-seq:
238
+ #new() -> DeviceManager
239
+ */
240
+ static VALUE DeviceManager_initialize(VALUE self)
241
+ {
242
+ GET_GOBJECT_DATA();
243
+ d->handle = frida_device_manager_new();
244
+ if (!d->handle)
245
+ return (raise_rerror("failed to retrieve a device manager.", NULL), Qnil);
246
+ d->destroy = DeviceManager_destroy;
247
+ return (self);
248
+ }
249
+
250
+ void define_DeviceManager()
251
+ {
252
+ cDeviceManager = rb_define_class_under(mCFrida, "DeviceManager", cGObject);
253
+
254
+ rb_define_method(cDeviceManager, "initialize", DeviceManager_initialize, 0);
255
+ rb_define_method(cDeviceManager, "enumerate_devices", DeviceManager_enumerate_devices, 0);
256
+ rb_define_method(cDeviceManager, "get_device_matching", DeviceManager_get_device_matching, 0);
257
+ rb_define_method(cDeviceManager, "add_remote_device", DeviceManager_add_remote_device, -1);
258
+ rb_define_method(cDeviceManager, "remove_remote_device", DeviceManager_remove_remote_device, 1);
259
+ rb_define_method(cDeviceManager, "close", DeviceManager_close, 0);
260
+ }
@@ -0,0 +1,189 @@
1
+ #include "EndpointParameters.h"
2
+
3
+ VALUE EndpointParameters_from_FridaEndpointParameters(FridaEndpointParameters *handle)
4
+ {
5
+ VALUE self;
6
+
7
+ if (!handle)
8
+ return (Qnil);
9
+ self = rb_class_new_instance(0, NULL, cEndpointParameters);
10
+ GET_GOBJECT_DATA();
11
+ d->handle = handle;
12
+ d->destroy = g_object_unref;
13
+ return (self);
14
+ }
15
+
16
+ static void frida_rb_authentication_service_iface_init (gpointer g_iface, gpointer iface_data);
17
+
18
+ G_DEFINE_TYPE_EXTENDED (FridaRBAuthenticationService, frida_rb_authentication_service, G_TYPE_OBJECT, 0,
19
+ G_IMPLEMENT_INTERFACE (FRIDA_TYPE_AUTHENTICATION_SERVICE, frida_rb_authentication_service_iface_init))
20
+
21
+ static void frida_rb_authentication_service_dispose (GObject * object);
22
+ static void frida_rb_authentication_service_authenticate (FridaAuthenticationService * service, const gchar * token, GCancellable * cancellable,
23
+ GAsyncReadyCallback callback, gpointer user_data);
24
+ static gchar *frida_rb_authentication_service_authenticate_finish (FridaAuthenticationService * service, GAsyncResult * result, GError ** error);
25
+
26
+ static FridaRBAuthenticationService *frida_rb_authentication_service_new(VALUE callback)
27
+ {
28
+ FridaRBAuthenticationService *service;
29
+
30
+ service = g_object_new(FRIDA_TYPE_RB_AUTHENTICATION_SERVICE, NULL);
31
+ service->callback = callback;
32
+
33
+ return (service);
34
+ }
35
+
36
+ static void frida_rb_authentication_service_class_init(FridaRBAuthenticationServiceClass * klass)
37
+ {
38
+ GObjectClass * object_class = G_OBJECT_CLASS (klass);
39
+
40
+ object_class->dispose = frida_rb_authentication_service_dispose;
41
+ }
42
+
43
+ static void frida_rb_authentication_service_iface_init (gpointer g_iface, gpointer iface_data)
44
+ {
45
+ puts("frida_rb_authentication_service_iface_init");
46
+ FridaAuthenticationServiceIface * iface = g_iface;
47
+
48
+ iface->authenticate = frida_rb_authentication_service_authenticate;
49
+ iface->authenticate_finish = frida_rb_authentication_service_authenticate_finish;
50
+ }
51
+
52
+ static void frida_rb_authentication_service_init (FridaRBAuthenticationService * self)
53
+ {
54
+ self->pool = g_thread_pool_new ((GFunc) gvl_bridge_forward_GT, self, 1, FALSE, NULL);
55
+ }
56
+
57
+ static void frida_rb_authentication_service_dispose (GObject * object)
58
+ {
59
+ FridaRBAuthenticationService * self = FRIDA_RB_AUTHENTICATION_SERVICE (object);
60
+
61
+ if (self->pool != NULL) {
62
+ g_thread_pool_free (self->pool, FALSE, FALSE);
63
+ self->pool = NULL;
64
+ }
65
+
66
+ G_OBJECT_CLASS (frida_rb_authentication_service_parent_class)->dispose (object);
67
+ }
68
+
69
+ static void frida_rb_authentication_service_authenticate (FridaAuthenticationService * service, const gchar * token, GCancellable * cancellable,
70
+ GAsyncReadyCallback callback, gpointer user_data)
71
+ {
72
+ FridaRBAuthenticationService * self;
73
+ GTask * task;
74
+
75
+ self = FRIDA_RB_AUTHENTICATION_SERVICE (service);
76
+
77
+ task = g_task_new (self, cancellable, callback, user_data);
78
+ g_task_set_task_data (task, g_strdup (token), g_free);
79
+
80
+ g_thread_pool_push (self->pool, task, NULL);
81
+ }
82
+
83
+ static gchar *frida_rb_authentication_service_authenticate_finish (FridaAuthenticationService * service, GAsyncResult * result, GError ** error)
84
+ {
85
+ return g_task_propagate_pointer (G_TASK (result), error);
86
+ }
87
+
88
+ /*
89
+ call-seq:
90
+ #new(address:, port:, certificate:, origin:, auth_token:, auth_callback:, asset_root:) -> EndpointParameters
91
+ */
92
+ static VALUE EndpointParameters_initialize(int argc, VALUE *argv, VALUE self)
93
+ {
94
+ GET_GOBJECT_DATA();
95
+ GTlsCertificate *certificate = NULL;
96
+ FridaAuthenticationService *auth_service = NULL;
97
+ GFile *asset_root = NULL;
98
+ unsigned short port = 0;
99
+ char *address = NULL, *origin = NULL, *auth_token = NULL;
100
+ VALUE kws;
101
+
102
+ rb_scan_args(argc, argv, ":", &kws);
103
+ if (!NIL_P(kws)) {
104
+ VALUE raddress = rb_hash_aref(kws, ID2SYM(rb_intern("address")));
105
+ if (!NIL_P(raddress)) {
106
+ if (!RB_TYPE_P(raddress, T_STRING)) {
107
+ raise_argerror("address must be a string.");
108
+ goto invalid_arg;
109
+ }
110
+ address = StringValueCStr(raddress);
111
+ }
112
+ VALUE rport = rb_hash_aref(kws, ID2SYM(rb_intern("port")));
113
+ if (!NIL_P(rport)) {
114
+ if (!RB_TYPE_P(rport, T_FIXNUM)) {
115
+ raise_argerror("port must be a number.");
116
+ goto invalid_arg;
117
+ }
118
+ port = (unsigned short)NUM2USHORT(rport);
119
+ }
120
+ VALUE rcert = rb_hash_aref(kws, ID2SYM(rb_intern("certificate")));
121
+ if (!NIL_P(rcert)) {
122
+ if (!RB_TYPE_P(rcert, T_STRING)) {
123
+ raise_argerror("certificate must be a string.");
124
+ goto invalid_arg;
125
+ }
126
+ char *cert = StringValueCStr(rcert);
127
+ if (!rbGObject_unmarshal_certificate(cert, &certificate)) {
128
+ raise_argerror("invalid certificate.");
129
+ goto invalid_arg;
130
+ }
131
+ }
132
+ VALUE rorigin = rb_hash_aref(kws, ID2SYM(rb_intern("origin")));
133
+ if (!NIL_P(rorigin)) {
134
+ if (!RB_TYPE_P(rorigin, T_STRING)) {
135
+ raise_argerror("origin must be a string.");
136
+ goto invalid_arg;
137
+ }
138
+ origin = StringValueCStr(rorigin);
139
+ }
140
+ VALUE rauth_token = rb_hash_aref(kws, ID2SYM(rb_intern("auth_token")));
141
+ if (!NIL_P(rauth_token)) {
142
+ if (!RB_TYPE_P(rauth_token, T_STRING)) {
143
+ raise_argerror("auth_token must be a string.");
144
+ goto invalid_arg;
145
+ }
146
+ auth_token = StringValueCStr(rauth_token);
147
+ }
148
+ VALUE rauth_callback = rb_hash_aref(kws, ID2SYM(rb_intern("auth_callback")));
149
+ if (!NIL_P(rauth_callback)) {
150
+ // currently support only methods.
151
+ if (rb_obj_is_kind_of(rauth_callback, rb_const_get(rb_cObject, rb_intern("Method"))) == Qfalse || \
152
+ NUM2INT(rb_funcall(rauth_callback, rb_intern("arity"), 0, NULL)) != 1) {
153
+ raise_argerror("auth_callback must be a method with one argument.");
154
+ goto invalid_arg;
155
+ }
156
+ // don't GC
157
+ rb_ivar_set(self, rb_intern("auth_callback"), rauth_callback);
158
+ }
159
+ if (auth_token)
160
+ auth_service = FRIDA_AUTHENTICATION_SERVICE(frida_static_authentication_service_new(auth_token));
161
+ else if (rauth_callback != Qnil)
162
+ auth_service = FRIDA_AUTHENTICATION_SERVICE(frida_rb_authentication_service_new(rauth_callback));
163
+ VALUE rasset_root = rb_hash_aref(kws, ID2SYM(rb_intern("asset_root")));
164
+ if (!NIL_P(rasset_root)) {
165
+ if (!RB_TYPE_P(rasset_root, T_STRING)) {
166
+ raise_argerror("asset_root must be a string.");
167
+ goto invalid_arg;
168
+ }
169
+ asset_root = g_file_new_for_path(StringValueCStr(rasset_root));
170
+ }
171
+ } else
172
+ return (self);
173
+ d->handle = frida_endpoint_parameters_new(address, port, certificate, origin, auth_service, asset_root);
174
+ d->destroy = g_object_unref;
175
+ return (self);
176
+
177
+ invalid_arg:
178
+ g_clear_object(&asset_root);
179
+ g_clear_object(&auth_service);
180
+ g_clear_object(&certificate);
181
+ return (Qnil);
182
+ }
183
+
184
+ void define_EndpointParameters()
185
+ {
186
+ cEndpointParameters = rb_define_class_under(mCFrida, "EndpointParameters", cGObject);
187
+
188
+ rb_define_method(cEndpointParameters, "initialize", EndpointParameters_initialize, -1);
189
+ }
@@ -0,0 +1,67 @@
1
+ #include "FileMonitor.h"
2
+
3
+ VALUE FileMonitor_from_FridaFileMonitor(FridaFileMonitor *handle)
4
+ {
5
+ VALUE self;
6
+
7
+ if (!handle)
8
+ return (Qnil);
9
+ self = rb_class_new_instance(0, NULL, cFileMonitor);
10
+ GET_GOBJECT_DATA();
11
+ d->handle = handle;
12
+ d->destroy = frida_unref;
13
+ return (self);
14
+ }
15
+
16
+ GVL_FREE_PROXY_FUNC(enable_sync, void *handle)
17
+ {
18
+ GError *gerr = NULL;
19
+
20
+ frida_file_monitor_enable_sync(handle, NULL, &gerr);
21
+ RETURN_GVL_FREE_RESULT(NULL);
22
+ }
23
+
24
+ /*
25
+ call-seq:
26
+ #enable() -> nil
27
+ */
28
+ static VALUE FileMonitor_enable(VALUE self)
29
+ {
30
+ GET_GOBJECT_DATA();
31
+ REQUIRE_GOBJECT_HANDLE();
32
+
33
+ CALL_GVL_FREE_WITH_RET(void *dummy, enable_sync, d->handle);
34
+ return (Qnil);
35
+
36
+ GERROR_BLOCK
37
+ }
38
+
39
+ GVL_FREE_PROXY_FUNC(disable_sync, void *handle)
40
+ {
41
+ GError *gerr = NULL;
42
+
43
+ frida_file_monitor_disable_sync(handle, NULL, &gerr);
44
+ RETURN_GVL_FREE_RESULT(NULL);
45
+ }
46
+
47
+ /*
48
+ call-seq:
49
+ #disable() -> nil
50
+ */
51
+ static VALUE FileMonitor_disable(VALUE self)
52
+ {
53
+ GET_GOBJECT_DATA();
54
+ REQUIRE_GOBJECT_HANDLE();
55
+
56
+ CALL_GVL_FREE_WITH_RET(void *dummy, disable_sync, d->handle);
57
+ return (Qnil);
58
+
59
+ GERROR_BLOCK
60
+ }
61
+
62
+ void define_FileMonitor()
63
+ {
64
+ cFileMonitor = rb_define_class_under(mCFrida, "FileMonitor", cGObject);
65
+ rb_define_method(cFileMonitor, "enable", FileMonitor_enable, 0);
66
+ rb_define_method(cFileMonitor, "disable", FileMonitor_disable, 0);
67
+ }
@@ -0,0 +1,138 @@
1
+ #include "GObject.h"
2
+
3
+ DEFINE_KLASS_DATA_TYPE(GObject);
4
+
5
+ void GObject_free(GObject_d *d)
6
+ {
7
+ if (d->handle)
8
+ d->destroy(d->handle);
9
+ xfree(d);
10
+ }
11
+
12
+ /*
13
+ call-seq:
14
+ #on(signal_name) {} -> nil
15
+ #on(signal_name, callback) -> nil
16
+ */
17
+ static VALUE GObject_on(int argc, VALUE *argv, VALUE self)
18
+ {
19
+ GET_DATA(GObject);
20
+ REQUIRE_GOBJECT_HANDLE();
21
+ VALUE signal_name, callback;
22
+ GType self_type = G_OBJECT_TYPE(d->handle);
23
+ guint signal_id;
24
+ bool is_lambda;
25
+ GSignalQuery squery;
26
+
27
+ if (rb_block_given_p()) {
28
+ // blocks are handy however we cannot compare them because they are unique
29
+ // so you cannot remove them later!
30
+ rb_scan_args(argc, argv, "1&", &signal_name, &callback);
31
+ } else {
32
+ rb_scan_args(argc, argv, "2", &signal_name, &callback);
33
+ // either a Method or Proc object.
34
+ if (!rb_respond_to(callback, rb_intern("call"))) {
35
+ raise_argerror("callback should be callable.");
36
+ return (Qnil);
37
+ }
38
+ }
39
+ if (!RB_TYPE_P(signal_name, T_STRING)) {
40
+ raise_argerror("signal name must be a string.");
41
+ return (Qnil);
42
+ }
43
+ signal_id = g_signal_lookup(StringValueCStr(signal_name), self_type);
44
+ if (!signal_id) {
45
+ raise_argerror("invalid signal name.");
46
+ return (Qnil);
47
+ }
48
+ is_lambda = (rb_obj_is_kind_of(callback, rb_const_get(rb_cObject, rb_intern("Method"))) == Qtrue) ? true : (rb_funcall(callback, rb_intern("lambda?"), 0, NULL) == Qtrue);
49
+ int arity = NUM2INT(rb_funcall(callback, rb_intern("arity"), 0, NULL));
50
+ g_signal_query(signal_id, &squery);
51
+ if (is_lambda && arity > (int)(squery.n_params + 1)) {
52
+ gchar *err = g_strdup_printf("callback expects too many arguments, signal expects %d.", squery.n_params);
53
+ raise_argerror(err);
54
+ g_free(err);
55
+ return (Qnil);
56
+ }
57
+ GClosure *closure = g_closure_new_simple(sizeof(RBClosure), (void*)callback);
58
+ g_closure_set_marshal(closure, (GClosureMarshal)gvl_bridge_forward_GC);
59
+ TO_RBCLOSURE(closure)->is_lambda = is_lambda;
60
+ TO_RBCLOSURE(closure)->signal_id = signal_id;
61
+ TO_RBCLOSURE(closure)->arity = arity;
62
+ g_signal_connect_closure_by_id(d->handle, signal_id, 0, closure, TRUE);
63
+ // objects generated on the fly (thus not referenced), e.g blocks `#on("signal", :id) {block}` / `#on("signal", &callable)`
64
+ // or Methods `#on("signal", method(:defined_fn))` or Procs `#on("signal", ->{})`
65
+ // will be picked up by the GC, since in Ruby's view there is no more usage/reference to them
66
+ // so we keep them in this hidden Hash.
67
+ if (rb_hash_aref(rb_iv_get(self, "callbacks"), signal_name) == Qnil)
68
+ rb_hash_aset(rb_iv_get(self, "callbacks"), signal_name, rb_ary_new());
69
+ rb_ary_push(rb_hash_aref(rb_iv_get(self, "callbacks"), signal_name), callback);
70
+ d->signal_closures = g_slist_prepend(d->signal_closures, closure);
71
+ return (Qnil);
72
+ }
73
+
74
+ static gint compare_callback(RBClosure *closure, VALUE callback)
75
+ {
76
+ return (rb_equal((VALUE)closure->gclosure.data, callback) == Qtrue ? 0 : -1);
77
+ }
78
+
79
+ /*
80
+ call-seq:
81
+ #off(signal_name, callback) -> nil
82
+ */
83
+ static VALUE GObject_off(int argc, VALUE *argv, VALUE self)
84
+ {
85
+ GET_DATA(GObject);
86
+ REQUIRE_GOBJECT_HANDLE();
87
+ VALUE signal_name, callback;
88
+ GType self_type = G_OBJECT_TYPE(d->handle);
89
+ guint signal_id;
90
+ GSList *callback_entry;
91
+ int is_block = rb_block_given_p();
92
+
93
+ // remember you cannot remove Proc objects that you do not hold reference to!
94
+ rb_scan_args(argc, argv, "2", &signal_name, &callback);
95
+ if (!RB_TYPE_P(signal_name, T_STRING)) {
96
+ raise_argerror("signal name must be a string.");
97
+ return (Qnil);
98
+ }
99
+ signal_id = g_signal_lookup(StringValueCStr(signal_name), self_type);
100
+ if (!signal_id) {
101
+ raise_argerror("invalid signal name.");
102
+ return (Qnil);
103
+ }
104
+ if (rb_respond_to(callback, rb_intern("call"))) {
105
+ callback_entry = g_slist_find_custom(d->signal_closures, (gpointer)callback, (GCompareFunc)compare_callback);
106
+ if (!callback_entry) {
107
+ raise_argerror("callback not found.");
108
+ return (Qnil);
109
+ }
110
+ } else {
111
+ raise_argerror("second argument should be callable.");
112
+ return (Qnil);
113
+ }
114
+ GClosure *closure = callback_entry->data;
115
+ d->signal_closures = g_slist_delete_link(d->signal_closures, callback_entry);
116
+ g_signal_handlers_disconnect_matched(d->handle, G_SIGNAL_MATCH_CLOSURE, signal_id, 0, closure, NULL, NULL);
117
+ // this will delete a duplicate callback for a signal, but that is fine ?
118
+ rb_ary_delete(rb_hash_aref(rb_iv_get(self, "callbacks"), signal_name), callback);
119
+ return (Qnil);
120
+ }
121
+
122
+ static VALUE GObject_alloc(VALUE klass)
123
+ {
124
+ MAKE_DATA(GObject);
125
+ d->handle = NULL;
126
+ d->destroy = NULL;
127
+ d->signal_closures = NULL;
128
+ rb_ivar_set(obj, rb_intern("callbacks"), rb_hash_new());
129
+ return (obj);
130
+ }
131
+
132
+ void define_GObject()
133
+ {
134
+ cGObject = rb_define_class_under(mCFrida, "GObject", rb_cObject);
135
+ rb_define_alloc_func(cGObject, GObject_alloc);
136
+ rb_define_method(cGObject, "on", GObject_on, -1);
137
+ rb_define_method(cGObject, "off", GObject_off, -1);
138
+ }