frida 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ }