From f1b4cb8b2003e0699b8ebf1b116a780e96e1229d Mon Sep 17 00:00:00 2001 From: dylanhitt Date: Thu, 19 Sep 2024 14:50:47 -0400 Subject: [PATCH 1/2] feat: add the ability to cancel params --- frida/device.go | 30 +++++++++++++++++++----------- frida/misc.go | 26 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/frida/device.go b/frida/device.go index aaf6aae..cd36be3 100644 --- a/frida/device.go +++ b/frida/device.go @@ -18,7 +18,7 @@ type DeviceInt interface { Bus() *Bus Manager() *DeviceManager IsLost() bool - Params() (map[string]any, error) + Params(opts ...OptFunc) (map[string]any, error) FrontmostApplication(scope Scope) (*Application, error) EnumerateApplications(identifier string, scope Scope) ([]*Application, error) ProcessByPID(pid int, scope Scope) (*Process, error) @@ -113,19 +113,27 @@ func (d *Device) IsLost() bool { } // Params returns system parameters of the device -func (d *Device) Params() (map[string]any, error) { - if d.device != nil { - var err *C.GError - ht := C.frida_device_query_system_parameters_sync(d.device, nil, &err) - if err != nil { - return nil, &FError{err} - } +func (d *Device) Params(opts ...OptFunc) (map[string]any, error) { + o := &options{} + for _, opt := range opts { // <--- this all can become a helper func + opt(o) + } - params := gHashTableToMap(ht) + return d.params(o) +} - return params, nil +func (d *Device) params(opts *options) (map[string]any, error) { + if d.device == nil { + return nil, errors.New("could not obtain params for nil device") } - return nil, errors.New("could not obtain params for nil device") + + var err *C.GError + ht := C.frida_device_query_system_parameters_sync(d.device, opts.cancellable, &err) + if err != nil { + return nil, &FError{err} + } + + return gHashTableToMap(ht), nil } // FrontmostApplication will return the frontmost application or the application in focus diff --git a/frida/misc.go b/frida/misc.go index bee19ce..46c954a 100644 --- a/frida/misc.go +++ b/frida/misc.go @@ -9,6 +9,32 @@ import ( "unsafe" ) +type Cancellable struct { + cancellable *C.GCancellable +} + +func NewCancellable() *Cancellable { + return &Cancellable{ + cancellable: C.g_cancellable_new(), + } +} + +func (c *Cancellable) Cancel() { + C.g_cancellable_cancel(c.cancellable) +} + +type options struct { + cancellable *C.GCancellable +} + +type OptFunc func(o *options) + +func WithCancel(cancel *Cancellable) OptFunc { + return func(o *options) { + o.cancellable = cancel.cancellable + } +} + // FError holds a pointer to GError type FError struct { error *C.GError From bca5e70498a12c0b2f13fde6255023e1c08cc07b Mon Sep 17 00:00:00 2001 From: dylanhitt Date: Thu, 19 Sep 2024 14:50:47 -0400 Subject: [PATCH 2/2] feat: add the ability to cancel params --- frida/device.go | 117 ++++++++++++++++++++++++++++++++---------------- frida/misc.go | 38 ++++++++++++++++ 2 files changed, 117 insertions(+), 38 deletions(-) diff --git a/frida/device.go b/frida/device.go index aaf6aae..317a24e 100644 --- a/frida/device.go +++ b/frida/device.go @@ -3,6 +3,7 @@ package frida //#include import "C" import ( + "context" "errors" "reflect" "runtime" @@ -18,9 +19,9 @@ type DeviceInt interface { Bus() *Bus Manager() *DeviceManager IsLost() bool - Params() (map[string]any, error) + Params(opts ...OptFunc) (map[string]any, error) FrontmostApplication(scope Scope) (*Application, error) - EnumerateApplications(identifier string, scope Scope) ([]*Application, error) + EnumerateApplications(identifier string, scope Scope, opts ...OptFunc) ([]*Application, error) ProcessByPID(pid int, scope Scope) (*Process, error) ProcessByName(name string, scope Scope) (*Process, error) FindProcessByPID(pid int, scope Scope) (*Process, error) @@ -112,20 +113,54 @@ func (d *Device) IsLost() bool { return false } -// Params returns system parameters of the device -func (d *Device) Params() (map[string]any, error) { - if d.device != nil { - var err *C.GError - ht := C.frida_device_query_system_parameters_sync(d.device, nil, &err) +func (d *Device) ParamsCtx(ctx context.Context) (map[string]any, error) { + paramC := make(chan map[string]any, 1) + errC := make(chan error, 1) + + c := NewCancellable() + go func() { + params, err := d.Params(WithCancel(c)) if err != nil { - return nil, &FError{err} + errC <- err + return } + paramC <- params + }() - params := gHashTableToMap(ht) + for { + select { + case <-ctx.Done(): + c.Cancel() + return nil, ErrContextCancelled + case params := <-paramC: + c.Unref() + return params, nil + case err := <-errC: + c.Unref() + return nil, err + } - return params, nil } - return nil, errors.New("could not obtain params for nil device") +} + +// Params returns system parameters of the device +func (d *Device) Params(opts ...OptFunc) (map[string]any, error) { + o := setupOptions(opts) + return d.params(o) +} + +func (d *Device) params(opts options) (map[string]any, error) { + if d.device == nil { + return nil, errors.New("could not obtain params for nil device") + } + + var err *C.GError + ht := C.frida_device_query_system_parameters_sync(d.device, opts.cancellable, &err) + if err != nil { + return nil, &FError{err} + } + + return gHashTableToMap(ht), nil } // FrontmostApplication will return the frontmost application or the application in focus @@ -155,42 +190,48 @@ func (d *Device) FrontmostApplication(scope Scope) (*Application, error) { return nil, errors.New("could not obtain frontmost app for nil device") } +func (d *Device) EnumerateApplications(identifier string, scope Scope, opts ...OptFunc) ([]*Application, error) { + o := setupOptions(opts) + return d.enumerateApplications(identifier, scope, o) +} + // EnumerateApplications will return slice of applications on the device -func (d *Device) EnumerateApplications(identifier string, scope Scope) ([]*Application, error) { +func (d *Device) enumerateApplications(identifier string, scope Scope, opts options) ([]*Application, error) { if d.device != nil { - queryOpts := C.frida_application_query_options_new() - C.frida_application_query_options_set_scope(queryOpts, C.FridaScope(scope)) + return nil, errors.New("could not enumerate applications for nil device") + } - if identifier != "" { - identifierC := C.CString(identifier) - defer C.free(unsafe.Pointer(identifierC)) - C.frida_application_query_options_select_identifier(queryOpts, identifierC) - } + queryOpts := C.frida_application_query_options_new() + C.frida_application_query_options_set_scope(queryOpts, C.FridaScope(scope)) - var err *C.GError - appList := C.frida_device_enumerate_applications_sync(d.device, queryOpts, nil, &err) - if err != nil { - return nil, &FError{err} - } + if identifier != "" { + identifierC := C.CString(identifier) + defer C.free(unsafe.Pointer(identifierC)) + C.frida_application_query_options_select_identifier(queryOpts, identifierC) + } - appListSize := int(C.frida_application_list_size(appList)) - apps := make([]*Application, appListSize) + var err *C.GError + appList := C.frida_device_enumerate_applications_sync(d.device, queryOpts, opts.cancellable, &err) + if err != nil { + return nil, &FError{err} + } - for i := 0; i < appListSize; i++ { - app := C.frida_application_list_get(appList, C.gint(i)) - apps[i] = &Application{app} - } + appListSize := int(C.frida_application_list_size(appList)) + apps := make([]*Application, appListSize) - sort.Slice(apps, func(i, j int) bool { - return apps[i].PID() > apps[j].PID() - }) + for i := 0; i < appListSize; i++ { + app := C.frida_application_list_get(appList, C.gint(i)) + apps[i] = &Application{app} + } - clean(unsafe.Pointer(queryOpts), unrefFrida) - clean(unsafe.Pointer(appList), unrefFrida) + sort.Slice(apps, func(i, j int) bool { + return apps[i].PID() > apps[j].PID() + }) - return apps, nil - } - return nil, errors.New("could not enumerate applications for nil device") + clean(unsafe.Pointer(queryOpts), unrefFrida) + clean(unsafe.Pointer(appList), unrefFrida) + + return apps, nil } // ProcessByPID returns the process by passed pid. diff --git a/frida/misc.go b/frida/misc.go index bee19ce..032f788 100644 --- a/frida/misc.go +++ b/frida/misc.go @@ -9,6 +9,44 @@ import ( "unsafe" ) +type Cancellable struct { + cancellable *C.GCancellable +} + +func NewCancellable() *Cancellable { + return &Cancellable{ + cancellable: C.g_cancellable_new(), + } +} + +func (c *Cancellable) Cancel() { + C.g_cancellable_cancel(c.cancellable) +} + +func (c *Cancellable) Unref() { + C.g_object_unref((C.gpointer)(c.cancellable)) +} + +type options struct { + cancellable *C.GCancellable +} + +func setupOptions(opts []OptFunc) options { + o := &options{} + for _, opt := range opts { + opt(o) + } + return *o +} + +type OptFunc func(o *options) + +func WithCancel(cancel *Cancellable) OptFunc { + return func(o *options) { + o.cancellable = cancel.cancellable + } +} + // FError holds a pointer to GError type FError struct { error *C.GError