Merge pull request #193 from yotamN/feature/deprecate-python2

Deprecate Python 2
This commit is contained in:
Yotam
2022-09-17 17:45:26 +03:00
committed by GitHub
37 changed files with 2254 additions and 1143 deletions
+16
View File
@@ -0,0 +1,16 @@
name: code-style
on: [push, pull_request]
jobs:
black:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: psf/black@stable
isort:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.8
- uses: jamescurtin/isort-action@master
+658
View File
@@ -0,0 +1,658 @@
from typing import Any, Callable, ClassVar, Dict, List, Optional, Sequence, Tuple, Union
# Exceptions
class AddressInUseError(Exception): ...
class ExecutableNotFoundError(Exception): ...
class ExecutableNotSupportedError(Exception): ...
class ServerNotRunningError(Exception): ...
class TimedOutError(Exception): ...
class TransportError(Exception): ...
class ProcessNotFoundError(Exception): ...
class ProcessNotRespondingError(Exception): ...
class ProtocolError(Exception): ...
class InvalidArgumentError(Exception): ...
class InvalidOperationError(Exception): ...
class NotSupportedError(Exception): ...
class OperationCancelledError(Exception): ...
class PermissionDeniedError(Exception): ...
class Object:
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
def on(self, signal: str, callback: Callable[..., Any]) -> None:
"""
Add a signal handler.
"""
...
def off(self, signal: str, callback: Callable[..., Any]) -> None:
"""
Remove a signal handler.
"""
...
class Application(Object):
@property
def identifier(self) -> str:
"""
Application identifier.
"""
...
@property
def name(self) -> str:
"""
Human-readable application name.
"""
...
@property
def parameters(self) -> Dict[str, Any]:
"""
Parameters.
"""
...
@property
def pid(self) -> int:
"""
Process ID, or 0 if not running.
"""
...
class Bus(Object):
def attach(self) -> None:
"""
Attach to the bus.
"""
...
def post(self, message: str, data: Optional[Union[bytes, str]]) -> None:
"""
Post a JSON-encoded message to the bus.
"""
...
class Cancellable(Object):
def cancel(self) -> None:
"""
Set cancellable to cancelled.
"""
...
def connect(self, callback: Callable[..., Any]) -> int:
"""
Register notification callback.
"""
...
def disconnect(self, handler_id: int) -> None:
"""
Unregister notification callback.
"""
...
@classmethod
def get_current(cls) -> "Cancellable":
"""
Get the top cancellable from the stack.
"""
...
def get_fd(self) -> int:
"""
Get file descriptor for integrating with an event loop.
"""
...
def is_cancelled(self) -> bool:
"""
Query whether cancellable has been cancelled.
"""
...
def pop_current(self) -> None:
"""
Pop cancellable off the cancellable stack.
"""
...
def push_current(self) -> None:
"""
Push cancellable onto the cancellable stack.
"""
...
def raise_if_cancelled(self) -> None:
"""
Raise an exception if cancelled.
"""
...
def release_fd(self) -> None:
"""
Release a resource previously allocated by get_fd().
"""
...
class Child(Object):
@property
def argv(self) -> List[str]:
"""
Argument vector.
"""
...
@property
def envp(self) -> Dict[str, str]:
"""
Environment vector.
"""
...
@property
def identifier(self) -> str:
"""
Application identifier.
"""
...
@property
def origin(self) -> str:
"""
Origin.
"""
...
@property
def parent_pid(self) -> int:
"""
Parent Process ID.
"""
...
@property
def path(self) -> str:
"""
Path of executable.
"""
...
@property
def pid(self) -> int:
"""
Process ID.
"""
...
class Crash(Object):
@property
def parameters(self) -> Dict[str, Any]:
"""
Parameters.
"""
...
@property
def pid(self) -> int:
"""
Process ID.
"""
...
@property
def process_name(self) -> str:
"""
Process name.
"""
...
@property
def report(self) -> str:
"""
Human-readable crash report.
"""
...
@property
def summary(self) -> str:
"""
Human-readable crash summary.
"""
...
class Device(Object):
@property
def id(self) -> Optional[str]:
"""
Device ID.
"""
...
@property
def name(self) -> Optional[str]:
"""
Human-readable device name.
"""
...
@property
def icon(self) -> Optional[Any]:
"""
Icon.
"""
...
@property
def type(self) -> Optional[str]:
"""
Device type. One of: local, remote, usb.
"""
...
@property
def bus(self) -> Optional[Bus]:
"""
Message bus.
"""
...
def attach(self, pid: int, realm: Optional[str] = None, persist_timeout: Optional[int] = None) -> "Session":
"""
Attach to a PID.
"""
...
def disable_spawn_gating(self) -> None:
"""
Disable spawn gating.
"""
...
def enable_spawn_gating(self) -> None:
"""
Enable spawn gating.
"""
...
def enumerate_applications(
self, identifiers: Optional[Sequence[str]] = None, scope: Optional[str] = None
) -> List[Application]:
"""
Enumerate applications.
"""
...
def enumerate_pending_children(self) -> List[Child]:
"""
Enumerate pending children.
"""
...
def enumerate_pending_spawn(self) -> List["Spawn"]:
"""
Enumerate pending spawn.
"""
...
def enumerate_processes(self, pids: Optional[Sequence[int]] = None, scope: Optional[str] = None) -> List[Process]:
"""
Enumerate processes.
"""
...
def get_frontmost_application(self, scope: Optional[str] = None) -> Optional[Application]:
"""
Get details about the frontmost application.
"""
...
def inject_library_blob(self, pid: int, blob_buffer: bytes, entrypoint: str, data: str) -> int:
"""
Inject a library blob to a PID.
"""
...
def inject_library_file(self, pid: int, path: str, entrypoint: str, data: str) -> int:
"""
Inject a library file to a PID.
"""
...
def input(self, pid: int, data: bytes) -> None:
"""
Input data on stdin of a spawned process.
"""
...
def is_lost(self) -> bool:
"""
Query whether the device has been lost.
"""
...
def kill(self, pid: int) -> None:
"""
Kill a PID.
"""
...
def open_channel(self, address: str) -> "IOStream":
"""
Open a device-specific communication channel.
"""
...
def query_system_parameters(self) -> Dict[str, Any]:
"""
Returns a dictionary of information about the host system.
"""
...
def resume(self, pid: int) -> None:
"""
Resume a process from the attachable state.
"""
...
def spawn(
self,
program: str,
argv: Union[None, List[Union[str, bytes]], Tuple[Union[str, bytes]]] = None,
envp: Optional[Dict[str, str]] = None,
env: Optional[Dict[str, str]] = None,
cwd: Optional[str] = None,
stdio: Optional[str] = None,
**kwargs: Any,
) -> int:
"""
Spawn a process into an attachable state.
"""
...
class DeviceManager(Object):
def add_remote_device(
self,
address: str,
certificate: Optional[str] = None,
origin: Optional[str] = None,
token: Optional[str] = None,
keepalive_interval: Optional[int] = None,
) -> Device:
"""
Add a remote device.
"""
...
def close(self) -> None:
"""
Close the device manager.
"""
...
def enumerate_devices(self) -> List[Device]:
"""
Enumerate devices.
"""
...
def get_device_matching(self, predicate: Callable[[Device], bool], timeout: int) -> Device:
"""
Get device matching predicate.
"""
...
def remove_remote_device(self, address: str) -> None:
"""
Remove a remote device.
"""
...
class EndpointParameters(Object): ...
class FileMonitor(Object):
def disable(self) -> None:
"""
Disable the file monitor.
"""
...
def enable(self) -> None:
"""
Enable the file monitor.
"""
...
class IOStream(Object):
def close(self) -> None:
"""
Close the stream.
"""
...
def is_closed(self) -> bool:
"""
Query whether the stream is closed.
"""
...
def read(self, size: int) -> bytes:
"""
Read up to the specified number of bytes from the stream.
"""
...
def read_all(self, size: int) -> bytes:
"""
Read exactly the specified number of bytes from the stream.
"""
...
def write(self, data: bytes) -> int:
"""
Write as much as possible of the provided data to the stream.
"""
...
def write_all(self, data: bytes) -> None:
"""
Write all of the provided data to the stream.
"""
...
class PortalMembership(Object):
def terminate(self) -> None:
"""
Terminate the membership.
"""
...
class PortalService(Object):
@property
def device(self) -> Device:
"""
Device for in-process control.
"""
...
def broadcast(self, message: str, data: Optional[Union[str, bytes]] = None) -> None:
"""
Broadcast a message to all control channels.
"""
...
def enumerate_tags(self, connection_id: int) -> List[str]:
"""
Enumerate tags of a specific connection.
"""
...
def kick(self, connection_id: int) -> None:
"""
Kick out a specific connection.
"""
...
def narrowcast(self, tag: str, message: str, data: Optional[Union[str, bytes]] = None) -> None:
"""
Post a message to control channels with a specific tag.
"""
...
def post(self, connection_id: int, message: str, data: Optional[Union[str, bytes]] = None) -> None:
"""
Post a message to a specific control channel.
"""
...
def start(self) -> None:
"""
Start listening for incoming connections.
"""
...
def stop(self) -> None:
"""
Stop listening for incoming connections, and kick any connected clients.
"""
...
def tag(self, connection_id: int, tag: str) -> None:
"""
Tag a specific control channel.
"""
...
def untag(self, connection_id: int, tag: str) -> None:
"""
Untag a specific control channel.
"""
...
class Process(Object):
@property
def pid(self) -> int:
"""
Process ID.
"""
...
@property
def name(self) -> str:
"""
Human-readable process name.
"""
...
@property
def parameters(self) -> Dict[str, Any]:
"""
Parameters.
"""
...
class Relay(Object):
def __init__(self, address: str, username: str, password: str, kind: str) -> None: ...
@property
def address(self) -> str:
"""
Network address or address:port of the TURN server.
"""
...
@property
def kind(self) -> str:
"""
Relay kind. One of: turn-udp, turn-tcp, turn-tls.
"""
...
@property
def password(self) -> str:
"""
The TURN password to use for the allocate request.
"""
...
@property
def username(self) -> str:
"""
The TURN username to use for the allocate request.
"""
...
class Script(Object):
def eternalize(self) -> None:
"""
Eternalize the script.
"""
...
def is_destroyed(self) -> bool:
"""
Query whether the script has been destroyed.
"""
...
def load(self) -> None:
"""
Load the script.
"""
...
def post(self, message: str, data: Optional[Union[str, bytes]] = None) -> None:
"""
Post a JSON-encoded message to the script.
"""
...
def unload(self) -> None:
"""
Unload the script.
"""
...
def enable_debugger(self, port: Optional[int]) -> None:
"""
Enable the Node.js compatible script debugger
"""
...
def disable_debugger(self) -> None:
"""
Disable the Node.js compatible script debugger
"""
...
class Session(Object):
@property
def pid(self) -> int:
"""
Process ID.
"""
...
def compile_script(self, source: str, name: Optional[str] = None, runtime: Optional[str] = None) -> bytes:
"""
Compile script source code to bytecode.
"""
...
def create_script(self, source: str, name: Optional[str] = None, runtime: Optional[str] = None) -> Script:
"""
Create a new script.
"""
...
def create_script_from_bytes(
self, data: bytes, name: Optional[str] = None, runtime: Optional[str] = None
) -> Script:
"""
Create a new script from bytecode.
"""
...
def snapshot_script(self, embed_script: str, warmup_script: Optional[str], runtime: Optional[str] = None) -> bytes:
"""
Evaluate script and snapshot the resulting VM state
"""
...
def detach(self) -> None:
"""
Detach session from the process.
"""
...
def disable_child_gating(self) -> None:
"""
Disable child gating.
"""
...
def enable_child_gating(self) -> None:
"""
Enable child gating.
"""
...
def is_detached(self) -> bool:
"""
Query whether the session is detached.
"""
...
def join_portal(
self, address: str, certificate: Optional[str] = None, token: Optional[str] = None, acl: Optional[Any] = None
) -> PortalMembership:
"""
Join a portal.
"""
...
def resume(self) -> None:
"""
Resume session after network error.
"""
...
def setup_peer_connection(
self, stun_server: Optional[str] = None, relays: Optional[Sequence[Relay]] = None
) -> None:
"""
Set up a peer connection with the target process.
"""
...
class Spawn(Object):
@property
def identifier(self) -> str:
"""
Application identifier.
"""
...
@property
def pid(self) -> int:
"""
Process ID.
"""
...
class Compiler(Object):
def build(
self,
entrypoint: str,
project_root: Optional[str] = None,
source_maps: Optional[str] = None,
compression: Optional[str] = None,
) -> str:
"""
Build an agent.
"""
...
def watch(
self,
entrypoint: str,
project_root: Optional[str] = None,
source_maps: Optional[str] = None,
compression: Optional[str] = None,
) -> None:
"""
Continuously build an agent.
"""
...
__version__: str
View File
+5 -6
View File
@@ -1,17 +1,16 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import frida
system_session = frida.attach(0)
bytecode = system_session.compile_script(name="bytecode-example", source="""\
bytecode = system_session.compile_script(
name="bytecode-example",
source="""\
rpc.exports = {
listThreads: function () {
return Process.enumerateThreadsSync();
}
};
""")
""",
)
session = frida.attach("Twitter")
script = session.create_script_from_bytes(bytecode)
-3
View File
@@ -1,8 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
import frida
device = frida.get_usb_device()
channel = device.open_channel("tcp:21")
+16 -16
View File
@@ -1,13 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import threading
import frida
from frida_tools.application import Reactor
import frida
class Application(object):
class Application:
def __init__(self):
self._stop_requested = threading.Event()
self._reactor = Reactor(run_until_return=lambda reactor: self._stop_requested.wait())
@@ -29,8 +27,8 @@ class Application(object):
"BADGER": "badger-badger-badger",
"SNAKE": "mushroom-mushroom",
}
print("✔ spawn(argv={})".format(argv))
pid = self._device.spawn(argv, env=env, stdio='pipe')
print(f"✔ spawn(argv={argv})")
pid = self._device.spawn(argv, env=env, stdio="pipe")
self._instrument(pid)
def _stop_if_idle(self):
@@ -38,13 +36,14 @@ class Application(object):
self._stop_requested.set()
def _instrument(self, pid):
print("✔ attach(pid={})".format(pid))
print(f"✔ attach(pid={pid})")
session = self._device.attach(pid)
session.on("detached", lambda reason: self._reactor.schedule(lambda: self._on_detached(pid, session, reason)))
print("✔ enable_child_gating()")
session.enable_child_gating()
print("✔ create_script()")
script = session.create_script("""\
script = session.create_script(
"""\
Interceptor.attach(Module.getExportByName(null, 'open'), {
onEnter: function (args) {
send({
@@ -53,31 +52,32 @@ Interceptor.attach(Module.getExportByName(null, 'open'), {
});
}
});
""")
"""
)
script.on("message", lambda message, data: self._reactor.schedule(lambda: self._on_message(pid, message)))
print("✔ load()")
script.load()
print("✔ resume(pid={})".format(pid))
print(f"✔ resume(pid={pid})")
self._device.resume(pid)
self._sessions.add(session)
def _on_child_added(self, child):
print("⚡ child_added: {}".format(child))
print(f"⚡ child_added: {child}")
self._instrument(child.pid)
def _on_child_removed(self, child):
print("⚡ child_removed: {}".format(child))
print(f"⚡ child_removed: {child}")
def _on_output(self, pid, fd, data):
print("⚡ output: pid={}, fd={}, data={}".format(pid, fd, repr(data)))
print(f"⚡ output: pid={pid}, fd={fd}, data={repr(data)}")
def _on_detached(self, pid, session, reason):
print("⚡ detached: pid={}, reason='{}'".format(pid, reason))
print(f"⚡ detached: pid={pid}, reason='{reason}'")
self._sessions.remove(session)
self._reactor.schedule(self._stop_if_idle, delay=0.5)
def _on_message(self, pid, message):
print("⚡ message: pid={}, payload={}".format(pid, message["payload"]))
print(f"⚡ message: pid={pid}, payload={message['payload']}")
app = Application()
+1
View File
@@ -2,6 +2,7 @@ from Cocoa import NSApp
from Foundation import NSObject
from MainWindowController import MainWindowController
class AppDelegate(NSObject):
def applicationDidFinishLaunching_(self, notification):
window = MainWindowController()
+47 -51
View File
@@ -1,11 +1,13 @@
import bisect
from Foundation import NSAutoreleasePool, NSObject, NSThread
from PyObjCTools import AppHelper
import re
import struct
from Foundation import NSAutoreleasePool, NSObject, NSThread
from PyObjCTools import AppHelper
PROBE_CALLS = re.compile(r"^\/stalker\/probes\/(.*?)\/calls$")
class Capture(NSObject):
def __new__(cls, device):
return cls.alloc().initWithDevice_(device)
@@ -31,7 +33,7 @@ class Capture(NSObject):
def attachToProcess_triggerPort_(self, process, triggerPort):
assert self.state == CaptureState.DETACHED
self._updateState_(CaptureState.ATTACHING)
NSThread.detachNewThreadSelector_toTarget_withObject_('_doAttachWithParams:', self, (process.pid, triggerPort))
NSThread.detachNewThreadSelector_toTarget_withObject_("_doAttachWithParams:", self, (process.pid, triggerPort))
def detach(self):
assert self.state == CaptureState.ATTACHED
@@ -40,10 +42,10 @@ class Capture(NSObject):
self.session = None
self.script = None
self._updateState_(CaptureState.DETACHED)
NSThread.detachNewThreadSelector_toTarget_withObject_('_doDetachWithParams:', self, (session, script))
NSThread.detachNewThreadSelector_toTarget_withObject_("_doDetachWithParams:", self, (session, script))
def _post(self, message):
NSThread.detachNewThreadSelector_toTarget_withObject_('_doPostWithParams:', self, (self.script, message))
NSThread.detachNewThreadSelector_toTarget_withObject_("_doPostWithParams:", self, (self.script, message))
def _updateState_(self, newState):
self.state = newState
@@ -57,13 +59,11 @@ class Capture(NSObject):
error = None
try:
session = self.device.attach(pid)
session.on('detached', self._onSessionDetached)
script = session.create_script(name="cpushark", source=SCRIPT_TEMPLATE % {
'trigger_port': triggerPort
})
script.on('message', self._onScriptMessage)
session.on("detached", self._onSessionDetached)
script = session.create_script(name="cpushark", source=SCRIPT_TEMPLATE % {"trigger_port": triggerPort})
script.on("message", self._onScriptMessage)
script.load()
except Exception, e:
except Exception as e:
if session is not None:
try:
session.detach()
@@ -93,8 +93,8 @@ class Capture(NSObject):
pool = NSAutoreleasePool.alloc().init()
try:
script.post(message)
except Exception, e:
print "Failed to post to script:", e
except Exception as e:
print("Failed to post to script:", e)
del pool
def _attachDidCompleteWithSession_script_error_(self, session, script, error):
@@ -113,22 +113,22 @@ class Capture(NSObject):
self._updateState_(CaptureState.DETACHED)
def _sessionDidReceiveMessage_data_(self, message, data):
if message['type'] == 'send':
stanza = message['payload']
fromAddress = stanza['from']
name = stanza['name']
if fromAddress == "/process/modules" and name == '+sync':
self.modules._sync(stanza['payload'])
elif fromAddress == "/stalker/calls" and name == '+add':
self.calls._add_(stanza['payload'])
elif fromAddress == "/interceptor/functions" and name == '+add':
if message["type"] == "send":
stanza = message["payload"]
fromAddress = stanza["from"]
name = stanza["name"]
if fromAddress == "/process/modules" and name == "+sync":
self.modules._sync(stanza["payload"])
elif fromAddress == "/stalker/calls" and name == "+add":
self.calls._add_(stanza["payload"])
elif fromAddress == "/interceptor/functions" and name == "+add":
self.recvTotal += 1
self._delegate.captureRecvTotalDidChange()
else:
if not self.calls._handleStanza_(stanza):
print "Woot! Got stanza: %s from=%s" % (stanza['name'], stanza['from'])
print(f"Woot! Got stanza: {stanza['name']} from={stanza['from']}")
else:
print "Unhandled message:", message
print("Unhandled message:", message)
def _onSessionDetached(self):
AppHelper.callAfter(self._sessionDidDetach)
@@ -136,11 +136,13 @@ class Capture(NSObject):
def _onScriptMessage(self, message, data):
AppHelper.callAfter(self._sessionDidReceiveMessage_data_, message, data)
class CaptureState:
DETACHED = 1
ATTACHING = 2
ATTACHED = 3
class Modules:
def __init__(self):
self._modules = []
@@ -148,11 +150,11 @@ class Modules:
def _sync(self, payload):
modules = []
for item in payload['items']:
modules.append(Module(item['name'], int(item['base'], 16), item['size']))
for item in payload["items"]:
modules.append(Module(item["name"], int(item["base"], 16), item["size"]))
modules.sort(lambda x, y: x.address - y.address)
self._modules = modules
self._indices = [ m.address for m in modules ]
self._indices = [m.address for m in modules]
def lookup(self, addr):
idx = bisect.bisect(self._indices, addr)
@@ -163,6 +165,7 @@ class Modules:
return None
return m
class Module:
def __init__(self, name, address, size):
self.name = name
@@ -172,6 +175,7 @@ class Module:
def __repr__(self):
return "(%d, %d, %s)" % (self.address, self.size, self.name)
class Calls(NSObject):
def __new__(cls, capture):
return cls.alloc().initWithCapture_(capture)
@@ -192,28 +196,16 @@ class Calls(NSObject):
self._delegate = delegate
def addProbe_(self, func):
self.capture._post({
'to': "/stalker/probes",
'name': '+add',
'payload': {
'address': "0x%x" % func.address
}
})
self.capture._post({"to": "/stalker/probes", "name": "+add", "payload": {"address": "0x%x" % func.address}})
self._probes[func.address] = func
def removeProbe_(self, func):
self.capture._post({
'to': "/stalker/probes",
'name': '+remove',
'payload': {
'address': "0x%x" % func.address
}
})
self.capture._post({"to": "/stalker/probes", "name": "+remove", "payload": {"address": "0x%x" % func.address}})
self._probes.pop(func.address, None)
def _add_(self, data):
modules = self.capture.modules
for rawTarget, count in data['summary'].items():
for rawTarget, count in data["summary"].items():
target = int(rawTarget, 16)
tm = self.getTargetModuleByModule_(modules.lookup(target))
if tm is not None:
@@ -237,13 +229,13 @@ class Calls(NSObject):
return x.total - y.total
def _handleStanza_(self, stanza):
m = PROBE_CALLS.match(stanza['from'])
m = PROBE_CALLS.match(stanza["from"])
if m is not None:
func = self._probes.get(int(m.groups()[0], 16), None)
if func is not None:
if len(func.calls) == 3:
func.calls.pop(0)
func.calls.append(FunctionCall(func, stanza['payload']['args']))
func.calls.append(FunctionCall(func, stanza["payload"]["args"]))
self._delegate.callItemDidChange_(func)
return True
return False
@@ -291,27 +283,28 @@ class Calls(NSObject):
def outlineView_objectValueForTableColumn_byItem_(self, outlineView, tableColumn, item):
identifier = tableColumn.identifier()
if isinstance(item, TargetModule):
if identifier == 'name':
if identifier == "name":
return item.module.name
elif identifier == 'total':
elif identifier == "total":
return item.total
else:
return False
elif isinstance(item, TargetFunction):
if identifier == 'name':
if identifier == "name":
return item.name
elif identifier == 'total':
elif identifier == "total":
return item.total
else:
return item.hasProbe
else:
if identifier == 'name':
if identifier == "name":
return item.summary
elif identifier == 'total':
elif identifier == "total":
return ""
else:
return False
class TargetModule(NSObject):
def __new__(cls, module):
return cls.alloc().initWithModule_(module)
@@ -332,6 +325,7 @@ class TargetModule(NSObject):
self._functionByAddress[address] = f
return f
class TargetFunction(NSObject):
def __new__(cls, module, offset):
return cls.alloc().initWithModule_offset_(module, offset)
@@ -347,6 +341,7 @@ class TargetFunction(NSObject):
self.calls = []
return self
class FunctionCall(NSObject):
def __new__(cls, func, args):
return cls.alloc().initWithFunction_args_(func, args)
@@ -355,9 +350,10 @@ class FunctionCall(NSObject):
self = self.init()
self.func = func
self.args = args
self.summary = "%s(%s)" % (func.name, ", ".join(args))
self.summary = f"{func.name}({', '.join(args)})"
return self
SCRIPT_TEMPLATE = """
var probes = Object.create(null);
+2
View File
@@ -1,4 +1,5 @@
import sys
sys.path.insert(0, "/Users/oleavr/src/frida/build/frida-macos-universal/lib/python2.7/site-packages")
import AppDelegate
import Capture
@@ -7,4 +8,5 @@ import ProcessList
if __name__ == "__main__":
from PyObjCTools import AppHelper
AppHelper.runEventLoop()
+4 -3
View File
@@ -1,8 +1,10 @@
import frida
from Capture import Capture, CaptureState, TargetFunction
from Cocoa import NSRunCriticalAlertPanel, NSUserDefaults, NSWindowController, objc
from ProcessList import ProcessList
import frida
class MainWindowController(NSWindowController):
processCombo = objc.IBOutlet()
triggerField = objc.IBOutlet()
@@ -26,7 +28,7 @@ class MainWindowController(NSWindowController):
def windowDidLoad(self):
NSWindowController.windowDidLoad(self)
device = [device for device in frida.get_device_manager().enumerate_devices() if device.type == 'local'][0]
device = [device for device in frida.get_device_manager().enumerate_devices() if device.type == "local"][0]
self.processList = ProcessList(device)
self.capture = Capture(device)
self.processCombo.setUsesDataSource_(True)
@@ -127,4 +129,3 @@ class MainWindowController(NSWindowController):
def callItemDidChange_(self, item):
self.callTableView.reloadItem_reloadChildren_(item, True)
+2 -2
View File
@@ -1,4 +1,5 @@
from Foundation import NSObject, NSNotFound
from Foundation import NSNotFound, NSObject
class ProcessList(NSObject):
def __new__(cls, device):
@@ -30,4 +31,3 @@ class ProcessList(NSObject):
def comboBox_indexOfItemWithStringValue_(self, comboBox, value):
return self._processIndexByName.get(value.lower(), NSNotFound)
+8 -8
View File
@@ -5,21 +5,21 @@ Usage:
python setup.py py2app
"""
from setuptools import setup
import py2app
from setuptools import setup
plist = dict(
CFBundleShortVersionString='CpuShark v1',
CFBundleIconFile='CpuShark.icns',
CFBundleGetInfoString='CpuShark v1',
CFBundleIdentifier='com.tillitech.CpuShark',
CFBundleShortVersionString="CpuShark v1",
CFBundleIconFile="CpuShark.icns",
CFBundleGetInfoString="CpuShark v1",
CFBundleIdentifier="com.tillitech.CpuShark",
CFBundleDocumentTypes=[],
CFBundleName='CpuShark'
CFBundleName="CpuShark",
)
setup(
app=["CpuShark.py"],
data_files=["MainMenu.xib", "MainWindow.xib"],
options={'py2app': {'plist': plist}},
setup_requires=['py2app'],
options={"py2app": {"plist": plist}},
setup_requires=["py2app"],
)
+4 -5
View File
@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import sys
import frida
@@ -10,14 +7,16 @@ def on_process_crashed(crash):
print("on_process_crashed")
print("\tcrash:", crash)
def on_detached(reason, crash):
print("on_detached()")
print("\treason:", reason)
print("\tcrash:", crash)
device = frida.get_usb_device()
device.on('process-crashed', on_process_crashed)
device.on("process-crashed", on_process_crashed)
session = device.attach("Hello")
session.on('detached', on_detached)
session.on("detached", on_detached)
print("[*] Ready")
sys.stdin.read()
+6 -6
View File
@@ -1,6 +1,3 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import sys
import frida
@@ -9,15 +6,18 @@ import frida
def on_detached():
print("on_detached")
def on_detached_with_reason(reason):
print("on_detached_with_reason:", reason)
def on_detached_with_varargs(*args):
print("on_detached_with_varargs:", args)
session = frida.attach("Twitter")
print("attached")
session.on('detached', on_detached)
session.on('detached', on_detached_with_reason)
session.on('detached', on_detached_with_varargs)
session.on("detached", on_detached)
session.on("detached", on_detached_with_reason)
session.on("detached", on_detached_with_varargs)
sys.stdin.read()
+10 -12
View File
@@ -1,26 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import frida
from pprint import pformat
from pygments import highlight
from pygments.formatters import Terminal256Formatter
from pygments.lexers import PythonLexer
import frida
device = frida.get_usb_device()
def trim_icon(icon):
result = dict(icon)
result['image'] = result['image'][0:16] + b"..."
result["image"] = result["image"][0:16] + b"..."
return result
apps = device.enumerate_applications(scope='full')
apps = device.enumerate_applications(scope="full")
for app in apps:
params = dict(app.parameters)
if 'icons' in params:
params['icons'] = [trim_icon(icon) for icon in params['icons']]
print("Application(identifier=\"{}\", name=\"{}\", pid={}, parameters={})".format(
app.identifier,
app.name,
app.pid,
highlight(pformat(params), PythonLexer(), Terminal256Formatter()).rstrip()))
if "icons" in params:
params["icons"] = [trim_icon(icon) for icon in params["icons"]]
parameters = highlight(pformat(params), PythonLexer(), Terminal256Formatter()).rstrip()
print(f'Application(identifier="{app.identifier}", name="{app.name}", pid={app.pid}, parameters={parameters})')
+11 -8
View File
@@ -1,22 +1,25 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import frida
from pprint import pformat
from pygments import highlight
from pygments.formatters import Terminal256Formatter
from pygments.lexers import PythonLexer
import frida
device = frida.get_usb_device()
def trim_icon(icon):
result = dict(icon)
result['image'] = result['image'][0:16] + b"..."
result["image"] = result["image"][0:16] + b"..."
return result
processes = device.enumerate_processes(scope='full')
processes = device.enumerate_processes(scope="full")
for proc in processes:
params = dict(proc.parameters)
if 'icons' in params:
params['icons'] = [trim_icon(icon) for icon in params['icons']]
print("Process(pid={}, name=\"{}\", parameters={})".format(proc.pid, proc.name, highlight(pformat(params), PythonLexer(), Terminal256Formatter()).rstrip()))
if "icons" in params:
params["icons"] = [trim_icon(icon) for icon in params["icons"]]
print(
f'Process(pid={proc.pid}, name="{proc.name}", parameters={highlight(pformat(params), PythonLexer(), Terminal256Formatter()).rstrip()})'
)
+9 -8
View File
@@ -1,24 +1,25 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import frida
from pprint import pformat
from pygments import highlight
from pygments.formatters import Terminal256Formatter
from pygments.lexers import PythonLexer
import frida
device = frida.get_usb_device()
def trim_icon(icon):
result = dict(icon)
result['image'] = result['image'][0:16] + b"..."
result["image"] = result["image"][0:16] + b"..."
return result
app = device.get_frontmost_application(scope='full')
app = device.get_frontmost_application(scope="full")
if app is not None:
params = dict(app.parameters)
if 'icons' in params:
params['icons'] = [trim_icon(icon) for icon in params['icons']]
print("{}:".format(app.identifier), highlight(pformat(params), PythonLexer(), Terminal256Formatter()))
if "icons" in params:
params["icons"] = [trim_icon(icon) for icon in params["icons"]]
print(f"{app.identifier}:", highlight(pformat(params), PythonLexer(), Terminal256Formatter()))
else:
print("No frontmost application")
+1 -2
View File
@@ -6,8 +6,6 @@
# $ python inject_blob.py Twitter example.dylib
#
from __future__ import unicode_literals, print_function
import sys
import frida
@@ -16,6 +14,7 @@ import frida
def on_uninjected(id):
print("on_uninjected id=%u" % id)
(target, library_path) = sys.argv[1:]
device = frida.get_local_device()
+1 -2
View File
@@ -6,8 +6,6 @@
# $ python inject_file.py Twitter ~/.Trash/example.dylib
#
from __future__ import unicode_literals, print_function
import sys
import frida
@@ -16,6 +14,7 @@ import frida
def on_uninjected(id):
print("on_uninjected id=%u" % id)
(target, library_path) = sys.argv[1:]
device = frida.get_local_device()
+34 -46
View File
@@ -1,22 +1,20 @@
import frida
from frida_tools.application import Reactor
import json
import sys
from frida_tools.application import Reactor
import frida
class Application:
def __init__(self, nick):
self._reactor = Reactor(run_until_return=self._process_input)
token = {
'nick': nick,
'secret': "knock-knock"
}
self._device = frida.get_device_manager().add_remote_device("::1",
token=json.dumps(token))
token = {"nick": nick, "secret": "knock-knock"}
self._device = frida.get_device_manager().add_remote_device("::1", token=json.dumps(token))
self._bus = self._device.bus
self._bus.on('message', lambda *args: self._reactor.schedule(lambda: self._on_bus_message(*args)))
self._bus.on("message", lambda *args: self._reactor.schedule(lambda: self._on_bus_message(*args)))
self._channel = None
self._prompt = "> "
@@ -45,54 +43,44 @@ class Application:
if text.startswith("/join "):
if self._channel is not None:
self._bus.post({
'type': 'part',
'channel': self._channel
})
self._bus.post({"type": "part", "channel": self._channel})
channel = text[6:]
self._channel = channel
self._prompt = "{} > ".format(channel)
self._bus.post({
'type': 'join',
'channel': channel
})
self._prompt = f"{channel} > "
self._bus.post({"type": "join", "channel": channel})
continue
if text.startswith("/announce "):
self._bus.post({
'type': 'announce',
'text': text[10:]
})
self._bus.post({"type": "announce", "text": text[10:]})
continue
if self._channel is not None:
self._bus.post({
'channel': self._channel,
'type': 'say',
'text': text
})
self._bus.post({"channel": self._channel, "type": "say", "text": text})
else:
self._print("*** Need to /join a channel first")
def _on_bus_message(self, message, data):
mtype = message['type']
if mtype == 'welcome':
self._print("*** Welcome! Available channels:", repr(message['channels']))
elif mtype == 'membership':
self._print("*** Joined", message['channel'])
self._print("- Members:\n\t" + "\n\t".join(["{} (connected from {})".format(m['nick'], m['address']) for m in message['members']]))
for item in message['history']:
self._print("<{}> {}".format(item['sender'], item['text']))
elif mtype == 'join':
user = message['user']
self._print("👋 {} ({}) joined {}".format(user['nick'], user['address'], message['channel']))
elif mtype == 'part':
user = message['user']
self._print("🚪 {} ({}) left {}".format(user['nick'], user['address'], message['channel']))
elif mtype == 'chat':
self._print("<{}> {}".format(message['sender'], message['text']))
elif mtype == 'announce':
self._print("📣 <{}> {}".format(message['sender'], message['text']))
mtype = message["type"]
if mtype == "welcome":
self._print("*** Welcome! Available channels:", repr(message["channels"]))
elif mtype == "membership":
self._print("*** Joined", message["channel"])
self._print(
"- Members:\n\t"
+ "\n\t".join([f"{m['nick']} (connected from {m['address']})" for m in message["members"]])
)
for item in message["history"]:
self._print(f"<{item['sender']}> {item['text']}")
elif mtype == "join":
user = message["user"]
self._print(f"👋 {user['nick']} ({user['address']}) joined {message['channel']}")
elif mtype == "part":
user = message["user"]
self._print(f"🚪 {user['nick']} ({user['address']}) left {message['channel']}")
elif mtype == "chat":
self._print(f"<{message['sender']}> {message['text']}")
elif mtype == "announce":
self._print(f"📣 <{message['sender']}> {message['text']}")
else:
self._print("Unhandled message:", message)
@@ -102,7 +90,7 @@ class Application:
sys.stdout.flush()
if __name__ == '__main__':
if __name__ == "__main__":
nick = sys.argv[1]
app = Application(nick)
app.run()
+55 -68
View File
@@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
import frida
from frida_tools.application import Reactor
import hashlib
import hmac
import json
from pathlib import Path
from frida_tools.application import Reactor
import frida
ENABLE_CONTROL_INTERFACE = True
@@ -14,16 +14,17 @@ class Application:
def __init__(self):
self._reactor = Reactor(run_until_return=self._process_input)
cluster_params = frida.EndpointParameters(address="unix:/Users/oleavr/src/cluster",
certificate="/Users/oleavr/src/identity2.pem",
authentication=('token', "wow-such-secret"))
cluster_params = frida.EndpointParameters(
address="unix:/Users/oleavr/src/cluster",
certificate="/Users/oleavr/src/identity2.pem",
authentication=("token", "wow-such-secret"),
)
if ENABLE_CONTROL_INTERFACE:
www = Path(__file__).parent.resolve() / "web_client" / "dist"
control_params = frida.EndpointParameters(address="::1",
port=27042,
authentication=('callback', self._authenticate),
asset_root=www)
control_params = frida.EndpointParameters(
address="::1", port=27042, authentication=("callback", self._authenticate), asset_root=www
)
else:
control_params = None
@@ -34,15 +35,20 @@ class Application:
self._nicks = set()
self._channels = {}
service.on('node-connected', lambda *args: self._reactor.schedule(lambda: self._on_node_connected(*args)))
service.on('node-joined', lambda *args: self._reactor.schedule(lambda: self._on_node_joined(*args)))
service.on('node-left', lambda *args: self._reactor.schedule(lambda: self._on_node_left(*args)))
service.on('node-disconnected', lambda *args: self._reactor.schedule(lambda: self._on_node_disconnected(*args)))
service.on('controller-connected', lambda *args: self._reactor.schedule(lambda: self._on_controller_connected(*args)))
service.on('controller-disconnected', lambda *args: self._reactor.schedule(lambda: self._on_controller_disconnected(*args)))
service.on('authenticated', lambda *args: self._reactor.schedule(lambda: self._on_authenticated(*args)))
service.on('subscribe', lambda *args: self._reactor.schedule(lambda: self._on_subscribe(*args)))
service.on('message', lambda *args: self._reactor.schedule(lambda: self._on_message(*args)))
service.on("node-connected", lambda *args: self._reactor.schedule(lambda: self._on_node_connected(*args)))
service.on("node-joined", lambda *args: self._reactor.schedule(lambda: self._on_node_joined(*args)))
service.on("node-left", lambda *args: self._reactor.schedule(lambda: self._on_node_left(*args)))
service.on("node-disconnected", lambda *args: self._reactor.schedule(lambda: self._on_node_disconnected(*args)))
service.on(
"controller-connected", lambda *args: self._reactor.schedule(lambda: self._on_controller_connected(*args))
)
service.on(
"controller-disconnected",
lambda *args: self._reactor.schedule(lambda: self._on_controller_disconnected(*args)),
)
service.on("authenticated", lambda *args: self._reactor.schedule(lambda: self._on_authenticated(*args)))
service.on("subscribe", lambda *args: self._reactor.schedule(lambda: self._on_subscribe(*args)))
service.on("message", lambda *args: self._reactor.schedule(lambda: self._on_message(*args)))
def run(self):
self._reactor.schedule(self._start)
@@ -75,18 +81,18 @@ class Application:
def _authenticate(self, raw_token):
try:
token = json.loads(raw_token)
nick = str(token['nick'])
secret = token['secret'].encode('utf-8')
nick = str(token["nick"])
secret = token["secret"].encode("utf-8")
except:
raise ValueError("invalid request")
provided = hashlib.sha1(secret).digest()
expected = hashlib.sha1("knock-knock".encode('utf-8')).digest()
expected = hashlib.sha1(b"knock-knock").digest()
if not hmac.compare_digest(provided, expected):
raise ValueError("get outta here")
return {
'nick': nick,
"nick": nick,
}
def _on_node_connected(self, connection_id, remote_address):
@@ -119,37 +125,30 @@ class Application:
peer = self._peers.get(connection_id, None)
if peer is None:
return
peer.nick = self._acquire_nick(session_info['nick'])
peer.nick = self._acquire_nick(session_info["nick"])
def _on_subscribe(self, connection_id):
print("on_subscribe()", connection_id)
self._service.post(connection_id, {
'type': 'welcome',
'channels': list(self._channels.keys())
})
self._service.post(connection_id, {"type": "welcome", "channels": list(self._channels.keys())})
def _on_message(self, connection_id, message, data):
peer = self._peers[connection_id]
mtype = message['type']
if mtype == 'join':
self._get_channel(message['channel']).add_member(peer)
elif mtype == 'part':
channel = self._channels.get(message['channel'], None)
mtype = message["type"]
if mtype == "join":
self._get_channel(message["channel"]).add_member(peer)
elif mtype == "part":
channel = self._channels.get(message["channel"], None)
if channel is None:
return
channel.remove_member(peer)
elif mtype == 'say':
channel = self._channels.get(message['channel'], None)
elif mtype == "say":
channel = self._channels.get(message["channel"], None)
if channel is None:
return
channel.post(message['text'], peer)
elif mtype == 'announce':
self._service.broadcast({
'type': 'announce',
'sender': peer.nick,
'text': message['text']
})
channel.post(message["text"], peer)
elif mtype == "announce":
self._service.broadcast({"type": "announce", "sender": peer.nick, "text": message["text"]})
else:
print("Unhandled message:", message)
@@ -184,10 +183,7 @@ class Peer:
self.memberships = set()
def to_json(self):
return {
'nick': self.nick,
'address': self.remote_address[0]
}
return {"nick": self.nick, "address": self.remote_address[0]}
class Channel:
@@ -205,19 +201,18 @@ class Channel:
peer.memberships.add(self)
self.members.add(peer)
self._service.narrowcast(self.name, {
'type': 'join',
'channel': self.name,
'user': peer.to_json()
})
self._service.narrowcast(self.name, {"type": "join", "channel": self.name, "user": peer.to_json()})
self._service.tag(peer.connection_id, self.name)
self._service.post(peer.connection_id, {
'type': 'membership',
'channel': self.name,
'members': [peer.to_json() for peer in self.members],
'history': self.history
})
self._service.post(
peer.connection_id,
{
"type": "membership",
"channel": self.name,
"members": [peer.to_json() for peer in self.members],
"history": self.history,
},
)
def remove_member(self, peer):
if self not in peer.memberships:
@@ -227,21 +222,13 @@ class Channel:
self.members.remove(peer)
self._service.untag(peer.connection_id, self.name)
self._service.narrowcast(self.name, {
'type': 'part',
'channel': self.name,
'user': peer.to_json()
})
self._service.narrowcast(self.name, {"type": "part", "channel": self.name, "user": peer.to_json()})
def post(self, text, peer):
if self not in peer.memberships:
return
item = {
'type': 'chat',
'sender': peer.nick,
'text': text
}
item = {"type": "chat", "sender": peer.nick, "text": text}
self._service.narrowcast(self.name, item)
@@ -251,6 +238,6 @@ class Channel:
history.pop(0)
if __name__ == '__main__':
if __name__ == "__main__":
app = Application()
app.run()
+6 -4
View File
@@ -1,11 +1,13 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import frida
from pprint import pformat
from pygments import highlight
from pygments.formatters import Terminal256Formatter
from pygments.lexers import PythonLexer
import frida
print("Local parameters:", highlight(pformat(frida.query_system_parameters()), PythonLexer(), Terminal256Formatter()))
print("USB device parameters:", highlight(pformat(frida.get_usb_device().query_system_parameters()), PythonLexer(), Terminal256Formatter()))
print(
"USB device parameters:",
highlight(pformat(frida.get_usb_device().query_system_parameters()), PythonLexer(), Terminal256Formatter()),
)
+4 -6
View File
@@ -1,11 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import frida
session = frida.attach("Twitter")
script = session.create_script("""\
script = session.create_script(
"""\
rpc.exports = {
hello: function () {
return 'Hello';
@@ -14,7 +11,8 @@ rpc.exports = {
oops;
}
};
""")
"""
)
script.load()
api = script.exports
print("api.hello() =>", api.hello())
+11 -9
View File
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
import frida
from frida_tools.application import Reactor
import frida
class Application(object):
class Application:
def __init__(self):
self._reactor = Reactor(run_until_return=self._process_input)
@@ -20,9 +20,10 @@ class Application(object):
session = self._device.attach("hello2", persist_timeout=30)
self._session = session
session.on('detached', lambda *args: self._reactor.schedule(lambda: self._on_detached(*args)))
session.on("detached", lambda *args: self._reactor.schedule(lambda: self._on_detached(*args)))
script = session.create_script("""
script = session.create_script(
"""
let _puts = null;
Interceptor.attach(DebugSymbol.getFunctionByName('f'), {
@@ -47,9 +48,10 @@ function puts(s) {
}
_puts(Memory.allocUtf8String(s));
}
""")
"""
)
self._script = script
script.on('message', lambda *args: self._reactor.schedule(lambda: self._on_message(*args)))
script.on("message", lambda *args: self._reactor.schedule(lambda: self._on_message(*args)))
script.load()
def _process_input(self, reactor):
@@ -69,10 +71,10 @@ function puts(s) {
print("Unknown command")
def _on_detached(self, reason, crash):
print("⚡ detached: reason={}, crash={}".format(reason, crash))
print(f"⚡ detached: reason={reason}, crash={crash}")
def _on_message(self, message, data):
print("⚡ message: {}".format(message))
print(f"⚡ message: {message}")
app = Application()
+2 -1
View File
@@ -1,6 +1,5 @@
import frida
embed_script = """
const button = {
color: 'blue',
@@ -28,9 +27,11 @@ session = frida.attach(0)
snapshot = session.snapshot_script(embed_script, warmup_script=warmup_script, runtime=runtime)
def on_message(message, data):
print("on_message:", message)
script = session.create_script(test_script, snapshot=snapshot, runtime=runtime)
script.on("message", on_message)
script.load()
+104 -40
View File
@@ -1,31 +1,22 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals, print_function
import threading
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
try:
import _frida
except Exception as ex:
import sys
print("")
print("***")
if str(ex).startswith("No module named "):
print("Frida native extension not found")
print("Please check your PYTHONPATH.")
else:
print("Failed to load the Frida native extension: %s" % ex)
if sys.version_info[0] == 2:
current_python_version = "%d.%d" % sys.version_info[:2]
else:
current_python_version = "%d.x" % sys.version_info[0]
print("Please ensure that the extension was compiled for Python " + current_python_version + ".")
print(f"Failed to load the Frida native extension: {ex}")
print("Please ensure that the extension was compiled correctly")
print("***")
print("")
raise ex
from . import core
__version__ = _frida.__version__
__version__: str = _frida.__version__
get_device_manager = core.get_device_manager
Relay = _frida.Relay
@@ -51,58 +42,131 @@ TransportError = _frida.TransportError
OperationCancelledError = _frida.OperationCancelledError
def query_system_parameters(**kwargs):
return get_local_device().query_system_parameters(**kwargs)
def query_system_parameters() -> Dict[str, Any]:
"""
Returns a dictionary of information about the host system
"""
return get_local_device().query_system_parameters()
def spawn(*args, **kwargs):
return get_local_device().spawn(*args, **kwargs)
def spawn(
program: Union[str, List[Union[str, bytes]], Tuple[Union[str, bytes]]],
argv: Union[None, List[Union[str, bytes]], Tuple[Union[str, bytes]]] = None,
envp: Optional[Dict[str, str]] = None,
env: Optional[Dict[str, str]] = None,
cwd: Optional[str] = None,
stdio: Optional[str] = None,
**kwargs: Any,
) -> int:
"""
Spawn a process into an attachable state
"""
return get_local_device().spawn(program=program, argv=argv, envp=envp, env=env, cwd=cwd, stdio=stdio, **kwargs)
def resume(target, **kwargs):
get_local_device().resume(target, **kwargs)
def resume(target: core.ProcessTarget) -> None:
"""
Resume a process from the attachable state
:param target: the PID or name of the process
"""
get_local_device().resume(target)
def kill(target, **kwargs):
get_local_device().kill(target, **kwargs)
def kill(target: core.ProcessTarget) -> None:
"""
Kill a process
:param target: the PID or name of the process
"""
get_local_device().kill(target)
def attach(target, *args, **kwargs):
return get_local_device().attach(target, *args, **kwargs)
def attach(
target: core.ProcessTarget, realm: Optional[str] = None, persist_timeout: Optional[int] = None
) -> core.Session:
"""
Attach to a process
:param target: the PID or name of the process
"""
return get_local_device().attach(target, realm=realm, persist_timeout=persist_timeout)
def inject_library_file(target, path, entrypoint, data, **kwargs):
return get_local_device().inject_library_file(target, path, entrypoint, data, **kwargs)
def inject_library_file(target: core.ProcessTarget, path: str, entrypoint: str, data: str) -> int:
"""
Inject a library file to a process.
:param target: the PID or name of the process
"""
return get_local_device().inject_library_file(target, path, entrypoint, data)
def inject_library_blob(target, blob, entrypoint, data, **kwargs):
return get_local_device().inject_library_blob(target, blob, entrypoint, data, **kwargs)
def inject_library_blob(target: core.ProcessTarget, blob: bytes, entrypoint: str, data: str) -> int:
"""
Inject a library blob to a process
:param target: the PID or name of the process
"""
return get_local_device().inject_library_blob(target, blob, entrypoint, data)
def get_local_device(**kwargs):
return get_device_matching(lambda d: d.type == 'local', timeout=0, **kwargs)
def get_local_device() -> core.Device:
"""
Get the local device
"""
return get_device_manager().get_local_device()
def get_remote_device(**kwargs):
return get_device_matching(lambda d: d.type == 'remote', timeout=0, **kwargs)
def get_remote_device() -> core.Device:
"""
Get the first remote device in the devices list
"""
return get_device_manager().get_remote_device()
def get_usb_device(timeout=0, **kwargs):
return get_device_matching(lambda d: d.type == 'usb', timeout, **kwargs)
def get_usb_device(timeout: int = 0) -> core.Device:
"""
Get the first device connected over USB in the devices list
"""
return get_device_manager().get_usb_device(timeout)
def get_device(id, timeout=0, **kwargs):
return get_device_manager().get_device(id, timeout, **kwargs)
def get_device(id: Optional[str], timeout: int = 0) -> core.Device:
"""
Get a device by its id
"""
return get_device_manager().get_device(id, timeout)
def get_device_matching(predicate, timeout=0, **kwargs):
return get_device_manager().get_device_matching(predicate, timeout, **kwargs)
def get_device_matching(predicate: Callable[[core.Device], bool], timeout: int = 0) -> core.Device:
"""
Get device matching predicate.
:param predicate: a function to filter the devices
:param timeout: operation timeout in seconds
"""
return get_device_manager().get_device_matching(predicate, timeout)
def enumerate_devices(**kwargs):
return get_device_manager().enumerate_devices(**kwargs)
def enumerate_devices() -> List[core.Device]:
"""
Enumerate all the devices from the device manager
"""
return get_device_manager().enumerate_devices()
@core.cancellable
def shutdown():
def shutdown() -> None:
"""
Shutdown the main device manager
"""
get_device_manager()._impl.close()
+949 -449
View File
File diff suppressed because it is too large Load Diff
View File
+1 -1
View File
@@ -54,7 +54,7 @@ if python_incdir == ''
endif
result = run_command(python, '-c',
'import sys; sys.stdout.write("%d.%d" % (sys.version_info[0], sys.version_info[1]))',
'import sys; sys.stdout.write(f"{sys.version_info[0]}.{sys.version_info[1]}")',
check: true)
python_version = result.stdout()
python_name = 'python' + python_version
+6
View File
@@ -0,0 +1,6 @@
[tool.black]
line-length = 120
[tool.isort]
profile = "black"
line_length = 120
+78 -103
View File
@@ -1,66 +1,45 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import sys
import codecs
import hashlib
import os
import platform
import re
import zipfile
import shutil
import struct
import codecs
import subprocess
import hashlib
import sys
import zipfile
from collections import namedtuple
from functools import partial
try:
from io import BytesIO
except:
try:
from cStringIO import StringIO as BytesIO
except:
from StringIO import StringIO as BytesIO
try:
from urllib.request import urlopen, Request
except:
from urllib2 import urlopen, Request
try:
from urllib.parse import urljoin, urlparse, urlunparse
except:
from urlparse import urljoin, urlparse, urlunparse
try:
from html.parser import HTMLParser
except:
from HTMLParser import HTMLParser
from html.parser import HTMLParser
from io import BytesIO
from urllib.parse import urljoin, urlparse, urlunparse
from urllib.request import urlopen
from setuptools import setup
from setuptools.command.build_ext import build_ext
from setuptools.extension import Extension
DEFAULT_INDEX_URL = "https://pypi.org/simple/"
python_version = sys.version_info[0:2]
python_major_version = python_version[0]
package_dir = os.path.dirname(os.path.realpath(__file__))
pkg_info = os.path.join(package_dir, "PKG-INFO")
in_source_package = os.path.isfile(pkg_info)
if in_source_package:
with codecs.open(pkg_info, "r", 'utf-8') as f:
with codecs.open(pkg_info, "r", "utf-8") as f:
version_line = [line.rstrip("\r") for line in f.read().split("\n") if line.startswith("Version: ")][0]
frida_version = version_line[9:]
long_description = None
else:
frida_version = os.environ.get('FRIDA_VERSION', None)
long_description = codecs.open(os.path.join(package_dir, "README.md"), "r", 'utf-8').read()
frida_extension = os.environ.get('FRIDA_EXTENSION', None)
frida_version = os.environ.get("FRIDA_VERSION", None)
long_description = codecs.open(os.path.join(package_dir, "README.md"), "r", "utf-8").read()
frida_extension = os.environ.get("FRIDA_EXTENSION", None)
index_url_pip_configs = ("global.index-url", "global.extra-index-url")
Tag = namedtuple("Tag", ["tagname", "attrs"])
ParsedUrlInfo = namedtuple("ParsedUrlInfo",
["url", "filename", "major", "minor", "micro"])
ParsedUrlInfo = namedtuple("ParsedUrlInfo", ["url", "filename", "major", "minor", "micro"])
class FridaPrebuiltExt(build_ext):
@@ -75,33 +54,36 @@ class FridaPrebuiltExt(build_ext):
if in_source_package:
system = platform.system()
arch = struct.calcsize('P') * 8
if system == 'Windows':
arch = struct.calcsize("P") * 8
if system == "Windows":
os_version = "win-amd64" if arch == 64 else "win32"
elif system == 'Darwin':
if platform.machine() == 'x86_64':
elif system == "Darwin":
if platform.machine() == "x86_64":
os_version = "macosx-10.9-x86_64"
elif python_major_version == 2:
os_version = "macosx-11.0-fat64"
else:
os_version = "macosx-11.0-arm64"
elif system == 'Linux':
os_name = 'android' if subprocess.check_output(["uname", "-o"]).decode('utf-8').rstrip() == 'Android' else 'linux'
elif system == "Linux":
os_name = (
"android"
if subprocess.check_output(["uname", "-o"]).decode("utf-8").rstrip() == "Android"
else "linux"
)
machine = platform.machine()
if machine == "" or "86" in machine:
arch_name = "x86_64" if arch == 64 else "i686"
elif os_name == 'android' and machine.startswith("armv"):
arch_name = 'armv7l'
elif os_name == "android" and machine.startswith("armv"):
arch_name = "armv7l"
else:
arch_name = machine
os_version = "{}-{}".format(os_name, arch_name)
elif system == 'FreeBSD':
os_version = f"{os_name}-{arch_name}"
elif system == "FreeBSD":
os_version = "freebsd-" + platform.machine()
else:
raise NotImplementedError("unsupported OS")
egg_path = os.path.expanduser(
"~{}frida-{}-py{}.{}-{}.egg".format(os.sep, frida_version, python_version[0], python_version[1], os_version))
f"~{os.sep}frida-{frida_version}-py{python_version[0]}.{python_version[1]}-{os_version}.egg"
)
print("looking for prebuilt extension in home directory, i.e.", egg_path)
try:
@@ -119,10 +101,7 @@ class FridaPrebuiltExt(build_ext):
index_url = normalize_url(index_url)
frida_url = urljoin(index_url, "frida/") # slash is necessary here
timeout = 20
errmsg = (
"unable to download it within {} seconds; "
"please download it manually to {}"
).format("{}", egg_path)
errmsg = "unable to download it within {} seconds; " f"please download it manually to {egg_path}"
print("downloading package list from", frida_url)
try:
@@ -133,13 +112,13 @@ class FridaPrebuiltExt(build_ext):
parser = PEP503PageParser("frida", frida_version, os_version)
parser.feed(links_html)
urls = [url for url in parser.urls if url.major == python_major_version]
if len(urls) == 0:
raise NotImplementedError("could not find prebuilt Frida extension; "
"prebuilds only provided for Python 2.7 and 3.4+")
if len(parser.urls) == 0:
raise NotImplementedError(
"could not find prebuilt Frida extension; " "prebuilds only provided for Python 3.4+"
)
url = urls[0]
url = parser.urls[0]
egg_url = urljoin(frida_url, url.url)
try:
@@ -162,12 +141,14 @@ class FridaPrebuiltExt(build_ext):
egg_zip = zipfile.ZipFile(egg_file)
extension_member = [info for info in egg_zip.infolist() if info.filename.endswith(target_extension)][0]
extension_data = egg_zip.read(extension_member)
if system == 'Windows' and python_major_version >= 3:
if system == "Windows":
trailer = b"\x00" if python_version[1] >= 10 else b"\x00\x00"
extension_data = re.sub(b"python[3-9][0-9][0-9]\\.dll\x00",
"python{0}{1}.dll".format(*python_version).encode('utf-8') + trailer,
extension_data)
with open(target, 'wb') as f:
extension_data = re.sub(
b"python[3-9][0-9][0-9]\\.dll\x00",
"python{}{}.dll".format(*python_version).encode("utf-8") + trailer,
extension_data,
)
with open(target, "wb") as f:
f.write(extension_data)
else:
shutil.copyfile(frida_extension, target)
@@ -190,15 +171,16 @@ def get_index_url():
else:
return index_url
print("using default index URL: {}".format(DEFAULT_INDEX_URL))
print(f"using default index URL: {DEFAULT_INDEX_URL}")
return DEFAULT_INDEX_URL
def get_index_url_from_pip(config_name):
assert config_name in index_url_pip_configs
return subprocess.check_output([sys.executable, "-m", "pip", "config", "get", config_name],
stderr=subprocess.PIPE).decode("utf-8")
return subprocess.check_output(
[sys.executable, "-m", "pip", "config", "get", config_name], stderr=subprocess.PIPE
).decode("utf-8")
def normalize_url(url):
@@ -206,20 +188,24 @@ def normalize_url(url):
path = parse_result.path
if not path.endswith("/"):
path += "/"
return urlunparse((
parse_result.scheme, parse_result.netloc, path,
parse_result.params, parse_result.query, parse_result.fragment,
))
return urlunparse(
(
parse_result.scheme,
parse_result.netloc,
path,
parse_result.params,
parse_result.query,
parse_result.fragment,
)
)
class PEP503PageParser(HTMLParser):
def __init__(self, name, version, os_version):
HTMLParser.__init__(self)
filename_pattern = (
r"^{}\-{}\-py(?P<major>\d+)\.(?P<minor>\d+)(\.(?P<micro>\d+))?-{}.egg$"
).format(*map(re.escape, [name, version, os_version]))
if python_major_version == 2:
filename_pattern = filename_pattern.decode("utf-8")
filename_pattern = (r"^{}\-{}\-py(?P<major>\d+)\.(?P<minor>\d+)(\.(?P<micro>\d+))?-{}.egg$").format(
*map(re.escape, [name, version, os_version])
)
self._filename_pattern = re.compile(filename_pattern)
def reset(self):
@@ -231,7 +217,7 @@ class PEP503PageParser(HTMLParser):
self._path.append(Tag(tag, dict(attrs)))
def handle_endtag(self, tag):
if tag == u"a":
if tag == "a":
while True:
if self._path.pop().tagname == tag:
break
@@ -240,21 +226,18 @@ class PEP503PageParser(HTMLParser):
self._path.pop()
def handle_data(self, data):
if not (len(self._path) > 0
and self._path[-1].tagname == u"a"
and self._path[-1].attrs.get("href")):
if not (len(self._path) > 0 and self._path[-1].tagname == "a" and self._path[-1].attrs.get("href")):
return
match = self._filename_pattern.match(data)
if match is not None:
self.urls.append(ParsedUrlInfo(
self._path[-1].attrs["href"],
data,
*map(
lambda g: int(g) if g else None,
map(match.group, ["major", "minor", "micro"])
self.urls.append(
ParsedUrlInfo(
self._path[-1].attrs["href"],
data,
*map(lambda g: int(g) if g else None, map(match.group, ["major", "minor", "micro"])),
)
))
)
def check_pep503_hash(bytes_io, url):
@@ -265,9 +248,7 @@ def check_pep503_hash(bytes_io, url):
hashname, hashvalue = fragment.split("=")
if hashname not in {"md5", "sha1", "sha224", "sha256", "sha348", "sha512"}:
raise ValueError("Unsupported hash algorithm: {}, hashvalue={}".format(
hashname, hashvalue,
))
raise ValueError(f"Unsupported hash algorithm: {hashname}, hashvalue={hashvalue}")
h = hashlib.new(hashname)
for block in iter(partial(bytes_io.read, 4096), b""): # iterate until EOF
@@ -278,10 +259,7 @@ def check_pep503_hash(bytes_io, url):
if digest == hashvalue:
return
else:
raise ValueError(
"`{}` hash checking failed! Expected: {}, but got: {}".format(
hashname, hashvalue, digest)
)
raise ValueError(f"`{hashname}` hash checking failed! Expected: {hashvalue}, but got: {digest}")
if __name__ == "__main__":
@@ -295,6 +273,7 @@ if __name__ == "__main__":
author_email="oleavr@frida.re",
url="https://frida.re",
install_requires=["setuptools"],
python_requires=">=3.7",
license="wxWindows Library Licence, Version 3.1",
keywords="frida debugger dynamic instrumentation inject javascript windows macos linux ios iphone ipad android qnx",
classifiers=[
@@ -309,23 +288,19 @@ if __name__ == "__main__":
"Operating System :: MacOS :: MacOS X",
"Operating System :: Microsoft :: Windows",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: JavaScript",
"Topic :: Software Development :: Debuggers",
"Topic :: Software Development :: Libraries :: Python Modules"
"Topic :: Software Development :: Libraries :: Python Modules",
],
packages=['frida'],
ext_modules=[Extension('_frida', [])],
cmdclass={
'build_ext': FridaPrebuiltExt
},
zip_safe=False
packages=["frida", "_frida"],
package_data={"frida": ["py.typed"], "_frida": ["py.typed"]},
ext_modules=[Extension("_frida", [])],
cmdclass={"build_ext": FridaPrebuiltExt},
zip_safe=False,
)
+7 -84
View File
@@ -46,29 +46,18 @@
#endif
#define PyUnicode_FromUTF8String(str) PyUnicode_DecodeUTF8 (str, strlen (str), "strict")
#if PY_MAJOR_VERSION >= 3
# define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name (void)
# define MOD_DEF(ob, name, doc, methods) \
#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name (void)
#define MOD_DEF(ob, name, doc, methods) \
{ \
static struct PyModuleDef moduledef = { \
PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \
ob = PyModule_Create (&moduledef); \
}
# define MOD_SUCCESS_VAL(val) val
# define MOD_ERROR_VAL NULL
# define PyRepr_FromString PyUnicode_FromString
# define PyRepr_FromFormat PyUnicode_FromFormat
# define PYFRIDA_GETARGSPEC_FUNCTION "getfullargspec"
#else
# define MOD_INIT(name) PyMODINIT_FUNC init##name (void)
# define MOD_DEF(ob, name, doc, methods) \
ob = Py_InitModule3 (name, methods, doc);
# define MOD_SUCCESS_VAL(val)
# define MOD_ERROR_VAL
# define PyRepr_FromString PyString_FromString
# define PyRepr_FromFormat PyString_FromFormat
# define PYFRIDA_GETARGSPEC_FUNCTION "getargspec"
#endif
#define MOD_SUCCESS_VAL(val) val
#define MOD_ERROR_VAL NULL
#define PyRepr_FromString PyUnicode_FromString
#define PyRepr_FromFormat PyUnicode_FromFormat
#define PYFRIDA_GETARGSPEC_FUNCTION "getfullargspec"
#if PY_VERSION_HEX >= 0x03080000
# define PYFRIDA_NO_PRINT_FUNC_OR_VECTORCALL_OFFSET 0
@@ -1938,11 +1927,7 @@ PyGObject_marshal_string (const gchar * str)
static gboolean
PyGObject_unmarshal_string (PyObject * value, const gchar ** str)
{
#if PY_MAJOR_VERSION >= 3
*str = PyUnicode_AsUTF8 (value);
#else
*str = PyString_AsString (value);
#endif
return *str != NULL;
}
@@ -3273,11 +3258,7 @@ PyDevice_input (PyDevice * self, PyObject * args)
GBytes * data;
GError * error = NULL;
#if PY_MAJOR_VERSION >= 3
if (!PyArg_ParseTuple (args, "ly#", &pid, &data_buffer, &data_size))
#else
if (!PyArg_ParseTuple (args, "ls#", &pid, &data_buffer, &data_size))
#endif
return NULL;
data = g_bytes_new (data_buffer, data_size);
@@ -3429,11 +3410,7 @@ PyDevice_inject_library_blob (PyDevice * self, PyObject * args)
GError * error = NULL;
guint id;
#if PY_MAJOR_VERSION >= 3
if (!PyArg_ParseTuple (args, "ly#ss", &pid, &blob_buffer, &blob_size, &entrypoint, &data))
#else
if (!PyArg_ParseTuple (args, "ls#ss", &pid, &blob_buffer, &blob_size, &entrypoint, &data))
#endif
return NULL;
blob = g_bytes_new (blob_buffer, blob_size);
@@ -4065,11 +4042,7 @@ PySession_create_script (PySession * self, PyObject * args, PyObject * kw)
GError * error = NULL;
FridaScript * handle;
#if PY_MAJOR_VERSION >= 3
if (!PyArg_ParseTupleAndKeywords (args, kw, "es|esy#z", keywords, "utf-8", &source, "utf-8", &name, &snapshot_data, &snapshot_size, &runtime_value))
#else
if (!PyArg_ParseTupleAndKeywords (args, kw, "es|ess#z", keywords, "utf-8", &source, "utf-8", &name, &snapshot_data, &snapshot_size, &runtime_value))
#endif
return NULL;
options = PySession_parse_script_options (name, snapshot_data, snapshot_size, runtime_value);
@@ -4109,11 +4082,7 @@ PySession_create_script_from_bytes (PySession * self, PyObject * args, PyObject
GError * error = NULL;
FridaScript * handle;
#if PY_MAJOR_VERSION >= 3
if (!PyArg_ParseTupleAndKeywords (args, kw, "y#|esy#z", keywords, &data, &size, "utf-8", &name, &snapshot_data, &snapshot_size, &runtime_value))
#else
if (!PyArg_ParseTupleAndKeywords (args, kw, "s#|ess#z", keywords, &data, &size, "utf-8", &name, &snapshot_data, &snapshot_size, &runtime_value))
#endif
return NULL;
bytes = g_bytes_new (data, size);
@@ -5453,7 +5422,6 @@ PyIOStream_write (PyIOStream * self, PyObject * args)
GError * error = NULL;
gssize bytes_written;
#if PY_MAJOR_VERSION >= 3
if (!PyArg_ParseTuple (args, "y*", &data))
return NULL;
@@ -5462,24 +5430,11 @@ PyIOStream_write (PyIOStream * self, PyObject * args)
PyErr_SetString (PyExc_TypeError, "expected a contiguous buffer");
return NULL;
}
#else
PyObject * data_obj;
if (!PyArg_ParseTuple (args, "O", &data_obj))
return NULL;
if (PyObject_GetBuffer (data_obj, &data, PyBUF_SIMPLE) != 0)
return NULL;
#endif
Py_BEGIN_ALLOW_THREADS
bytes_written = g_output_stream_write (self->output, data.buf, data.len, g_cancellable_get_current (), &error);
Py_END_ALLOW_THREADS
#if PY_MAJOR_VERSION < 3
PyBuffer_Release (&data);
#endif
if (error != NULL)
return PyFrida_raise (error);
@@ -5492,7 +5447,6 @@ PyIOStream_write_all (PyIOStream * self, PyObject * args)
Py_buffer data;
GError * error = NULL;
#if PY_MAJOR_VERSION >= 3
if (!PyArg_ParseTuple (args, "y*", &data))
return NULL;
@@ -5501,24 +5455,11 @@ PyIOStream_write_all (PyIOStream * self, PyObject * args)
PyErr_SetString (PyExc_TypeError, "expected a contiguous buffer");
return NULL;
}
#else
PyObject * data_obj;
if (!PyArg_ParseTuple (args, "O", &data_obj))
return NULL;
if (PyObject_GetBuffer (data_obj, &data, PyBUF_SIMPLE) != 0)
return NULL;
#endif
Py_BEGIN_ALLOW_THREADS
g_output_stream_write_all (self->output, data.buf, data.len, NULL, g_cancellable_get_current (), &error);
Py_END_ALLOW_THREADS
#if PY_MAJOR_VERSION < 3
PyBuffer_Release (&data);
#endif
if (error != NULL)
return PyFrida_raise (error);
@@ -5773,17 +5714,7 @@ PyFrida_raise (GError * error)
g_string_append_unichar (message, g_unichar_tolower (g_utf8_get_char (error->message)));
g_string_append (message, g_utf8_offset_to_pointer (error->message, 1));
#if PY_MAJOR_VERSION >= 3
PyErr_SetString (exception, message->str);
#else
{
PyObject * value;
value = PyUnicode_FromUTF8String (message->str);
PyErr_SetObject (exception, value);
Py_DECREF (value);
}
#endif
g_string_free (message, TRUE);
g_error_free (error);
@@ -5794,11 +5725,7 @@ PyFrida_raise (GError * error)
static gboolean
PyFrida_is_string (PyObject * obj)
{
#if PY_MAJOR_VERSION >= 3
return PyUnicode_Check (obj);
#else
return PyString_Check (obj);
#endif
}
static gchar *
@@ -5859,10 +5786,6 @@ MOD_INIT (_frida)
{
PyObject * inspect, * datetime, * module;
#if PY_VERSION_HEX < 0x03070000
PyEval_InitThreads ();
#endif
inspect = PyImport_ImportModule ("inspect");
inspect_getargspec = PyObject_GetAttrString (inspect, PYFRIDA_GETARGSPEC_FUNCTION);
inspect_ismethod = PyObject_GetAttrString (inspect, "ismethod");
-2
View File
@@ -1,4 +1,2 @@
# -*- coding: utf-8 -*-
from .test_core import TestCore
from .test_rpc import TestRpc
+5 -8
View File
@@ -1,20 +1,17 @@
# -*- coding: utf-8 -*-
import os
import platform
import sys
system = platform.system()
if system == 'Windows':
if system == "Windows":
target_program = r"C:\Windows\notepad.exe"
elif system == 'Darwin':
elif system == "Darwin":
target_program = os.path.join(os.path.dirname(__file__), "unixvictim-macos")
elif system == 'Linux' and platform.machine() == 'x86_64':
arch = 'x86_64' if sys.maxsize > 2**32 else 'x86'
elif system == "Linux" and platform.machine() == "x86_64":
arch = "x86_64" if sys.maxsize > 2**32 else "x86"
target_program = os.path.join(os.path.dirname(__file__), "unixvictim-" + system.lower() + "-" + arch)
else:
target_program = "/bin/cat"
__all__ = ['target_program']
__all__ = ["target_program"]
+14 -20
View File
@@ -1,12 +1,6 @@
# -*- coding: utf-8 -*-
import sys
import threading
import time
try:
import unittest2 as unittest
except:
import unittest
import unittest
import frida
@@ -17,39 +11,39 @@ class TestCore(unittest.TestCase):
self.assertTrue(len(devices) > 0)
def test_get_existing_device(self):
device = frida.get_device_matching(lambda d: d.id == 'local')
device = frida.get_device_matching(lambda d: d.id == "local")
self.assertEqual(device.name, "Local System")
device = frida.get_device_manager().get_device_matching(lambda d: d.id == 'local')
device = frida.get_device_manager().get_device_matching(lambda d: d.id == "local")
self.assertEqual(device.name, "Local System")
def test_get_nonexistent_device(self):
def get_nonexistent():
frida.get_device_manager().get_device_matching(lambda device: device.type == 'lol')
self.assertRaisesMatching(frida.InvalidArgumentError, "device not found", get_nonexistent)
frida.get_device_manager().get_device_matching(lambda device: device.type == "lol")
self.assertRaisesRegex(frida.InvalidArgumentError, "device not found", get_nonexistent)
def test_wait_for_nonexistent_device(self):
def wait_for_nonexistent():
frida.get_device_manager().get_device_matching(lambda device: device.type == 'lol', timeout=0.1)
self.assertRaisesMatching(frida.InvalidArgumentError, "device not found", wait_for_nonexistent)
frida.get_device_manager().get_device_matching(lambda device: device.type == "lol", timeout=0.1)
self.assertRaisesRegex(frida.InvalidArgumentError, "device not found", wait_for_nonexistent)
def test_cancel_wait_for_nonexistent_device(self):
cancellable = frida.Cancellable()
def wait_for_nonexistent():
frida.get_device_manager().get_device_matching(lambda device: device.type == 'lol', timeout=-1, cancellable=cancellable)
frida.get_device_manager().get_device_matching(
lambda device: device.type == "lol", timeout=-1, cancellable=cancellable
)
def cancel_after_100ms():
time.sleep(0.1)
cancellable.cancel()
threading.Thread(target=cancel_after_100ms).start()
self.assertRaisesMatching(frida.OperationCancelledError, "operation was cancelled", wait_for_nonexistent)
def assertRaisesMatching(self, exception, regex, operation):
m = self.assertRaisesRegex if sys.version_info[0] >= 3 else self.assertRaisesRegexp
m(exception, regex, operation)
self.assertRaisesRegex(frida.OperationCancelledError, "operation was cancelled", wait_for_nonexistent)
if __name__ == '__main__':
if __name__ == "__main__":
unittest.main()
+143 -137
View File
@@ -1,54 +1,44 @@
# coding: utf-8
import sys
try:
import unittest2 as unittest
except:
import unittest
import unittest
import setup
py_major_version = sys.version_info[0]
htmls = []
cases = []
html = (
'<!DOCTYPE html>\n<html>\n <head>\n <title>Links for frida</title>\n '
"<!DOCTYPE html>\n<html>\n <head>\n <title>Links for frida</title>\n "
' </head>\n <body>\n <h1>Links for frida</h1>\n <a href="../../pac'
'kages/5d/80/3b140c5998df9d81e40169f188a2347b6c705156a2b556ff308e2f8b7e0a'
'/frida-1.4.1-py2.6-macosx-10.9-intel.egg#sha256=eef92210084ef083b34f8972'
"kages/5d/80/3b140c5998df9d81e40169f188a2347b6c705156a2b556ff308e2f8b7e0a"
"/frida-1.4.1-py2.6-macosx-10.9-intel.egg#sha256=eef92210084ef083b34f8972"
'078550c6ef45255e444905f95495792c7f709546">frida-1.4.1-py2.6-macosx-10.9-'
'intel.egg</a><br/>\n<a href="../../packages/4e/ca/ee40ef1d5013300a77152f'
'f0687caedc3b5ea1f786bf3e0b778de5fc0b8a/frida-9.1.9.tar.gz#sha256=d215884'
"f0687caedc3b5ea1f786bf3e0b778de5fc0b8a/frida-9.1.9.tar.gz#sha256=d215884"
'4cc20cd3e2f8d2cd95f90449f0c27d051d9868706c1eda5d357eb86d7">frida-9.1.9.t'
'ar.gz</a><br/>\n </body>\n</html>\n<!--SERIAL 11362971-->'
"ar.gz</a><br/>\n </body>\n</html>\n<!--SERIAL 11362971-->"
)
htmls.append(html)
cases.extend([
(
setup.PEP503PageParser("frida", "15.1.1", "win-amd64"),
html,
[]
),
(
setup.PEP503PageParser("frida", "1.4.1", "macosx-10.9-intel"),
html,
[
setup.ParsedUrlInfo(
url='../../packages/5d/80/3b140c5998df9d81e40169f188a2347b6c705156a2b556ff308e2f8b7e0a/frida-1.4.1-py2.6-macosx-10.9-intel.egg#sha256=eef92210084ef083b34f8972078550c6ef45255e444905f95495792c7f709546',
filename='frida-1.4.1-py2.6-macosx-10.9-intel.egg',
major=2, minor=6, micro=None
)
]
),
(
setup.PEP503PageParser("frida", "1.4.1", "macosx-11.0-arm64"),
html,
[]
),
])
cases.extend(
[
(setup.PEP503PageParser("frida", "15.1.1", "win-amd64"), html, []),
(
setup.PEP503PageParser("frida", "1.4.1", "macosx-10.9-intel"),
html,
[
setup.ParsedUrlInfo(
url="../../packages/5d/80/3b140c5998df9d81e40169f188a2347b6c705156a2b556ff308e2f8b7e0a/frida-1.4.1-py2.6-macosx-10.9-intel.egg#sha256=eef92210084ef083b34f8972078550c6ef45255e444905f95495792c7f709546",
filename="frida-1.4.1-py2.6-macosx-10.9-intel.egg",
major=2,
minor=6,
micro=None,
)
],
),
(setup.PEP503PageParser("frida", "1.4.1", "macosx-11.0-arm64"), html, []),
]
)
html = (
'<a href="../../packages/e3/21/da75f6207f76750799d68938707a74d46512e666293eb550247bf5314613/frida-15.0.7-py2.7-linux-i686.egg#sha256=444246bad3b2222efec301e96c2d6ac5da039d41acd655f6d5b6e548637cae09">frida-15.0.7-py2.7-linux-i686.egg</a><br/>'
@@ -85,121 +75,137 @@ html = (
'<a href="../../packages/e4/0f/9954d94b174ba703b7018ae01c5e37189715a7b1616a2341794ccbefe834/frida-15.0.8.tar.gz#sha256=de2df2924770601ce39cdc992fa3690b4a0891d614a515cad03bc1b94e762ff1">frida-15.0.8.tar.gz</a><br/>'
)
htmls.append(html)
cases.extend([
(
setup.PEP503PageParser("frida", "15.1.0", "win-amd64"),
html,
[]
),
(
setup.PEP503PageParser("frida", "15.0.7", "win-amd64"),
html,
[
setup.ParsedUrlInfo(
url='../../packages/e0/5c/b45c8f27482d81179eb640726b703f95c624cc4f32ae3ed3f8bc858ae5d9/frida-15.0.7-py2.7-win-amd64.egg#sha256=eb696528b9c19f1895123e731b094363a87a4412d7ea4fcb54ef71841f7b3c1e',
filename='frida-15.0.7-py2.7-win-amd64.egg',
major=2, minor=7, micro=None
),
setup.ParsedUrlInfo(
url='../../packages/77/34/6ebaea697f3df72818e60c6494a716c51f7f13b3da323598c1711d21779c/frida-15.0.7-py3.8-win-amd64.egg#sha256=a9964cc6dd4e3ea71c42b1800c79571c670905dc82cd769302b066499fff7bf4',
filename='frida-15.0.7-py3.8-win-amd64.egg',
major=3, minor=8, micro=None
),
]
),
(
setup.PEP503PageParser("frida", "15.0.7", "linux-i686"),
html,
[
setup.ParsedUrlInfo(
url='../../packages/e3/21/da75f6207f76750799d68938707a74d46512e666293eb550247bf5314613/frida-15.0.7-py2.7-linux-i686.egg#sha256=444246bad3b2222efec301e96c2d6ac5da039d41acd655f6d5b6e548637cae09',
filename='frida-15.0.7-py2.7-linux-i686.egg',
major=2, minor=7, micro=None
),
setup.ParsedUrlInfo(
url='../../packages/19/d3/a4a1980005e232399575aeb2ae973d2087a94ec7dbaf6d7a481612979fc7/frida-15.0.7-py3.8-linux-i686.egg#sha256=ed922ec0258e95f39b4004066b72fb48546041d28602e55d44ca12effa80e8bf',
filename='frida-15.0.7-py3.8-linux-i686.egg',
major=3, minor=8, micro=None
),
]
),
(
setup.PEP503PageParser("frida", "15.0.8", "linux-x86_64"),
html,
[
setup.ParsedUrlInfo(
url='../../packages/64/4a/1e1735a8c2f606c953cccfb9d7086c15d19b5151ebd6e0cbcab2e817d6e2/frida-15.0.8-py2.7-linux-x86_64.egg#sha256=e5b29da8394ef5643fc42877856859d544cd2aba0a874a4a700f2ce4521d9780',
filename='frida-15.0.8-py2.7-linux-x86_64.egg',
major=2, minor=7, micro=None
),
setup.ParsedUrlInfo(
url='../../packages/0b/20/11101c2cc053bbe3695c8778ffb239e49c0bc24066257bc3246ef67770d9/frida-15.0.8-py3.8-linux-x86_64.egg#sha256=6b3f42225c22a1f149107f963abe9f7b5f32eb4915fe8fa8286e5657a7b6c789',
filename='frida-15.0.8-py3.8-linux-x86_64.egg',
major=3, minor=8, micro=None
),
]
),
(
setup.PEP503PageParser("frida", "15.0.7", "linux-amd64"),
html,
[]
),
(
setup.PEP503PageParser("frida", "15.0.8", "macosx-11.0-fat64"),
html,
[
setup.ParsedUrlInfo(
url='../../packages/d1/20/a65170d6a898541839acb03a16d1dd26499928c937350078765fe1e4beb3/frida-15.0.8-py2.7-macosx-11.0-fat64.egg#sha256=f9e58ff7f6d53640a991d3e77711b0095927103d7bdfef55268b58091938f72e',
filename='frida-15.0.8-py2.7-macosx-11.0-fat64.egg',
major=2, minor=7, micro=None
),
]
),
])
cases.extend(
[
(setup.PEP503PageParser("frida", "15.1.0", "win-amd64"), html, []),
(
setup.PEP503PageParser("frida", "15.0.7", "win-amd64"),
html,
[
setup.ParsedUrlInfo(
url="../../packages/e0/5c/b45c8f27482d81179eb640726b703f95c624cc4f32ae3ed3f8bc858ae5d9/frida-15.0.7-py2.7-win-amd64.egg#sha256=eb696528b9c19f1895123e731b094363a87a4412d7ea4fcb54ef71841f7b3c1e",
filename="frida-15.0.7-py2.7-win-amd64.egg",
major=2,
minor=7,
micro=None,
),
setup.ParsedUrlInfo(
url="../../packages/77/34/6ebaea697f3df72818e60c6494a716c51f7f13b3da323598c1711d21779c/frida-15.0.7-py3.8-win-amd64.egg#sha256=a9964cc6dd4e3ea71c42b1800c79571c670905dc82cd769302b066499fff7bf4",
filename="frida-15.0.7-py3.8-win-amd64.egg",
major=3,
minor=8,
micro=None,
),
],
),
(
setup.PEP503PageParser("frida", "15.0.7", "linux-i686"),
html,
[
setup.ParsedUrlInfo(
url="../../packages/e3/21/da75f6207f76750799d68938707a74d46512e666293eb550247bf5314613/frida-15.0.7-py2.7-linux-i686.egg#sha256=444246bad3b2222efec301e96c2d6ac5da039d41acd655f6d5b6e548637cae09",
filename="frida-15.0.7-py2.7-linux-i686.egg",
major=2,
minor=7,
micro=None,
),
setup.ParsedUrlInfo(
url="../../packages/19/d3/a4a1980005e232399575aeb2ae973d2087a94ec7dbaf6d7a481612979fc7/frida-15.0.7-py3.8-linux-i686.egg#sha256=ed922ec0258e95f39b4004066b72fb48546041d28602e55d44ca12effa80e8bf",
filename="frida-15.0.7-py3.8-linux-i686.egg",
major=3,
minor=8,
micro=None,
),
],
),
(
setup.PEP503PageParser("frida", "15.0.8", "linux-x86_64"),
html,
[
setup.ParsedUrlInfo(
url="../../packages/64/4a/1e1735a8c2f606c953cccfb9d7086c15d19b5151ebd6e0cbcab2e817d6e2/frida-15.0.8-py2.7-linux-x86_64.egg#sha256=e5b29da8394ef5643fc42877856859d544cd2aba0a874a4a700f2ce4521d9780",
filename="frida-15.0.8-py2.7-linux-x86_64.egg",
major=2,
minor=7,
micro=None,
),
setup.ParsedUrlInfo(
url="../../packages/0b/20/11101c2cc053bbe3695c8778ffb239e49c0bc24066257bc3246ef67770d9/frida-15.0.8-py3.8-linux-x86_64.egg#sha256=6b3f42225c22a1f149107f963abe9f7b5f32eb4915fe8fa8286e5657a7b6c789",
filename="frida-15.0.8-py3.8-linux-x86_64.egg",
major=3,
minor=8,
micro=None,
),
],
),
(setup.PEP503PageParser("frida", "15.0.7", "linux-amd64"), html, []),
(
setup.PEP503PageParser("frida", "15.0.8", "macosx-11.0-fat64"),
html,
[
setup.ParsedUrlInfo(
url="../../packages/d1/20/a65170d6a898541839acb03a16d1dd26499928c937350078765fe1e4beb3/frida-15.0.8-py2.7-macosx-11.0-fat64.egg#sha256=f9e58ff7f6d53640a991d3e77711b0095927103d7bdfef55268b58091938f72e",
filename="frida-15.0.8-py2.7-macosx-11.0-fat64.egg",
major=2,
minor=7,
micro=None,
),
],
),
]
)
html = (
'<h3>frida-15.1.1-py3.8-linux-x86_64.egg</h3>'
"<h3>frida-15.1.1-py3.8-linux-x86_64.egg</h3>"
'<a href="../../packages/e4/c1/82e361bbaa535b334f5b1b432b4573a7871fa973ede'
'b3aab9dbb6b3b4cdc/frida-15.1.1-py3.8-linux-x86_64.egg#sha256=505f4ffa34cc'
"b3aab9dbb6b3b4cdc/frida-15.1.1-py3.8-linux-x86_64.egg#sha256=505f4ffa34cc"
'7d68664fcd00d469f5d832e6778800d112aadb8a13692f984b40">frida-15.1.1-py3.8-'
'linux-x86_64.egg</a><br/>'
"linux-x86_64.egg</a><br/>"
)
htmls.append(html)
cases.extend([
(
setup.PEP503PageParser("frida", "15.1.1", "linux-x86_64"),
html,
[
setup.ParsedUrlInfo(
url='../../packages/e4/c1/82e361bbaa535b334f5b1b432b4573a7871fa973edeb3aab9dbb6b3b4cdc/frida-15.1.1-py3.8-linux-x86_64.egg#sha256=505f4ffa34cc7d68664fcd00d469f5d832e6778800d112aadb8a13692f984b40',
filename='frida-15.1.1-py3.8-linux-x86_64.egg',
major=3, minor=8, micro=None
),
]
),
])
cases.extend(
[
(
setup.PEP503PageParser("frida", "15.1.1", "linux-x86_64"),
html,
[
setup.ParsedUrlInfo(
url="../../packages/e4/c1/82e361bbaa535b334f5b1b432b4573a7871fa973edeb3aab9dbb6b3b4cdc/frida-15.1.1-py3.8-linux-x86_64.egg#sha256=505f4ffa34cc7d68664fcd00d469f5d832e6778800d112aadb8a13692f984b40",
filename="frida-15.1.1-py3.8-linux-x86_64.egg",
major=3,
minor=8,
micro=None,
),
],
),
]
)
html = (
'<a role="button" tabindex="0">frida-15.0.1-py3.8-android-aarch64.egg</a>'
'<a href="../../packages/3e/80/78fa3ed5fd636b606dc06157069b37eb677652cd985'
'739cde35a86d7a362/frida-15.0.1-py3.8-android-aarch64.egg#sha256=d44bc3415'
"739cde35a86d7a362/frida-15.0.1-py3.8-android-aarch64.egg#sha256=d44bc3415"
'90dd8cf2623089b54aa16d697536fb016fde0ecd6df5262723c652b">frida-15.0.1-py3'
'.8-android-aarch64.egg</a><br/>'
".8-android-aarch64.egg</a><br/>"
)
htmls.append(html)
cases.extend([
(
setup.PEP503PageParser("frida", "15.0.1", "android-aarch64"),
html,
[
setup.ParsedUrlInfo(
url='../../packages/3e/80/78fa3ed5fd636b606dc06157069b37eb677652cd985739cde35a86d7a362/frida-15.0.1-py3.8-android-aarch64.egg#sha256=d44bc341590dd8cf2623089b54aa16d697536fb016fde0ecd6df5262723c652b',
filename='frida-15.0.1-py3.8-android-aarch64.egg',
major=3, minor=8, micro=None
),
]
),
])
cases.extend(
[
(
setup.PEP503PageParser("frida", "15.0.1", "android-aarch64"),
html,
[
setup.ParsedUrlInfo(
url="../../packages/3e/80/78fa3ed5fd636b606dc06157069b37eb677652cd985739cde35a86d7a362/frida-15.0.1-py3.8-android-aarch64.egg#sha256=d44bc341590dd8cf2623089b54aa16d697536fb016fde0ecd6df5262723c652b",
filename="frida-15.0.1-py3.8-android-aarch64.egg",
major=3,
minor=8,
micro=None,
),
],
),
]
)
class TestPEP503PageParser(unittest.TestCase):
+34 -33
View File
@@ -1,14 +1,8 @@
# -*- coding: utf-8 -*-
import platform
import subprocess
import sys
import threading
import time
try:
import unittest2 as unittest
except:
import unittest
import unittest
import frida
@@ -16,6 +10,9 @@ from .data import target_program
class TestRpc(unittest.TestCase):
target: subprocess.Popen
session: frida.core.Session
@classmethod
def setUp(cls):
system = platform.system()
@@ -32,7 +29,9 @@ class TestRpc(unittest.TestCase):
cls.target.wait()
def test_basics(self):
script = self.session.create_script(name="test-rpc", source="""\
script = self.session.create_script(
name="test-rpc",
source="""\
rpc.exports = {
add: function (a, b) {
var result = a + b;
@@ -48,21 +47,24 @@ rpc.exports = {
return Memory.readByteArray(buf, 2);
}
};
""")
""",
)
script.load()
self.assertEqual(script.exports.add(2, 3), 5)
self.assertEqual(script.exports.sub(5, 3), 2)
self.assertRaises(Exception, lambda: script.exports.add(1, -2))
self.assertListEqual([x for x in iterbytes(script.exports.speak())],
[0x59, 0x6f])
self.assertListEqual([x for x in iter(script.exports.speak())], [0x59, 0x6F])
def test_post_failure(self):
script = self.session.create_script(name="test-rpc", source="""\
script = self.session.create_script(
name="test-rpc",
source="""\
rpc.exports = {
init: function () {
},
};
""")
""",
)
script.load()
agent = script.exports
@@ -71,13 +73,16 @@ rpc.exports = {
self.assertEqual(script._pending, {})
def test_unload_mid_request(self):
script = self.session.create_script(name="test-rpc", source="""\
script = self.session.create_script(
name="test-rpc",
source="""\
rpc.exports = {
waitForever: function () {
return new Promise(function () {});
},
};
""")
""",
)
script.load()
agent = script.exports
@@ -90,13 +95,16 @@ rpc.exports = {
self.assertEqual(script._pending, {})
def test_detach_mid_request(self):
script = self.session.create_script(name="test-rpc", source="""\
script = self.session.create_script(
name="test-rpc",
source="""\
rpc.exports = {
waitForever: function () {
return new Promise(function () {});
},
};
""")
""",
)
script.load()
agent = script.exports
@@ -109,13 +117,16 @@ rpc.exports = {
self.assertEqual(script._pending, {})
def test_cancellation_mid_request(self):
script = self.session.create_script(name="test-rpc", source="""\
script = self.session.create_script(
name="test-rpc",
source="""\
rpc.exports = {
waitForever: function () {
return new Promise(function () {});
},
};
""")
""",
)
script.load()
agent = script.exports
@@ -131,28 +142,18 @@ rpc.exports = {
def call_wait_forever_with_cancellable():
with cancellable:
agent.wait_forever()
cancellable = frida.Cancellable()
threading.Thread(target=cancel_after_100ms).start()
self.assertRaisesOperationCancelled(call_wait_forever_with_cancellable)
self.assertEqual(script._pending, {})
def assertRaisesScriptDestroyed(self, operation):
self.assertRaisesMatching(frida.InvalidOperationError, "script has been destroyed", operation)
self.assertRaisesRegex(frida.InvalidOperationError, "script has been destroyed", operation)
def assertRaisesOperationCancelled(self, operation):
self.assertRaisesMatching(frida.OperationCancelledError, "operation was cancelled", operation)
def assertRaisesMatching(self, exception, regex, operation):
m = self.assertRaisesRegex if sys.version_info[0] >= 3 else self.assertRaisesRegexp
m(exception, regex, operation)
self.assertRaisesRegex(frida.OperationCancelledError, "operation was cancelled", operation)
if sys.version_info[0] >= 3:
iterbytes = lambda x: iter(x)
else:
def iterbytes(data):
return (ord(char) for char in data)
if __name__ == '__main__':
if __name__ == "__main__":
unittest.main()