From ad624be5ee01da333a13a1e74da5faa3ef518c9b Mon Sep 17 00:00:00 2001 From: dylanhitt Date: Thu, 25 Jan 2024 01:59:16 -0500 Subject: [PATCH] feat: provide interface frida.Device --- frida/device.go | 93 ++++++++++++++++++++++++++-------------- frida/frida.go | 14 +++--- frida/manager.go | 50 ++++++++++----------- frida/portal_service.go | 4 +- frida/types_converter.go | 2 +- 5 files changed, 97 insertions(+), 66 deletions(-) diff --git a/frida/device.go b/frida/device.go index 1ca79c0..d74debb 100644 --- a/frida/device.go +++ b/frida/device.go @@ -10,13 +10,44 @@ import ( "unsafe" ) -// Device represents FridaDevice struct from frida-core -type Device struct { +type Device interface { + ID() string + Name() string + DeviceIcon() any + Bus() *Bus + Manager() DeviceManager + IsLost() bool + Params() (map[string]any, error) + FrontmostApplication(scope Scope) (*Application, error) + EnumerateApplications(identifier string, scope Scope) ([]*Application, error) + ProcessByPID(pid int, scope Scope) (*Process, error) + ProcessByName(name string, scope Scope) (*Process, error) + FindProcessByPID(pid int, scope Scope) (*Process, error) + FindProcessByName(name string, scope Scope) (*Process, error) + EnumerateProcesses(scope Scope) ([]*Process, error) + EnableSpawnGating() error + DisableSpawnGating() error + EnumeratePendingSpawn() ([]*Spawn, error) + EnumeratePendingChildren() ([]*Child, error) + Spawn(name string, opts *SpawnOptions) (int, error) + Input(pid int, data []byte) error + Resume(pid int) error + Kill(pid int) error + Attach(val any, opts *SessionOptions) (*Session, error) + InjectLibraryFile(target any, path, entrypoint, data string) (uint, error) + InjectLibraryBlob(target any, byteData []byte, entrypoint, data string) (uint, error) + OpenChannel(address string) (*IOStream, error) + Clean() + On(sigName string, fn any) +} + +// DeviceImpl represents DeviceImpl struct from frida-core +type DeviceImpl struct { device *C.FridaDevice } // ID will return the ID of the device. -func (d *Device) ID() string { +func (d *DeviceImpl) ID() string { if d.device != nil { return C.GoString(C.frida_device_get_id(d.device)) } @@ -24,7 +55,7 @@ func (d *Device) ID() string { } // Name will return the name of the device. -func (d *Device) Name() string { +func (d *DeviceImpl) Name() string { if d.device != nil { return C.GoString(C.frida_device_get_name(d.device)) } @@ -32,7 +63,7 @@ func (d *Device) Name() string { } // DeviceIcon will return the device icon. -func (d *Device) DeviceIcon() any { +func (d *DeviceImpl) DeviceIcon() any { if d.device != nil { icon := C.frida_device_get_icon(d.device) dt := gPointerToGo((C.gpointer)(icon)) @@ -42,7 +73,7 @@ func (d *Device) DeviceIcon() any { } // DeviceType returns type of the device. -func (d *Device) DeviceType() DeviceType { +func (d *DeviceImpl) DeviceType() DeviceType { if d.device != nil { fdt := C.frida_device_get_dtype(d.device) return DeviceType(fdt) @@ -51,7 +82,7 @@ func (d *Device) DeviceType() DeviceType { } // Bus returns device bus. -func (d *Device) Bus() *Bus { +func (d *DeviceImpl) Bus() *Bus { if d.device != nil { bus := C.frida_device_get_bus(d.device) return &Bus{ @@ -62,7 +93,7 @@ func (d *Device) Bus() *Bus { } // Manager returns device manager for the device. -func (d *Device) Manager() DeviceManager { +func (d *DeviceImpl) Manager() DeviceManager { if d.device != nil { mgr := C.frida_device_get_manager(d.device) return &deviceManager{mgr} @@ -71,7 +102,7 @@ func (d *Device) Manager() DeviceManager { } // IsLost returns boolean whether device is lost or not. -func (d *Device) IsLost() bool { +func (d *DeviceImpl) IsLost() bool { if d.device != nil { lost := C.frida_device_is_lost(d.device) return int(lost) == 1 @@ -80,7 +111,7 @@ func (d *Device) IsLost() bool { } // Params returns system parameters of the device -func (d *Device) Params() (map[string]any, error) { +func (d *DeviceImpl) 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) @@ -97,7 +128,7 @@ func (d *Device) Params() (map[string]any, error) { // FrontmostApplication will return the frontmost application or the application in focus // on the device. -func (d *Device) FrontmostApplication(scope Scope) (*Application, error) { +func (d *DeviceImpl) FrontmostApplication(scope Scope) (*Application, error) { if d.device != nil { var err *C.GError app := &Application{} @@ -123,7 +154,7 @@ func (d *Device) FrontmostApplication(scope Scope) (*Application, error) { } // EnumerateApplications will return slice of applications on the device -func (d *Device) EnumerateApplications(identifier string, scope Scope) ([]*Application, error) { +func (d *DeviceImpl) EnumerateApplications(identifier string, scope Scope) ([]*Application, error) { if d.device != nil { queryOpts := C.frida_application_query_options_new() C.frida_application_query_options_set_scope(queryOpts, C.FridaScope(scope)) @@ -161,7 +192,7 @@ func (d *Device) EnumerateApplications(identifier string, scope Scope) ([]*Appli } // ProcessByPID returns the process by passed pid. -func (d *Device) ProcessByPID(pid int, scope Scope) (*Process, error) { +func (d *DeviceImpl) ProcessByPID(pid int, scope Scope) (*Process, error) { if d.device != nil { opts := C.frida_process_match_options_new() C.frida_process_match_options_set_timeout(opts, C.gint(defaultProcessTimeout)) @@ -179,7 +210,7 @@ func (d *Device) ProcessByPID(pid int, scope Scope) (*Process, error) { } // ProcessByName returns the process by passed name. -func (d *Device) ProcessByName(name string, scope Scope) (*Process, error) { +func (d *DeviceImpl) ProcessByName(name string, scope Scope) (*Process, error) { if d.device != nil { nameC := C.CString(name) defer C.free(unsafe.Pointer(nameC)) @@ -200,7 +231,7 @@ func (d *Device) ProcessByName(name string, scope Scope) (*Process, error) { } // FindProcessByPID will try to find the process with given pid. -func (d *Device) FindProcessByPID(pid int, scope Scope) (*Process, error) { +func (d *DeviceImpl) FindProcessByPID(pid int, scope Scope) (*Process, error) { if d.device != nil { opts := C.frida_process_match_options_new() C.frida_process_match_options_set_timeout(opts, C.gint(defaultProcessTimeout)) @@ -218,7 +249,7 @@ func (d *Device) FindProcessByPID(pid int, scope Scope) (*Process, error) { } // FindProcessByName will try to find the process with name specified. -func (d *Device) FindProcessByName(name string, scope Scope) (*Process, error) { +func (d *DeviceImpl) FindProcessByName(name string, scope Scope) (*Process, error) { if d.device != nil { nameC := C.CString(name) defer C.free(unsafe.Pointer(nameC)) @@ -239,7 +270,7 @@ func (d *Device) FindProcessByName(name string, scope Scope) (*Process, error) { } // EnumerateProcesses will slice of processes running with scope provided -func (d *Device) EnumerateProcesses(scope Scope) ([]*Process, error) { +func (d *DeviceImpl) EnumerateProcesses(scope Scope) ([]*Process, error) { if d.device != nil { opts := C.frida_process_query_options_new() C.frida_process_query_options_set_scope(opts, C.FridaScope(scope)) @@ -266,7 +297,7 @@ func (d *Device) EnumerateProcesses(scope Scope) ([]*Process, error) { } // EnableSpawnGating will enable spawn gating on the device. -func (d *Device) EnableSpawnGating() error { +func (d *DeviceImpl) EnableSpawnGating() error { if d.device != nil { var err *C.GError C.frida_device_enable_spawn_gating_sync(d.device, nil, &err) @@ -279,7 +310,7 @@ func (d *Device) EnableSpawnGating() error { } // DisableSpawnGating will disable spawn gating on the device. -func (d *Device) DisableSpawnGating() error { +func (d *DeviceImpl) DisableSpawnGating() error { if d.device != nil { var err *C.GError C.frida_device_disable_spawn_gating_sync(d.device, nil, &err) @@ -292,7 +323,7 @@ func (d *Device) DisableSpawnGating() error { } // EnumeratePendingSpawn will return the slice of pending spawns. -func (d *Device) EnumeratePendingSpawn() ([]*Spawn, error) { +func (d *DeviceImpl) EnumeratePendingSpawn() ([]*Spawn, error) { if d.device != nil { var err *C.GError spawnList := C.frida_device_enumerate_pending_spawn_sync(d.device, nil, &err) @@ -315,7 +346,7 @@ func (d *Device) EnumeratePendingSpawn() ([]*Spawn, error) { } // EnumeratePendingChildren will return the slice of pending children. -func (d *Device) EnumeratePendingChildren() ([]*Child, error) { +func (d *DeviceImpl) EnumeratePendingChildren() ([]*Child, error) { if d.device != nil { var err *C.GError childList := C.frida_device_enumerate_pending_children_sync(d.device, nil, &err) @@ -338,7 +369,7 @@ func (d *Device) EnumeratePendingChildren() ([]*Child, error) { } // Spawn will spawn an application or binary. -func (d *Device) Spawn(name string, opts *SpawnOptions) (int, error) { +func (d *DeviceImpl) Spawn(name string, opts *SpawnOptions) (int, error) { if d.device != nil { var opt *C.FridaSpawnOptions = nil if opts != nil { @@ -361,7 +392,7 @@ func (d *Device) Spawn(name string, opts *SpawnOptions) (int, error) { } // Input inputs []bytes into the process with pid specified. -func (d *Device) Input(pid int, data []byte) error { +func (d *DeviceImpl) Input(pid int, data []byte) error { if d.device != nil { gBytesData := goBytesToGBytes(data) runtime.SetFinalizer(gBytesData, func(g *C.GBytes) { @@ -380,7 +411,7 @@ func (d *Device) Input(pid int, data []byte) error { } // Resume will resume the process with pid. -func (d *Device) Resume(pid int) error { +func (d *DeviceImpl) Resume(pid int) error { if d.device != nil { var err *C.GError C.frida_device_resume_sync(d.device, C.guint(pid), nil, &err) @@ -393,7 +424,7 @@ func (d *Device) Resume(pid int) error { } // Kill kills process with pid specified. -func (d *Device) Kill(pid int) error { +func (d *DeviceImpl) Kill(pid int) error { if d.device != nil { var err *C.GError C.frida_device_kill_sync(d.device, C.guint(pid), nil, &err) @@ -408,7 +439,7 @@ func (d *Device) Kill(pid int) error { // Attach will attach on specified process name or PID. // You can pass the nil as SessionOptions or you can create it if you want // the session to persist for specific timeout. -func (d *Device) Attach(val any, opts *SessionOptions) (*Session, error) { +func (d *DeviceImpl) Attach(val any, opts *SessionOptions) (*Session, error) { if d.device != nil { var pid int switch v := reflect.ValueOf(val); v.Kind() { @@ -443,7 +474,7 @@ func (d *Device) Attach(val any, opts *SessionOptions) (*Session, error) { // InjectLibraryFile will inject the library in the target with path to library specified. // Entrypoint is the entrypoint to the library and the data is any data you need to pass // to the library. -func (d *Device) InjectLibraryFile(target any, path, entrypoint, data string) (uint, error) { +func (d *DeviceImpl) InjectLibraryFile(target any, path, entrypoint, data string) (uint, error) { if d.device != nil { var pid int switch v := reflect.ValueOf(target); v.Kind() { @@ -500,7 +531,7 @@ func (d *Device) InjectLibraryFile(target any, path, entrypoint, data string) (u // InjectLibraryBlob will inject the library in the target with byteData path. // Entrypoint is the entrypoint to the library and the data is any data you need to pass // to the library. -func (d *Device) InjectLibraryBlob(target any, byteData []byte, entrypoint, data string) (uint, error) { +func (d *DeviceImpl) InjectLibraryBlob(target any, byteData []byte, entrypoint, data string) (uint, error) { if d.device != nil { var pid int switch v := reflect.ValueOf(target); v.Kind() { @@ -557,7 +588,7 @@ func (d *Device) InjectLibraryBlob(target any, byteData []byte, entrypoint, data } // OpenChannel open channel with the address and returns the IOStream -func (d *Device) OpenChannel(address string) (*IOStream, error) { +func (d *DeviceImpl) OpenChannel(address string) (*IOStream, error) { if d.device != nil { addressC := C.CString(address) defer C.free(unsafe.Pointer(addressC)) @@ -573,7 +604,7 @@ func (d *Device) OpenChannel(address string) (*IOStream, error) { } // Clean will clean the resources held by the device. -func (d *Device) Clean() { +func (d *DeviceImpl) Clean() { if d.device != nil { clean(unsafe.Pointer(d.device), unrefFrida) } @@ -591,7 +622,7 @@ func (d *Device) Clean() { // - "output" with callback as func(pid, fd int, data []byte) {} // - "uninjected" with callback as func(id int) {} // - "lost" with callback as func() {} -func (d *Device) On(sigName string, fn any) { +func (d *DeviceImpl) On(sigName string, fn any) { if d.device != nil { connectClosure(unsafe.Pointer(d.device), sigName, fn) } diff --git a/frida/frida.go b/frida/frida.go index 87655d6..20f3aef 100644 --- a/frida/frida.go +++ b/frida/frida.go @@ -41,11 +41,11 @@ func getDeviceManager() DeviceManager { data.Store("mgr", mgr) return mgr } - return v.(DeviceManager) + return v.(*deviceManager) } // LocalDevice is a wrapper around DeviceByType(DeviceTypeLocal). -func LocalDevice() *Device { +func LocalDevice() Device { mgr := getDeviceManager() v, ok := data.Load("localDevice") if !ok { @@ -53,11 +53,11 @@ func LocalDevice() *Device { data.Store("localDevice", dev) return dev } - return v.(*Device) + return v.(*DeviceImpl) } // USBDevice is a wrapper around DeviceByType(DeviceTypeUsb). -func USBDevice() *Device { +func USBDevice() Device { mgr := getDeviceManager() v, ok := data.Load("usbDevice") if !ok { @@ -73,11 +73,11 @@ func USBDevice() *Device { data.Store("usbDevice", dev) return dev } - return v.(*Device) + return v.(*DeviceImpl) } // DeviceByID tries to get the device by id on the default manager -func DeviceByID(id string) (*Device, error) { +func DeviceByID(id string) (Device, error) { mgr := getDeviceManager() v, ok := data.Load(id) if !ok { @@ -93,7 +93,7 @@ func DeviceByID(id string) (*Device, error) { data.Store(id, dev) return dev, nil } - return v.(*Device), nil + return v.(*DeviceImpl), nil } // Attach attaches at val(string or int pid) using local device. diff --git a/frida/manager.go b/frida/manager.go index e9585e4..86c96ae 100644 --- a/frida/manager.go +++ b/frida/manager.go @@ -8,15 +8,15 @@ import "unsafe" // DeviceManager is the device DeviceManager interface type DeviceManager interface { Close() error - EnumerateDevices() ([]*Device, error) - LocalDevice() (*Device, error) - USBDevice() (*Device, error) - RemoteDevice() (*Device, error) - DeviceByID(id string) (*Device, error) - DeviceByType(devType DeviceType) (*Device, error) - FindDeviceByID(id string) (*Device, error) - FindDeviceByType(devType DeviceType) (*Device, error) - AddRemoteDevice(address string, remoteOpts *RemoteDeviceOptions) (*Device, error) + EnumerateDevices() ([]Device, error) + LocalDevice() (Device, error) + USBDevice() (Device, error) + RemoteDevice() (Device, error) + DeviceByID(id string) (Device, error) + DeviceByType(devType DeviceType) (Device, error) + FindDeviceByID(id string) (Device, error) + FindDeviceByType(devType DeviceType) (Device, error) + AddRemoteDevice(address string, remoteOpts *RemoteDeviceOptions) (Device, error) RemoveRemoteDevice(address string) error Clean() On(sigName string, fn any) @@ -47,7 +47,7 @@ func (d *deviceManager) Close() error { } // EnumerateDevices will return all connected devices. -func (d *deviceManager) EnumerateDevices() ([]*Device, error) { +func (d *deviceManager) EnumerateDevices() ([]Device, error) { var err *C.GError deviceList := C.frida_device_manager_enumerate_devices_sync(d.manager, nil, &err) if err != nil { @@ -55,11 +55,11 @@ func (d *deviceManager) EnumerateDevices() ([]*Device, error) { } numDevices := int(C.frida_device_list_size(deviceList)) - devices := make([]*Device, numDevices) + devices := make([]Device, numDevices) for i := 0; i < numDevices; i++ { device := C.frida_device_list_get(deviceList, C.gint(i)) - devices[i] = &Device{device} + devices[i] = &DeviceImpl{device} } clean(unsafe.Pointer(deviceList), unrefFrida) @@ -67,23 +67,23 @@ func (d *deviceManager) EnumerateDevices() ([]*Device, error) { } // LocalDevice returns the device with type DeviceTypeLocal. -func (d *deviceManager) LocalDevice() (*Device, error) { +func (d *deviceManager) LocalDevice() (Device, error) { return d.DeviceByType(DeviceTypeLocal) } // USBDevice returns the device with type DeviceTypeUsb. -func (d *deviceManager) USBDevice() (*Device, error) { +func (d *deviceManager) USBDevice() (Device, error) { return d.DeviceByType(DeviceTypeUsb) } // RemoteDevice returns the device with type DeviceTypeRemote. -func (d *deviceManager) RemoteDevice() (*Device, error) { +func (d *deviceManager) RemoteDevice() (Device, error) { return d.DeviceByType(DeviceTypeRemote) } // DeviceByID will return device with id passed or an error if it can't find any. // Note: the caller must call EnumerateDevices() to get devices that are of type usb -func (d *deviceManager) DeviceByID(id string) (*Device, error) { +func (d *deviceManager) DeviceByID(id string) (Device, error) { idC := C.CString(id) defer C.free(unsafe.Pointer(idC)) @@ -94,12 +94,12 @@ func (d *deviceManager) DeviceByID(id string) (*Device, error) { if err != nil { return nil, &FError{err} } - return &Device{device: device}, nil + return &DeviceImpl{device: device}, nil } // DeviceByType will return device or an error by device type specified. // Note: the caller must call EnumerateDevices() to get devices that are of type usb -func (d *deviceManager) DeviceByType(devType DeviceType) (*Device, error) { +func (d *deviceManager) DeviceByType(devType DeviceType) (Device, error) { var err *C.GError device := C.frida_device_manager_get_device_by_type_sync(d.manager, C.FridaDeviceType(devType), @@ -109,12 +109,12 @@ func (d *deviceManager) DeviceByType(devType DeviceType) (*Device, error) { if err != nil { return nil, &FError{err} } - return &Device{device: device}, nil + return &DeviceImpl{device: device}, nil } // FindDeviceByID will try to find the device by id specified // Note: the caller must call EnumerateDevices() to get devices that are of type usb -func (d *deviceManager) FindDeviceByID(id string) (*Device, error) { +func (d *deviceManager) FindDeviceByID(id string) (Device, error) { devID := C.CString(id) defer C.free(unsafe.Pointer(devID)) @@ -130,12 +130,12 @@ func (d *deviceManager) FindDeviceByID(id string) (*Device, error) { return nil, &FError{err} } - return &Device{device: device}, nil + return &DeviceImpl{device: device}, nil } // FindDeviceByType will try to find the device by device type specified // Note: the caller must call EnumerateDevices() to get devices that are of type usb -func (d *deviceManager) FindDeviceByType(devType DeviceType) (*Device, error) { +func (d *deviceManager) FindDeviceByType(devType DeviceType) (Device, error) { timeout := C.gint(defaultDeviceTimeout) var err *C.GError @@ -148,11 +148,11 @@ func (d *deviceManager) FindDeviceByType(devType DeviceType) (*Device, error) { return nil, &FError{err} } - return &Device{device: device}, nil + return &DeviceImpl{device: device}, nil } // AddRemoteDevice add a remote device from the provided address with remoteOpts populated -func (d *deviceManager) AddRemoteDevice(address string, remoteOpts *RemoteDeviceOptions) (*Device, error) { +func (d *deviceManager) AddRemoteDevice(address string, remoteOpts *RemoteDeviceOptions) (Device, error) { addressC := C.CString(address) defer C.free(unsafe.Pointer(addressC)) @@ -162,7 +162,7 @@ func (d *deviceManager) AddRemoteDevice(address string, remoteOpts *RemoteDevice return nil, &FError{err} } - return &Device{device: device}, nil + return &DeviceImpl{device: device}, nil } // RemoveRemoteDevice removes remote device available at address diff --git a/frida/portal_service.go b/frida/portal_service.go index f03c718..6361a37 100644 --- a/frida/portal_service.go +++ b/frida/portal_service.go @@ -22,9 +22,9 @@ func NewPortal(clusterParams, controlParams *EndpointParameters) *Portal { } // Device returns portal device. -func (p *Portal) Device() *Device { +func (p *Portal) Device() Device { dev := C.frida_portal_service_get_device(p.portal) - return &Device{dev} + return &DeviceImpl{dev} } // ClusterParams returns the cluster parameters for the portal. diff --git a/frida/types_converter.go b/frida/types_converter.go index 145b904..726a305 100644 --- a/frida/types_converter.go +++ b/frida/types_converter.go @@ -189,7 +189,7 @@ func getFridaChild(val *C.GValue) any { func getFridaDevice(val *C.GValue) any { dev := (*C.FridaDevice)(C.g_value_get_object(val)) - return &Device{ + return &DeviceImpl{ device: dev, } }