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.
- checksums.yaml +4 -4
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +25 -0
- data/LICENSE.txt +21 -0
- data/README.md +64 -0
- data/Rakefile +20 -0
- data/exe/frida +3 -0
- data/ext/c_frida/Application.c +79 -0
- data/ext/c_frida/Bus.c +91 -0
- data/ext/c_frida/Child.c +134 -0
- data/ext/c_frida/Compiler.c +208 -0
- data/ext/c_frida/Crash.c +92 -0
- data/ext/c_frida/Device.c +955 -0
- data/ext/c_frida/DeviceManager.c +260 -0
- data/ext/c_frida/EndpointParameters.c +189 -0
- data/ext/c_frida/FileMonitor.c +67 -0
- data/ext/c_frida/GObject.c +138 -0
- data/ext/c_frida/IOStream.c +228 -0
- data/ext/c_frida/PortalMembership.c +43 -0
- data/ext/c_frida/PortalService.c +413 -0
- data/ext/c_frida/Process.c +67 -0
- data/ext/c_frida/Relay.c +121 -0
- data/ext/c_frida/Script.c +221 -0
- data/ext/c_frida/Session.c +626 -0
- data/ext/c_frida/Spawn.c +53 -0
- data/ext/c_frida/c_frida.c +68 -0
- data/ext/c_frida/extconf.rb +26 -25
- data/ext/c_frida/gutils.c +498 -0
- data/ext/c_frida/gvl_bridge.c +131 -0
- data/ext/c_frida/inc/Application.h +9 -0
- data/ext/c_frida/inc/Bus.h +15 -0
- data/ext/c_frida/inc/Child.h +9 -0
- data/ext/c_frida/inc/Compiler.h +21 -0
- data/ext/c_frida/inc/Crash.h +9 -0
- data/ext/c_frida/inc/Device.h +71 -0
- data/ext/c_frida/inc/DeviceManager.h +20 -0
- data/ext/c_frida/inc/EndpointParameters.h +15 -0
- data/ext/c_frida/inc/FileMonitor.h +9 -0
- data/ext/c_frida/inc/GObject.h +10 -0
- data/ext/c_frida/inc/IOStream.h +29 -0
- data/ext/c_frida/inc/PortalMembership.h +9 -0
- data/ext/c_frida/inc/PortalService.h +47 -0
- data/ext/c_frida/inc/Process.h +9 -0
- data/ext/c_frida/inc/Relay.h +9 -0
- data/ext/c_frida/inc/Script.h +21 -0
- data/ext/c_frida/inc/Session.h +40 -0
- data/ext/c_frida/inc/Spawn.h +9 -0
- data/ext/c_frida/inc/c_frida.h +129 -0
- data/ext/c_frida/inc/gutils.h +21 -0
- data/ext/c_frida/inc/gvl_bridge.h +42 -0
- data/frida.gemspec +39 -0
- data/lib/frida/version.rb +5 -0
- data/lib/frida.rb +8 -0
- 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
|
+
}
|